| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "media/base/android/webaudio_media_codec_bridge.h" | |
| 6 | |
| 7 #include <errno.h> | |
| 8 #include <fcntl.h> | |
| 9 #include <stddef.h> | |
| 10 #include <string.h> | |
| 11 #include <sys/stat.h> | |
| 12 #include <sys/types.h> | |
| 13 #include <unistd.h> | |
| 14 #include <vector> | |
| 15 | |
| 16 #include "base/android/context_utils.h" | |
| 17 #include "base/android/jni_android.h" | |
| 18 #include "base/android/jni_array.h" | |
| 19 #include "base/android/jni_string.h" | |
| 20 #include "base/files/scoped_file.h" | |
| 21 #include "base/logging.h" | |
| 22 #include "base/posix/eintr_wrapper.h" | |
| 23 #include "jni/WebAudioMediaCodecBridge_jni.h" | |
| 24 #include "media/base/android/webaudio_media_codec_info.h" | |
| 25 | |
| 26 using base::android::AttachCurrentThread; | |
| 27 | |
| 28 namespace media { | |
| 29 | |
| 30 void WebAudioMediaCodecBridge::RunWebAudioMediaCodec( | |
| 31 base::SharedMemoryHandle encoded_audio_handle, | |
| 32 base::FileDescriptor pcm_output, | |
| 33 uint32_t data_size, | |
| 34 base::Closure on_decode_finished_cb) { | |
| 35 WebAudioMediaCodecBridge bridge( | |
| 36 encoded_audio_handle, pcm_output, data_size); | |
| 37 | |
| 38 bridge.DecodeInMemoryAudioFile(); | |
| 39 on_decode_finished_cb.Run(); | |
| 40 } | |
| 41 | |
| 42 WebAudioMediaCodecBridge::WebAudioMediaCodecBridge( | |
| 43 base::SharedMemoryHandle encoded_audio_handle, | |
| 44 base::FileDescriptor pcm_output, | |
| 45 uint32_t data_size) | |
| 46 : encoded_audio_handle_(encoded_audio_handle), | |
| 47 pcm_output_(pcm_output.fd), | |
| 48 data_size_(data_size) { | |
| 49 DVLOG(1) << "WebAudioMediaCodecBridge start **********************" | |
| 50 << " output fd = " << pcm_output.fd; | |
| 51 } | |
| 52 | |
| 53 WebAudioMediaCodecBridge::~WebAudioMediaCodecBridge() { | |
| 54 if (close(pcm_output_)) { | |
| 55 DVLOG(1) << "Couldn't close output fd " << pcm_output_ | |
| 56 << ": " << strerror(errno); | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 int WebAudioMediaCodecBridge::SaveEncodedAudioToFile( | |
| 61 JNIEnv* env, | |
| 62 jobject context) { | |
| 63 // Create a temporary file where we can save the encoded audio data. | |
| 64 std::string temporaryFile = | |
| 65 base::android::ConvertJavaStringToUTF8( | |
| 66 env, | |
| 67 Java_WebAudioMediaCodecBridge_createTempFile(env, context).obj()); | |
| 68 | |
| 69 // Open the file and unlink it, so that it will be actually removed | |
| 70 // when we close the file. | |
| 71 base::ScopedFD fd(open(temporaryFile.c_str(), O_RDWR)); | |
| 72 if (unlink(temporaryFile.c_str())) { | |
| 73 VLOG(0) << "Couldn't unlink temp file " << temporaryFile | |
| 74 << ": " << strerror(errno); | |
| 75 } | |
| 76 | |
| 77 if (!fd.is_valid()) { | |
| 78 return -1; | |
| 79 } | |
| 80 | |
| 81 // Create a local mapping of the shared memory containing the | |
| 82 // encoded audio data, and save the contents to the temporary file. | |
| 83 base::SharedMemory encoded_data(encoded_audio_handle_, true); | |
| 84 | |
| 85 if (!encoded_data.Map(data_size_)) { | |
| 86 VLOG(0) << "Unable to map shared memory!"; | |
| 87 return -1; | |
| 88 } | |
| 89 | |
| 90 if (static_cast<uint32_t>(write(fd.get(), encoded_data.memory(), data_size_)) | |
| 91 != data_size_) { | |
| 92 VLOG(0) << "Failed to write all audio data to temp file!"; | |
| 93 return -1; | |
| 94 } | |
| 95 | |
| 96 lseek(fd.get(), 0, SEEK_SET); | |
| 97 | |
| 98 return fd.release(); | |
| 99 } | |
| 100 | |
| 101 bool WebAudioMediaCodecBridge::DecodeInMemoryAudioFile() { | |
| 102 JNIEnv* env = AttachCurrentThread(); | |
| 103 CHECK(env); | |
| 104 | |
| 105 jobject context = base::android::GetApplicationContext(); | |
| 106 | |
| 107 int sourceFd = SaveEncodedAudioToFile(env, context); | |
| 108 | |
| 109 if (sourceFd < 0) | |
| 110 return false; | |
| 111 | |
| 112 jboolean decoded = Java_WebAudioMediaCodecBridge_decodeAudioFile( | |
| 113 env, | |
| 114 context, | |
| 115 reinterpret_cast<intptr_t>(this), | |
| 116 sourceFd, | |
| 117 data_size_); | |
| 118 | |
| 119 close(sourceFd); | |
| 120 | |
| 121 DVLOG(1) << "decoded = " << (decoded ? "true" : "false"); | |
| 122 | |
| 123 return decoded; | |
| 124 } | |
| 125 | |
| 126 void WebAudioMediaCodecBridge::InitializeDestination( | |
| 127 JNIEnv* env, | |
| 128 const JavaParamRef<jobject>& /*java object*/, | |
| 129 jint channel_count, | |
| 130 jint sample_rate, | |
| 131 jlong duration_microsec) { | |
| 132 // Send information about this audio file: number of channels, | |
| 133 // sample rate (Hz), and the number of frames. | |
| 134 struct WebAudioMediaCodecInfo info = { | |
| 135 static_cast<unsigned long>(channel_count), | |
| 136 static_cast<unsigned long>(sample_rate), | |
| 137 // The number of frames is the duration of the file | |
| 138 // (in microseconds) times the sample rate. | |
| 139 static_cast<unsigned long>( | |
| 140 0.5 + (duration_microsec * 0.000001 * | |
| 141 sample_rate)) | |
| 142 }; | |
| 143 | |
| 144 DVLOG(1) << "InitializeDestination:" | |
| 145 << " channel count = " << channel_count | |
| 146 << " rate = " << sample_rate | |
| 147 << " duration = " << duration_microsec << " microsec"; | |
| 148 | |
| 149 HANDLE_EINTR(write(pcm_output_, &info, sizeof(info))); | |
| 150 } | |
| 151 | |
| 152 void WebAudioMediaCodecBridge::OnChunkDecoded( | |
| 153 JNIEnv* env, | |
| 154 const JavaParamRef<jobject>& /*java object*/, | |
| 155 const JavaParamRef<jobject>& buf, | |
| 156 jint buf_size, | |
| 157 jint input_channel_count, | |
| 158 jint output_channel_count) { | |
| 159 if (buf_size <= 0 || !buf) | |
| 160 return; | |
| 161 | |
| 162 int8_t* buffer = | |
| 163 static_cast<int8_t*>(env->GetDirectBufferAddress(buf)); | |
| 164 size_t count = static_cast<size_t>(buf_size); | |
| 165 std::vector<int16_t> decoded_data; | |
| 166 | |
| 167 if (input_channel_count == 1 && output_channel_count == 2) { | |
| 168 // See crbug.com/266006. The file has one channel, but the | |
| 169 // decoder decided to return two channels. To be consistent with | |
| 170 // the number of channels in the file, only send one channel (the | |
| 171 // first). | |
| 172 int16_t* data = static_cast<int16_t*>(env->GetDirectBufferAddress(buf)); | |
| 173 int frame_count = buf_size / sizeof(*data) / 2; | |
| 174 | |
| 175 decoded_data.resize(frame_count); | |
| 176 for (int k = 0; k < frame_count; ++k) { | |
| 177 decoded_data[k] = *data; | |
| 178 data += 2; | |
| 179 } | |
| 180 buffer = reinterpret_cast<int8_t*>(decoded_data.data()); | |
| 181 DCHECK(buffer); | |
| 182 count = frame_count * sizeof(*data); | |
| 183 } | |
| 184 | |
| 185 // Write out the data to the pipe in small chunks if necessary. | |
| 186 while (count > 0) { | |
| 187 int bytes_to_write = (count >= PIPE_BUF) ? PIPE_BUF : count; | |
| 188 ssize_t bytes_written = HANDLE_EINTR(write(pcm_output_, | |
| 189 buffer, | |
| 190 bytes_to_write)); | |
| 191 if (bytes_written == -1) | |
| 192 break; | |
| 193 count -= bytes_written; | |
| 194 buffer += bytes_written; | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 bool WebAudioMediaCodecBridge::RegisterWebAudioMediaCodecBridge(JNIEnv* env) { | |
| 199 return RegisterNativesImpl(env); | |
| 200 } | |
| 201 | |
| 202 } // namespace | |
| OLD | NEW |