FFSampledSP
FFStreamInputStream.c
Go to the documentation of this file.
1 /*
2  * =================================================
3  * Copyright 2013 tagtraum industries incorporated
4  * This file is part of FFSampledSP.
5  *
6  * FFSampledSP is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFSampledSP is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFSampledSP; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  * =================================================
20  *
21  * @author <a href="mailto:hs@tagtraum.com">Hendrik Schreiber</a>
22  */
23 
24 #include "com_tagtraum_ffsampledsp_FFStreamInputStream.h"
25 #include "FFUtils.h"
26 
27 static jmethodID fillReadBuffer_MID = NULL;
28 static jfieldID readBuffer_FID = NULL;
29 
30 static int CALLBACK_BUFFERSIZE = 32*1024;
31 
38 static void init_ids(JNIEnv *env, jobject stream) {
39  if (!fillReadBuffer_MID || !readBuffer_FID) {
40  jclass streamClass = NULL;
41  streamClass = (*env)->GetObjectClass(env, stream);
42  readBuffer_FID = (*env)->GetFieldID(env, streamClass, "readBuffer", "Ljava/nio/ByteBuffer;");
43  fillReadBuffer_MID = (*env)->GetMethodID(env, streamClass, "fillReadBuffer", "()I");
44  }
45 }
46 
55 static int read_callback(void *opaque, uint8_t *buf, int size) {
56  int res = 0;
57  int available_data;
58  jobject read_buffer = NULL;
59  uint8_t *java_buffer = NULL;
60  FFAudioIO *aio = (FFAudioIO*)opaque;
61 
62  // tell java to fill buffer
63  available_data = (int) (*aio->env)->CallIntMethod(aio->env, aio->java_instance, fillReadBuffer_MID);
64  if (available_data > size) {
65  res = -1;
66  throwIOExceptionIfError(aio->env, 1, "Available data must not be larger than callback buffer.");
67  goto bail;
68  }
69  if ((*aio->env)->ExceptionCheck(aio->env)) {
70  // needed?
71  //(*aio->env)->ExceptionDescribe(aio->env);
72  res = -1;
73  goto bail;
74  }
75 
76  if (available_data <= 0) {
77  res = 0;
78  goto bail;
79  }
80 
81  read_buffer = (*aio->env)->GetObjectField(aio->env, aio->java_instance, readBuffer_FID);
82  if (!read_buffer) {
83  res = -1;
84  throwIOExceptionIfError(aio->env, 1, "Failed to get read buffer.");
85  goto bail;
86  }
87 
88  java_buffer = (uint8_t *)(*aio->env)->GetDirectBufferAddress(aio->env, read_buffer);
89  if (!java_buffer) {
90  res = -1;
91  throwIOExceptionIfError(aio->env, 1, "Failed to get address for read buffer.");
92  goto bail;
93  }
94 
95  // copy to c buffer
96  memcpy(buf, (const uint8_t *)java_buffer, available_data);
97  // return size of buffer
98  res = available_data;
99 
100 bail:
101 
102  return res;
103 }
104 
105 
113 JNIEXPORT void JNICALL Java_com_tagtraum_ffsampledsp_FFStreamInputStream_fillNativeBuffer(JNIEnv *env, jobject stream, jlong aio_pointer) {
114 
115  FFAudioIO *aio = (FFAudioIO*)(intptr_t)aio_pointer;
116  aio->env = env;
117  aio->java_instance = stream;
118  ff_fill_buffer(aio);
119  // we can ignore the return value,
120  // because all ff_fill_buffer already
121  // throws a suitable Java exception
122  // in the case of an error
123 }
124 
132 JNIEXPORT jlong JNICALL Java_com_tagtraum_ffsampledsp_FFStreamInputStream_open(JNIEnv *env, jobject stream, jint streamIndex) {
133 
134  int res = 0;
135  FFAudioIO *aio;
136  AVIOContext *io_context;
137  unsigned char* callback_buffer = NULL;
138 
139  init_ids(env, stream);
140 
141  aio = calloc(1, sizeof(FFAudioIO));
142  if (!aio) {
143  res = AVERROR(ENOMEM);
144  throwIOExceptionIfError(env, res, "Could not allocate audio IO.");
145  goto bail;
146  }
147  aio->env = env;
148  aio->java_instance = stream;
149 
150  aio->format_context = avformat_alloc_context();
151  if (!aio->format_context) {
152  res = AVERROR(ENOMEM);
153  throwIOExceptionIfError(env, res, "Could not allocate format context.");
154  goto bail;
155  }
156 
157  // limit probe to less than what we read in one chunk...
158  aio->format_context->probesize = 8*1024;
159  aio->format_context->max_analyze_duration = 5*AV_TIME_BASE;
160 
161  callback_buffer = (unsigned char*)av_malloc(CALLBACK_BUFFERSIZE * sizeof(uint8_t));
162  if (!callback_buffer) {
163  res = AVERROR(ENOMEM);
164  throwIOExceptionIfError(env, res, "Could not allocate callback buffer.");
165  goto bail;
166  }
167 
168  io_context = avio_alloc_context(
169  callback_buffer, // IOBuffer
170  CALLBACK_BUFFERSIZE, // Buffer Size (32kb corresponds to Java code)
171  0, // Write flag, only reading, so 0
172  aio, // FFAudioIO pointer (opaque)
173  read_callback, // Read callback
174  NULL, // Write callback
175  NULL // Seek callback
176  );
177  if (!io_context) {
178  res = AVERROR(ENOMEM);
179  throwIOExceptionIfError(env, res, "Could not allocate custom IO context.");
180  goto bail;
181  }
182  // we didn't supply a seek function in avio_alloc_context,
183  // so we need to make sure we don't seek...
184  io_context->seekable = 0;
185 
186  aio->format_context->pb = io_context;
187  aio->stream_index = (int)streamIndex;
188 
189  res = ff_open_file(env, &aio->format_context, &aio->stream, &aio->decode_context, &aio->stream_index, "MemoryAVIOContext");
190  if (res) {
191  // exception is already thrown
192  goto bail;
193  }
194 
195  res = ff_init_audioio(env, aio);
196  if (res) {
197  // exception is already thrown
198  goto bail;
199  }
200 
201 #ifdef DEBUG
202  fprintf(stderr, "stream->codecpar->bits_per_coded_sample: %i\n", aio->stream->codecpar->bits_per_coded_sample);
203  fprintf(stderr, "stream->codecpar->bits_per_raw_sample : %i\n", aio->stream->codecpar->bits_per_raw_sample);
204  fprintf(stderr, "stream->codecpar->bit_rate : %i\n", aio->stream->codecpar->bit_rate);
205  fprintf(stderr, "frames : %" PRId64 "\n", aio->stream->nb_frames);
206  fprintf(stderr, "sample_rate: %i\n", aio->stream->codecpar->sample_rate);
207  fprintf(stderr, "channels : %i\n", aio->stream->codecpar->channels);
208  fprintf(stderr, "frame_size : %i\n", aio->stream->codecpar->frame_size);
209  fprintf(stderr, "codec_id : %i\n", aio->stream->codecpar->codec_id);
210 #endif
211 
212 bail:
213 
214  if (res) ff_audioio_free(aio);
215  return (jlong)(intptr_t)aio;
216 }
217 
225 JNIEXPORT void JNICALL Java_com_tagtraum_ffsampledsp_FFStreamInputStream_close(JNIEnv *env, jobject stream, jlong aio_pointer) {
226  ff_audioio_free((FFAudioIO*)(intptr_t)aio_pointer);
227 }
JNIEXPORT void JNICALL Java_com_tagtraum_ffsampledsp_FFStreamInputStream_close(JNIEnv *env, jobject stream, jlong aio_pointer)
Frees all resources associated with the given FFAudioIO.
int stream_index
Index of the audio stream we are using.
Definition: FFUtils.h:65
void throwIOExceptionIfError(JNIEnv *env, int err, const char *message)
Throws an IOException.
Definition: FFUtils.c:188
AVFormatContext * format_context
Current AVFormatContext.
Definition: FFUtils.h:63
AVStream * stream
Audio stream we are interested in.
Definition: FFUtils.h:64
JNIEXPORT void JNICALL Java_com_tagtraum_ffsampledsp_FFStreamInputStream_fillNativeBuffer(JNIEnv *env, jobject stream, jlong aio_pointer)
Fills the java-side buffer (allocated via Java code) with fresh audio data.
JNIEXPORT jlong JNICALL Java_com_tagtraum_ffsampledsp_FFStreamInputStream_open(JNIEnv *env, jobject stream, jint streamIndex)
Creates the FFAudioIO, custom AVIOContext etc for reading data from the stream.
int ff_open_file(JNIEnv *env, AVFormatContext **format_context, AVStream **openedStream, AVCodecContext **context, int *stream_index, const char *url)
Opens the input file/url, allocates a AVFormatContext for it and opens the audio stream with an appro...
Definition: FFUtils.c:279
int ff_fill_buffer(FFAudioIO *aio)
Reads a frame via av_read_frame(AVFormatContext, AVPacket), decodes it to a AVPacket, and writes the result to the Java-side nativeBuffer.
Definition: FFUtils.c:822
int ff_init_audioio(JNIEnv *env, FFAudioIO *aio)
Initialize our main context FFAudioIO, so that SwrContext, decode buffers and the encoder are set to ...
Definition: FFUtils.c:516
JNIEnv * env
JNI environment.
Definition: FFUtils.h:58
AVCodecContext * decode_context
Codec context for decoding.
Definition: FFUtils.h:66
Central context representing the native peer to the Java FFNativePeerInputStream object.
Definition: FFUtils.h:56
void ff_audioio_free(FFAudioIO *aio)
Free all resources held by aio and then itself.
Definition: FFUtils.c:858
jobject java_instance
Calling Java instance.
Definition: FFUtils.h:59