FFSampledSP
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 "com_tagtraum_ffsampledsp_FFCodecInputStream.h"
25 #include "FFUtils.h"
26 #include <math.h>
27 
28 
29 static jmethodID getEncoding_MID = NULL;
30 static jmethodID getSampleRate_MID = NULL;
31 static jmethodID getFrameSize_MID = NULL;
32 static jmethodID getSampleSizeInBits_MID = NULL;
33 static jmethodID getChannels_MID = NULL;
34 static jmethodID isBigEndian_MID = NULL;
35 static jmethodID toString_MID = NULL;
36 
42 static void init_ids(JNIEnv *env) {
43  if (getSampleRate_MID == NULL) {
44  jclass audioFormat_class = NULL;
45  jclass encoding_class = NULL;
46 
47  audioFormat_class = (*env)->FindClass(env, "javax/sound/sampled/AudioFormat");
48  encoding_class = (*env)->FindClass(env, "javax/sound/sampled/AudioFormat$Encoding");
49 
50  getEncoding_MID = (*env)->GetMethodID(env, audioFormat_class, "getEncoding", "()Ljavax/sound/sampled/AudioFormat$Encoding;");
51  getSampleRate_MID = (*env)->GetMethodID(env, audioFormat_class, "getSampleRate", "()F");
52  getFrameSize_MID = (*env)->GetMethodID(env, audioFormat_class, "getFrameSize", "()I");
53  getSampleSizeInBits_MID = (*env)->GetMethodID(env, audioFormat_class, "getSampleSizeInBits", "()I");
54  getChannels_MID = (*env)->GetMethodID(env, audioFormat_class, "getChannels", "()I");
55  isBigEndian_MID = (*env)->GetMethodID(env, audioFormat_class, "isBigEndian", "()Z");
56  toString_MID = (*env)->GetMethodID(env, encoding_class, "toString", "()Ljava/lang/String;");
57  }
58 }
59 
60 
70 JNIEXPORT jlong JNICALL Java_com_tagtraum_ffsampledsp_FFCodecInputStream_open(JNIEnv *env, jobject object, jobject target_format, jlong aio_pointer) {
71  int res = 0;
72  enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_NONE;
73  int out_channel_layout = AV_CH_LAYOUT_STEREO;
74  int is_float = 0;
75  int is_signed = 0;
76  AVCodec *encoder = NULL;
77  int dither_method = SWR_DITHER_NONE;
78  int output_sample_bits = 0;
79 
80  init_ids(env);
81 
82  FFAudioIO *aio = (FFAudioIO*)(intptr_t)aio_pointer;
83 
84  jfloat sample_rate = (*env)->CallFloatMethod(env, target_format, getSampleRate_MID);
85  jint sample_size_in_bits = (*env)->CallIntMethod(env, target_format, getSampleSizeInBits_MID);
86  jint channels = (*env)->CallIntMethod(env, target_format, getChannels_MID);
87  jboolean big_endian = (*env)->CallBooleanMethod(env, target_format, isBigEndian_MID);
88  jobject encoding = (*env)->CallObjectMethod(env, target_format, getEncoding_MID);
89  jstring jencoding_name = (jstring)(*env)->CallObjectMethod(env, encoding, toString_MID);
90 
91  const char *encoding_name = (*env)->GetStringUTFChars(env, jencoding_name, NULL);
92  is_float = strcmp("PCM_FLOAT", encoding_name) == 0;
93  is_signed = strcmp("PCM_SIGNED", encoding_name) == 0;
94  (*env)->ReleaseStringUTFChars(env, jencoding_name, encoding_name);
95 
96 #ifdef DEBUG
97  fprintf(stderr, "encoding = %s\n", encoding_name);
98  fprintf(stderr, "signed = %d\n", is_signed);
99  fprintf(stderr, "float = %d\n", is_float);
100  fprintf(stderr, "bits = %d\n", (int)sample_size_in_bits);
101 #endif
102 
103  if (sample_size_in_bits <= 8) {
104  out_sample_fmt = AV_SAMPLE_FMT_U8;
105  } else if (sample_size_in_bits <=16) {
106  out_sample_fmt = AV_SAMPLE_FMT_S16;
107  } else if (sample_size_in_bits <= 32 && is_float) {
108  out_sample_fmt = AV_SAMPLE_FMT_FLT;
109  } else if (sample_size_in_bits <=32) {
110  out_sample_fmt = AV_SAMPLE_FMT_S32;
111  } else if (sample_size_in_bits <= 64 && is_float) {
112  out_sample_fmt = AV_SAMPLE_FMT_DBL;
113  } else {
114  fprintf(stderr, "Will use 64 bit PCM_FLOAT even though it might not have been desired.\n");
115  out_sample_fmt = AV_SAMPLE_FMT_DBL;
116  }
117 
118  if (aio->stream->codecpar->channels == channels) {
119  out_channel_layout = aio->stream->codecpar->channel_layout;
120  } else if (channels == 1) {
121  out_channel_layout = AV_CH_LAYOUT_MONO;
122  } else if (channels == 2) {
123  out_channel_layout = AV_CH_LAYOUT_STEREO;
124  } else {
125  fprintf(stderr, "Undetermined channel layout, will use stereo.\n");
126  channels = 2;
127  }
128 
129  if (aio->stream->codecpar->bits_per_coded_sample > sample_size_in_bits) {
130  dither_method = SWR_DITHER_TRIANGULAR;
131  output_sample_bits = sample_size_in_bits;
132  }
133 
134 #ifdef DEBUG
135  fprintf(stderr, "setting out format to: %d\n", out_sample_fmt);
136 #endif
137 
138  // remove default setup
139  if (aio->swr_context) {
140  swr_free(&aio->swr_context);
141  }
142  // allocate new
143  aio->swr_context = swr_alloc();
144  if (!aio->swr_context) {
145  res = AVERROR(ENOMEM);
146  throwIOExceptionIfError(env, res, "Could not allocate swr context.");
147  goto bail;
148  }
149 
150  // standard stuff from input
151  av_opt_set_sample_fmt(aio->swr_context, "in_sample_fmt", aio->stream->codecpar->format, 0);
152  av_opt_set_int(aio->swr_context, "in_channel_count", aio->stream->codecpar->channels, 0);
153  av_opt_set_int(aio->swr_context, "in_channel_layout", aio->stream->codecpar->channel_layout, 0);
154  av_opt_set_int(aio->swr_context, "in_sample_rate", aio->stream->codecpar->sample_rate, 0);
155  // custom stuff
156  av_opt_set_int(aio->swr_context, "out_channel_layout", out_channel_layout, 0);
157  av_opt_set_int(aio->swr_context, "out_channel_count", channels, 0);
158  av_opt_set_int(aio->swr_context, "out_sample_rate", (int)round(sample_rate), 0);
159  av_opt_set_sample_fmt(aio->swr_context, "out_sample_fmt", out_sample_fmt, 0);
160  av_opt_set_int(aio->swr_context, "dither_method", dither_method, 0);
161  av_opt_set_int(aio->swr_context, "output_sample_bits", output_sample_bits, 0);
162 
163  res = swr_init(aio->swr_context);
164  if (res < 0) {
165  res = AVERROR(ENOMEM);
166  throwIOExceptionIfError(env, res, "Could not re-initialize swr context.");
167  goto bail;
168  }
169 
170 #ifdef DEBUG
171  fprintf(stderr, "open codec: dither method : %d\n", dither_method);
172 #endif
173 
174  // re-adjust encoder
175  encoder = ff_find_encoder(out_sample_fmt, sample_size_in_bits, big_endian, is_signed);
176  if (!encoder) {
177  res = AVERROR(EINVAL);
178  throwIOExceptionIfError(env, res, "Could not find suitable encoder.");
179  goto bail;
180  }
181  res = ff_init_encoder(env, aio, encoder);
182  if (res < 0) {
183  goto bail;
184  }
185 
186  bail:
187 
188  return (jlong)(intptr_t)aio;
189 }
void throwIOExceptionIfError(JNIEnv *env, int err, const char *message)
Throws an IOException.
Definition: FFUtils.c:192
AVStream * stream
Audio stream we are interested in.
Definition: FFUtils.h:64
int ff_init_encoder(JNIEnv *env, FFAudioIO *aio, AVCodec *encoder)
Allocates and initializes the encoder context and frame in FFAudioIO.
Definition: FFUtils.c:484
SwrContext * swr_context
Resampling context.
Definition: FFUtils.h:75
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:431
Central context representing the native peer to the Java FFNativePeerInputStream object.
Definition: FFUtils.h:56