Index: content/renderer/media/android/audio_decoder_android.cc |
diff --git a/content/renderer/media/android/audio_decoder_android.cc b/content/renderer/media/android/audio_decoder_android.cc |
deleted file mode 100644 |
index 2601fea43cc950bcbdde1b592661a0df8f786295..0000000000000000000000000000000000000000 |
--- a/content/renderer/media/android/audio_decoder_android.cc |
+++ /dev/null |
@@ -1,596 +0,0 @@ |
-// Copyright 2013 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "content/renderer/media/android/audio_decoder_android.h" |
- |
-#include <errno.h> |
-#include <fcntl.h> |
-#include <limits.h> |
-#include <stdint.h> |
-#include <sys/mman.h> |
-#include <unistd.h> |
-#include <vector> |
- |
-#include "base/file_descriptor_posix.h" |
-#include "base/logging.h" |
-#include "base/macros.h" |
-#include "base/memory/shared_memory.h" |
-#include "base/posix/eintr_wrapper.h" |
-#include "base/process/process_handle.h" |
-#include "content/common/view_messages.h" |
-#include "media/base/android/webaudio_media_codec_info.h" |
-#include "media/base/audio_bus.h" |
-#include "media/base/limits.h" |
-#include "third_party/WebKit/public/platform/WebAudioBus.h" |
- |
-namespace content { |
- |
-class AudioDecoderIO { |
- public: |
- AudioDecoderIO(const char* data, size_t data_size); |
- ~AudioDecoderIO(); |
- bool ShareEncodedToProcess(base::SharedMemoryHandle* handle); |
- |
- // Returns true if AudioDecoderIO was successfully created. |
- bool IsValid() const; |
- |
- int read_fd() const { return read_fd_; } |
- int write_fd() const { return write_fd_; } |
- |
- private: |
- // Shared memory that will hold the encoded audio data. This is |
- // used by MediaCodec for decoding. |
- base::SharedMemory encoded_shared_memory_; |
- |
- // A pipe used to communicate with MediaCodec. MediaCodec owns |
- // write_fd_ and writes to it. |
- int read_fd_; |
- int write_fd_; |
- |
- DISALLOW_COPY_AND_ASSIGN(AudioDecoderIO); |
-}; |
- |
-AudioDecoderIO::AudioDecoderIO(const char* data, size_t data_size) |
- : read_fd_(-1), |
- write_fd_(-1) { |
- |
- if (!data || !data_size || data_size > 0x80000000) |
- return; |
- |
- // Create the shared memory and copy our data to it so that |
- // MediaCodec can access it. |
- encoded_shared_memory_.CreateAndMapAnonymous(data_size); |
- |
- if (!encoded_shared_memory_.memory()) |
- return; |
- |
- memcpy(encoded_shared_memory_.memory(), data, data_size); |
- |
- // Create a pipe for reading/writing the decoded PCM data |
- int pipefd[2]; |
- |
- if (pipe(pipefd)) |
- return; |
- |
- read_fd_ = pipefd[0]; |
- write_fd_ = pipefd[1]; |
-} |
- |
-AudioDecoderIO::~AudioDecoderIO() { |
- // Close the read end of the pipe. The write end should have been |
- // closed by MediaCodec. |
- if (read_fd_ >= 0 && close(read_fd_)) { |
- DVLOG(1) << "Cannot close read fd " << read_fd_ |
- << ": " << strerror(errno); |
- } |
-} |
- |
-bool AudioDecoderIO::IsValid() const { |
- return read_fd_ >= 0 && write_fd_ >= 0 && |
- encoded_shared_memory_.memory(); |
-} |
- |
-bool AudioDecoderIO::ShareEncodedToProcess(base::SharedMemoryHandle* handle) { |
- return encoded_shared_memory_.ShareToProcess(base::GetCurrentProcessHandle(), |
- handle); |
-} |
- |
-static float ConvertSampleToFloat(int16_t sample) { |
- const float kMaxScale = 1.0f / std::numeric_limits<int16_t>::max(); |
- const float kMinScale = -1.0f / std::numeric_limits<int16_t>::min(); |
- |
- return sample * (sample < 0 ? kMinScale : kMaxScale); |
-} |
- |
-// A basic WAVE file decoder. See |
-// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ for a |
-// basic guide to the WAVE file format. |
-class WAVEDecoder { |
- public: |
- WAVEDecoder(const uint8_t* data, size_t data_size); |
- ~WAVEDecoder(); |
- |
- // Try to decode the data as a WAVE file. If the data is a supported |
- // WAVE file, |destination_bus| is filled with the decoded data and |
- // DecodeWAVEFile returns true. Otherwise, DecodeWAVEFile returns |
- // false. |
- bool DecodeWAVEFile(blink::WebAudioBus* destination_bus); |
- |
- private: |
- // Minimum number of bytes in a WAVE file to hold all of the data we |
- // need to interpret it as a WAVE file. |
- static const unsigned kMinimumWAVLength = 44; |
- |
- // Number of bytes in the chunk ID field. |
- static const unsigned kChunkIDLength = 4; |
- |
- // Number of bytes in the chunk size field. |
- static const unsigned kChunkSizeLength = 4; |
- |
- // Number of bytes in the format field of the "RIFF" chunk. |
- static const unsigned kFormatFieldLength = 4; |
- |
- // Number of bytes in a valid "fmt" chunk. |
- static const unsigned kFMTChunkLength = 16; |
- |
- // Supported audio format in a WAVE file. |
- // TODO(rtoy): Consider supporting other formats here, if necessary. |
- static const int16_t kAudioFormatPCM = 1; |
- |
- // Maximum number (inclusive) of bytes per sample supported by this |
- // decoder. |
- static const unsigned kMaximumBytesPerSample = 3; |
- |
- // Read an unsigned integer of |length| bytes from |buffer|. The |
- // integer is interpreted as being in little-endian order. |
- uint32_t ReadUnsignedInteger(const uint8_t* buffer, size_t length); |
- |
- // Read a PCM sample from the WAVE data at |pcm_data|. |
- int16_t ReadPCMSample(const uint8_t* pcm_data); |
- |
- // Read a WAVE chunk header including the chunk ID and chunk size. |
- // Returns false if the header could not be read. |
- bool ReadChunkHeader(); |
- |
- // Read and parse the "fmt" chunk. Returns false if the fmt chunk |
- // could not be read or contained unsupported formats. |
- bool ReadFMTChunk(); |
- |
- // Read data chunk and save it to |destination_bus|. Returns false |
- // if the data chunk could not be read correctly. |
- bool CopyDataChunkToBus(blink::WebAudioBus* destination_bus); |
- |
- // The WAVE chunk ID that identifies the chunk. |
- uint8_t chunk_id_[kChunkIDLength]; |
- |
- // The number of bytes in the data portion of the chunk. |
- size_t chunk_size_; |
- |
- // The total number of bytes in the encoded data. |
- size_t data_size_; |
- |
- // The current position within the WAVE file. |
- const uint8_t* buffer_; |
- |
- // Points one byte past the end of the in-memory WAVE file. Used for |
- // detecting if we've reached the end of the file. |
- const uint8_t* buffer_end_; |
- |
- size_t bytes_per_sample_; |
- |
- uint16_t number_of_channels_; |
- |
- // Sample rate of the WAVE data, in Hz. |
- uint32_t sample_rate_; |
- |
- DISALLOW_COPY_AND_ASSIGN(WAVEDecoder); |
-}; |
- |
-WAVEDecoder::WAVEDecoder(const uint8_t* encoded_data, size_t data_size) |
- : data_size_(data_size), |
- buffer_(encoded_data), |
- buffer_end_(encoded_data + 1), |
- bytes_per_sample_(0), |
- number_of_channels_(0), |
- sample_rate_(0) { |
- if (buffer_ + data_size > buffer_) |
- buffer_end_ = buffer_ + data_size; |
-} |
- |
-WAVEDecoder::~WAVEDecoder() {} |
- |
-uint32_t WAVEDecoder::ReadUnsignedInteger(const uint8_t* buffer, |
- size_t length) { |
- unsigned value = 0; |
- |
- if (length == 0 || length > sizeof(value)) { |
- DCHECK(false) << "ReadUnsignedInteger: Invalid length: " << length; |
- return 0; |
- } |
- |
- // All integer fields in a WAVE file are little-endian. |
- for (size_t k = length; k > 0; --k) |
- value = (value << 8) + buffer[k - 1]; |
- |
- return value; |
-} |
- |
-int16_t WAVEDecoder::ReadPCMSample(const uint8_t* pcm_data) { |
- uint32_t unsigned_sample = ReadUnsignedInteger(pcm_data, bytes_per_sample_); |
- int16_t sample; |
- |
- // Convert the unsigned data into a 16-bit PCM sample. |
- switch (bytes_per_sample_) { |
- case 1: |
- sample = (unsigned_sample - 128) << 8; |
- break; |
- case 2: |
- sample = static_cast<int16_t>(unsigned_sample); |
- break; |
- case 3: |
- // Android currently converts 24-bit WAVE data into 16-bit |
- // samples by taking the high-order 16 bits without rounding. |
- // We do the same here for consistency. |
- sample = static_cast<int16_t>(unsigned_sample >> 8); |
- break; |
- default: |
- sample = 0; |
- break; |
- } |
- return sample; |
-} |
- |
-bool WAVEDecoder::ReadChunkHeader() { |
- if (buffer_ + kChunkIDLength + kChunkSizeLength >= buffer_end_) |
- return false; |
- |
- memcpy(chunk_id_, buffer_, kChunkIDLength); |
- |
- chunk_size_ = ReadUnsignedInteger(buffer_ + kChunkIDLength, kChunkSizeLength); |
- |
- // Adjust for padding |
- if (chunk_size_ % 2) |
- ++chunk_size_; |
- |
- // Check for completely bogus chunk size. |
- if (chunk_size_ > data_size_) |
- return false; |
- |
- return true; |
-} |
- |
-bool WAVEDecoder::ReadFMTChunk() { |
- // The fmt chunk has basic info about the format of the audio |
- // data. Only a basic PCM format is supported. |
- if (chunk_size_ < kFMTChunkLength) { |
- DVLOG(1) << "FMT chunk too short: " << chunk_size_; |
- return 0; |
- } |
- |
- uint16_t audio_format = ReadUnsignedInteger(buffer_, 2); |
- |
- if (audio_format != kAudioFormatPCM) { |
- DVLOG(1) << "Audio format not supported: " << audio_format; |
- return false; |
- } |
- |
- number_of_channels_ = ReadUnsignedInteger(buffer_ + 2, 2); |
- sample_rate_ = ReadUnsignedInteger(buffer_ + 4, 4); |
- unsigned bits_per_sample = ReadUnsignedInteger(buffer_ + 14, 2); |
- |
- // Sanity checks. |
- |
- if (!number_of_channels_ || |
- number_of_channels_ > media::limits::kMaxChannels) { |
- DVLOG(1) << "Unsupported number of channels: " << number_of_channels_; |
- return false; |
- } |
- |
- if (sample_rate_ < media::limits::kMinSampleRate || |
- sample_rate_ > media::limits::kMaxSampleRate) { |
- DVLOG(1) << "Unsupported sample rate: " << sample_rate_; |
- return false; |
- } |
- |
- // We only support 8, 16, and 24 bits per sample. |
- if (bits_per_sample == 8 || bits_per_sample == 16 || bits_per_sample == 24) { |
- bytes_per_sample_ = bits_per_sample / 8; |
- return true; |
- } |
- |
- DVLOG(1) << "Unsupported bits per sample: " << bits_per_sample; |
- return false; |
-} |
- |
-bool WAVEDecoder::CopyDataChunkToBus(blink::WebAudioBus* destination_bus) { |
- // The data chunk contains the audio data itself. |
- if (!bytes_per_sample_ || bytes_per_sample_ > kMaximumBytesPerSample) { |
- DVLOG(1) << "WARNING: data chunk without preceeding fmt chunk," |
- << " or invalid bytes per sample."; |
- return false; |
- } |
- |
- DVLOG(0) << "Decoding WAVE file: " << number_of_channels_ << " channels, " |
- << sample_rate_ << " kHz, " |
- << chunk_size_ / bytes_per_sample_ / number_of_channels_ |
- << " frames, " << 8 * bytes_per_sample_ << " bits/sample"; |
- |
- // Create the destination bus of the appropriate size and then decode |
- // the data into the bus. |
- size_t number_of_frames = |
- chunk_size_ / bytes_per_sample_ / number_of_channels_; |
- |
- destination_bus->initialize( |
- number_of_channels_, number_of_frames, sample_rate_); |
- |
- for (size_t m = 0; m < number_of_frames; ++m) { |
- for (uint16_t k = 0; k < number_of_channels_; ++k) { |
- int16_t sample = ReadPCMSample(buffer_); |
- |
- buffer_ += bytes_per_sample_; |
- destination_bus->channelData(k)[m] = ConvertSampleToFloat(sample); |
- } |
- } |
- |
- return true; |
-} |
- |
-bool WAVEDecoder::DecodeWAVEFile(blink::WebAudioBus* destination_bus) { |
- // Parse and decode WAVE file. If we can't parse it, return false. |
- |
- if (buffer_ + kMinimumWAVLength > buffer_end_) { |
- DVLOG(1) << "Buffer too small to contain full WAVE header: "; |
- return false; |
- } |
- |
- // Do we have a RIFF file? |
- ReadChunkHeader(); |
- if (memcmp(chunk_id_, "RIFF", kChunkIDLength) != 0) { |
- DVLOG(1) << "RIFF missing"; |
- return false; |
- } |
- buffer_ += kChunkIDLength + kChunkSizeLength; |
- |
- // Check the format field of the RIFF chunk |
- memcpy(chunk_id_, buffer_, kFormatFieldLength); |
- if (memcmp(chunk_id_, "WAVE", kFormatFieldLength) != 0) { |
- DVLOG(1) << "Invalid WAVE file: missing WAVE header"; |
- return false; |
- } |
- // Advance past the format field |
- buffer_ += kFormatFieldLength; |
- |
- // We have a WAVE file. Start parsing the chunks. |
- |
- while (buffer_ < buffer_end_) { |
- if (!ReadChunkHeader()) { |
- DVLOG(1) << "Couldn't read chunk header"; |
- return false; |
- } |
- |
- // Consume the chunk ID and chunk size |
- buffer_ += kChunkIDLength + kChunkSizeLength; |
- |
- // Make sure we can read all chunk_size bytes. |
- if (buffer_ + chunk_size_ > buffer_end_) { |
- DVLOG(1) << "Insufficient bytes to read chunk of size " << chunk_size_; |
- return false; |
- } |
- |
- if (memcmp(chunk_id_, "fmt ", kChunkIDLength) == 0) { |
- if (!ReadFMTChunk()) |
- return false; |
- } else if (memcmp(chunk_id_, "data", kChunkIDLength) == 0) { |
- // Return after reading the data chunk, whether we succeeded or |
- // not. |
- return CopyDataChunkToBus(destination_bus); |
- } else { |
- // Ignore these chunks that we don't know about. |
- DVLOG(0) << "Ignoring WAVE chunk `" << chunk_id_ << "' size " |
- << chunk_size_; |
- } |
- |
- // Advance to next chunk. |
- buffer_ += chunk_size_; |
- } |
- |
- // If we get here, that means we didn't find a data chunk, so we |
- // couldn't handle this WAVE file. |
- |
- return false; |
-} |
- |
-// The number of frames is known so preallocate the destination |
-// bus and copy the pcm data to the destination bus as it's being |
-// received. |
-static void CopyPcmDataToBus(int input_fd, |
- blink::WebAudioBus* destination_bus, |
- size_t number_of_frames, |
- unsigned number_of_channels, |
- double file_sample_rate) { |
- destination_bus->initialize(number_of_channels, |
- number_of_frames, |
- file_sample_rate); |
- |
- int16_t pipe_data[PIPE_BUF / sizeof(int16_t)]; |
- size_t decoded_frames = 0; |
- size_t current_sample_in_frame = 0; |
- ssize_t nread; |
- |
- while ((nread = HANDLE_EINTR(read(input_fd, pipe_data, sizeof(pipe_data)))) > |
- 0) { |
- size_t samples_in_pipe = nread / sizeof(int16_t); |
- |
- // The pipe may not contain a whole number of frames. This is |
- // especially true if the number of channels is greater than |
- // 2. Thus, keep track of which sample in a frame is being |
- // processed, so we handle the boundary at the end of the pipe |
- // correctly. |
- for (size_t m = 0; m < samples_in_pipe; ++m) { |
- if (decoded_frames >= number_of_frames) |
- break; |
- |
- destination_bus->channelData(current_sample_in_frame)[decoded_frames] = |
- ConvertSampleToFloat(pipe_data[m]); |
- ++current_sample_in_frame; |
- |
- if (current_sample_in_frame >= number_of_channels) { |
- current_sample_in_frame = 0; |
- ++decoded_frames; |
- } |
- } |
- } |
- |
- // number_of_frames is only an estimate. Resize the buffer with the |
- // actual number of received frames. |
- if (decoded_frames < number_of_frames) |
- destination_bus->resizeSmaller(decoded_frames); |
-} |
- |
-// The number of frames is unknown, so keep reading and buffering |
-// until there's no more data and then copy the data to the |
-// destination bus. |
-static void BufferAndCopyPcmDataToBus(int input_fd, |
- blink::WebAudioBus* destination_bus, |
- unsigned number_of_channels, |
- double file_sample_rate) { |
- int16_t pipe_data[PIPE_BUF / sizeof(int16_t)]; |
- std::vector<int16_t> decoded_samples; |
- ssize_t nread; |
- |
- while ((nread = HANDLE_EINTR(read(input_fd, pipe_data, sizeof(pipe_data)))) > |
- 0) { |
- size_t samples_in_pipe = nread / sizeof(int16_t); |
- if (decoded_samples.size() + samples_in_pipe > decoded_samples.capacity()) { |
- decoded_samples.reserve(std::max(samples_in_pipe, |
- 2 * decoded_samples.capacity())); |
- } |
- std::copy(pipe_data, |
- pipe_data + samples_in_pipe, |
- back_inserter(decoded_samples)); |
- } |
- |
- DVLOG(1) << "Total samples read = " << decoded_samples.size(); |
- |
- // Convert the samples and save them in the audio bus. |
- size_t number_of_samples = decoded_samples.size(); |
- size_t number_of_frames = decoded_samples.size() / number_of_channels; |
- size_t decoded_frames = 0; |
- |
- destination_bus->initialize(number_of_channels, |
- number_of_frames, |
- file_sample_rate); |
- |
- for (size_t m = 0; m < number_of_samples; m += number_of_channels) { |
- if (decoded_frames >= number_of_frames) |
- break; |
- |
- for (size_t k = 0; k < number_of_channels; ++k) { |
- int16_t sample = decoded_samples[m + k]; |
- destination_bus->channelData(k)[decoded_frames] = |
- ConvertSampleToFloat(sample); |
- } |
- ++decoded_frames; |
- } |
- |
- // number_of_frames is only an estimate. Resize the buffer with the |
- // actual number of received frames. |
- if (decoded_frames < number_of_frames) |
- destination_bus->resizeSmaller(decoded_frames); |
-} |
- |
-static bool TryWAVEFileDecoder(blink::WebAudioBus* destination_bus, |
- const uint8_t* encoded_data, |
- size_t data_size) { |
- WAVEDecoder decoder(encoded_data, data_size); |
- |
- return decoder.DecodeWAVEFile(destination_bus); |
-} |
- |
-// To decode audio data, we want to use the Android MediaCodec class. |
-// But this can't run in a sandboxed process so we need initiate the |
-// request to MediaCodec in the browser. To do this, we create a |
-// shared memory buffer that holds the audio data. We send a message |
-// to the browser to start the decoder using this buffer and one end |
-// of a pipe. The MediaCodec class will decode the data from the |
-// shared memory and write the PCM samples back to us over a pipe. |
-bool DecodeAudioFileData(blink::WebAudioBus* destination_bus, const char* data, |
- size_t data_size, |
- scoped_refptr<ThreadSafeSender> sender) { |
- // Try to decode the data as a WAVE file first. If it can't be |
- // decoded, use MediaCodec. See crbug.com/259048. |
- if (TryWAVEFileDecoder( |
- destination_bus, reinterpret_cast<const uint8_t*>(data), data_size)) { |
- return true; |
- } |
- |
- AudioDecoderIO audio_decoder(data, data_size); |
- |
- if (!audio_decoder.IsValid()) |
- return false; |
- |
- base::SharedMemoryHandle encoded_data_handle; |
- audio_decoder.ShareEncodedToProcess(&encoded_data_handle); |
- base::FileDescriptor fd(audio_decoder.write_fd(), true); |
- |
- DVLOG(1) << "DecodeAudioFileData: Starting MediaCodec"; |
- |
- // Start MediaCodec processing in the browser which will read from |
- // encoded_data_handle for our shared memory and write the decoded |
- // PCM samples (16-bit integer) to our pipe. |
- |
- sender->Send(new ViewHostMsg_RunWebAudioMediaCodec( |
- encoded_data_handle, fd, data_size)); |
- |
- // First, read the number of channels, the sample rate, and the |
- // number of frames and a flag indicating if the file is an |
- // ogg/vorbis file. This must be coordinated with |
- // WebAudioMediaCodecBridge! |
- // |
- // If we know the number of samples, we can create the destination |
- // bus directly and do the conversion directly to the bus instead of |
- // buffering up everything before saving the data to the bus. |
- |
- int input_fd = audio_decoder.read_fd(); |
- struct media::WebAudioMediaCodecInfo info; |
- |
- DVLOG(1) << "Reading audio file info from fd " << input_fd; |
- ssize_t nread = HANDLE_EINTR(read(input_fd, &info, sizeof(info))); |
- DVLOG(1) << "read: " << nread << " bytes:\n" |
- << " 0: number of channels = " << info.channel_count << "\n" |
- << " 1: sample rate = " << info.sample_rate << "\n" |
- << " 2: number of frames = " << info.number_of_frames << "\n"; |
- |
- if (nread != sizeof(info)) |
- return false; |
- |
- unsigned number_of_channels = info.channel_count; |
- double file_sample_rate = static_cast<double>(info.sample_rate); |
- size_t number_of_frames = info.number_of_frames; |
- |
- // Sanity checks |
- if (!number_of_channels || |
- number_of_channels > media::limits::kMaxChannels || |
- file_sample_rate < media::limits::kMinSampleRate || |
- file_sample_rate > media::limits::kMaxSampleRate) { |
- return false; |
- } |
- |
- if (number_of_frames > 0) { |
- CopyPcmDataToBus(input_fd, |
- destination_bus, |
- number_of_frames, |
- number_of_channels, |
- file_sample_rate); |
- } else { |
- BufferAndCopyPcmDataToBus(input_fd, |
- destination_bus, |
- number_of_channels, |
- file_sample_rate); |
- } |
- |
- return true; |
-} |
- |
-} // namespace content |