27static const int CALLBACK_BUFFERSIZE = 32 * 1024;
29static jmethodID limit_MID = NULL;
30static jmethodID ffAudioFileFormat_MID = NULL;
43 JNIEnv *env, jclass readerClass) {
45 fprintf(stderr,
"FFAudioFileReader.c initIDs(env)\n");
48 jclass bufferClass = (*env)->FindClass(env,
"java/nio/Buffer");
51 fprintf(stderr,
"Failed to find java/nio/Buffer\n");
55 jclass ffAudioFileFormat_class =
56 (*env)->FindClass(env,
"com/tagtraum/ffsampledsp/FFAudioFileFormat");
57 if (!ffAudioFileFormat_class) {
60 "Failed to find com/tagtraum/ffsampledsp/FFAudioFileFormat\n");
65 limit_MID = (*env)->GetMethodID(env, bufferClass,
"limit",
"()I");
68 fprintf(stderr,
"Failed to find limit method\n");
72 ffAudioFileFormat_MID = (*env)->GetMethodID(
73 env, ffAudioFileFormat_class,
"<init>",
74 "(Ljava/lang/String;IFIIIFIZJILjava/lang/Boolean;Z)V");
76 if (!ffAudioFileFormat_MID)
77 fprintf(stderr,
"Failed to find constructor of AudioFileFormat\n");
99static int read_callback(
void *opaque, uint8_t *buf,
int size) {
102 uint8_t *java_buffer = NULL;
112 (uint8_t *)(*callback->
env)
117 "Failed to get address for byte buffer");
124 if ((*callback->
env)->ExceptionCheck(callback->
env)) {
129 if (availableData > size)
130 availableData = size;
132 memcpy(buf, (
const uint8_t *)java_buffer, availableData);
148static int is_pcm(
enum AVCodecID
id) {
151 case AV_CODEC_ID_PCM_S8:
152 case AV_CODEC_ID_PCM_U8:
153 case AV_CODEC_ID_PCM_S16BE:
154 case AV_CODEC_ID_PCM_S16LE:
155 case AV_CODEC_ID_PCM_U16BE:
156 case AV_CODEC_ID_PCM_U16LE:
157 case AV_CODEC_ID_PCM_S24BE:
158 case AV_CODEC_ID_PCM_S24LE:
159 case AV_CODEC_ID_PCM_S32BE:
160 case AV_CODEC_ID_PCM_S32LE:
161 case AV_CODEC_ID_PCM_U32BE:
162 case AV_CODEC_ID_PCM_U32LE:
163 case AV_CODEC_ID_PCM_F32BE:
164 case AV_CODEC_ID_PCM_F32LE:
165 case AV_CODEC_ID_PCM_F64BE:
166 case AV_CODEC_ID_PCM_F64LE:
167 case AV_CODEC_ID_PCM_F16LE:
168 case AV_CODEC_ID_PCM_F24LE:
171 fprintf(stderr,
"is PCM\n");
177 fprintf(stderr,
"is NOT PCM\n");
190static jlong duration(AVFormatContext *format_context, AVStream *stream) {
191 jlong duration_in_microseconds = -1;
193 if (format_context->duration != AV_NOPTS_VALUE) {
194 int64_t micro_seconds_base = AV_TIME_BASE / 1000000;
195 duration_in_microseconds =
196 (jlong)(format_context->duration / micro_seconds_base);
198 fprintf(stderr,
"format_context->duration: %i\n", format_context->duration);
199 fprintf(stderr,
"duration_in_microseconds 0: %li\n",
200 duration_in_microseconds);
204 if (stream->nb_frames != 0 && duration_in_microseconds <= 0 &&
205 stream->codecpar->sample_rate > 0) {
206 duration_in_microseconds =
207 stream->nb_frames * 1000000L / stream->codecpar->sample_rate;
209 fprintf(stderr,
"stream->nb_frames: %i\n", stream->nb_frames);
210 fprintf(stderr,
"stream->codecpar->sample_rate: %f\n",
211 stream->codecpar->sample_rate);
212 fprintf(stderr,
"duration_in_microseconds 1: %i\n",
213 duration_in_microseconds);
217 fprintf(stderr,
"duration_in_microseconds final: %i\n",
218 duration_in_microseconds);
220 return duration_in_microseconds;
230static jfloat get_frame_rate(AVStream *stream, jlong duration) {
231 jfloat frame_rate = -1;
233 if (frame_rate <= 0 && stream->nb_frames > 0 && duration > 0) {
234 frame_rate = stream->nb_frames * 1000000LL / (jfloat)duration;
237 if (frame_rate <= 0 && stream->codecpar->frame_size > 0 &&
238 stream->codecpar->sample_rate > 0) {
239 frame_rate = (jfloat)stream->codecpar->sample_rate /
240 (jfloat)stream->codecpar->frame_size;
243 if (frame_rate <= 0 && stream->codecpar->frame_size == 0 &&
244 is_pcm(stream->codecpar->codec_id) && stream->codecpar->sample_rate > 0) {
245 frame_rate = (jfloat)stream->codecpar->sample_rate;
249 if (stream->nb_frames > 0 && duration > 0) {
250 fprintf(stderr,
"1 frame rate : %f\n",
251 stream->nb_frames * 1000000LL / (jfloat)duration);
253 if (stream->codecpar->frame_size > 0 && stream->codecpar->sample_rate > 0) {
254 fprintf(stderr,
"2 frame rate : %f\n",
255 (jfloat)stream->codecpar->sample_rate /
256 (jfloat)stream->codecpar->frame_size);
257 fprintf(stderr,
"frame_size : %f, sample_rate : %f\n",
258 (jfloat)stream->codecpar->frame_size,
259 (jfloat)stream->codecpar->sample_rate);
261 if (stream->codecpar->frame_size == 0 && is_pcm(stream->codecpar->codec_id) &&
262 stream->codecpar->sample_rate > 0) {
263 fprintf(stderr,
"3 frame rate : %f\n",
264 (jfloat)stream->codecpar->sample_rate);
278static jobject create_ffaudiofileformat(
279 JNIEnv *env, jclass ffAudioFileFormat_class, jstring url, jint codecId,
280 jfloat sampleRate, jint sampleSize, jint channels, jint frame_size,
281 jfloat frame_rate, jint frame_length, jboolean big_endian, jlong duration,
282 jint bitRate, jobject vbr, jboolean encrypted) {
284 return (*env)->NewObject(env, ffAudioFileFormat_class, ffAudioFileFormat_MID,
285 url, codecId, sampleRate, sampleSize, channels,
286 frame_size, frame_rate, frame_length, big_endian,
287 duration, bitRate, vbr, encrypted);
290static int create_ffaudiofileformats(JNIEnv *env,
291 AVFormatContext *format_context,
292 jobjectArray *array, jstring url) {
295 jlong duration_in_microseconds = -1;
296 jfloat frame_rate = -1;
297 jint frame_size = -1;
298 jint sample_size = -1;
299 jint frame_length = -1;
300 int audio_stream_count = 0;
301 int audio_stream_number = 0;
302 jboolean encrypted = 0;
304 jboolean big_endian = 1;
305 jobject audio_format = NULL;
306 jclass audioFileFormat_class = NULL;
307 jclass ffAudioFileFormat_class = NULL;
311 for (i = 0; i < format_context->nb_streams; i++) {
312 AVStream *stream = format_context->streams[i];
313 if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
314 audio_stream_count++;
319 fprintf(stderr,
"Found %i audio streams.\n", audio_stream_count);
323 if (audio_stream_count == 0) {
325 "Failed to find audio stream");
330 audioFileFormat_class =
331 (*env)->FindClass(env,
"javax/sound/sampled/AudioFileFormat");
332 if (!audioFileFormat_class) {
336 ffAudioFileFormat_class =
337 (*env)->FindClass(env,
"com/tagtraum/ffsampledsp/FFAudioFileFormat");
338 if (!ffAudioFileFormat_class) {
344 *array = (*env)->NewObjectArray(env, audio_stream_count,
345 audioFileFormat_class, NULL);
346 if (*array == NULL) {
352 fprintf(stderr,
"Created audio file format array.\n");
356 for (i = 0; i < format_context->nb_streams; i++) {
357 AVStream *stream = format_context->streams[i];
358 if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
365 duration_in_microseconds = duration(format_context, stream);
366 frame_rate = get_frame_rate(stream, duration_in_microseconds);
368 pcm = is_pcm(stream->codecpar->codec_id);
371 frame_size = (stream->codecpar->bits_per_coded_sample / 8) *
372 stream->codecpar->ch_layout.nb_channels;
376 if (stream->nb_frames > 0) {
377 frame_length = stream->nb_frames;
378 }
else if (duration_in_microseconds > 0 && pcm &&
379 stream->codecpar->sample_rate > 0) {
381 (jint)round(duration_in_microseconds *
382 stream->codecpar->sample_rate / 1000. / 1000.);
383 }
else if (duration_in_microseconds > 0 &&
384 stream->codecpar->frame_size > 0 &&
385 stream->codecpar->sample_rate > 0) {
386 frame_length = (jint)round(
387 duration_in_microseconds * stream->codecpar->sample_rate /
388 stream->codecpar->frame_size / 1000. / 1000.);
390 duration_in_microseconds = (jlong)(1000. * 1000. * frame_length *
391 stream->codecpar->frame_size /
392 stream->codecpar->sample_rate);
397 sample_size = stream->codecpar->bits_per_raw_sample
398 ? stream->codecpar->bits_per_raw_sample
399 : stream->codecpar->bits_per_coded_sample;
400 sample_size = sample_size <= 0 ? -1 : sample_size;
405 fprintf(stderr,
"stream->codecpar->bits_per_coded_sample: %i\n",
406 stream->codecpar->bits_per_coded_sample);
407 fprintf(stderr,
"stream->codecpar->bits_per_raw_sample : %i\n",
408 stream->codecpar->bits_per_raw_sample);
409 fprintf(stderr,
"stream->codecpar->bit_rate : %lli\n",
410 stream->codecpar->bit_rate);
411 fprintf(stderr,
"stream->codecpar->codec_tag=drms : %i\n",
413 fprintf(stderr,
"format_context->packet_size : %i\n",
414 format_context->packet_size);
415 fprintf(stderr,
"sample_rate : %i\n", stream->codecpar->sample_rate);
416 fprintf(stderr,
"sampleSize : %i\n", sample_size);
417 fprintf(stderr,
"channels : %i\n",
418 stream->codecpar->ch_layout.nb_channels);
419 fprintf(stderr,
"frame_size : %i\n", (
int)frame_size);
420 fprintf(stderr,
"frame_rate : %f\n", frame_rate);
421 fprintf(stderr,
"frame_length: %i\n", frame_length);
422 fprintf(stderr,
"codec_id : %i\n", stream->codecpar->codec_id);
423 fprintf(stderr,
"duration : %li\n", duration_in_microseconds);
425 fprintf(stderr,
"big_endian : true\n");
427 fprintf(stderr,
"big_endian : false\n");
431 audio_format = create_ffaudiofileformat(
432 env, ffAudioFileFormat_class, url, stream->codecpar->codec_id,
433 (jfloat)stream->codecpar->sample_rate, sample_size,
434 stream->codecpar->ch_layout.nb_channels, frame_size, frame_rate,
435 frame_length, big_endian, duration_in_microseconds,
436 stream->codecpar->bit_rate, vbr, encrypted);
442 (*env)->SetObjectArrayElement(env, *array, audio_stream_number,
445 (*env)->DeleteLocalRef(env, audio_format);
446 audio_stream_number++;
451 if (res < 0 && *array) {
453 (*env)->DeleteLocalRef(env, *array);
456 if (ffAudioFileFormat_class)
457 (*env)->DeleteLocalRef(env, ffAudioFileFormat_class);
458 if (audioFileFormat_class)
459 (*env)->DeleteLocalRef(env, audioFileFormat_class);
471JNIEXPORT jobjectArray JNICALL
473 JNIEnv *env, jobject instance, jstring url, jbyteArray urlBytes) {
476 fprintf(stderr,
"openFromUrl_1\n");
480 AVFormatContext *format_context = NULL;
481 jobjectArray array = NULL;
482 char *input_url = NULL;
485 jsize urlLen = (*env)->GetArrayLength(env, urlBytes);
486 jbyte *urlBuf = (*env)->GetByteArrayElements(env, urlBytes, NULL);
487 input_url = (
char *)malloc(urlLen + 1);
489 (*env)->ReleaseByteArrayElements(env, urlBytes, urlBuf, JNI_ABORT);
491 "Failed to allocate URL buffer");
494 memcpy(input_url, urlBuf, urlLen);
495 input_url[urlLen] =
'\0';
496 (*env)->ReleaseByteArrayElements(env, urlBytes, urlBuf, JNI_ABORT);
504 res = create_ffaudiofileformats(env, format_context, &array, url);
510 if (format_context) {
511 avformat_close_input(&format_context);
526JNIEXPORT jobjectArray JNICALL
528 JNIEnv *env, jobject instance, jobject byte_buffer) {
530 AVFormatContext *format_context = NULL;
531 jobjectArray array = NULL;
533 unsigned char *callbackBuffer = NULL;
535 AVIOContext *io_context;
539 res = AVERROR(ENOMEM);
547 format_context = avformat_alloc_context();
548 if (!format_context) {
549 res = AVERROR(ENOMEM);
555 format_context->probesize = 8 * 1024;
556 format_context->max_analyze_duration = 5 * AV_TIME_BASE;
559 (
unsigned char *)av_malloc(CALLBACK_BUFFERSIZE *
sizeof(uint8_t));
560 if (!callbackBuffer) {
561 res = AVERROR(ENOMEM);
566 io_context = avio_alloc_context(
576 av_free(callbackBuffer);
577 callbackBuffer = NULL;
578 res = AVERROR(ENOMEM);
584 io_context->seekable = 0;
586 format_context->pb = io_context;
593 res = create_ffaudiofileformats(env, format_context, &array, NULL);
600 if (format_context) {
601 AVFormatContext *s = format_context;
602 if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
603 (s->flags & AVFMT_FLAG_CUSTOM_IO)) {
606 avio_context_free(&s->pb);
610 avformat_close_input(&format_context);
JNIEXPORT void JNICALL Java_com_tagtraum_ffsampledsp_FFAudioFileReader_initIDs(JNIEnv *env, jclass readerClass)
Init static method and field ids for Java methods/fields.
JNIEXPORT jobjectArray JNICALL Java_com_tagtraum_ffsampledsp_FFAudioFileReader_getAudioFileFormatsFromURL(JNIEnv *env, jobject instance, jstring url, jbyteArray urlBytes)
Opens the given URL to determine its AudioFileFormat.
JNIEXPORT jobjectArray JNICALL Java_com_tagtraum_ffsampledsp_FFAudioFileReader_getAudioFileFormatsFromBuffer(JNIEnv *env, jobject instance, jobject byte_buffer)
Opens the byte buffer to determine its AudioFileFormat.
int ff_big_endian(enum AVCodecID codec_id)
Indicates whether the given id belongs to a big endian codec.
int ff_open_format_context(JNIEnv *env, AVFormatContext **format_context, const char *url, int io_buffer_size)
Opens the input file/url and allocates a AVFormatContext for it, but does not open the audio stream w...
void throwUnsupportedAudioFileExceptionIfError(JNIEnv *env, int err, const char *message)
Throws an UnsupportedAudioFileException.
void throwIOExceptionIfError(JNIEnv *env, int err, const char *message)
Throws an IOException.
const uint32_t CODEC_TAG_DRMS
Simple struct used for the read_callback of stream data.
int call_count
number of times the read_callback was called
jobject byte_buffer
byte buffer passed in (read only once)
JNIEnv * env
JNI environment.