|
35 | 35 | #include "modules/audio_processing/include/audio_processing.h" |
36 | 36 | #include "rtc_base/logging.h" |
37 | 37 |
|
| 38 | +#include <algorithm> |
| 39 | +#include <memory> |
| 40 | + |
38 | 41 | JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioProcessing_applyConfig |
39 | 42 | (JNIEnv * env, jobject caller, jobject config) |
40 | 43 | { |
@@ -67,120 +70,87 @@ JNIEXPORT jint JNICALL Java_dev_onvoid_webrtc_media_audio_AudioProcessing_getStr |
67 | 70 | return apm->stream_delay_ms(); |
68 | 71 | } |
69 | 72 |
|
70 | | -JNIEXPORT jint JNICALL Java_dev_onvoid_webrtc_media_audio_AudioProcessing_processStream |
71 | | -(JNIEnv * env, jobject caller, jbyteArray src, jobject inputConfig, jobject outputConfig, jbyteArray dest) |
| 73 | +// Shared by processStream and processReverseStream, which differ only in the |
| 74 | +// direction they feed the audio processing module. |
| 75 | +static jint processFrame(JNIEnv * env, jobject caller, jbyteArray src, jobject inputConfig, |
| 76 | + jobject outputConfig, jbyteArray dest, bool reverse) |
72 | 77 | { |
73 | | - webrtc::AudioProcessing * apm = GetHandle<webrtc::AudioProcessing>(env, caller); |
74 | | - CHECK_HANDLEV(apm, 0); |
75 | | - |
76 | | - webrtc::StreamConfig srcConfig = jni::AudioProcessingStreamConfig::toNative(env, jni::JavaLocalRef<jobject>(env, inputConfig)); |
77 | | - webrtc::StreamConfig dstConfig = jni::AudioProcessingStreamConfig::toNative(env, jni::JavaLocalRef<jobject>(env, outputConfig)); |
78 | | - |
79 | | - jboolean isDstCopy = JNI_FALSE; |
80 | | - |
81 | | - jbyte * srcPtr = env->GetByteArrayElements(src, NULL); |
82 | | - jbyte * dstPtr = env->GetByteArrayElements(dest, &isDstCopy); |
83 | | - |
84 | | - const int16_t * srcFrame = reinterpret_cast<const int16_t *>(srcPtr); |
85 | | - int16_t * dstFrame = reinterpret_cast<int16_t *>(dstPtr); |
| 78 | + try { |
| 79 | + webrtc::AudioProcessing * apm = GetHandle<webrtc::AudioProcessing>(env, caller); |
| 80 | + CHECK_HANDLEV(apm, 0); |
86 | 81 |
|
87 | | - int result; |
| 82 | + webrtc::StreamConfig srcConfig = jni::AudioProcessingStreamConfig::toNative(env, jni::JavaLocalRef<jobject>(env, inputConfig)); |
| 83 | + webrtc::StreamConfig dstConfig = jni::AudioProcessingStreamConfig::toNative(env, jni::JavaLocalRef<jobject>(env, outputConfig)); |
88 | 84 |
|
89 | | - if (srcConfig.num_channels() == 1 && dstConfig.num_channels() == 2) { |
90 | | - // Up-mixing, only mono to stereo. |
| 85 | + // Up-mixing, only mono to stereo, builds the stereo input in dest. |
91 | 86 | // For complex channel layouts a channel mixer is required. |
92 | | - |
| 87 | + const bool upMix = srcConfig.num_channels() == 1 && dstConfig.num_channels() == 2; |
93 | 88 | const size_t srcNumSamples = srcConfig.num_samples(); |
94 | 89 | const size_t dstNumChannels = dstConfig.num_channels(); |
95 | | - const size_t frameSize = srcNumSamples * dstNumChannels; |
| 90 | + const size_t dstNumSamples = upMix |
| 91 | + ? std::max(srcNumSamples * dstNumChannels, dstConfig.num_samples()) |
| 92 | + : dstConfig.num_samples(); |
96 | 93 |
|
97 | | - if (frameSize > webrtc::AudioFrame::kMaxDataSizeSamples) { |
98 | | - return -9; |
| 94 | + if (upMix && srcNumSamples * dstNumChannels > webrtc::AudioFrame::kMaxDataSizeSamples) { |
| 95 | + return webrtc::AudioProcessing::kBadNumberChannelsError; |
99 | 96 | } |
100 | | - |
101 | | - for (int i = static_cast<int>(srcNumSamples) - 1; i >= 0; i--) { |
102 | | - for (size_t j = 0; j < dstNumChannels; ++j) { |
103 | | - dstFrame[dstNumChannels * i + j] = srcFrame[i]; |
104 | | - } |
| 97 | + if (static_cast<size_t>(env->GetArrayLength(src)) < srcNumSamples * sizeof(int16_t) || |
| 98 | + static_cast<size_t>(env->GetArrayLength(dest)) < dstNumSamples * sizeof(int16_t)) { |
| 99 | + return webrtc::AudioProcessing::kBadDataLengthError; |
105 | 100 | } |
106 | 101 |
|
107 | | - srcConfig.set_num_channels(dstNumChannels); |
108 | | - |
109 | | - result = apm->ProcessStream(dstFrame, srcConfig, dstConfig, dstFrame); |
110 | | - } |
111 | | - else { |
112 | | - // Will also down-mix if required, e.g. from stereo to mono. |
113 | | - result = apm->ProcessStream(srcFrame, srcConfig, dstConfig, dstFrame); |
114 | | - } |
115 | | - |
116 | | - if (isDstCopy == JNI_TRUE) { |
117 | | - jsize dstLength = env->GetArrayLength(dest); |
118 | | - |
119 | | - env->SetByteArrayRegion(dest, 0, dstLength, dstPtr); |
120 | | - } |
121 | | - |
122 | | - env->ReleaseByteArrayElements(src, srcPtr, JNI_ABORT); |
123 | | - env->ReleaseByteArrayElements(dest, dstPtr, JNI_ABORT); |
124 | | - |
125 | | - return result; |
126 | | -} |
127 | | - |
128 | | -JNIEXPORT jint JNICALL Java_dev_onvoid_webrtc_media_audio_AudioProcessing_processReverseStream |
129 | | -(JNIEnv * env, jobject caller, jbyteArray src, jobject inputConfig, jobject outputConfig, jbyteArray dest) |
130 | | -{ |
131 | | - webrtc::AudioProcessing * apm = GetHandle<webrtc::AudioProcessing>(env, caller); |
132 | | - CHECK_HANDLEV(apm, 0); |
133 | | - |
134 | | - webrtc::StreamConfig srcConfig = jni::AudioProcessingStreamConfig::toNative(env, jni::JavaLocalRef<jobject>(env, inputConfig)); |
135 | | - webrtc::StreamConfig dstConfig = jni::AudioProcessingStreamConfig::toNative(env, jni::JavaLocalRef<jobject>(env, outputConfig)); |
136 | | - |
137 | | - jboolean isDstCopy = JNI_FALSE; |
138 | | - |
139 | | - jbyte * srcPtr = env->GetByteArrayElements(src, nullptr); |
140 | | - jbyte * dstPtr = env->GetByteArrayElements(dest, &isDstCopy); |
141 | | - |
142 | | - const int16_t * srcFrame = reinterpret_cast<const int16_t *>(srcPtr); |
143 | | - int16_t * dstFrame = reinterpret_cast<int16_t *>(dstPtr); |
144 | | - |
145 | | - int result; |
146 | | - |
147 | | - if (srcConfig.num_channels() == 1 && dstConfig.num_channels() == 2) { |
148 | | - // Up-mixing, only mono to stereo. |
149 | | - // For complex channel layouts a channel mixer is required. |
150 | | - |
151 | | - const size_t srcNumSamples = srcConfig.num_samples(); |
152 | | - const size_t dstNumChannels = dstConfig.num_channels(); |
153 | | - const size_t frameSize = srcNumSamples * dstNumChannels; |
| 102 | + auto releaseSrc = [env, src](jbyte * bytes) { |
| 103 | + env->ReleaseByteArrayElements(src, bytes, JNI_ABORT); |
| 104 | + }; |
| 105 | + std::unique_ptr<jbyte, decltype(releaseSrc)> srcPtr(env->GetByteArrayElements(src, nullptr), releaseSrc); |
| 106 | + if (!srcPtr) { |
| 107 | + return webrtc::AudioProcessing::kUnspecifiedError; |
| 108 | + } |
154 | 109 |
|
155 | | - if (frameSize > webrtc::AudioFrame::kMaxDataSizeSamples) { |
156 | | - return -9; |
| 110 | + // Mode 0 writes the samples back when the VM handed out a copy. |
| 111 | + auto releaseDst = [env, dest](jbyte * bytes) { |
| 112 | + env->ReleaseByteArrayElements(dest, bytes, 0); |
| 113 | + }; |
| 114 | + std::unique_ptr<jbyte, decltype(releaseDst)> dstPtr(env->GetByteArrayElements(dest, nullptr), releaseDst); |
| 115 | + if (!dstPtr) { |
| 116 | + return webrtc::AudioProcessing::kUnspecifiedError; |
157 | 117 | } |
158 | 118 |
|
159 | | - for (int i = static_cast<int>(srcNumSamples) - 1; i >= 0; i--) { |
160 | | - for (size_t j = 0; j < dstNumChannels; ++j) { |
161 | | - dstFrame[dstNumChannels * i + j] = srcFrame[i]; |
| 119 | + const int16_t * srcFrame = reinterpret_cast<const int16_t *>(srcPtr.get()); |
| 120 | + int16_t * dstFrame = reinterpret_cast<int16_t *>(dstPtr.get()); |
| 121 | + |
| 122 | + if (upMix) { |
| 123 | + for (int i = static_cast<int>(srcNumSamples) - 1; i >= 0; i--) { |
| 124 | + for (size_t j = 0; j < dstNumChannels; ++j) { |
| 125 | + dstFrame[dstNumChannels * i + j] = srcFrame[i]; |
| 126 | + } |
162 | 127 | } |
163 | | - } |
164 | 128 |
|
165 | | - srcConfig.set_num_channels(dstNumChannels); |
| 129 | + srcConfig.set_num_channels(dstNumChannels); |
| 130 | + srcFrame = dstFrame; |
| 131 | + } |
166 | 132 |
|
167 | | - result = apm->ProcessReverseStream(dstFrame, srcConfig, dstConfig, dstFrame); |
168 | | - } |
169 | | - else { |
170 | 133 | // Will also down-mix if required, e.g. from stereo to mono. |
171 | | - result = apm->ProcessReverseStream(srcFrame, srcConfig, dstConfig, dstFrame); |
| 134 | + return reverse |
| 135 | + ? apm->ProcessReverseStream(srcFrame, srcConfig, dstConfig, dstFrame) |
| 136 | + : apm->ProcessStream(srcFrame, srcConfig, dstConfig, dstFrame); |
172 | 137 | } |
173 | | - |
174 | | - if (isDstCopy == JNI_TRUE) { |
175 | | - jsize dstLength = env->GetArrayLength(dest); |
176 | | - |
177 | | - env->SetByteArrayRegion(dest, 0, dstLength, dstPtr); |
| 138 | + catch (...) { |
| 139 | + ThrowCxxJavaException(env); |
| 140 | + return webrtc::AudioProcessing::kUnspecifiedError; |
178 | 141 | } |
| 142 | +} |
179 | 143 |
|
180 | | - env->ReleaseByteArrayElements(src, srcPtr, JNI_ABORT); |
181 | | - env->ReleaseByteArrayElements(dest, dstPtr, JNI_ABORT); |
| 144 | +JNIEXPORT jint JNICALL Java_dev_onvoid_webrtc_media_audio_AudioProcessing_processStream |
| 145 | +(JNIEnv * env, jobject caller, jbyteArray src, jobject inputConfig, jobject outputConfig, jbyteArray dest) |
| 146 | +{ |
| 147 | + return processFrame(env, caller, src, inputConfig, outputConfig, dest, false); |
| 148 | +} |
182 | 149 |
|
183 | | - return result; |
| 150 | +JNIEXPORT jint JNICALL Java_dev_onvoid_webrtc_media_audio_AudioProcessing_processReverseStream |
| 151 | +(JNIEnv * env, jobject caller, jbyteArray src, jobject inputConfig, jobject outputConfig, jbyteArray dest) |
| 152 | +{ |
| 153 | + return processFrame(env, caller, src, inputConfig, outputConfig, dest, true); |
184 | 154 | } |
185 | 155 |
|
186 | 156 | JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioProcessing_dispose |
|
0 commit comments