Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to FFmpeg 6.0 #707

Merged
merged 3 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions libraries/decoder_ffmpeg/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<preferred location for ffmpeg>" && \
git clone git://source.ffmpeg.org/ffmpeg && \
cd ffmpeg && \
git checkout release/4.2 && \
git checkout release/6.0 && \
FFMPEG_PATH="$(pwd)"
```

Expand Down
12 changes: 6 additions & 6 deletions libraries/decoder_ffmpeg/src/main/jni/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}")

Expand Down Expand Up @@ -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()
3 changes: 2 additions & 1 deletion libraries/decoder_ffmpeg/src/main/jni/build_ffmpeg.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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[@]}"
Expand Down
72 changes: 39 additions & 33 deletions libraries/decoder_ffmpeg/src/main/jni/ffmpeg_jni.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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) {
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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<SwrContext *>(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);
Expand Down