MediaCodec: inject SPS and PPS into the first video frame

For some reason, this is required to make livestreams functional
This commit is contained in:
Julian Winkler
2025-01-27 21:10:54 +01:00
parent a743361ae4
commit ecd5996ad2

View File

@@ -38,6 +38,8 @@ struct ATL_codec_context {
struct { struct {
struct SwsContext *sws; // for software decoding struct SwsContext *sws; // for software decoding
SurfaceViewWidget *surface_view_widget; SurfaceViewWidget *surface_view_widget;
size_t extradata_size;
uint8_t *extradata;
} video; } video;
}; };
}; };
@@ -203,7 +205,6 @@ JNIEXPORT void JNICALL Java_android_media_MediaCodec_native_1configure_1video(JN
AVCodecContext *codec_ctx = ctx->codec; AVCodecContext *codec_ctx = ctx->codec;
jarray array_ref; jarray array_ref;
jbyte *array; jbyte *array;
void *data;
int sps_size; int sps_size;
int pps_size; int pps_size;
@@ -212,32 +213,22 @@ JNIEXPORT void JNICALL Java_android_media_MediaCodec_native_1configure_1video(JN
sps_size = get_nio_buffer_size(env, csd0); sps_size = get_nio_buffer_size(env, csd0);
pps_size = get_nio_buffer_size(env, csd1); pps_size = get_nio_buffer_size(env, csd1);
// libavcodec wants the complete AVC decoder configuration record, but android APIs pass only SPS and PPS records size_t extradata_size = sps_size + pps_size;
// see https://stackoverflow.com/questions/29790334/how-to-fill-extradata-field-of-avcodeccontext-with-sps-and-pps-data uint8_t *extradata = av_mallocz(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
codec_ctx->extradata_size = 11 + sps_size + pps_size; memcpy(extradata, get_nio_buffer(env, csd0, &array_ref, &array), extradata_size);
codec_ctx->extradata = av_mallocz(codec_ctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
codec_ctx->extradata[0] = 0x01;
codec_ctx->extradata[1] = 0x4d;
codec_ctx->extradata[2] = 0x40;
codec_ctx->extradata[3] = 0x1f;
codec_ctx->extradata[4] = 0xff;
codec_ctx->extradata[5] = 0xe0 | 1;
codec_ctx->extradata[6] = (sps_size>>8) & 0xff;
codec_ctx->extradata[7] = (sps_size) & 0xff;
data = get_nio_buffer(env, csd0, &array_ref, &array);
memcpy(codec_ctx->extradata + 8, data, sps_size);
release_nio_buffer(env, array_ref, array); release_nio_buffer(env, array_ref, array);
codec_ctx->extradata[8 + sps_size] = 1; memcpy(extradata + sps_size, get_nio_buffer(env, csd1, &array_ref, &array), extradata_size);
codec_ctx->extradata[9 + sps_size] = (pps_size>>8) & 0xff;
codec_ctx->extradata[10 + sps_size] = (pps_size) & 0xff;
data = get_nio_buffer(env, csd1, &array_ref, &array);
memcpy(codec_ctx->extradata + 11 + sps_size, data, pps_size);
release_nio_buffer(env, array_ref, array); release_nio_buffer(env, array_ref, array);
for (int i = 0; i < codec_ctx->extradata_size; i++) { for (int i = 0; i < extradata_size; i++) {
printf("params->extradata[%d] = %x\n", i, codec_ctx->extradata[i]); printf("extradata[%d] = %x\n", i, extradata[i]);
} }
/* For some reason, using AVCodecContext.extradata doesn't work with livestreams.
As a workaround, we inject the extradata into the first frame. */
ctx->video.extradata = extradata;
ctx->video.extradata_size = extradata_size;
int i = 0; int i = 0;
while (1) { while (1) {
const AVCodecHWConfig *config = avcodec_get_hw_config(codec_ctx->codec, i); const AVCodecHWConfig *config = avcodec_get_hw_config(codec_ctx->codec, i);
@@ -311,6 +302,14 @@ JNIEXPORT jint JNICALL Java_android_media_MediaCodec_native_1queueInputBuffer(JN
pkt = av_packet_alloc(); pkt = av_packet_alloc();
pkt->size = get_nio_buffer_size(env, buffer); pkt->size = get_nio_buffer_size(env, buffer);
pkt->data = get_nio_buffer(env, buffer, &array_ref, &array); pkt->data = get_nio_buffer(env, buffer, &array_ref, &array);
if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO && ctx->video.extradata_size) {
uint8_t *data = pkt->data;
pkt->data = av_malloc(pkt->size + ctx->video.extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
memcpy(pkt->data, ctx->video.extradata, ctx->video.extradata_size);
memcpy(pkt->data + ctx->video.extradata_size, data, pkt->size);
pkt->size += ctx->video.extradata_size;
ctx->video.extradata_size = 0;
}
pkt->pts = presentationTimeUs; pkt->pts = presentationTimeUs;
} }
ret = avcodec_send_packet(codec_ctx, pkt); ret = avcodec_send_packet(codec_ctx, pkt);