|
|
|
@ -10,6 +10,7 @@ |
|
|
|
#include <atomic> |
|
|
|
#include <atomic> |
|
|
|
#include <cmath> |
|
|
|
#include <cmath> |
|
|
|
#include <chrono> |
|
|
|
#include <chrono> |
|
|
|
|
|
|
|
#include <deque> |
|
|
|
|
|
|
|
|
|
|
|
extern "C" { |
|
|
|
extern "C" { |
|
|
|
#include "utun_instance.h" |
|
|
|
#include "utun_instance.h" |
|
|
|
@ -29,12 +30,39 @@ extern "C" { |
|
|
|
static QList<QByteArray> snapshots; |
|
|
|
static QList<QByteArray> snapshots; |
|
|
|
#if defined(TEST_NULL_AUDIO) && defined(HAVE_FFMPEG) |
|
|
|
#if defined(TEST_NULL_AUDIO) && defined(HAVE_FFMPEG) |
|
|
|
static std::atomic<bool> delayedDecoder{false}; |
|
|
|
static std::atomic<bool> delayedDecoder{false}; |
|
|
|
|
|
|
|
static AVCodecContext* delayedContext = nullptr; |
|
|
|
|
|
|
|
static std::deque<AVFrame*> delayedFrames; |
|
|
|
|
|
|
|
static bool delayedDraining = false; |
|
|
|
extern "C" int __real_avcodec_open2(AVCodecContext*, const AVCodec*, AVDictionary**); |
|
|
|
extern "C" int __real_avcodec_open2(AVCodecContext*, const AVCodec*, AVDictionary**); |
|
|
|
extern "C" int __wrap_avcodec_open2(AVCodecContext* context, const AVCodec* codec, AVDictionary** options) { |
|
|
|
extern "C" int __wrap_avcodec_open2(AVCodecContext* context, const AVCodec* codec, AVDictionary** options) { |
|
|
|
if (delayedDecoder && av_codec_is_decoder(codec)) { |
|
|
|
int result = __real_avcodec_open2(context, codec, options); |
|
|
|
context->thread_count = 4; context->thread_type = FF_THREAD_FRAME; |
|
|
|
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 |
|
|
|
#endif |
|
|
|
#ifdef TEST_NULL_AUDIO |
|
|
|
#ifdef TEST_NULL_AUDIO |
|
|
|
|