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