@@ -35,6 +35,8 @@ extern "C" {
35
35
#define LOG_TAG " ffmpeg_jni"
36
36
#define LOGE (...) \
37
37
((void )__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
38
+ #define LOGD (...) \
39
+ ((void )__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
38
40
39
41
#define LIBRARY_FUNC (RETURN_TYPE, NAME, ...) \
40
42
extern " C" { \
@@ -67,6 +69,8 @@ static const AVSampleFormat OUTPUT_FORMAT_PCM_FLOAT = AV_SAMPLE_FMT_FLT;
67
69
static const int AUDIO_DECODER_ERROR_INVALID_DATA = -1 ;
68
70
static const int AUDIO_DECODER_ERROR_OTHER = -2 ;
69
71
72
+ static jmethodID growOutputBufferMethod;
73
+
70
74
/* *
71
75
* Returns the AVCodec with the specified name, or NULL if it is not available.
72
76
*/
@@ -81,13 +85,22 @@ AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
81
85
jboolean outputFloat, jint rawSampleRate,
82
86
jint rawChannelCount);
83
87
88
+ struct GrowOutputBufferCallback {
89
+ uint8_t *operator ()(int requiredSize) const ;
90
+
91
+ JNIEnv *env;
92
+ jobject thiz;
93
+ jobject decoderOutputBuffer;
94
+ };
95
+
84
96
/* *
85
97
* Decodes the packet into the output buffer, returning the number of bytes
86
98
* written, or a negative AUDIO_DECODER_ERROR constant value in the case of an
87
99
* error.
88
100
*/
89
101
int decodePacket (AVCodecContext *context, AVPacket *packet,
90
- uint8_t *outputBuffer, int outputSize);
102
+ uint8_t *outputBuffer, int outputSize,
103
+ GrowOutputBufferCallback growBuffer);
91
104
92
105
/* *
93
106
* Transforms ffmpeg AVERROR into a negative AUDIO_DECODER_ERROR constant value.
@@ -107,6 +120,21 @@ void releaseContext(AVCodecContext *context);
107
120
jint JNI_OnLoad (JavaVM *vm, void *reserved) {
108
121
JNIEnv *env;
109
122
if (vm->GetEnv (reinterpret_cast <void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
123
+ LOGE (" JNI_OnLoad: GetEnv failed" );
124
+ return -1 ;
125
+ }
126
+ jclass clazz =
127
+ env->FindClass (" androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder" );
128
+ if (!clazz) {
129
+ LOGE (" JNI_OnLoad: FindClass failed" );
130
+ return -1 ;
131
+ }
132
+ growOutputBufferMethod =
133
+ env->GetMethodID (clazz, " growOutputBuffer" ,
134
+ " (Landroidx/media3/decoder/"
135
+ " SimpleDecoderOutputBuffer;I)Ljava/nio/ByteBuffer;" );
136
+ if (!growOutputBufferMethod) {
137
+ LOGE (" JNI_OnLoad: GetMethodID failed" );
110
138
return -1 ;
111
139
}
112
140
avcodec_register_all ();
@@ -138,12 +166,13 @@ AUDIO_DECODER_FUNC(jlong, ffmpegInitialize, jstring codecName,
138
166
}
139
167
140
168
AUDIO_DECODER_FUNC (jint, ffmpegDecode, jlong context, jobject inputData,
141
- jint inputSize, jobject outputData, jint outputSize) {
169
+ jint inputSize, jobject decoderOutputBuffer,
170
+ jobject outputData, jint outputSize) {
142
171
if (!context) {
143
172
LOGE (" Context must be non-NULL." );
144
173
return -1 ;
145
174
}
146
- if (!inputData || !outputData) {
175
+ if (!inputData || !decoderOutputBuffer || ! outputData) {
147
176
LOGE (" Input and output buffers must be non-NULL." );
148
177
return -1 ;
149
178
}
@@ -162,7 +191,19 @@ AUDIO_DECODER_FUNC(jint, ffmpegDecode, jlong context, jobject inputData,
162
191
packet.data = inputBuffer;
163
192
packet.size = inputSize;
164
193
return decodePacket ((AVCodecContext *)context, &packet, outputBuffer,
165
- outputSize);
194
+ outputSize,
195
+ GrowOutputBufferCallback{env, thiz, decoderOutputBuffer});
196
+ }
197
+
198
+ uint8_t *GrowOutputBufferCallback::operator ()(int requiredSize) const {
199
+ jobject newOutputData = env->CallObjectMethod (
200
+ thiz, growOutputBufferMethod, decoderOutputBuffer, requiredSize);
201
+ if (env->ExceptionCheck ()) {
202
+ LOGE (" growOutputBuffer() failed" );
203
+ env->ExceptionDescribe ();
204
+ return nullptr ;
205
+ }
206
+ return static_cast <uint8_t *>(env->GetDirectBufferAddress (newOutputData));
166
207
}
167
208
168
209
AUDIO_DECODER_FUNC (jint, ffmpegGetChannelCount, jlong context) {
@@ -264,7 +305,8 @@ AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
264
305
}
265
306
266
307
int decodePacket (AVCodecContext *context, AVPacket *packet,
267
- uint8_t *outputBuffer, int outputSize) {
308
+ uint8_t *outputBuffer, int outputSize,
309
+ GrowOutputBufferCallback growBuffer) {
268
310
int result = 0 ;
269
311
// Queue input data.
270
312
result = avcodec_send_packet (context, packet);
@@ -320,15 +362,22 @@ int decodePacket(AVCodecContext *context, AVPacket *packet,
320
362
}
321
363
context->opaque = resampleContext;
322
364
}
323
- int inSampleSize = av_get_bytes_per_sample (sampleFormat);
365
+
324
366
int outSampleSize = av_get_bytes_per_sample (context->request_sample_fmt );
325
367
int outSamples = swr_get_out_samples (resampleContext, sampleCount);
326
368
int bufferOutSize = outSampleSize * channelCount * outSamples;
327
369
if (outSize + bufferOutSize > outputSize) {
328
- LOGE (" Output buffer size (%d) too small for output data (%d)." ,
329
- outputSize, outSize + bufferOutSize);
330
- av_frame_free (&frame);
331
- return AUDIO_DECODER_ERROR_INVALID_DATA;
370
+ LOGD (
371
+ " Output buffer size (%d) too small for output data (%d), "
372
+ " reallocating buffer." ,
373
+ outputSize, outSize + bufferOutSize);
374
+ outputSize = outSize + bufferOutSize;
375
+ outputBuffer = growBuffer (outputSize);
376
+ if (!outputBuffer) {
377
+ LOGE (" Failed to reallocate output buffer." );
378
+ av_frame_free (&frame);
379
+ return AUDIO_DECODER_ERROR_OTHER;
380
+ }
332
381
}
333
382
result = swr_convert (resampleContext, &outputBuffer, bufferOutSize,
334
383
(const uint8_t **)frame->data , frame->nb_samples );
0 commit comments