diff --git a/.circleci/config.yml b/.circleci/config.yml index 3aa0f18..ef8c1d3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,22 +9,22 @@ jobs: CIRCLE_TEST_REPORTS: /tmp/circleci-test-results UV_THREADPOOL_SIZE: 16 docker: - - image: streampunkmedia/testbeam:10-4.1 + - image: streampunkmedia/testbeam:16-4.4 steps: - checkout - run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS - restore_cache: keys: # This branch if available - - v1-dep-{{ .Branch }}- + - v2-dep-{{ .Branch }}- # Default branch if not - - v1-dep-master- + - v2-dep-master- # Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly - - v1-dep- + - v2-dep- - run: npm install tap-xunit - run: npm install --unsafe-perm - save_cache: - key: v1-dep-{{ .Branch }}-{{ epoch }} + key: v2-dep-{{ .Branch }}-{{ epoch }} paths: - ./node_modules - run: echo 'export PATH="~/Streampunk/beamcoder/node_modules/.bin:$PATH"' >> $BASH_ENV diff --git a/.circleci/test_image/Dockerfile b/.circleci/test_image/Dockerfile new file mode 100644 index 0000000..a693f50 --- /dev/null +++ b/.circleci/test_image/Dockerfile @@ -0,0 +1,13 @@ +FROM cimg/node:16.13 + +# install FFmpeg +RUN sudo apt-get update \ + && sudo apt-get install software-properties-common \ + && sudo add-apt-repository ppa:savoury1/ffmpeg4 \ + && sudo apt-get update \ + && sudo apt-get upgrade && sudo apt-get dist-upgrade \ + && sudo apt-get install libavcodec-dev libavformat-dev libavdevice-dev libavfilter-dev libavutil-dev libpostproc-dev libswresample-dev libswscale-dev + +# delete all the apt list files since they're big and get stale quickly +RUN sudo rm -rf /var/lib/apt/lists/* +# this forces "apt-get update" in dependent images, which is also good diff --git a/.circleci/test_image/build.md b/.circleci/test_image/build.md new file mode 100644 index 0000000..0add2ab --- /dev/null +++ b/.circleci/test_image/build.md @@ -0,0 +1,13 @@ +# Instructions for building the CircleCI docker image for testing + +- install docker desktop +- cd to this directory +- docker build -t streampunkmedia/testbeam:x-y.z . +- run container locally to check build +- push to Docker Hub +- update config.yml to pull new version tag +- push to git to trigger new build and test + +(x: NodeAPI base version, y.z: FFmpeg build number) + +See https://circleci.com/developer/images/image/cimg/node for CircleCI docker image tags diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 5d6fbd8..08f9378 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -9,8 +9,8 @@ "C:/Program Files (x86)/Windows Kits/8.1/Include/shared", "C:/Program Files (x86)/Windows Kits/8.1/Include/winrt", "${workspaceFolder}/**", - "${workspaceFolder}/ffmpeg/ffmpeg-4.3.1-win64-shared/include/**", - "${env:USERPROFILE}/AppData/Local/node-gyp/Cache/12.18.2/include/node" + "${workspaceFolder}/ffmpeg/ffmpeg-4.x-win64-shared/include/**", + "${env:USERPROFILE}/AppData/Local/node-gyp/Cache/12.21.0/include/node" ], "defines": [ "_DEBUG", diff --git a/.vscode/settings.json b/.vscode/settings.json index e05a089..91f65f1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -53,5 +53,6 @@ "xtr1common": "cpp", "xtree": "cpp", "xutility": "cpp" - } + }, + "cmake.configureOnOpen": false } \ No newline at end of file diff --git a/README.md b/README.md index f1eb0bb..f898b09 100644 --- a/README.md +++ b/README.md @@ -92,8 +92,7 @@ This will install all necessary dependencies, download the FFmpeg shared and dev Note that if you want to use a local version of FFmpeg then, before the install, symbolic link or copy appropriate folders to: - ./node_modules/beamcoder/ffmpeg/ffmpeg-4.1-win64-shared - ./node_modules/beamcoder/ffmpeg/ffmpeg-4.1-win64-dev + ./node_modules/beamcoder/ffmpeg/ffmpeg-4.x-win64-shared To ensure that sufficient threads are available to process several requests in parallel, set the `UV_THREADPOOL_SIZE` environment variable, e.g.: diff --git a/binding.gyp b/binding.gyp index b342fc5..ff1e5f8 100644 --- a/binding.gyp +++ b/binding.gyp @@ -45,30 +45,30 @@ } }, "include_dirs" : [ - "ffmpeg/ffmpeg-4.3-win64-shared/include" + "ffmpeg/ffmpeg-4.x-win64-shared/include" ], "libraries": [ - "-l../ffmpeg/ffmpeg-4.3-win64-shared/lib/avcodec", - "-l../ffmpeg/ffmpeg-4.3-win64-shared/lib/avdevice", - "-l../ffmpeg/ffmpeg-4.3-win64-shared/lib/avfilter", - "-l../ffmpeg/ffmpeg-4.3-win64-shared/lib/avformat", - "-l../ffmpeg/ffmpeg-4.3-win64-shared/lib/avutil", - "-l../ffmpeg/ffmpeg-4.3-win64-shared/lib/postproc", - "-l../ffmpeg/ffmpeg-4.3-win64-shared/lib/swresample", - "-l../ffmpeg/ffmpeg-4.3-win64-shared/lib/swscale" + "-l../ffmpeg/ffmpeg-4.x-win64-shared/lib/avcodec", + "-l../ffmpeg/ffmpeg-4.x-win64-shared/lib/avdevice", + "-l../ffmpeg/ffmpeg-4.x-win64-shared/lib/avfilter", + "-l../ffmpeg/ffmpeg-4.x-win64-shared/lib/avformat", + "-l../ffmpeg/ffmpeg-4.x-win64-shared/lib/avutil", + "-l../ffmpeg/ffmpeg-4.x-win64-shared/lib/postproc", + "-l../ffmpeg/ffmpeg-4.x-win64-shared/lib/swresample", + "-l../ffmpeg/ffmpeg-4.x-win64-shared/lib/swscale" ], "copies": [ { "destination": "build/Release/", "files": [ - "ffmpeg/ffmpeg-4.3-win64-shared/bin/avcodec-58.dll", - "ffmpeg/ffmpeg-4.3-win64-shared/bin/avdevice-58.dll", - "ffmpeg/ffmpeg-4.3-win64-shared/bin/avfilter-7.dll", - "ffmpeg/ffmpeg-4.3-win64-shared/bin/avformat-58.dll", - "ffmpeg/ffmpeg-4.3-win64-shared/bin/avutil-56.dll", - "ffmpeg/ffmpeg-4.3-win64-shared/bin/postproc-55.dll", - "ffmpeg/ffmpeg-4.3-win64-shared/bin/swresample-3.dll", - "ffmpeg/ffmpeg-4.3-win64-shared/bin/swscale-5.dll" + "ffmpeg/ffmpeg-4.x-win64-shared/bin/avcodec-58.dll", + "ffmpeg/ffmpeg-4.x-win64-shared/bin/avdevice-58.dll", + "ffmpeg/ffmpeg-4.x-win64-shared/bin/avfilter-7.dll", + "ffmpeg/ffmpeg-4.x-win64-shared/bin/avformat-58.dll", + "ffmpeg/ffmpeg-4.x-win64-shared/bin/avutil-56.dll", + "ffmpeg/ffmpeg-4.x-win64-shared/bin/postproc-55.dll", + "ffmpeg/ffmpeg-4.x-win64-shared/bin/swresample-3.dll", + "ffmpeg/ffmpeg-4.x-win64-shared/bin/swscale-5.dll" ] } ] diff --git a/install_ffmpeg.js b/install_ffmpeg.js index 5ef062d..ef51271 100644 --- a/install_ffmpeg.js +++ b/install_ffmpeg.js @@ -100,14 +100,16 @@ async function win32() { else throw e; }); - const ffmpegFilename = 'ffmpeg-4.3-win64-shared'; + const ffmpegFilename = 'ffmpeg-4.x-win64-shared'; await access(`ffmpeg/${ffmpegFilename}`, fs.constants.R_OK).catch(async () => { const html = await getHTML('https://github.com/BtbN/FFmpeg-Builds/wiki/Latest', 'latest autobuilds'); const htmlStr = html.toString('utf-8'); const autoPos = htmlStr.indexOf('

', autoPos); const autoStr = htmlStr.substring(autoPos, endPos); - const sharedEndPos = autoStr.lastIndexOf('">win64-gpl-shared-4.3'); + const sharedEndPos = autoStr.lastIndexOf('">win64-gpl-shared-4.'); + if (sharedEndPos === -1) + throw new Error('Failed to find latest v4.x autobuild from "https://github.com/BtbN/FFmpeg-Builds/wiki/Latest"'); const startStr = '

s.discard = (0 == s.index) ? 'default' : 'all'); - let decoder = beamcoder.decoder({ name: 'h264' }); - //console.log(JSON.stringify(decoder, null, 2)); + // let decoder = beamcoder.decoder({ name: 'h264', thread_count: 4, thread_type: { FRAME: false, SLICE: true } }); + let decoder = beamcoder.decoder({ name: 'h264', thread_count: 1, hwaccel: true }); + // console.dir(decoder, { getters: true, depth: 3 }); let packet = {}; - for ( let x = 0 ; x < 200 && packet != null; x++ ) { - let packet = await demuxer.read(); - if (packet.stream_index === 0) { + for ( let x = 0 ; x < 2000 && packet != null; x++ ) { + packet = await demuxer.read(); + if (packet && packet.stream_index === 0) { //console.log(JSON.stringify(packet, null, 2)); let frames = await decoder.decode(packet); - console.log(JSON.stringify(frames.frames[0], null, 2)); + // console.log(JSON.stringify(frames.frames[0], null, 2)); + if (frames.frames[0]) { + console.log(frames.frames[0].data); + } console.log(x, frames.total_time); } } let frames = await decoder.flush(); console.log('flush', frames.total_time, frames.length); - console.log(await demuxer.seek({ pos: 79389000 })); - console.log(await demuxer.read()); } run(); diff --git a/src/codec.cc b/src/codec.cc index 176a61b..95df3e0 100644 --- a/src/codec.cc +++ b/src/codec.cc @@ -4871,12 +4871,6 @@ napi_value getCodecCtxDebug(napi_env env, napi_callback_info info) { CHECK_STATUS; status = beam_set_bool(env, result, "BUGS", codec->debug & FF_DEBUG_BUGS); CHECK_STATUS; - #if FF_API_DEBUG_MV - status = beam_set_bool(env, result, "VIS_QP", codec->debug & FF_DEBUG_VIS_QP); - CHECK_STATUS; - status = beam_set_bool(env, result, "VIS_MB_TYPE", codec->debug & FF_DEBUG_VIS_MB_TYPE); - CHECK_STATUS; - #endif status = beam_set_bool(env, result, "BUFFERS", codec->debug & FF_DEBUG_BUFFERS); CHECK_STATUS; status = beam_set_bool(env, result, "THREADS", codec->debug & FF_DEBUG_THREADS); @@ -4963,18 +4957,6 @@ napi_value setCodecCtxDebug(napi_env env, napi_callback_info info) { if (present) { codec->debug = (flag) ? codec->debug | FF_DEBUG_BUGS : codec->debug & ~FF_DEBUG_BUGS; } -#if FF_API_DEBUG_MV - status = beam_get_bool(env, args[0], "VIS_QP", &present, &flag); - CHECK_STATUS; - if (present) { codec->debug = (flag) ? - codec->debug | FF_DEBUG_VIS_QP : - codec->debug & ~FF_DEBUG_VIS_QP; } - status = beam_get_bool(env, args[0], "VIS_MB_TYPE", &present, &flag); - CHECK_STATUS; - if (present) { codec->debug = (flag) ? - codec->debug | FF_DEBUG_VIS_MB_TYPE : - codec->debug & ~FF_DEBUG_VIS_MB_TYPE; } -#endif status = beam_get_bool(env, args[0], "BUFFERS", &present, &flag); CHECK_STATUS; if (present) { codec->debug = (flag) ? @@ -5409,9 +5391,9 @@ napi_value getCodecCtxThreadType(napi_env env, napi_callback_info info) { status = napi_create_object(env, &result); CHECK_STATUS; - status = beam_set_bool(env, result, "FRAME", codec->thread_type & FF_THREAD_FRAME); + status = beam_set_bool(env, result, "FRAME", (codec->thread_type & FF_THREAD_FRAME) == FF_THREAD_FRAME); CHECK_STATUS; - status = beam_set_bool(env, result, "SLICE", codec->thread_type & FF_THREAD_SLICE); + status = beam_set_bool(env, result, "SLICE", (codec->thread_type & FF_THREAD_SLICE) == FF_THREAD_SLICE); CHECK_STATUS; return result; @@ -5463,55 +5445,14 @@ napi_value getCodecCtxActThreadType(napi_env env, napi_callback_info info) { status = napi_create_object(env, &result); CHECK_STATUS; - status = beam_set_bool(env, result, "FRAME", codec->active_thread_type & FF_THREAD_FRAME); - CHECK_STATUS; - status = beam_set_bool(env, result, "SLICE", codec->active_thread_type & FF_THREAD_SLICE); - CHECK_STATUS; - - return result; -} - -napi_value getCodecCtxThreadSafeCBs(napi_env env, napi_callback_info info) { - napi_status status; - napi_value result; - AVCodecContext* codec; - - size_t argc = 0; - status = napi_get_cb_info(env, info, &argc, nullptr, nullptr, (void**) &codec); + status = beam_set_bool(env, result, "FRAME", (codec->active_thread_type & FF_THREAD_FRAME) == FF_THREAD_FRAME); CHECK_STATUS; - status = napi_create_int32(env, codec->thread_safe_callbacks, &result); + status = beam_set_bool(env, result, "SLICE", (codec->active_thread_type & FF_THREAD_SLICE) == FF_THREAD_SLICE); CHECK_STATUS; return result; } -napi_value setCodecCtxThreadSafeCBs(napi_env env, napi_callback_info info) { - napi_status status; - napi_value result; - napi_valuetype type; - AVCodecContext* codec; - - size_t argc = 1; - napi_value args[1]; - status = napi_get_cb_info(env, info, &argc, args, nullptr, (void**) &codec); - CHECK_STATUS; - if (argc < 1) { - NAPI_THROW_ERROR("A value is required to set the thread_safe_callbacks property."); - } - status = napi_typeof(env, args[0], &type); - CHECK_STATUS; - if (type != napi_number) { - NAPI_THROW_ERROR("A number is required to set the thread_safe_callbacks property."); - } - - status = napi_get_value_int32(env, args[0], &codec->thread_safe_callbacks); - CHECK_STATUS; - - status = napi_get_undefined(env, &result); - CHECK_STATUS; - return result; -} - napi_value getCodecCtxNsseWeight(napi_env env, napi_callback_info info) { napi_status status; napi_value result; @@ -6004,15 +5945,22 @@ napi_value getCodecCtxSwPixFmt(napi_env env, napi_callback_info info) { napi_status status; napi_value result; AVCodecContext* codec; + AVBufferRef* hwFramesContextRef; + AVHWFramesContext* hwFramesContext = nullptr; + AVPixelFormat sw_pix_fmt; const char* pixFmtName; size_t argc = 0; status = napi_get_cb_info(env, info, &argc, nullptr, nullptr, (void**) &codec); CHECK_STATUS; - pixFmtName = av_get_pix_fmt_name(codec->sw_pix_fmt); + hwFramesContextRef = codec->hw_frames_ctx; + if (hwFramesContextRef) + hwFramesContext = (AVHWFramesContext*)hwFramesContextRef->data; + sw_pix_fmt = hwFramesContext ? hwFramesContext->sw_format : codec->sw_pix_fmt; + pixFmtName = av_get_pix_fmt_name(sw_pix_fmt); if (pixFmtName != nullptr) { - status = napi_create_string_utf8(env, (char*) pixFmtName, NAPI_AUTO_LENGTH, &result); + status = napi_create_string_utf8(env, pixFmtName, NAPI_AUTO_LENGTH, &result); CHECK_STATUS; } else { status = napi_get_null(env, &result); @@ -7221,8 +7169,6 @@ napi_status fromAVCodecContext(napi_env env, AVCodecContext* codec, { "active_thread_type", nullptr, nullptr, getCodecCtxActThreadType, failBoth, nullptr, napi_enumerable, codec}, // TODO find a way of exposing a custom getBuffer? - { "thread_safe_callbacks", nullptr, nullptr, getCodecCtxThreadSafeCBs, setCodecCtxThreadSafeCBs, nullptr, - (napi_property_attributes) (napi_writable | napi_enumerable), codec}, { "nsse_weight", nullptr, nullptr, encoding ? getCodecCtxNsseWeight : nullptr, encoding ? setCodecCtxNsseWeight : failDecoding, nullptr, @@ -7230,10 +7176,10 @@ napi_status fromAVCodecContext(napi_env env, AVCodecContext* codec, { "profile", nullptr, nullptr, getCodecCtxProfile, encoding ? setCodecCtxProfile : failDecoding, nullptr, encoding ? (napi_property_attributes) (napi_writable | napi_enumerable) : napi_enumerable, codec}, - // 110 { "level", nullptr, nullptr, getCodecCtxLevel, encoding ? setCodecCtxLevel : failDecoding, nullptr, encoding ? (napi_property_attributes) (napi_writable | napi_enumerable) : napi_enumerable, codec}, + // 110 { "skip_loop_filter", nullptr, nullptr, encoding ? nullptr : getCodecCtxSkipLpFilter, encoding ? failEncoding : setCodecCtxSkipLpFilter, nullptr, @@ -7267,11 +7213,11 @@ napi_status fromAVCodecContext(napi_env env, AVCodecContext* codec, encoding ? napi_default : napi_enumerable, codec}, // TODO not exposing lowres ... it's on its way out // not exposing PTS correct stats - "not intended to be used by user apps" - // 120 { "sub_charenc", nullptr, nullptr, encoding ? nullptr : getCodecCtxSubCharenc, encoding ? failEncoding : setCodecCtxSubCharenc, nullptr, encoding ? napi_default : (napi_property_attributes) (napi_writable | napi_enumerable), codec}, + // 120 { "sub_charenc_mode", nullptr, nullptr, encoding ? nullptr : getCodecCtxSubCharencMode, failBoth, nullptr, encoding ? napi_default : napi_enumerable, codec}, @@ -7302,11 +7248,11 @@ napi_status fromAVCodecContext(napi_env env, AVCodecContext* codec, getCodecHWFramesCtx, encoding ? setCodecHWFramesCtx : failDecoding, nullptr, encoding ? (napi_property_attributes) (napi_writable | napi_enumerable) : napi_enumerable, codec}, - // 130 { "sub_text_format", nullptr, nullptr, encoding ? nullptr : getCodecCtxSubTextFmt, encoding ? failEncoding : setCodecCtxSubTextFmt, nullptr, encoding ? napi_default : (napi_property_attributes) (napi_writable | napi_enumerable), codec}, + // 130 { "trailing_padding", nullptr, nullptr, getCodecCtxTrailPad, setCodecCtxTrailPad, nullptr, (napi_property_attributes) (napi_writable | napi_enumerable), codec}, { "max_pixels", nullptr, nullptr, getCodecCtxMaxPixels, setCodecCtxMaxPixels, nullptr, @@ -7330,15 +7276,15 @@ napi_status fromAVCodecContext(napi_env env, AVCodecContext* codec, encoding ? flushEnc : flushDec, nullptr, nullptr, nullptr, napi_enumerable, codec}, { "extractParams", nullptr, extractParams, nullptr, nullptr, nullptr, napi_enumerable, nullptr}, { "useParams", nullptr, useParams, nullptr, nullptr, nullptr, napi_enumerable, nullptr}, - // 140 // Hidden values - to allow Object.assign to work { "params", nullptr, nullptr, nullptr, nop, undef, // Set for muxing napi_writable, nullptr}, + // 140 { "stream_index", nullptr, nullptr, nullptr, nop, undef, napi_writable, nullptr }, { "demuxer", nullptr, nullptr, nullptr, nop, undef, napi_writable, nullptr}, { "_CodecContext", nullptr, nullptr, nullptr, nullptr, extCodec, napi_default, nullptr } }; - status = napi_define_properties(env, jsCodec, 144, desc); + status = napi_define_properties(env, jsCodec, 143, desc); PASS_STATUS; *result = jsCodec; diff --git a/src/decode.cc b/src/decode.cc index 9ff6d14..b196ddb 100644 --- a/src/decode.cc +++ b/src/decode.cc @@ -21,11 +21,47 @@ #include "decode.h" +AVPixelFormat get_format(AVCodecContext *s, const AVPixelFormat *pix_fmts) +{ + const AVPixelFormat *p; + int i, err; + + for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p); + const AVCodecHWConfig *config = NULL; + + if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) + break; + + for (i = 0;; i++) { + config = avcodec_get_hw_config(s->codec, i); + if (!config) + break; + if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) + continue; + if (config->pix_fmt == *p) + break; + } + + if (config) { + err = av_hwdevice_ctx_create(&s->hw_device_ctx, config->device_type, NULL, NULL, 0); + if (err < 0) { + char errstr[128]; + av_make_error_string(errstr, 128, err); + printf("Error in get_format av_hwdevice_ctx_create: %s\n", errstr); + } + break; + } + } + + return *p; +} + napi_value decoder(napi_env env, napi_callback_info info) { napi_status status; napi_value result, value, formatJS, formatExt, global, jsObject, assign, jsParams; napi_valuetype type; - bool isArray, hasName, hasID, hasFormat, hasStream, hasParams; + bool isArray, hasName, hasID, hasFormat, hasStream, hasParams, hasHWaccel; AVCodecContext* decoder = nullptr; AVFormatContext* format = nullptr; const AVCodec* codec = nullptr; @@ -35,6 +71,7 @@ napi_value decoder(napi_env env, napi_callback_info info) { char* codecName = nullptr; size_t codecNameLen = 0; int32_t codecID = -1; + bool hwaccel = false; size_t argc = 1; napi_value args[1]; @@ -81,6 +118,7 @@ napi_value decoder(napi_env env, napi_callback_info info) { NAPI_THROW_ERROR("Stream index is out of bounds for the given format."); } params = format->streams[streamIdx]->codecpar; + codecID = params->codec_id; codecName = (char*) avcodec_get_name(params->codec_id); codecNameLen = strlen(codecName); goto create; @@ -98,6 +136,7 @@ napi_value decoder(napi_env env, napi_callback_info info) { } status = napi_get_value_external(env, jsParams, (void**) ¶ms); CHECK_STATUS; + codecID = params->codec_id; codecName = (char*) avcodec_get_name(params->codec_id); codecNameLen = strlen(codecName); goto create; @@ -146,6 +185,17 @@ napi_value decoder(napi_env env, napi_callback_info info) { } } + status = napi_has_named_property(env, args[0], "hwaccel", &hasHWaccel); + CHECK_STATUS; + if (hasHWaccel) { + status = napi_get_named_property(env, args[0], "hwaccel", &value); + CHECK_STATUS; + status = napi_get_value_bool(env, value, &hwaccel); + CHECK_STATUS; + if (hwaccel) + decoder->get_format = get_format; + } + status = fromAVCodecContext(env, decoder, &result, false); const napi_value fargs[2] = { result, args[0] }; CHECK_BAIL; @@ -180,6 +230,7 @@ void decodeExecute(napi_env env, void* data) { decodeCarrier* c = (decodeCarrier*) data; int ret = 0; AVFrame* frame = nullptr; + AVFrame *sw_frame = nullptr; HR_TIME_POINT decodeStart = NOW; for ( auto it = c->packets.cbegin() ; it != c->packets.cend() ; it++ ) { @@ -217,15 +268,30 @@ void decodeExecute(napi_env env, void* data) { } } // loop through input packets + AVPixelFormat frame_hw_pix_fmt = AV_PIX_FMT_NONE; + if (c->decoder->hw_frames_ctx) + frame_hw_pix_fmt = ((AVHWFramesContext*)c->decoder->hw_frames_ctx->data)->format; + + frame = av_frame_alloc(); + sw_frame = av_frame_alloc(); do { - frame = av_frame_alloc(); ret = avcodec_receive_frame(c->decoder, frame); if (ret == 0) { - c->frames.push_back(frame); + if (frame->format == frame_hw_pix_fmt) { + if ((ret = av_hwframe_transfer_data(sw_frame, frame, 0)) < 0) { + printf("Error transferring hw data to system memory\n"); + } + c->frames.push_back(sw_frame); + av_frame_free(&frame); + } else + c->frames.push_back(frame); + frame = av_frame_alloc(); + sw_frame = av_frame_alloc(); } } while (ret == 0); av_frame_free(&frame); + av_frame_free(&sw_frame); c->totalTime = microTime(decodeStart); }; diff --git a/src/filter.cc b/src/filter.cc index c9bc01b..c055eee 100644 --- a/src/filter.cc +++ b/src/filter.cc @@ -992,13 +992,7 @@ void filtererExecute(napi_env env, void* data) { } p = c->outParams[i].find("channel_layouts"); if (p != c->outParams[i].end()) { - const int64_t out_channel_layout = av_get_channel_layout(p->second.c_str()); - int out_channel_counts[] = { av_get_channel_layout_nb_channels(out_channel_layout), -1 }; - ret = av_opt_set_int_list(sinkCtx, "channel_counts", out_channel_counts, -1, - AV_OPT_SEARCH_CHILDREN); - if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot set output channel count\n"); } - - const int64_t out_channel_layouts[] = { out_channel_layout, -1 }; + const int64_t out_channel_layouts[] = { (int64_t)av_get_channel_layout(p->second.c_str()), -1 }; ret = av_opt_set_int_list(sinkCtx, "channel_layouts", out_channel_layouts, -1, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n"); } @@ -1453,8 +1447,8 @@ napi_value filterer(napi_env env, napi_callback_info info) { struct filterCarrier : carrier { filtContexts *srcCtxs = nullptr; filtContexts *sinkCtxs = nullptr; - std::map > srcFrames; - std::map > dstFrames; + std::unordered_map > srcFrames; + std::unordered_map > dstFrames; std::vector frameRefs; ~filterCarrier() {} }; diff --git a/src/frame.cc b/src/frame.cc index da40cd2..baf9d4b 100644 --- a/src/frame.cc +++ b/src/frame.cc @@ -1051,21 +1051,46 @@ napi_value getFrameData(napi_env env, napi_callback_info info) { napi_status status; napi_value array, element; frameData* f; - AVBufferRef* hintRef; + uint8_t* data; + AVBufferRef* ref; + size_t size; + int curElem; status = napi_get_cb_info(env, info, 0, nullptr, nullptr, (void**) &f); CHECK_STATUS; status = napi_create_array(env, &array); CHECK_STATUS; - for ( int x = 0 ; x < AV_NUM_DATA_POINTERS ; x++ ) { - // printf("Buffer %i is %p\n", x, f->frame->buf[x]); - if (f->frame->buf[x] == nullptr) continue; - hintRef = av_buffer_ref(f->frame->buf[x]); - status = napi_create_external_buffer(env, hintRef->size, hintRef->data, - frameBufferFinalizer, hintRef, &element); + + data = f->frame->data[0]; + ref = f->frame->buf[0] ? av_buffer_ref(f->frame->buf[0]) : nullptr; + size = ref ? ref->size : 0; + curElem = 0; + // work through frame bufs checking whether allocation refcounts are shared + for ( int x = 1 ; x < AV_NUM_DATA_POINTERS ; x++ ) { + // printf("Buffer %i is %p\n", x, f->frame->data[x]); + if (f->frame->data[x] == nullptr) continue; + size_t bufSize = size; + if (f->frame->buf[x] == nullptr) + bufSize = f->frame->data[x] - f->frame->data[x-1]; + status = napi_create_external_buffer(env, bufSize, data, frameBufferFinalizer, ref, &element); CHECK_STATUS; - status = napi_set_element(env, array, x, element); + status = napi_set_element(env, array, curElem, element); + CHECK_STATUS; + data = f->frame->data[x]; + if (f->frame->buf[x]) { + ref = av_buffer_ref(f->frame->buf[x]); + size = ref->size; + } else { + ref = nullptr; + size -= f->frame->data[x] - f->frame->data[x-1]; + } + curElem++; + } + if (data) { + status = napi_create_external_buffer(env, size, data, frameBufferFinalizer, ref, &element); + CHECK_STATUS; + status = napi_set_element(env, array, curElem, element); CHECK_STATUS; } diff --git a/src/hwcontext.cc b/src/hwcontext.cc index 850dada..c739dd6 100644 --- a/src/hwcontext.cc +++ b/src/hwcontext.cc @@ -208,7 +208,7 @@ napi_value getHWFramesCtxSwPixFmt(napi_env env, napi_callback_info info) { status = napi_get_cb_info(env, info, 0, nullptr, nullptr, (void**) &frames_context); CHECK_STATUS; - AVPixelFormat pixFmt = frames_context->format; + AVPixelFormat pixFmt = frames_context->sw_format; status = napi_create_string_utf8(env, av_get_pix_fmt_name(pixFmt), NAPI_AUTO_LENGTH, &result); CHECK_STATUS; diff --git a/types/Muxer.d.ts b/types/Muxer.d.ts index 81cec39..fed21bf 100644 --- a/types/Muxer.d.ts +++ b/types/Muxer.d.ts @@ -1,6 +1,5 @@ import { Packet } from "./Packet" import { Frame } from "./Frame" -import { Stream } from "./Stream" import { OutputFormat, FormatContext } from "./FormatContext" export interface Muxer extends Omit - metadata: Array + metadata: { [key: string]: string } /** * Average framerate *