diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 32ade8267d..fe1c84a501 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -93,6 +93,8 @@ ([#710](https://github.com/androidx/media/pull/710)). * MIDI: Fix issue where seeking forward skips the Program Change events ([#704](https://github.com/androidx/media/issues/704). + * Migrate to FFmpeg 6.0 + ([#707](https://github.com/androidx/media/pull/707)). * Leanback extension: * Cast Extension: * Sanitize creation of a `Timeline` to not crash the app when loading diff --git a/libraries/decoder_ffmpeg/README.md b/libraries/decoder_ffmpeg/README.md index 5adf97bdcd..fac88921de 100644 --- a/libraries/decoder_ffmpeg/README.md +++ b/libraries/decoder_ffmpeg/README.md @@ -42,13 +42,13 @@ HOST_PLATFORM="linux-x86_64" ``` * Fetch FFmpeg and checkout an appropriate branch. We cannot guarantee - compatibility with all versions of FFmpeg. We currently recommend version 4.2: + compatibility with all versions of FFmpeg. We currently recommend version 6.0: ``` cd "" && \ git clone git://source.ffmpeg.org/ffmpeg && \ cd ffmpeg && \ -git checkout release/4.2 && \ +git checkout release/6.0 && \ FFMPEG_PATH="$(pwd)" ``` diff --git a/libraries/decoder_ffmpeg/src/main/jni/CMakeLists.txt b/libraries/decoder_ffmpeg/src/main/jni/CMakeLists.txt index 2a81cd5430..fe74c78048 100644 --- a/libraries/decoder_ffmpeg/src/main/jni/CMakeLists.txt +++ b/libraries/decoder_ffmpeg/src/main/jni/CMakeLists.txt @@ -21,12 +21,6 @@ set(CMAKE_CXX_STANDARD 11) project(libffmpegJNI C CXX) -# Additional flags needed for "arm64-v8a" from NDK 23.1.7779620 and above. -# See https://github.com/google/ExoPlayer/issues/9933#issuecomment-1029775358. -if(${ANDROID_ABI} MATCHES "arm64-v8a") - set(CMAKE_CXX_FLAGS "-Wl,-Bsymbolic") -endif() - set(ffmpeg_location "${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg") set(ffmpeg_binaries "${ffmpeg_location}/android-libs/${ANDROID_ABI}") @@ -56,3 +50,9 @@ target_link_libraries(ffmpegJNI PRIVATE avcodec PRIVATE avutil PRIVATE ${android_log_lib}) + +# Additional flags needed for "arm64-v8a" from NDK 23.1.7779620 and above. +# See https://github.com/google/ExoPlayer/issues/9933#issuecomment-1029775358. +if(ANDROID_ABI STREQUAL "arm64-v8a") + target_link_options(ffmpegJNI PRIVATE "-Wl,-Bsymbolic") +endif() diff --git a/libraries/decoder_ffmpeg/src/main/jni/build_ffmpeg.sh b/libraries/decoder_ffmpeg/src/main/jni/build_ffmpeg.sh index 1583c1c964..0ab1231c0b 100755 --- a/libraries/decoder_ffmpeg/src/main/jni/build_ffmpeg.sh +++ b/libraries/decoder_ffmpeg/src/main/jni/build_ffmpeg.sh @@ -35,9 +35,10 @@ COMMON_OPTIONS=" --disable-postproc --disable-avfilter --disable-symver - --disable-avresample --enable-swresample --extra-ldexeflags=-pie + --disable-v4l2-m2m + --disable-vulkan " TOOLCHAIN_PREFIX="${NDK_PATH}/toolchains/llvm/prebuilt/${HOST_PLATFORM}/bin" for decoder in "${ENABLED_DECODERS[@]}" diff --git a/libraries/decoder_ffmpeg/src/main/jni/ffmpeg_jni.cc b/libraries/decoder_ffmpeg/src/main/jni/ffmpeg_jni.cc index 4fa02479ba..222a224da0 100644 --- a/libraries/decoder_ffmpeg/src/main/jni/ffmpeg_jni.cc +++ b/libraries/decoder_ffmpeg/src/main/jni/ffmpeg_jni.cc @@ -74,14 +74,14 @@ static jmethodID growOutputBufferMethod; /** * Returns the AVCodec with the specified name, or NULL if it is not available. */ -AVCodec *getCodecByName(JNIEnv *env, jstring codecName); +const AVCodec *getCodecByName(JNIEnv *env, jstring codecName); /** * Allocates and opens a new AVCodecContext for the specified codec, passing the * provided extraData as initialization data for the decoder if it is non-NULL. * Returns the created context. */ -AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData, +AVCodecContext *createContext(JNIEnv *env, const AVCodec *codec, jbyteArray extraData, jboolean outputFloat, jint rawSampleRate, jint rawChannelCount); @@ -137,7 +137,6 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) { LOGE("JNI_OnLoad: GetMethodID failed"); return -1; } - avcodec_register_all(); return JNI_VERSION_1_6; } @@ -156,7 +155,7 @@ LIBRARY_FUNC(jboolean, ffmpegHasDecoder, jstring codecName) { AUDIO_DECODER_FUNC(jlong, ffmpegInitialize, jstring codecName, jbyteArray extraData, jboolean outputFloat, jint rawSampleRate, jint rawChannelCount) { - AVCodec *codec = getCodecByName(env, codecName); + const AVCodec *codec = getCodecByName(env, codecName); if (!codec) { LOGE("Codec not found."); return 0L; @@ -186,13 +185,17 @@ AUDIO_DECODER_FUNC(jint, ffmpegDecode, jlong context, jobject inputData, } uint8_t *inputBuffer = (uint8_t *)env->GetDirectBufferAddress(inputData); uint8_t *outputBuffer = (uint8_t *)env->GetDirectBufferAddress(outputData); - AVPacket packet; - av_init_packet(&packet); - packet.data = inputBuffer; - packet.size = inputSize; - return decodePacket((AVCodecContext *)context, &packet, outputBuffer, - outputSize, - GrowOutputBufferCallback{env, thiz, decoderOutputBuffer}); + AVPacket* packet = av_packet_alloc(); + if (!packet) { + LOGE("Failed to allocate packet."); + return -1; + } + packet->data = inputBuffer; + packet->size = inputSize; + const int ret = decodePacket((AVCodecContext *)context, packet, outputBuffer, + outputSize, GrowOutputBufferCallback{env, thiz, decoderOutputBuffer}); + av_packet_free(&packet); + return ret; } uint8_t *GrowOutputBufferCallback::operator()(int requiredSize) const { @@ -211,7 +214,7 @@ AUDIO_DECODER_FUNC(jint, ffmpegGetChannelCount, jlong context) { LOGE("Context must be non-NULL."); return -1; } - return ((AVCodecContext *)context)->channels; + return ((AVCodecContext *)context)->ch_layout.nb_channels; } AUDIO_DECODER_FUNC(jint, ffmpegGetSampleRate, jlong context) { @@ -234,7 +237,7 @@ AUDIO_DECODER_FUNC(jlong, ffmpegReset, jlong jContext, jbyteArray extraData) { // Release and recreate the context if the codec is TrueHD. // TODO: Figure out why flushing doesn't work for this codec. releaseContext(context); - AVCodec *codec = avcodec_find_decoder(codecId); + const AVCodec *codec = avcodec_find_decoder(codecId); if (!codec) { LOGE("Unexpected error finding codec %d.", codecId); return 0L; @@ -256,17 +259,17 @@ AUDIO_DECODER_FUNC(void, ffmpegRelease, jlong context) { } } -AVCodec *getCodecByName(JNIEnv *env, jstring codecName) { +const AVCodec *getCodecByName(JNIEnv *env, jstring codecName) { if (!codecName) { return NULL; } const char *codecNameChars = env->GetStringUTFChars(codecName, NULL); - AVCodec *codec = avcodec_find_decoder_by_name(codecNameChars); + const AVCodec *codec = avcodec_find_decoder_by_name(codecNameChars); env->ReleaseStringUTFChars(codecName, codecNameChars); return codec; } -AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData, +AVCodecContext *createContext(JNIEnv *env, const AVCodec *codec, jbyteArray extraData, jboolean outputFloat, jint rawSampleRate, jint rawChannelCount) { AVCodecContext *context = avcodec_alloc_context3(codec); @@ -291,8 +294,7 @@ AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData, if (context->codec_id == AV_CODEC_ID_PCM_MULAW || context->codec_id == AV_CODEC_ID_PCM_ALAW) { context->sample_rate = rawSampleRate; - context->channels = rawChannelCount; - context->channel_layout = av_get_default_channel_layout(rawChannelCount); + av_channel_layout_default(&context->ch_layout, rawChannelCount); } context->err_recognition = AV_EF_IGNORE_ERR; int result = avcodec_open2(context, codec, NULL); @@ -335,25 +337,29 @@ int decodePacket(AVCodecContext *context, AVPacket *packet, // Resample output. AVSampleFormat sampleFormat = context->sample_fmt; - int channelCount = context->channels; - int channelLayout = context->channel_layout; + int channelCount = context->ch_layout.nb_channels; int sampleRate = context->sample_rate; int sampleCount = frame->nb_samples; int dataSize = av_samples_get_buffer_size(NULL, channelCount, sampleCount, sampleFormat, 1); - SwrContext *resampleContext; - if (context->opaque) { - resampleContext = (SwrContext *)context->opaque; - } else { - resampleContext = swr_alloc(); - av_opt_set_int(resampleContext, "in_channel_layout", channelLayout, 0); - av_opt_set_int(resampleContext, "out_channel_layout", channelLayout, 0); - av_opt_set_int(resampleContext, "in_sample_rate", sampleRate, 0); - av_opt_set_int(resampleContext, "out_sample_rate", sampleRate, 0); - av_opt_set_int(resampleContext, "in_sample_fmt", sampleFormat, 0); - // The output format is always the requested format. - av_opt_set_int(resampleContext, "out_sample_fmt", - context->request_sample_fmt, 0); + SwrContext *resampleContext = static_cast(context->opaque); + if (!resampleContext) { + result = swr_alloc_set_opts2( + &resampleContext, // ps + &context->ch_layout, // out_ch_layout + context->request_sample_fmt, // out_sample_fmt + sampleRate, // out_sample_rate + &context->ch_layout, // in_ch_layout + sampleFormat, // in_sample_fmt + sampleRate, // in_sample_rate + 0, // log_offset + NULL // log_ctx + ); + if (result < 0) { + logError("swr_alloc_set_opts2", result); + av_frame_free(&frame); + return transformError(result); + } result = swr_init(resampleContext); if (result < 0) { logError("swr_init", result);