FFSampledSP
Loading...
Searching...
No Matches
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 "FFUtils.h"
26
27static jmethodID fillReadBuffer_MID = NULL;
28static jfieldID readBuffer_FID = NULL;
29
30static const int CALLBACK_BUFFERSIZE = 32 * 1024;
31
42JNIEXPORT void JNICALL
44 jclass streamClass) {
45 readBuffer_FID = (*env)->GetFieldID(env, streamClass, "readBuffer",
46 "Ljava/nio/ByteBuffer;");
47 if (!readBuffer_FID)
48 return;
49 fillReadBuffer_MID =
50 (*env)->GetMethodID(env, streamClass, "fillReadBuffer", "()I");
51}
52
62static int read_callback(void *opaque, uint8_t *buf, int size) {
63 int res = 0;
64 int available_data;
65 jobject read_buffer = NULL;
66 uint8_t *java_buffer = NULL;
67 FFAudioIO *aio = (FFAudioIO *)opaque;
68
69 // FFmpeg may call us again after we reported an error (e.g. via avio_feof()
70 // in a demuxer's resync loop). Don't call Java with an exception pending.
71 if ((*aio->env)->ExceptionCheck(aio->env)) {
72 res = AVERROR(EIO);
73 goto bail;
74 }
75
76 // tell java to fill buffer
77 available_data = (int)(*aio->env)->CallIntMethod(aio->env, aio->java_instance,
78 fillReadBuffer_MID);
79 if ((*aio->env)->ExceptionCheck(aio->env)) {
80 res = AVERROR(EIO);
81 goto bail;
82 }
83 if (available_data > size) {
84 res = -1;
86 aio->env, 1, "Available data must not be larger than callback buffer.");
87 goto bail;
88 }
89
90 if (available_data <= 0) {
91 res = AVERROR_EOF;
92 goto bail;
93 }
94
95 read_buffer =
96 (*aio->env)->GetObjectField(aio->env, aio->java_instance, readBuffer_FID);
97 if (!read_buffer) {
98 res = -1;
99 throwIOExceptionIfError(aio->env, 1, "Failed to get read buffer.");
100 goto bail;
101 }
102
103 java_buffer =
104 (uint8_t *)(*aio->env)->GetDirectBufferAddress(aio->env, read_buffer);
105 if (!java_buffer) {
106 res = -1;
108 "Failed to get address for read buffer.");
109 goto bail;
110 }
111
112 // copy to c buffer
113 memcpy(buf, (const uint8_t *)java_buffer, available_data);
114 // return size of buffer
115 res = available_data;
116
117bail:
118 // called many times per native call, don't accumulate local refs
119 if (read_buffer)
120 (*aio->env)->DeleteLocalRef(aio->env, read_buffer);
121 return res;
122}
123
131JNIEXPORT void JNICALL
133 JNIEnv *env, jobject stream, jlong aio_pointer) {
134
135 FFAudioIO *aio = (FFAudioIO *)(intptr_t)aio_pointer;
136 aio->env = env;
137 aio->java_instance = stream;
138 ff_fill_buffer(aio);
139 // we can ignore the return value,
140 // because all ff_fill_buffer already
141 // throws a suitable Java exception
142 // in the case of an error
143}
144
154 JNIEnv *env, jobject stream, jint streamIndex) {
155
156 int res = 0;
157 FFAudioIO *aio;
158 AVIOContext *io_context;
159 unsigned char *callback_buffer = NULL;
160
161 aio = calloc(1, sizeof(FFAudioIO));
162 if (!aio) {
163 res = AVERROR(ENOMEM);
164 throwIOExceptionIfError(env, res, "Could not allocate audio IO.");
165 goto bail;
166 }
167 aio->env = env;
168 aio->java_instance = stream;
169
170 aio->format_context = avformat_alloc_context();
171 if (!aio->format_context) {
172 res = AVERROR(ENOMEM);
173 throwIOExceptionIfError(env, res, "Could not allocate format context.");
174 goto bail;
175 }
176
177 // limit probe to less than what we read in one chunk...
178 aio->format_context->probesize = 8 * 1024;
179 aio->format_context->max_analyze_duration = 5 * AV_TIME_BASE;
180
181 callback_buffer =
182 (unsigned char *)av_malloc(CALLBACK_BUFFERSIZE * sizeof(uint8_t));
183 if (!callback_buffer) {
184 res = AVERROR(ENOMEM);
185 throwIOExceptionIfError(env, res, "Could not allocate callback buffer.");
186 goto bail;
187 }
188
189 io_context = avio_alloc_context(
190 callback_buffer, // IOBuffer
191 CALLBACK_BUFFERSIZE, // Buffer Size (32kb corresponds to Java code)
192 0, // Write flag, only reading, so 0
193 aio, // FFAudioIO pointer (opaque)
194 read_callback, // Read callback
195 NULL, // Write callback
196 NULL // Seek callback
197 );
198 if (!io_context) {
199 res = AVERROR(ENOMEM);
200 throwIOExceptionIfError(env, res, "Could not allocate custom IO context.");
201 goto bail;
202 }
203 // we didn't supply a seek function in avio_alloc_context,
204 // so we need to make sure we don't seek...
205 io_context->seekable = 0;
206
207 aio->format_context->pb = io_context;
208 aio->stream_index = (int)streamIndex;
209
210 res = ff_open_file(env, &aio->format_context, &aio->stream,
211 &aio->decode_context, &aio->stream_index,
212 "MemoryAVIOContext", 0);
213 if (res) {
214 // exception is already thrown
215 goto bail;
216 }
217
218 res = ff_init_audioio(env, aio);
219 if (res) {
220 // exception is already thrown
221 goto bail;
222 }
223
224#ifdef DEBUG
225 fprintf(stderr, "stream->codecpar->bits_per_coded_sample: %i\n",
226 aio->stream->codecpar->bits_per_coded_sample);
227 fprintf(stderr, "stream->codecpar->bits_per_raw_sample : %i\n",
228 aio->stream->codecpar->bits_per_raw_sample);
229 fprintf(stderr, "stream->codecpar->bit_rate : %lli\n",
230 aio->stream->codecpar->bit_rate);
231 fprintf(stderr, "frames : %" PRId64 "\n", aio->stream->nb_frames);
232 fprintf(stderr, "sample_rate: %i\n", aio->stream->codecpar->sample_rate);
233 fprintf(stderr, "channels : %i\n",
234 aio->stream->codecpar->ch_layout.nb_channels);
235 fprintf(stderr, "frame_size : %i\n", aio->stream->codecpar->frame_size);
236 fprintf(stderr, "codec_id : %i\n", aio->stream->codecpar->codec_id);
237#endif
238
239 // FFmpeg may treat a read error as end of input and still succeed,
240 // but the exception thrown by the Java stream is pending.
241 if (!res && (*env)->ExceptionCheck(env)) {
242 res = -1;
243 }
244
245bail:
246
247 if (res)
248 ff_audioio_free(aio);
249 return (jlong)(intptr_t)aio;
250}
251
260 JNIEnv *env, jobject stream, jlong aio_pointer) {
261 ff_audioio_free((FFAudioIO *)(intptr_t)aio_pointer);
262}
JNIEXPORT void JNICALL Java_com_tagtraum_ffsampledsp_FFStreamInputStream_initIDs(JNIEnv *env, jclass streamClass)
Init static method and field ids for Java methods/fields.
JNIEXPORT void JNICALL Java_com_tagtraum_ffsampledsp_FFStreamInputStream_close(JNIEnv *env, jobject stream, jlong aio_pointer)
Frees all resources associated with the given FFAudioIO.
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.
void ff_audioio_free(FFAudioIO *aio)
Free all resources held by aio and then itself.
Definition FFUtils.c:1169
int ff_open_file(JNIEnv *env, AVFormatContext **format_context, AVStream **openedStream, AVCodecContext **context, int *stream_index, const char *url, int io_buffer_size)
Opens the input file/url, allocates a AVFormatContext for it and opens the audio stream with an appro...
Definition FFUtils.c:378
int ff_fill_buffer(FFAudioIO *aio)
Reads frames via av_read_frame, decodes them, and writes the result to the Java-side nativeBuffer.
Definition FFUtils.c:1107
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:663
void throwIOExceptionIfError(JNIEnv *env, int err, const char *message)
Throws an IOException.
Definition FFUtils.c:213
Central context representing the native peer to the Java FFNativePeerInputStream object.
Definition FFUtils.h:59
AVFormatContext * format_context
Current AVFormatContext.
Definition FFUtils.h:66
int stream_index
Index of the audio stream we are using.
Definition FFUtils.h:68
AVStream * stream
Audio stream we are interested in.
Definition FFUtils.h:67
JNIEnv * env
JNI environment.
Definition FFUtils.h:61
AVCodecContext * decode_context
Codec context for decoding.
Definition FFUtils.h:69
jobject java_instance
Calling Java instance.
Definition FFUtils.h:62