diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt index 3409698d..db366af7 100644 --- a/tools/chatgui/CMakeLists.txt +++ b/tools/chatgui/CMakeLists.txt @@ -537,7 +537,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_options(test_stream_view PRIVATE "-Wl,--wrap=ma_context_init" "-Wl,--wrap=ma_device_init" "-Wl,--wrap=audio_stream_frame_time") if(FFMPEG_FOUND) - target_link_options(test_stream_view PRIVATE "-Wl,--wrap=avcodec_open2") + target_link_options(test_stream_view PRIVATE "-Wl,--wrap=avcodec_open2" "-Wl,--wrap=avcodec_receive_frame" + "-Wl,--wrap=avcodec_free_context") endif() endif() add_test(NAME test_stream_view COMMAND test_stream_view) diff --git a/tools/chatgui/tests/test_stream_view.cpp b/tools/chatgui/tests/test_stream_view.cpp index d98d6e56..e8544dda 100644 --- a/tools/chatgui/tests/test_stream_view.cpp +++ b/tools/chatgui/tests/test_stream_view.cpp @@ -10,6 +10,7 @@ #include #include #include +#include extern "C" { #include "utun_instance.h" @@ -29,12 +30,39 @@ extern "C" { static QList snapshots; #if defined(TEST_NULL_AUDIO) && defined(HAVE_FFMPEG) static std::atomic delayedDecoder{false}; +static AVCodecContext* delayedContext = nullptr; +static std::deque delayedFrames; +static bool delayedDraining = false; extern "C" int __real_avcodec_open2(AVCodecContext*, const AVCodec*, AVDictionary**); extern "C" int __wrap_avcodec_open2(AVCodecContext* context, const AVCodec* codec, AVDictionary** options) { - if (delayedDecoder && av_codec_is_decoder(codec)) { - context->thread_count = 4; context->thread_type = FF_THREAD_FRAME; + int result = __real_avcodec_open2(context, codec, options); + if (!result && delayedDecoder && codec && av_codec_is_decoder(codec)) { + delayedContext = context; delayedDraining = false; + DEBUG_INFO(DEBUG_CATEGORY_STREAM, "fixture decoder name=%s holds four real decoded frames before output", codec->name); } - return __real_avcodec_open2(context, codec, options); + return result; +} +extern "C" int __real_avcodec_receive_frame(AVCodecContext*, AVFrame*); +extern "C" int __wrap_avcodec_receive_frame(AVCodecContext* context, AVFrame* frame) { + if (context != delayedContext) return __real_avcodec_receive_frame(context, frame); + int result = __real_avcodec_receive_frame(context, frame); + if (!result) { + AVFrame* held = av_frame_clone(frame); if (!held) return AVERROR(ENOMEM); + delayedFrames.push_back(held); av_frame_unref(frame); + } else if (result == AVERROR_EOF) delayedDraining = true; + else if (result != AVERROR(EAGAIN)) return result; + if ((delayedDraining || delayedFrames.size() >= 4) && !delayedFrames.empty()) { + AVFrame* held = delayedFrames.front(); delayedFrames.pop_front(); av_frame_move_ref(frame, held); av_frame_free(&held); return 0; + } + return delayedDraining ? AVERROR_EOF : AVERROR(EAGAIN); +} +extern "C" void __real_avcodec_free_context(AVCodecContext**); +extern "C" void __wrap_avcodec_free_context(AVCodecContext** context) { + if (context && *context == delayedContext) { + for (auto* frame : delayedFrames) av_frame_free(&frame); + delayedFrames.clear(); delayedContext = nullptr; delayedDraining = false; + } + __real_avcodec_free_context(context); } #endif #ifdef TEST_NULL_AUDIO