mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Merge pull request #707 from equeim:ffmpeg-6.0
PiperOrigin-RevId: 584893190
This commit is contained in:
commit
45b51d8c97
@ -93,6 +93,8 @@
|
|||||||
([#710](https://github.com/androidx/media/pull/710)).
|
([#710](https://github.com/androidx/media/pull/710)).
|
||||||
* MIDI: Fix issue where seeking forward skips the Program Change events
|
* MIDI: Fix issue where seeking forward skips the Program Change events
|
||||||
([#704](https://github.com/androidx/media/issues/704).
|
([#704](https://github.com/androidx/media/issues/704).
|
||||||
|
* Migrate to FFmpeg 6.0
|
||||||
|
([#707](https://github.com/androidx/media/pull/707)).
|
||||||
* Leanback extension:
|
* Leanback extension:
|
||||||
* Cast Extension:
|
* Cast Extension:
|
||||||
* Sanitize creation of a `Timeline` to not crash the app when loading
|
* Sanitize creation of a `Timeline` to not crash the app when loading
|
||||||
|
@ -41,14 +41,15 @@ NDK_PATH="<path to Android NDK>"
|
|||||||
HOST_PLATFORM="linux-x86_64"
|
HOST_PLATFORM="linux-x86_64"
|
||||||
```
|
```
|
||||||
|
|
||||||
* Fetch FFmpeg and checkout an appropriate branch. We cannot guarantee
|
* 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>" && \
|
cd "<preferred location for ffmpeg>" && \
|
||||||
git clone git://source.ffmpeg.org/ffmpeg && \
|
git clone git://source.ffmpeg.org/ffmpeg && \
|
||||||
cd ffmpeg && \
|
cd ffmpeg && \
|
||||||
git checkout release/4.2 && \
|
git checkout release/6.0 && \
|
||||||
FFMPEG_PATH="$(pwd)"
|
FFMPEG_PATH="$(pwd)"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -21,12 +21,6 @@ set(CMAKE_CXX_STANDARD 11)
|
|||||||
|
|
||||||
project(libffmpegJNI C CXX)
|
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_location "${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg")
|
||||||
set(ffmpeg_binaries "${ffmpeg_location}/android-libs/${ANDROID_ABI}")
|
set(ffmpeg_binaries "${ffmpeg_location}/android-libs/${ANDROID_ABI}")
|
||||||
|
|
||||||
@ -56,3 +50,9 @@ target_link_libraries(ffmpegJNI
|
|||||||
PRIVATE avcodec
|
PRIVATE avcodec
|
||||||
PRIVATE avutil
|
PRIVATE avutil
|
||||||
PRIVATE ${android_log_lib})
|
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()
|
||||||
|
@ -35,9 +35,10 @@ COMMON_OPTIONS="
|
|||||||
--disable-postproc
|
--disable-postproc
|
||||||
--disable-avfilter
|
--disable-avfilter
|
||||||
--disable-symver
|
--disable-symver
|
||||||
--disable-avresample
|
|
||||||
--enable-swresample
|
--enable-swresample
|
||||||
--extra-ldexeflags=-pie
|
--extra-ldexeflags=-pie
|
||||||
|
--disable-v4l2-m2m
|
||||||
|
--disable-vulkan
|
||||||
"
|
"
|
||||||
TOOLCHAIN_PREFIX="${NDK_PATH}/toolchains/llvm/prebuilt/${HOST_PLATFORM}/bin"
|
TOOLCHAIN_PREFIX="${NDK_PATH}/toolchains/llvm/prebuilt/${HOST_PLATFORM}/bin"
|
||||||
for decoder in "${ENABLED_DECODERS[@]}"
|
for decoder in "${ENABLED_DECODERS[@]}"
|
||||||
|
@ -74,16 +74,16 @@ static jmethodID growOutputBufferMethod;
|
|||||||
/**
|
/**
|
||||||
* Returns the AVCodec with the specified name, or NULL if it is not available.
|
* 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
|
* 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.
|
* provided extraData as initialization data for the decoder if it is non-NULL.
|
||||||
* Returns the created context.
|
* Returns the created context.
|
||||||
*/
|
*/
|
||||||
AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
|
AVCodecContext *createContext(JNIEnv *env, const AVCodec *codec,
|
||||||
jboolean outputFloat, jint rawSampleRate,
|
jbyteArray extraData, jboolean outputFloat,
|
||||||
jint rawChannelCount);
|
jint rawSampleRate, jint rawChannelCount);
|
||||||
|
|
||||||
struct GrowOutputBufferCallback {
|
struct GrowOutputBufferCallback {
|
||||||
uint8_t *operator()(int requiredSize) const;
|
uint8_t *operator()(int requiredSize) const;
|
||||||
@ -137,7 +137,6 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
|||||||
LOGE("JNI_OnLoad: GetMethodID failed");
|
LOGE("JNI_OnLoad: GetMethodID failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
avcodec_register_all();
|
|
||||||
return JNI_VERSION_1_6;
|
return JNI_VERSION_1_6;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +155,7 @@ LIBRARY_FUNC(jboolean, ffmpegHasDecoder, jstring codecName) {
|
|||||||
AUDIO_DECODER_FUNC(jlong, ffmpegInitialize, jstring codecName,
|
AUDIO_DECODER_FUNC(jlong, ffmpegInitialize, jstring codecName,
|
||||||
jbyteArray extraData, jboolean outputFloat,
|
jbyteArray extraData, jboolean outputFloat,
|
||||||
jint rawSampleRate, jint rawChannelCount) {
|
jint rawSampleRate, jint rawChannelCount) {
|
||||||
AVCodec *codec = getCodecByName(env, codecName);
|
const AVCodec *codec = getCodecByName(env, codecName);
|
||||||
if (!codec) {
|
if (!codec) {
|
||||||
LOGE("Codec not found.");
|
LOGE("Codec not found.");
|
||||||
return 0L;
|
return 0L;
|
||||||
@ -186,13 +185,18 @@ AUDIO_DECODER_FUNC(jint, ffmpegDecode, jlong context, jobject inputData,
|
|||||||
}
|
}
|
||||||
uint8_t *inputBuffer = (uint8_t *)env->GetDirectBufferAddress(inputData);
|
uint8_t *inputBuffer = (uint8_t *)env->GetDirectBufferAddress(inputData);
|
||||||
uint8_t *outputBuffer = (uint8_t *)env->GetDirectBufferAddress(outputData);
|
uint8_t *outputBuffer = (uint8_t *)env->GetDirectBufferAddress(outputData);
|
||||||
AVPacket packet;
|
AVPacket *packet = av_packet_alloc();
|
||||||
av_init_packet(&packet);
|
if (!packet) {
|
||||||
packet.data = inputBuffer;
|
LOGE("Failed to allocate packet.");
|
||||||
packet.size = inputSize;
|
return -1;
|
||||||
return decodePacket((AVCodecContext *)context, &packet, outputBuffer,
|
}
|
||||||
outputSize,
|
packet->data = inputBuffer;
|
||||||
GrowOutputBufferCallback{env, thiz, decoderOutputBuffer});
|
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 {
|
uint8_t *GrowOutputBufferCallback::operator()(int requiredSize) const {
|
||||||
@ -211,7 +215,7 @@ AUDIO_DECODER_FUNC(jint, ffmpegGetChannelCount, jlong context) {
|
|||||||
LOGE("Context must be non-NULL.");
|
LOGE("Context must be non-NULL.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return ((AVCodecContext *)context)->channels;
|
return ((AVCodecContext *)context)->ch_layout.nb_channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
AUDIO_DECODER_FUNC(jint, ffmpegGetSampleRate, jlong context) {
|
AUDIO_DECODER_FUNC(jint, ffmpegGetSampleRate, jlong context) {
|
||||||
@ -234,7 +238,7 @@ AUDIO_DECODER_FUNC(jlong, ffmpegReset, jlong jContext, jbyteArray extraData) {
|
|||||||
// Release and recreate the context if the codec is TrueHD.
|
// Release and recreate the context if the codec is TrueHD.
|
||||||
// TODO: Figure out why flushing doesn't work for this codec.
|
// TODO: Figure out why flushing doesn't work for this codec.
|
||||||
releaseContext(context);
|
releaseContext(context);
|
||||||
AVCodec *codec = avcodec_find_decoder(codecId);
|
const AVCodec *codec = avcodec_find_decoder(codecId);
|
||||||
if (!codec) {
|
if (!codec) {
|
||||||
LOGE("Unexpected error finding codec %d.", codecId);
|
LOGE("Unexpected error finding codec %d.", codecId);
|
||||||
return 0L;
|
return 0L;
|
||||||
@ -256,19 +260,19 @@ AUDIO_DECODER_FUNC(void, ffmpegRelease, jlong context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AVCodec *getCodecByName(JNIEnv *env, jstring codecName) {
|
const AVCodec *getCodecByName(JNIEnv *env, jstring codecName) {
|
||||||
if (!codecName) {
|
if (!codecName) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
const char *codecNameChars = env->GetStringUTFChars(codecName, 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);
|
env->ReleaseStringUTFChars(codecName, codecNameChars);
|
||||||
return codec;
|
return codec;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
|
AVCodecContext *createContext(JNIEnv *env, const AVCodec *codec,
|
||||||
jboolean outputFloat, jint rawSampleRate,
|
jbyteArray extraData, jboolean outputFloat,
|
||||||
jint rawChannelCount) {
|
jint rawSampleRate, jint rawChannelCount) {
|
||||||
AVCodecContext *context = avcodec_alloc_context3(codec);
|
AVCodecContext *context = avcodec_alloc_context3(codec);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
LOGE("Failed to allocate context.");
|
LOGE("Failed to allocate context.");
|
||||||
@ -291,8 +295,7 @@ AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
|
|||||||
if (context->codec_id == AV_CODEC_ID_PCM_MULAW ||
|
if (context->codec_id == AV_CODEC_ID_PCM_MULAW ||
|
||||||
context->codec_id == AV_CODEC_ID_PCM_ALAW) {
|
context->codec_id == AV_CODEC_ID_PCM_ALAW) {
|
||||||
context->sample_rate = rawSampleRate;
|
context->sample_rate = rawSampleRate;
|
||||||
context->channels = rawChannelCount;
|
av_channel_layout_default(&context->ch_layout, rawChannelCount);
|
||||||
context->channel_layout = av_get_default_channel_layout(rawChannelCount);
|
|
||||||
}
|
}
|
||||||
context->err_recognition = AV_EF_IGNORE_ERR;
|
context->err_recognition = AV_EF_IGNORE_ERR;
|
||||||
int result = avcodec_open2(context, codec, NULL);
|
int result = avcodec_open2(context, codec, NULL);
|
||||||
@ -335,25 +338,29 @@ int decodePacket(AVCodecContext *context, AVPacket *packet,
|
|||||||
|
|
||||||
// Resample output.
|
// Resample output.
|
||||||
AVSampleFormat sampleFormat = context->sample_fmt;
|
AVSampleFormat sampleFormat = context->sample_fmt;
|
||||||
int channelCount = context->channels;
|
int channelCount = context->ch_layout.nb_channels;
|
||||||
int channelLayout = context->channel_layout;
|
|
||||||
int sampleRate = context->sample_rate;
|
int sampleRate = context->sample_rate;
|
||||||
int sampleCount = frame->nb_samples;
|
int sampleCount = frame->nb_samples;
|
||||||
int dataSize = av_samples_get_buffer_size(NULL, channelCount, sampleCount,
|
int dataSize = av_samples_get_buffer_size(NULL, channelCount, sampleCount,
|
||||||
sampleFormat, 1);
|
sampleFormat, 1);
|
||||||
SwrContext *resampleContext;
|
SwrContext *resampleContext = static_cast<SwrContext *>(context->opaque);
|
||||||
if (context->opaque) {
|
if (!resampleContext) {
|
||||||
resampleContext = (SwrContext *)context->opaque;
|
result =
|
||||||
} else {
|
swr_alloc_set_opts2(&resampleContext, // ps
|
||||||
resampleContext = swr_alloc();
|
&context->ch_layout, // out_ch_layout
|
||||||
av_opt_set_int(resampleContext, "in_channel_layout", channelLayout, 0);
|
context->request_sample_fmt, // out_sample_fmt
|
||||||
av_opt_set_int(resampleContext, "out_channel_layout", channelLayout, 0);
|
sampleRate, // out_sample_rate
|
||||||
av_opt_set_int(resampleContext, "in_sample_rate", sampleRate, 0);
|
&context->ch_layout, // in_ch_layout
|
||||||
av_opt_set_int(resampleContext, "out_sample_rate", sampleRate, 0);
|
sampleFormat, // in_sample_fmt
|
||||||
av_opt_set_int(resampleContext, "in_sample_fmt", sampleFormat, 0);
|
sampleRate, // in_sample_rate
|
||||||
// The output format is always the requested format.
|
0, // log_offset
|
||||||
av_opt_set_int(resampleContext, "out_sample_fmt",
|
NULL // log_ctx
|
||||||
context->request_sample_fmt, 0);
|
);
|
||||||
|
if (result < 0) {
|
||||||
|
logError("swr_alloc_set_opts2", result);
|
||||||
|
av_frame_free(&frame);
|
||||||
|
return transformError(result);
|
||||||
|
}
|
||||||
result = swr_init(resampleContext);
|
result = swr_init(resampleContext);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
logError("swr_init", result);
|
logError("swr_init", result);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user