tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit d2894a5af8a8a56011b5740bab1e99a4aa84235d
parent 39cd7bea2aea4afcef9b6a53b0aaad5853cb5fe9
Author: Andrew Osmond <aosmond@gmail.com>
Date:   Mon,  5 Jan 2026 17:17:28 +0000

Bug 2008421 - Teach ffmpeg to force keyframes with MediaCodec encoders. r=media-playback-reviewers,padenot

FFmpegVideoEncoder attempts to request keyframes when the frame is
marked as a keyframe by setting the frame's pict_type to
AV_PICTURE_TYPE_I. The MediaCodec encoder integration ignores this
setting so it will only produce keyframes when its internal logic
decides to based on the keyframe interval requested at setup.

This patch makes it so that we use the "request-sync" AMediaFormat
parameter to request the next available frame to become a keyframe. It
is not guaranteed that the next frame will be a keyframe, but it should
be reasonably accurate and close to the desired frame.

Differential Revision: https://phabricator.services.mozilla.com/D277802

Diffstat:
Mmedia/ffvpx/README_MOZILLA | 7+++++++
Mmedia/ffvpx/libavcodec/mediacodec_wrapper.c | 29+++++++++++++++++++++++++++++
Mmedia/ffvpx/libavcodec/mediacodec_wrapper.h | 7+++++++
Mmedia/ffvpx/libavcodec/mediacodecenc.c | 7+++++++
Amedia/ffvpx/mediacodec_force_key_frames.patch | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 151 insertions(+), 0 deletions(-)

diff --git a/media/ffvpx/README_MOZILLA b/media/ffvpx/README_MOZILLA @@ -145,3 +145,10 @@ Finally, apply the patches: stdio.h function, that causes bug 1879740 issue on Windows. - opusenc-dtx.patch to allow enabling DTX in the opus encoder. - libaomenc-svc.patch to allow configuring SVC in the libaom encoder. +- mediacodec_h264.patch to remove the H264 extradata parsing that brings in + encumbered code on Android. +- mediacodec_eos.patch to add a method to check definitely if a decoder has + reached the end of stream on Android. +- mediacodec_drm.patch to add support for encrypted packets on Android. +- mediacodec_force_key_frames.patch to add support for forcing key frames when + encoding via the frame pict_type on Android. diff --git a/media/ffvpx/libavcodec/mediacodec_wrapper.c b/media/ffvpx/libavcodec/mediacodec_wrapper.c @@ -1411,6 +1411,12 @@ fail: return ret; } +static int mediacodec_jni_setParameters(FFAMediaCodec *ctx, + const FFAMediaFormat* format_ctx) +{ + return AVERROR_PATCHWELCOME; +} + static int mediacodec_jni_start(FFAMediaCodec* ctx) { int ret = 0; @@ -1807,6 +1813,7 @@ static const FFAMediaCodec media_codec_jni = { .delete = mediacodec_jni_delete, .configure = mediacodec_jni_configure, + .setParameters = mediacodec_jni_setParameters, .start = mediacodec_jni_start, .stop = mediacodec_jni_stop, .flush = mediacodec_jni_flush, @@ -2225,6 +2232,27 @@ static int mediacodec_ndk_configure(FFAMediaCodec* ctx, return 0; } +static int mediacodec_ndk_setParameters(FFAMediaCodec *ctx, + const FFAMediaFormat* format_ctx) +{ + FFAMediaCodecNdk *codec = (FFAMediaCodecNdk *)ctx; + FFAMediaFormatNdk *format = (FFAMediaFormatNdk *)format_ctx; + media_status_t status; + + if (format_ctx->class != &amediaformat_ndk_class) { + av_log(ctx, AV_LOG_ERROR, "invalid media format\n"); + return AVERROR(EINVAL); + } + + status = AMediaCodec_setParameters(codec->impl, format->impl); + if (status != AMEDIA_OK) { + av_log(codec, AV_LOG_ERROR, "setParameters failed, %d\n", status); + return AVERROR_EXTERNAL; + } + + return 0; +} + #define MEDIACODEC_NDK_WRAPPER(method) \ static int mediacodec_ndk_ ## method(FFAMediaCodec* ctx) \ { \ @@ -2512,6 +2540,7 @@ static const FFAMediaCodec media_codec_ndk = { .delete = mediacodec_ndk_delete, .configure = mediacodec_ndk_configure, + .setParameters = mediacodec_ndk_setParameters, .start = mediacodec_ndk_start, .stop = mediacodec_ndk_stop, .flush = mediacodec_ndk_flush, diff --git a/media/ffvpx/libavcodec/mediacodec_wrapper.h b/media/ffvpx/libavcodec/mediacodec_wrapper.h @@ -205,6 +205,7 @@ struct FFAMediaCodec { int (*delete)(FFAMediaCodec* codec); int (*configure)(FFAMediaCodec* codec, const FFAMediaFormat* format, FFANativeWindow* surface, void *crypto, uint32_t flags); + int (*setParameters)(FFAMediaCodec* codec, const FFAMediaFormat* format); int (*start)(FFAMediaCodec* codec); int (*stop)(FFAMediaCodec* codec); int (*flush)(FFAMediaCodec* codec); @@ -260,6 +261,12 @@ static inline int ff_AMediaCodec_configure(FFAMediaCodec *codec, return codec->configure(codec, format, surface, crypto, flags); } +static inline int ff_AMediaCodec_setParameters(FFAMediaCodec *codec, + const FFAMediaFormat *format) +{ + return codec->setParameters(codec, format); +} + static inline int ff_AMediaCodec_start(FFAMediaCodec* codec) { return codec->start(codec); diff --git a/media/ffvpx/libavcodec/mediacodecenc.c b/media/ffvpx/libavcodec/mediacodecenc.c @@ -785,6 +785,13 @@ static int mediacodec_send(AVCodecContext *avctx, copy_frame_to_buffer(avctx, frame, input_buf, input_size); pts = av_rescale_q(frame->pts, avctx->time_base, AV_TIME_BASE_Q); + + if (frame->pict_type == AV_PICTURE_TYPE_I) { + FFAMediaFormat *format = ff_AMediaFormat_new(s->use_ndk_codec); + ff_AMediaFormat_setInt32(format, "request-sync", 0); + ff_AMediaCodec_setParameters(codec, format); + ff_AMediaFormat_delete(format); + } } else { flags |= ff_AMediaCodec_getBufferFlagEndOfStream(codec); s->eof_sent = 1; diff --git a/media/ffvpx/mediacodec_force_key_frames.patch b/media/ffvpx/mediacodec_force_key_frames.patch @@ -0,0 +1,101 @@ +diff --git a/media/ffvpx/libavcodec/mediacodec_wrapper.c b/media/ffvpx/libavcodec/mediacodec_wrapper.c +--- a/media/ffvpx/libavcodec/mediacodec_wrapper.c ++++ b/media/ffvpx/libavcodec/mediacodec_wrapper.c +@@ -1411,6 +1411,12 @@ fail: + return ret; + } + ++static int mediacodec_jni_setParameters(FFAMediaCodec *ctx, ++ const FFAMediaFormat* format_ctx) ++{ ++ return AVERROR_PATCHWELCOME; ++} ++ + static int mediacodec_jni_start(FFAMediaCodec* ctx) + { + int ret = 0; +@@ -1807,6 +1813,7 @@ static const FFAMediaCodec media_codec_jni = { + .delete = mediacodec_jni_delete, + + .configure = mediacodec_jni_configure, ++ .setParameters = mediacodec_jni_setParameters, + .start = mediacodec_jni_start, + .stop = mediacodec_jni_stop, + .flush = mediacodec_jni_flush, +@@ -2225,6 +2232,27 @@ static int mediacodec_ndk_configure(FFAMediaCodec* ctx, + return 0; + } + ++static int mediacodec_ndk_setParameters(FFAMediaCodec *ctx, ++ const FFAMediaFormat* format_ctx) ++{ ++ FFAMediaCodecNdk *codec = (FFAMediaCodecNdk *)ctx; ++ FFAMediaFormatNdk *format = (FFAMediaFormatNdk *)format_ctx; ++ media_status_t status; ++ ++ if (format_ctx->class != &amediaformat_ndk_class) { ++ av_log(ctx, AV_LOG_ERROR, "invalid media format\n"); ++ return AVERROR(EINVAL); ++ } ++ ++ status = AMediaCodec_setParameters(codec->impl, format->impl); ++ if (status != AMEDIA_OK) { ++ av_log(codec, AV_LOG_ERROR, "setParameters failed, %d\n", status); ++ return AVERROR_EXTERNAL; ++ } ++ ++ return 0; ++} ++ + #define MEDIACODEC_NDK_WRAPPER(method) \ + static int mediacodec_ndk_ ## method(FFAMediaCodec* ctx) \ + { \ +@@ -2512,6 +2540,7 @@ static const FFAMediaCodec media_codec_ndk = { + .delete = mediacodec_ndk_delete, + + .configure = mediacodec_ndk_configure, ++ .setParameters = mediacodec_ndk_setParameters, + .start = mediacodec_ndk_start, + .stop = mediacodec_ndk_stop, + .flush = mediacodec_ndk_flush, +diff --git a/media/ffvpx/libavcodec/mediacodec_wrapper.h b/media/ffvpx/libavcodec/mediacodec_wrapper.h +--- a/media/ffvpx/libavcodec/mediacodec_wrapper.h ++++ b/media/ffvpx/libavcodec/mediacodec_wrapper.h +@@ -205,6 +205,7 @@ struct FFAMediaCodec { + int (*delete)(FFAMediaCodec* codec); + + int (*configure)(FFAMediaCodec* codec, const FFAMediaFormat* format, FFANativeWindow* surface, void *crypto, uint32_t flags); ++ int (*setParameters)(FFAMediaCodec* codec, const FFAMediaFormat* format); + int (*start)(FFAMediaCodec* codec); + int (*stop)(FFAMediaCodec* codec); + int (*flush)(FFAMediaCodec* codec); +@@ -260,6 +261,12 @@ static inline int ff_AMediaCodec_configure(FFAMediaCodec *codec, + return codec->configure(codec, format, surface, crypto, flags); + } + ++static inline int ff_AMediaCodec_setParameters(FFAMediaCodec *codec, ++ const FFAMediaFormat *format) ++{ ++ return codec->setParameters(codec, format); ++} ++ + static inline int ff_AMediaCodec_start(FFAMediaCodec* codec) + { + return codec->start(codec); +diff --git a/media/ffvpx/libavcodec/mediacodecenc.c b/media/ffvpx/libavcodec/mediacodecenc.c +--- a/media/ffvpx/libavcodec/mediacodecenc.c ++++ b/media/ffvpx/libavcodec/mediacodecenc.c +@@ -785,6 +785,13 @@ static int mediacodec_send(AVCodecContext *avctx, + copy_frame_to_buffer(avctx, frame, input_buf, input_size); + + pts = av_rescale_q(frame->pts, avctx->time_base, AV_TIME_BASE_Q); ++ ++ if (frame->pict_type == AV_PICTURE_TYPE_I) { ++ FFAMediaFormat *format = ff_AMediaFormat_new(s->use_ndk_codec); ++ ff_AMediaFormat_setInt32(format, "request-sync", 0); ++ ff_AMediaCodec_setParameters(codec, format); ++ ff_AMediaFormat_delete(format); ++ } + } else { + flags |= ff_AMediaCodec_getBufferFlagEndOfStream(codec); + s->eof_sent = 1;