| // 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/starboard/player/filter/player_components.h" |
| |
| #include <queue> |
| |
| #include "starboard/shared/starboard/player/closure.h" |
| #include "starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h" |
| #include "starboard/shared/starboard/player/filter/video_renderer_impl_internal.h" |
| #include "starboard/shared/starboard/player/job_queue.h" |
| |
| namespace starboard { |
| namespace shared { |
| namespace starboard { |
| namespace player { |
| namespace filter { |
| |
| namespace { |
| |
| SbMediaAudioSampleType GetSupportedSampleType() { |
| if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) { |
| return kSbMediaAudioSampleTypeFloat32; |
| } |
| SB_DCHECK( |
| SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)); |
| return kSbMediaAudioSampleTypeInt16; |
| } |
| |
| } // namespace |
| |
| class StubAudioDecoder : public AudioDecoder, JobQueue::JobOwner { |
| public: |
| explicit StubAudioDecoder(const SbMediaAudioHeader& audio_header) |
| : sample_type_(GetSupportedSampleType()), |
| audio_header_(audio_header), |
| stream_ended_(false) {} |
| void Initialize(const Closure& output_cb) SB_OVERRIDE { |
| output_cb_ = output_cb; |
| } |
| void Decode(const InputBuffer& input_buffer, |
| const Closure& consumed_cb) SB_OVERRIDE { |
| // Values to represent what kind of dummy audio to fill the decoded audio |
| // we produce with. |
| enum FillType { |
| kSilence, |
| kWave, |
| }; |
| // Can be set locally to fill with different types. |
| const FillType fill_type = kSilence; |
| |
| if (last_input_buffer_.is_valid()) { |
| SbMediaTime diff = input_buffer.pts() - last_input_buffer_.pts(); |
| size_t sample_size = |
| GetSampleType() == kSbMediaAudioSampleTypeInt16 ? 2 : 4; |
| size_t size = diff * GetSamplesPerSecond() * sample_size * |
| audio_header_.number_of_channels / kSbMediaTimeSecond; |
| decoded_audios_.push(new DecodedAudio(input_buffer.pts(), size)); |
| |
| if (fill_type == kSilence) { |
| SbMemorySet(decoded_audios_.back()->buffer(), 0, size); |
| } else { |
| SB_DCHECK(fill_type == kWave); |
| for (int i = 0; i < size / sample_size; ++i) { |
| if (sample_size == 2) { |
| *(reinterpret_cast<int16_t*>(decoded_audios_.back()->buffer()) + |
| i) = i; |
| } else { |
| SB_DCHECK(sample_size == 4); |
| *(reinterpret_cast<float*>(decoded_audios_.back()->buffer()) + i) = |
| ((i % 1024) - 512) / 512.0f; |
| } |
| } |
| } |
| } |
| last_input_buffer_ = input_buffer; |
| Schedule(consumed_cb); |
| Schedule(output_cb_); |
| } |
| void WriteEndOfStream() SB_OVERRIDE { |
| if (last_input_buffer_.is_valid()) { |
| // There won't be a next pts, so just guess that the decoded size is |
| // 4 times the encoded size. |
| decoded_audios_.push(new DecodedAudio(last_input_buffer_.pts(), |
| 4 * last_input_buffer_.size())); |
| } |
| decoded_audios_.push(new DecodedAudio()); |
| stream_ended_ = true; |
| Schedule(output_cb_); |
| } |
| scoped_refptr<DecodedAudio> Read() SB_OVERRIDE { |
| scoped_refptr<DecodedAudio> result; |
| if (!decoded_audios_.empty()) { |
| result = decoded_audios_.front(); |
| decoded_audios_.pop(); |
| } |
| return result; |
| } |
| void Reset() SB_OVERRIDE { |
| while (!decoded_audios_.empty()) { |
| decoded_audios_.pop(); |
| } |
| stream_ended_ = false; |
| last_input_buffer_ = InputBuffer(); |
| |
| CancelPendingJobs(); |
| } |
| SbMediaAudioSampleType GetSampleType() const SB_OVERRIDE { |
| return sample_type_; |
| } |
| int GetSamplesPerSecond() const SB_OVERRIDE { |
| return audio_header_.samples_per_second; |
| } |
| bool CanAcceptMoreData() const SB_OVERRIDE { |
| return !stream_ended_ && decoded_audios_.size() <= kMaxDecodedAudiosSize; |
| } |
| |
| private: |
| static const kMaxDecodedAudiosSize = 64; |
| |
| Closure output_cb_; |
| SbMediaAudioSampleType sample_type_; |
| SbMediaAudioHeader audio_header_; |
| bool stream_ended_; |
| std::queue<scoped_refptr<DecodedAudio> > decoded_audios_; |
| InputBuffer last_input_buffer_; |
| }; |
| |
| class StubVideoDecoder : public HostedVideoDecoder { |
| public: |
| StubVideoDecoder() : host_(NULL) {} |
| void WriteInputBuffer(const InputBuffer& input_buffer) SB_OVERRIDE { |
| SB_DCHECK(host_ != NULL); |
| host_->OnDecoderStatusUpdate( |
| kNeedMoreInput, VideoFrame::CreateEmptyFrame(input_buffer.pts())); |
| } |
| void WriteEndOfStream() SB_OVERRIDE { |
| SB_DCHECK(host_ != NULL); |
| host_->OnDecoderStatusUpdate(kBufferFull, VideoFrame::CreateEOSFrame()); |
| } |
| void Reset() SB_OVERRIDE {} |
| void SetHost(Host* host) { |
| SB_DCHECK(host != NULL); |
| SB_DCHECK(host_ == NULL); |
| host_ = host; |
| } |
| |
| private: |
| Host* host_; |
| }; |
| |
| #if SB_API_VERSION >= 4 |
| // static |
| bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode, |
| SbMediaVideoCodec codec, |
| SbDrmSystem drm_system) { |
| return output_mode == kSbPlayerOutputModePunchOut; |
| } |
| #endif // SB_API_VERSION >= 4 |
| |
| // static |
| scoped_ptr<PlayerComponents> PlayerComponents::Create( |
| const AudioParameters& audio_parameters, |
| const VideoParameters& video_parameters) { |
| StubAudioDecoder* audio_decoder = |
| new StubAudioDecoder(audio_parameters.audio_header); |
| StubVideoDecoder* video_decoder = new StubVideoDecoder(); |
| AudioRendererImpl* audio_renderer = |
| new AudioRendererImpl(audio_parameters.job_queue, |
| scoped_ptr<AudioDecoder>(audio_decoder).Pass(), |
| audio_parameters.audio_header); |
| VideoRendererImpl* video_renderer = new VideoRendererImpl( |
| scoped_ptr<HostedVideoDecoder>(video_decoder).Pass()); |
| |
| return scoped_ptr<PlayerComponents>( |
| new PlayerComponents(audio_renderer, video_renderer)); |
| } |
| |
| } // namespace filter |
| } // namespace player |
| } // namespace starboard |
| } // namespace shared |
| } // namespace starboard |