FFSampledSP
Loading...
Searching...
No Matches
FFCodecInputStream.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#include <math.h>
27
28static jmethodID getEncoding_MID = NULL;
29static jmethodID getSampleRate_MID = NULL;
30static jmethodID getFrameSize_MID = NULL;
31static jmethodID getSampleSizeInBits_MID = NULL;
32static jmethodID getChannels_MID = NULL;
33static jmethodID isBigEndian_MID = NULL;
34static jmethodID toString_MID = NULL;
35
47 JNIEnv *env, jclass streamClass) {
48 jclass audioFormat_class =
49 (*env)->FindClass(env, "javax/sound/sampled/AudioFormat");
50 if (!audioFormat_class)
51 return;
52 jclass encoding_class =
53 (*env)->FindClass(env, "javax/sound/sampled/AudioFormat$Encoding");
54 if (!encoding_class)
55 return;
56
57 getEncoding_MID =
58 (*env)->GetMethodID(env, audioFormat_class, "getEncoding",
59 "()Ljavax/sound/sampled/AudioFormat$Encoding;");
60 if (!getEncoding_MID)
61 return;
62 getSampleRate_MID =
63 (*env)->GetMethodID(env, audioFormat_class, "getSampleRate", "()F");
64 if (!getSampleRate_MID)
65 return;
66 getFrameSize_MID =
67 (*env)->GetMethodID(env, audioFormat_class, "getFrameSize", "()I");
68 if (!getFrameSize_MID)
69 return;
70 getSampleSizeInBits_MID =
71 (*env)->GetMethodID(env, audioFormat_class, "getSampleSizeInBits", "()I");
72 if (!getSampleSizeInBits_MID)
73 return;
74 getChannels_MID =
75 (*env)->GetMethodID(env, audioFormat_class, "getChannels", "()I");
76 if (!getChannels_MID)
77 return;
78 isBigEndian_MID =
79 (*env)->GetMethodID(env, audioFormat_class, "isBigEndian", "()Z");
80 if (!isBigEndian_MID)
81 return;
82 toString_MID = (*env)->GetMethodID(env, encoding_class, "toString",
83 "()Ljava/lang/String;");
84}
85
98 JNIEnv *env, jobject object, jobject target_format, jlong aio_pointer) {
99 int res = 0;
100 enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_NONE;
101 AVChannelLayout out_ch_layout = AV_CHANNEL_LAYOUT_STEREO;
102 int is_float = 0;
103 int is_signed = 0;
104 AVCodec *encoder = NULL;
105 int dither_method = SWR_DITHER_NONE;
106 int output_sample_bits = 0;
107
108 FFAudioIO *aio = (FFAudioIO *)(intptr_t)aio_pointer;
109
110 jfloat sample_rate = 0;
111 jint sample_size_in_bits = 0;
112 jint channels = 0;
113 jboolean big_endian = JNI_FALSE;
114 jobject encoding = NULL;
115 jstring jencoding_name = NULL;
116 const char *encoding_name = NULL;
117
118 // AudioFormat getters may be overridden, so check for exceptions after
119 // each call
120 sample_rate = (*env)->CallFloatMethod(env, target_format, getSampleRate_MID);
121 if ((*env)->ExceptionCheck(env))
122 goto bail;
123 sample_size_in_bits =
124 (*env)->CallIntMethod(env, target_format, getSampleSizeInBits_MID);
125 if ((*env)->ExceptionCheck(env))
126 goto bail;
127 channels = (*env)->CallIntMethod(env, target_format, getChannels_MID);
128 if ((*env)->ExceptionCheck(env))
129 goto bail;
130 big_endian = (*env)->CallBooleanMethod(env, target_format, isBigEndian_MID);
131 if ((*env)->ExceptionCheck(env))
132 goto bail;
133 encoding = (*env)->CallObjectMethod(env, target_format, getEncoding_MID);
134 if ((*env)->ExceptionCheck(env))
135 goto bail;
136 if (!encoding) {
137 throwIOExceptionIfError(env, AVERROR(EINVAL),
138 "Target format has no encoding.");
139 goto bail;
140 }
141 jencoding_name =
142 (jstring)(*env)->CallObjectMethod(env, encoding, toString_MID);
143 (*env)->DeleteLocalRef(env, encoding);
144 if ((*env)->ExceptionCheck(env))
145 goto bail;
146 if (!jencoding_name) {
147 throwIOExceptionIfError(env, AVERROR(EINVAL),
148 "Target format has no encoding name.");
149 goto bail;
150 }
151
152 encoding_name = (*env)->GetStringUTFChars(env, jencoding_name, NULL);
153 if (!encoding_name) {
154 // OutOfMemoryError is pending
155 (*env)->DeleteLocalRef(env, jencoding_name);
156 goto bail;
157 }
158 is_float = strcmp("PCM_FLOAT", encoding_name) == 0;
159 is_signed = strcmp("PCM_SIGNED", encoding_name) == 0;
160
161#ifdef DEBUG
162 fprintf(stderr, "encoding = %s\n", encoding_name);
163 fprintf(stderr, "signed = %d\n", is_signed);
164 fprintf(stderr, "float = %d\n", is_float);
165 fprintf(stderr, "bits = %d\n", (int)sample_size_in_bits);
166#endif
167
168 (*env)->ReleaseStringUTFChars(env, jencoding_name, encoding_name);
169 (*env)->DeleteLocalRef(env, jencoding_name);
170
171 if (sample_size_in_bits <= 8) {
172 out_sample_fmt = AV_SAMPLE_FMT_U8;
173 } else if (sample_size_in_bits <= 16) {
174 out_sample_fmt = AV_SAMPLE_FMT_S16;
175 } else if (sample_size_in_bits <= 32 && is_float) {
176 out_sample_fmt = AV_SAMPLE_FMT_FLT;
177 } else if (sample_size_in_bits <= 32) {
178 out_sample_fmt = AV_SAMPLE_FMT_S32;
179 } else if (sample_size_in_bits <= 64 && is_float) {
180 out_sample_fmt = AV_SAMPLE_FMT_DBL;
181 } else {
182 fprintf(stderr, "Will use 64 bit PCM_FLOAT even though it might not have "
183 "been desired.\n");
184 out_sample_fmt = AV_SAMPLE_FMT_DBL;
185 }
186
187 if (aio->stream->codecpar->ch_layout.nb_channels == channels) {
188 av_channel_layout_copy(&out_ch_layout, &aio->stream->codecpar->ch_layout);
189 } else if (channels == 1) {
190 out_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
191 } else if (channels == 2) {
192 out_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;
193 } else {
194 fprintf(stderr, "Undetermined channel layout, will use stereo.\n");
195 channels = 2;
196 out_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;
197 }
198
199 if (aio->stream->codecpar->bits_per_coded_sample > sample_size_in_bits) {
200 dither_method = SWR_DITHER_TRIANGULAR;
201 output_sample_bits = sample_size_in_bits;
202 }
203
204#ifdef DEBUG
205 fprintf(stderr, "setting out format to: %d\n", out_sample_fmt);
206#endif
207
208 // remove default setup
209 if (aio->swr_context) {
210 swr_free(&aio->swr_context);
211 }
212 // allocate new
213 aio->swr_context = swr_alloc();
214 if (!aio->swr_context) {
215 res = AVERROR(ENOMEM);
216 throwIOExceptionIfError(env, res, "Could not allocate swr context.");
217 goto bail;
218 }
219
220 // standard stuff from input
221 av_opt_set_sample_fmt(aio->swr_context, "in_sample_fmt",
222 aio->stream->codecpar->format, 0);
223 av_opt_set_chlayout(aio->swr_context, "in_chlayout",
224 &aio->stream->codecpar->ch_layout, 0);
225 av_opt_set_int(aio->swr_context, "in_sample_rate",
226 aio->stream->codecpar->sample_rate, 0);
227 // custom stuff
228 av_opt_set_chlayout(aio->swr_context, "out_chlayout", &out_ch_layout, 0);
229 av_opt_set_int(aio->swr_context, "out_sample_rate", (int)round(sample_rate),
230 0);
231 av_opt_set_sample_fmt(aio->swr_context, "out_sample_fmt", out_sample_fmt, 0);
232 av_opt_set_int(aio->swr_context, "dither_method", dither_method, 0);
233 av_opt_set_int(aio->swr_context, "output_sample_bits", output_sample_bits, 0);
234 av_channel_layout_uninit(&out_ch_layout);
235
236 res = swr_init(aio->swr_context);
237 if (res < 0) {
238 res = AVERROR(ENOMEM);
239 throwIOExceptionIfError(env, res, "Could not re-initialize swr context.");
240 goto bail;
241 }
242
243#ifdef DEBUG
244 fprintf(stderr, "open codec: dither method : %d\n", dither_method);
245#endif
246
247 // re-adjust encoder
248 encoder = ff_find_encoder(out_sample_fmt, sample_size_in_bits, big_endian,
249 is_signed);
250 if (!encoder) {
251 res = AVERROR(EINVAL);
252 throwIOExceptionIfError(env, res, "Could not find suitable encoder.");
253 goto bail;
254 }
255 res = ff_init_encoder(env, aio, encoder);
256 if (res < 0) {
257 goto bail;
258 }
259
260bail:
261
262 return (jlong)(intptr_t)aio;
263}
JNIEXPORT void JNICALL Java_com_tagtraum_ffsampledsp_FFCodecInputStream_initIDs(JNIEnv *env, jclass streamClass)
Init static method and field ids for Java methods/fields.
JNIEXPORT jlong JNICALL Java_com_tagtraum_ffsampledsp_FFCodecInputStream_open(JNIEnv *env, jobject object, jobject target_format, jlong aio_pointer)
Re-configures SwrContext and Encoder to match the provided target_format.
AVCodec * ff_find_encoder(enum AVSampleFormat sampleFormat, int bits, int big_endian, int signedSamples)
Finds an AVCodec encoder for the given sample format, bits per sample, byte order and signed/unsigned...
Definition FFUtils.c:528
int ff_init_encoder(JNIEnv *env, FFAudioIO *aio, AVCodec *encoder)
Allocates and initializes the encoder context and frame in FFAudioIO.
Definition FFUtils.c:591
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
AVStream * stream
Audio stream we are interested in.
Definition FFUtils.h:67
SwrContext * swr_context
Resampling context.
Definition FFUtils.h:77