From 4bdf632dfadec432582db64bff898588a4ed6e13 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 10 Mar 2023 19:25:45 +0100 Subject: [PATCH] Pass AVCodecContext to packet sinks Create the codec context from the demuxer, so that it can fill context data for the decoder and recorder. --- app/src/audio_player.c | 1 + app/src/decoder.c | 50 ++++++----------------------------- app/src/decoder.h | 2 +- app/src/demuxer.c | 32 +++++++++++++++++++++- app/src/recorder.c | 10 +++---- app/src/trait/frame_sink.h | 3 +-- app/src/trait/packet_sink.h | 8 +++--- app/src/trait/packet_source.c | 4 +-- app/src/trait/packet_source.h | 2 +- 9 files changed, 52 insertions(+), 60 deletions(-) diff --git a/app/src/audio_player.c b/app/src/audio_player.c index 77de0ddb..7a348f93 100644 --- a/app/src/audio_player.c +++ b/app/src/audio_player.c @@ -1,5 +1,6 @@ #include "audio_player.h" +#include #include #include "util/log.h" diff --git a/app/src/decoder.c b/app/src/decoder.c index ecad8373..5d42b8b0 100644 --- a/app/src/decoder.c +++ b/app/src/decoder.c @@ -12,52 +12,20 @@ #define DOWNCAST(SINK) container_of(SINK, struct sc_decoder, packet_sink) static bool -sc_decoder_open(struct sc_decoder *decoder, const AVCodec *codec) { - decoder->codec_ctx = avcodec_alloc_context3(codec); - if (!decoder->codec_ctx) { - LOG_OOM(); - return false; - } - - decoder->codec_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY; - - if (codec->type == AVMEDIA_TYPE_VIDEO) { - // Hardcoded video properties - decoder->codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; - } else { - // Hardcoded audio properties -#ifdef SCRCPY_LAVU_HAS_CHLAYOUT - decoder->codec_ctx->ch_layout = - (AVChannelLayout) AV_CHANNEL_LAYOUT_STEREO; -#else - decoder->codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO; - decoder->codec_ctx->channels = 2; -#endif - decoder->codec_ctx->sample_rate = 48000; - } - - if (avcodec_open2(decoder->codec_ctx, codec, NULL) < 0) { - LOGE("Decoder '%s': could not open codec", decoder->name); - avcodec_free_context(&decoder->codec_ctx); - return false; - } - +sc_decoder_open(struct sc_decoder *decoder, AVCodecContext *ctx) { decoder->frame = av_frame_alloc(); if (!decoder->frame) { LOG_OOM(); - avcodec_close(decoder->codec_ctx); - avcodec_free_context(&decoder->codec_ctx); return false; } - if (!sc_frame_source_sinks_open(&decoder->frame_source, - decoder->codec_ctx)) { + if (!sc_frame_source_sinks_open(&decoder->frame_source, ctx)) { av_frame_free(&decoder->frame); - avcodec_close(decoder->codec_ctx); - avcodec_free_context(&decoder->codec_ctx); return false; } + decoder->ctx = ctx; + return true; } @@ -65,8 +33,6 @@ static void sc_decoder_close(struct sc_decoder *decoder) { sc_frame_source_sinks_close(&decoder->frame_source); av_frame_free(&decoder->frame); - avcodec_close(decoder->codec_ctx); - avcodec_free_context(&decoder->codec_ctx); } static bool @@ -77,7 +43,7 @@ sc_decoder_push(struct sc_decoder *decoder, const AVPacket *packet) { return true; } - int ret = avcodec_send_packet(decoder->codec_ctx, packet); + int ret = avcodec_send_packet(decoder->ctx, packet); if (ret < 0 && ret != AVERROR(EAGAIN)) { LOGE("Decoder '%s': could not send video packet: %d", decoder->name, ret); @@ -85,7 +51,7 @@ sc_decoder_push(struct sc_decoder *decoder, const AVPacket *packet) { } for (;;) { - ret = avcodec_receive_frame(decoder->codec_ctx, decoder->frame); + ret = avcodec_receive_frame(decoder->ctx, decoder->frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } @@ -110,9 +76,9 @@ sc_decoder_push(struct sc_decoder *decoder, const AVPacket *packet) { } static bool -sc_decoder_packet_sink_open(struct sc_packet_sink *sink, const AVCodec *codec) { +sc_decoder_packet_sink_open(struct sc_packet_sink *sink, AVCodecContext *ctx) { struct sc_decoder *decoder = DOWNCAST(sink); - return sc_decoder_open(decoder, codec); + return sc_decoder_open(decoder, ctx); } static void diff --git a/app/src/decoder.h b/app/src/decoder.h index 87aaf6a2..ba8903f4 100644 --- a/app/src/decoder.h +++ b/app/src/decoder.h @@ -16,7 +16,7 @@ struct sc_decoder { const char *name; // must be statically allocated (e.g. a string literal) - AVCodecContext *codec_ctx; + AVCodecContext *ctx; AVFrame *frame; }; diff --git a/app/src/demuxer.c b/app/src/demuxer.c index a4fa19f4..eabcb81e 100644 --- a/app/src/demuxer.c +++ b/app/src/demuxer.c @@ -160,10 +160,37 @@ run_demuxer(void *data) { goto end; } - if (!sc_packet_source_sinks_open(&demuxer->packet_source, codec)) { + AVCodecContext *codec_ctx = avcodec_alloc_context3(codec); + if (!codec_ctx) { + LOG_OOM(); goto end; } + codec_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY; + + if (codec->type == AVMEDIA_TYPE_VIDEO) { + // Hardcoded video properties + codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; + } else { + // Hardcoded audio properties +#ifdef SCRCPY_LAVU_HAS_CHLAYOUT + codec_ctx->ch_layout = (AVChannelLayout) AV_CHANNEL_LAYOUT_STEREO; +#else + codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO; + codec_ctx->channels = 2; +#endif + codec_ctx->sample_rate = 48000; + } + + if (avcodec_open2(codec_ctx, codec, NULL) < 0) { + LOGE("Demuxer '%s': could not open codec", demuxer->name); + goto finally_free_context; + } + + if (!sc_packet_source_sinks_open(&demuxer->packet_source, codec_ctx)) { + goto finally_free_context; + } + // Config packets must be merged with the next non-config packet only for // video streams bool must_merge_config_packet = codec->type == AVMEDIA_TYPE_VIDEO; @@ -214,6 +241,9 @@ run_demuxer(void *data) { av_packet_free(&packet); finally_close_sinks: sc_packet_source_sinks_close(&demuxer->packet_source); +finally_free_context: + // This also calls avcodec_close() internally + avcodec_free_context(&codec_ctx); end: demuxer->cbs->on_ended(demuxer, status, demuxer->cbs_userdata); diff --git a/app/src/recorder.c b/app/src/recorder.c index 572d3e24..1e89608a 100644 --- a/app/src/recorder.c +++ b/app/src/recorder.c @@ -536,9 +536,8 @@ run_recorder(void *data) { static bool sc_recorder_video_packet_sink_open(struct sc_packet_sink *sink, - const AVCodec *codec) { + AVCodecContext *ctx) { struct sc_recorder *recorder = DOWNCAST_VIDEO(sink); - assert(codec); sc_mutex_lock(&recorder->mutex); if (recorder->stopped) { @@ -546,7 +545,7 @@ sc_recorder_video_packet_sink_open(struct sc_packet_sink *sink, return false; } - recorder->video_codec = codec; + recorder->video_codec = ctx->codec; sc_cond_signal(&recorder->stream_cond); sc_mutex_unlock(&recorder->mutex); @@ -601,15 +600,14 @@ sc_recorder_video_packet_sink_push(struct sc_packet_sink *sink, static bool sc_recorder_audio_packet_sink_open(struct sc_packet_sink *sink, - const AVCodec *codec) { + AVCodecContext *ctx) { struct sc_recorder *recorder = DOWNCAST_AUDIO(sink); assert(recorder->audio); // only written from this thread, no need to lock assert(!recorder->audio_disabled); - assert(codec); sc_mutex_lock(&recorder->mutex); - recorder->audio_codec = codec; + recorder->audio_codec = ctx->codec; sc_cond_signal(&recorder->stream_cond); sc_mutex_unlock(&recorder->mutex); diff --git a/app/src/trait/frame_sink.h b/app/src/trait/frame_sink.h index 30bf0d37..8ef248b6 100644 --- a/app/src/trait/frame_sink.h +++ b/app/src/trait/frame_sink.h @@ -7,8 +7,6 @@ #include #include -typedef struct AVFrame AVFrame; - /** * Frame sink trait. * @@ -19,6 +17,7 @@ struct sc_frame_sink { }; struct sc_frame_sink_ops { + /* The codec context is valid until the sink is closed */ bool (*open)(struct sc_frame_sink *sink, const AVCodecContext *ctx); void (*close)(struct sc_frame_sink *sink); bool (*push)(struct sc_frame_sink *sink, const AVFrame *frame); diff --git a/app/src/trait/packet_sink.h b/app/src/trait/packet_sink.h index 099c8c52..84cfe814 100644 --- a/app/src/trait/packet_sink.h +++ b/app/src/trait/packet_sink.h @@ -5,9 +5,7 @@ #include #include - -typedef struct AVCodec AVCodec; -typedef struct AVPacket AVPacket; +#include /** * Packet sink trait. @@ -19,8 +17,8 @@ struct sc_packet_sink { }; struct sc_packet_sink_ops { - /* The codec instance is static, it is valid until the end of the program */ - bool (*open)(struct sc_packet_sink *sink, const AVCodec *codec); + /* The codec context is valid until the sink is closed */ + bool (*open)(struct sc_packet_sink *sink, AVCodecContext *ctx); void (*close)(struct sc_packet_sink *sink); bool (*push)(struct sc_packet_sink *sink, const AVPacket *packet); diff --git a/app/src/trait/packet_source.c b/app/src/trait/packet_source.c index df678e16..c0836f1d 100644 --- a/app/src/trait/packet_source.c +++ b/app/src/trait/packet_source.c @@ -25,11 +25,11 @@ sc_packet_source_sinks_close_firsts(struct sc_packet_source *source, bool sc_packet_source_sinks_open(struct sc_packet_source *source, - const AVCodec *codec) { + AVCodecContext *ctx) { assert(source->sink_count); for (unsigned i = 0; i < source->sink_count; ++i) { struct sc_packet_sink *sink = source->sinks[i]; - if (!sink->ops->open(sink, codec)) { + if (!sink->ops->open(sink, ctx)) { sc_packet_source_sinks_close_firsts(source, i); return false; } diff --git a/app/src/trait/packet_source.h b/app/src/trait/packet_source.h index c34aa5d3..16d56e86 100644 --- a/app/src/trait/packet_source.h +++ b/app/src/trait/packet_source.h @@ -26,7 +26,7 @@ sc_packet_source_add_sink(struct sc_packet_source *source, bool sc_packet_source_sinks_open(struct sc_packet_source *source, - const AVCodec *codec); + AVCodecContext *ctx); void sc_packet_source_sinks_close(struct sc_packet_source *source);