summaryrefslogtreecommitdiff
path: root/tree/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tree/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc')
-rw-r--r--tree/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc363
1 files changed, 0 insertions, 363 deletions
diff --git a/tree/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc b/tree/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc
deleted file mode 100644
index adbf515f..00000000
--- a/tree/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <jni.h>
-#include <stdlib.h>
-#include <android/log.h>
-
-extern "C" {
-#ifdef __cplusplus
-#define __STDC_CONSTANT_MACROS
-#ifdef _STDINT_H
-#undef _STDINT_H
-#endif
-#include <stdint.h>
-#endif
-#include <libavcodec/avcodec.h>
-#include <libavutil/channel_layout.h>
-#include <libavutil/error.h>
-#include <libavutil/opt.h>
-#include <libswresample/swresample.h>
-}
-
-#define LOG_TAG "ffmpeg_jni"
-#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, \
- __VA_ARGS__))
-
-#define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
- extern "C" { \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegLibrary_##NAME( \
- JNIEnv *env, jobject thiz, ##__VA_ARGS__); \
- } \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegLibrary_##NAME( \
- JNIEnv *env, jobject thiz, ##__VA_ARGS__)
-
-#define AUDIO_DECODER_FUNC(RETURN_TYPE, NAME, ...) \
- extern "C" { \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegAudioDecoder_##NAME( \
- JNIEnv *env, jobject thiz, ##__VA_ARGS__); \
- } \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegAudioDecoder_##NAME( \
- JNIEnv *env, jobject thiz, ##__VA_ARGS__)
-
-#define ERROR_STRING_BUFFER_LENGTH 256
-
-// Output format corresponding to AudioFormat.ENCODING_PCM_16BIT.
-static const AVSampleFormat OUTPUT_FORMAT_PCM_16BIT = AV_SAMPLE_FMT_S16;
-// Output format corresponding to AudioFormat.ENCODING_PCM_FLOAT.
-static const AVSampleFormat OUTPUT_FORMAT_PCM_FLOAT = AV_SAMPLE_FMT_FLT;
-
-// LINT.IfChange
-static const int AUDIO_DECODER_ERROR_INVALID_DATA = -1;
-static const int AUDIO_DECODER_ERROR_OTHER = -2;
-// LINT.ThenChange(../java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioDecoder.java)
-
-/**
- * Returns the AVCodec with the specified name, or NULL if it is not available.
- */
-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,
- jboolean outputFloat, jint rawSampleRate,
- jint rawChannelCount);
-
-/**
- * Decodes the packet into the output buffer, returning the number of bytes
- * written, or a negative AUDIO_DECODER_ERROR constant value in the case of an
- * error.
- */
-int decodePacket(AVCodecContext *context, AVPacket *packet,
- uint8_t *outputBuffer, int outputSize);
-
-/**
- * Outputs a log message describing the avcodec error number.
- */
-void logError(const char *functionName, int errorNumber);
-
-/**
- * Releases the specified context.
- */
-void releaseContext(AVCodecContext *context);
-
-jint JNI_OnLoad(JavaVM *vm, void *reserved) {
- JNIEnv *env;
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
- return -1;
- }
- avcodec_register_all();
- return JNI_VERSION_1_6;
-}
-
-LIBRARY_FUNC(jstring, ffmpegGetVersion) {
- return env->NewStringUTF(LIBAVCODEC_IDENT);
-}
-
-LIBRARY_FUNC(jboolean, ffmpegHasDecoder, jstring codecName) {
- return getCodecByName(env, codecName) != NULL;
-}
-
-AUDIO_DECODER_FUNC(jlong, ffmpegInitialize, jstring codecName,
- jbyteArray extraData, jboolean outputFloat,
- jint rawSampleRate, jint rawChannelCount) {
- AVCodec *codec = getCodecByName(env, codecName);
- if (!codec) {
- LOGE("Codec not found.");
- return 0L;
- }
- return (jlong)createContext(env, codec, extraData, outputFloat, rawSampleRate,
- rawChannelCount);
-}
-
-AUDIO_DECODER_FUNC(jint, ffmpegDecode, jlong context, jobject inputData,
- jint inputSize, jobject outputData, jint outputSize) {
- if (!context) {
- LOGE("Context must be non-NULL.");
- return -1;
- }
- if (!inputData || !outputData) {
- LOGE("Input and output buffers must be non-NULL.");
- return -1;
- }
- if (inputSize < 0) {
- LOGE("Invalid input buffer size: %d.", inputSize);
- return -1;
- }
- if (outputSize < 0) {
- LOGE("Invalid output buffer length: %d", outputSize);
- return -1;
- }
- 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);
-}
-
-AUDIO_DECODER_FUNC(jint, ffmpegGetChannelCount, jlong context) {
- if (!context) {
- LOGE("Context must be non-NULL.");
- return -1;
- }
- return ((AVCodecContext *) context)->channels;
-}
-
-AUDIO_DECODER_FUNC(jint, ffmpegGetSampleRate, jlong context) {
- if (!context) {
- LOGE("Context must be non-NULL.");
- return -1;
- }
- return ((AVCodecContext *) context)->sample_rate;
-}
-
-AUDIO_DECODER_FUNC(jlong, ffmpegReset, jlong jContext, jbyteArray extraData) {
- AVCodecContext *context = (AVCodecContext *) jContext;
- if (!context) {
- LOGE("Tried to reset without a context.");
- return 0L;
- }
-
- AVCodecID codecId = context->codec_id;
- if (codecId == AV_CODEC_ID_TRUEHD) {
- // 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);
- if (!codec) {
- LOGE("Unexpected error finding codec %d.", codecId);
- return 0L;
- }
- jboolean outputFloat =
- (jboolean)(context->request_sample_fmt == OUTPUT_FORMAT_PCM_FLOAT);
- return (jlong)createContext(env, codec, extraData, outputFloat,
- /* rawSampleRate= */ -1,
- /* rawChannelCount= */ -1);
- }
-
- avcodec_flush_buffers(context);
- return (jlong) context;
-}
-
-AUDIO_DECODER_FUNC(void, ffmpegRelease, jlong context) {
- if (context) {
- releaseContext((AVCodecContext *) context);
- }
-}
-
-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);
- env->ReleaseStringUTFChars(codecName, codecNameChars);
- return codec;
-}
-
-AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
- jboolean outputFloat, jint rawSampleRate,
- jint rawChannelCount) {
- AVCodecContext *context = avcodec_alloc_context3(codec);
- if (!context) {
- LOGE("Failed to allocate context.");
- return NULL;
- }
- context->request_sample_fmt =
- outputFloat ? OUTPUT_FORMAT_PCM_FLOAT : OUTPUT_FORMAT_PCM_16BIT;
- if (extraData) {
- jsize size = env->GetArrayLength(extraData);
- context->extradata_size = size;
- context->extradata =
- (uint8_t *) av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (!context->extradata) {
- LOGE("Failed to allocate extradata.");
- releaseContext(context);
- return NULL;
- }
- env->GetByteArrayRegion(extraData, 0, size, (jbyte *) context->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);
- }
- context->err_recognition = AV_EF_IGNORE_ERR;
- int result = avcodec_open2(context, codec, NULL);
- if (result < 0) {
- logError("avcodec_open2", result);
- releaseContext(context);
- return NULL;
- }
- return context;
-}
-
-int decodePacket(AVCodecContext *context, AVPacket *packet,
- uint8_t *outputBuffer, int outputSize) {
- int result = 0;
- // Queue input data.
- result = avcodec_send_packet(context, packet);
- if (result) {
- logError("avcodec_send_packet", result);
- return result == AVERROR_INVALIDDATA ? AUDIO_DECODER_ERROR_INVALID_DATA
- : AUDIO_DECODER_ERROR_OTHER;
- }
-
- // Dequeue output data until it runs out.
- int outSize = 0;
- while (true) {
- AVFrame *frame = av_frame_alloc();
- if (!frame) {
- LOGE("Failed to allocate output frame.");
- return -1;
- }
- result = avcodec_receive_frame(context, frame);
- if (result) {
- av_frame_free(&frame);
- if (result == AVERROR(EAGAIN)) {
- break;
- }
- logError("avcodec_receive_frame", result);
- return result;
- }
-
- // Resample output.
- AVSampleFormat sampleFormat = context->sample_fmt;
- int channelCount = context->channels;
- int channelLayout = context->channel_layout;
- 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);
- result = swr_init(resampleContext);
- if (result < 0) {
- logError("swr_init", result);
- av_frame_free(&frame);
- return -1;
- }
- context->opaque = resampleContext;
- }
- int inSampleSize = av_get_bytes_per_sample(sampleFormat);
- int outSampleSize = av_get_bytes_per_sample(context->request_sample_fmt);
- int outSamples = swr_get_out_samples(resampleContext, sampleCount);
- int bufferOutSize = outSampleSize * channelCount * outSamples;
- if (outSize + bufferOutSize > outputSize) {
- LOGE("Output buffer size (%d) too small for output data (%d).",
- outputSize, outSize + bufferOutSize);
- av_frame_free(&frame);
- return -1;
- }
- result = swr_convert(resampleContext, &outputBuffer, bufferOutSize,
- (const uint8_t **)frame->data, frame->nb_samples);
- av_frame_free(&frame);
- if (result < 0) {
- logError("swr_convert", result);
- return result;
- }
- int available = swr_get_out_samples(resampleContext, 0);
- if (available != 0) {
- LOGE("Expected no samples remaining after resampling, but found %d.",
- available);
- return -1;
- }
- outputBuffer += bufferOutSize;
- outSize += bufferOutSize;
- }
- return outSize;
-}
-
-void logError(const char *functionName, int errorNumber) {
- char *buffer = (char *) malloc(ERROR_STRING_BUFFER_LENGTH * sizeof(char));
- av_strerror(errorNumber, buffer, ERROR_STRING_BUFFER_LENGTH);
- LOGE("Error in %s: %s", functionName, buffer);
- free(buffer);
-}
-
-void releaseContext(AVCodecContext *context) {
- if (!context) {
- return;
- }
- SwrContext *swrContext;
- if ((swrContext = (SwrContext *)context->opaque)) {
- swr_free(&swrContext);
- context->opaque = NULL;
- }
- avcodec_free_context(&context);
-}
-