blob: 711eb809e047e66107d3f9ed1eaa6cd8b47b782d [file] [log] [blame]
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/shared/win32/win32_audio_decoder.h"
#include <algorithm>
#include <queue>
#include "starboard/atomic.h"
#include "starboard/shared/starboard/thread_checker.h"
#include "starboard/shared/win32/atomic_queue.h"
#include "starboard/shared/win32/audio_decoder.h"
#include "starboard/shared/win32/audio_transform.h"
#include "starboard/shared/win32/decrypting_decoder.h"
#include "starboard/shared/win32/error_utils.h"
#include "starboard/shared/win32/media_foundation_utils.h"
namespace starboard {
namespace shared {
namespace win32 {
using Microsoft::WRL::ComPtr;
using ::starboard::shared::starboard::ThreadChecker;
using ::starboard::shared::win32::CreateAudioTransform;
const size_t kAacSamplesPerFrame = 1024;
// We are using float samples on Xb1.
const size_t kBytesPerSample = sizeof(float);
namespace {
class AbstractWin32AudioDecoderImpl : public AbstractWin32AudioDecoder {
public:
AbstractWin32AudioDecoderImpl(SbMediaAudioCodec codec,
SbMediaAudioFrameStorageType audio_frame_fmt,
SbMediaAudioSampleType sample_type,
const SbMediaAudioHeader& audio_header,
SbDrmSystem drm_system)
: thread_checker_(ThreadChecker::kSetThreadIdOnFirstCheck),
codec_(codec),
audio_frame_fmt_(audio_frame_fmt),
sample_type_(sample_type),
number_of_channels_(audio_header.number_of_channels),
heaac_detected_(false),
samples_per_second_(static_cast<int>(audio_header.samples_per_second)) {
scoped_ptr<MediaTransform> audio_decoder =
CreateAudioTransform(audio_header, codec_);
impl_.reset(
new DecryptingDecoder("audio", audio_decoder.Pass(), drm_system));
}
void Consume(ComPtr<IMFSample> sample) {
DWORD buff_count = 0;
HRESULT hr = sample->GetBufferCount(&buff_count);
CheckResult(hr);
SB_DCHECK(buff_count == 1);
ComPtr<IMFMediaBuffer> media_buffer;
hr = sample->GetBufferByIndex(0, &media_buffer);
if (FAILED(hr)) {
return;
}
LONGLONG win32_timestamp = 0;
hr = sample->GetSampleTime(&win32_timestamp);
CheckResult(hr);
BYTE* buffer;
DWORD length;
hr = media_buffer->Lock(&buffer, NULL, &length);
CheckResult(hr);
SB_DCHECK(length);
const uint8_t* data = reinterpret_cast<uint8_t*>(buffer);
const size_t data_size = static_cast<size_t>(length);
const size_t expect_size_in_bytes =
number_of_channels_ * kAacSamplesPerFrame * kBytesPerSample;
if (data_size / expect_size_in_bytes == 2) {
heaac_detected_.store(true);
}
DecodedAudioPtr data_ptr(
new DecodedAudio(number_of_channels_, sample_type_, audio_frame_fmt_,
ConvertToMediaTime(win32_timestamp), data_size));
std::copy(data, data + data_size, data_ptr->buffer());
output_queue_.push(data_ptr);
media_buffer->Unlock();
}
bool TryWrite(const scoped_refptr<InputBuffer>& buff) SB_OVERRIDE {
SB_DCHECK(thread_checker_.CalledOnValidThread());
// The incoming audio is in ADTS format which has a 7 bytes header. But
// the audio decoder is configured to accept raw AAC. So we have to adjust
// the data, size, and subsample mapping to skip the ADTS header.
const int kADTSHeaderSize = 7;
const bool write_ok = impl_->TryWriteInputBuffer(buff, kADTSHeaderSize);
return write_ok;
}
void WriteEndOfStream() SB_OVERRIDE {
SB_DCHECK(thread_checker_.CalledOnValidThread());
impl_->Drain();
ComPtr<IMFSample> sample;
ComPtr<IMFMediaType> media_type;
while (impl_->ProcessAndRead(&sample, &media_type)) {
if (sample) {
Consume(sample);
}
}
output_queue_.push(new DecodedAudio);
}
scoped_refptr<DecodedAudio> ProcessAndRead() SB_OVERRIDE {
SB_DCHECK(thread_checker_.CalledOnValidThread());
ComPtr<IMFSample> sample;
ComPtr<IMFMediaType> media_type;
while (impl_->ProcessAndRead(&sample, &media_type)) {
if (sample) {
Consume(sample);
}
}
if (output_queue_.empty()) {
return NULL;
}
scoped_refptr<DecodedAudio> output = output_queue_.front();
output_queue_.pop();
return output;
}
void Reset() SB_OVERRIDE {
impl_->Reset();
std::queue<DecodedAudioPtr> empty;
output_queue_.swap(empty);
thread_checker_.Detach();
}
int GetSamplesPerSecond() const SB_OVERRIDE {
if (heaac_detected_.load()) {
return samples_per_second_ * 2;
}
return samples_per_second_;
}
// The object is single-threaded and is driven by a dedicated thread.
// However the thread may gets destroyed and re-created over the life time of
// this object. We enforce that certain member functions can only called
// from one thread while still allows this object to be driven by different
// threads by:
// 1. The |thread_checker_| is initially created without attaching to any
// thread.
// 2. When a thread is destroyed, Reset() will be called which in turn calls
// Detach() on the |thread_checker_| to allow the object to attach to a
// new thread.
::starboard::shared::starboard::ThreadChecker thread_checker_;
const SbMediaAudioCodec codec_;
const SbMediaAudioSampleType sample_type_;
const SbMediaAudioFrameStorageType audio_frame_fmt_;
scoped_ptr<DecryptingDecoder> impl_;
std::queue<DecodedAudioPtr> output_queue_;
uint16_t number_of_channels_;
atomic_bool heaac_detected_;
int samples_per_second_;
};
} // anonymous namespace.
scoped_ptr<AbstractWin32AudioDecoder> AbstractWin32AudioDecoder::Create(
SbMediaAudioCodec code,
SbMediaAudioFrameStorageType audio_frame_fmt,
SbMediaAudioSampleType sample_type,
const SbMediaAudioHeader& audio_header,
SbDrmSystem drm_system) {
return scoped_ptr<AbstractWin32AudioDecoder>(
new AbstractWin32AudioDecoderImpl(code, audio_frame_fmt, sample_type,
audio_header, drm_system));
}
} // namespace win32
} // namespace shared
} // namespace starboard