| // Copyright 2015 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 "media/mojo/services/mojo_decryptor_service.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "media/base/audio_decoder_config.h" |
| #include "media/base/cdm_context.h" |
| #include "media/base/decoder_buffer.h" |
| #include "media/base/decryptor.h" |
| #include "media/base/video_decoder_config.h" |
| #include "media/base/video_frame.h" |
| #include "media/mojo/common/media_type_converters.h" |
| #include "media/mojo/common/mojo_decoder_buffer_converter.h" |
| #include "media/mojo/common/mojo_shared_buffer_video_frame.h" |
| #include "media/mojo/mojom/demuxer_stream.mojom.h" |
| #include "media/mojo/services/mojo_cdm_service_context.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| |
| namespace media { |
| |
| namespace { |
| |
| // A mojom::FrameResourceReleaser implementation. This object is created when |
| // DecryptAndDecodeVideo() returns a shared memory video frame, and holds |
| // on to the local frame. When MojoDecryptor is done using the frame, |
| // the connection should be broken and this will free the shared resources |
| // associated with the frame. |
| class FrameResourceReleaserImpl final : public mojom::FrameResourceReleaser { |
| public: |
| explicit FrameResourceReleaserImpl(scoped_refptr<VideoFrame> frame) |
| : frame_(std::move(frame)) { |
| DVLOG(3) << __func__; |
| DCHECK_EQ(VideoFrame::STORAGE_MOJO_SHARED_BUFFER, frame_->storage_type()); |
| } |
| |
| FrameResourceReleaserImpl(const FrameResourceReleaserImpl&) = delete; |
| FrameResourceReleaserImpl& operator=(const FrameResourceReleaserImpl&) = |
| delete; |
| |
| ~FrameResourceReleaserImpl() override { DVLOG(3) << __func__; } |
| |
| private: |
| scoped_refptr<VideoFrame> frame_; |
| }; |
| |
| const char kInvalidStateMessage[] = "MojoDecryptorService - invalid state"; |
| |
| } // namespace |
| |
| MojoDecryptorService::MojoDecryptorService( |
| media::Decryptor* decryptor, |
| std::unique_ptr<CdmContextRef> cdm_context_ref) |
| : decryptor_(decryptor), cdm_context_ref_(std::move(cdm_context_ref)) { |
| DVLOG(1) << __func__; |
| DCHECK(decryptor_); |
| // |cdm_context_ref_| could be null, in which case the owner of |this| will |
| // make sure |decryptor_| is always valid. |
| weak_this_ = weak_factory_.GetWeakPtr(); |
| } |
| |
| MojoDecryptorService::~MojoDecryptorService() { |
| DVLOG(1) << __func__; |
| } |
| |
| void MojoDecryptorService::Initialize( |
| mojo::ScopedDataPipeConsumerHandle audio_pipe, |
| mojo::ScopedDataPipeConsumerHandle video_pipe, |
| mojo::ScopedDataPipeConsumerHandle decrypt_pipe, |
| mojo::ScopedDataPipeProducerHandle decrypted_pipe) { |
| DVLOG(1) << __func__; |
| |
| if (has_initialize_been_called_) { |
| mojo::ReportBadMessage(kInvalidStateMessage); |
| return; |
| } |
| has_initialize_been_called_ = true; |
| |
| audio_buffer_reader_ = |
| std::make_unique<MojoDecoderBufferReader>(std::move(audio_pipe)); |
| video_buffer_reader_ = |
| std::make_unique<MojoDecoderBufferReader>(std::move(video_pipe)); |
| decrypt_buffer_reader_ = |
| std::make_unique<MojoDecoderBufferReader>(std::move(decrypt_pipe)); |
| decrypted_buffer_writer_ = |
| std::make_unique<MojoDecoderBufferWriter>(std::move(decrypted_pipe)); |
| } |
| |
| void MojoDecryptorService::Decrypt(StreamType stream_type, |
| mojom::DecoderBufferPtr encrypted, |
| DecryptCallback callback) { |
| DVLOG(3) << __func__; |
| |
| if (!decrypt_buffer_reader_) { |
| mojo::ReportBadMessage(kInvalidStateMessage); |
| return; |
| } |
| |
| decrypt_buffer_reader_->ReadDecoderBuffer( |
| std::move(encrypted), |
| base::BindOnce(&MojoDecryptorService::OnReadDone, weak_this_, stream_type, |
| std::move(callback))); |
| } |
| |
| void MojoDecryptorService::CancelDecrypt(StreamType stream_type) { |
| DVLOG(2) << __func__; |
| decryptor_->CancelDecrypt(stream_type); |
| } |
| |
| void MojoDecryptorService::InitializeAudioDecoder( |
| const AudioDecoderConfig& config, |
| InitializeAudioDecoderCallback callback) { |
| DVLOG(1) << __func__; |
| decryptor_->InitializeAudioDecoder( |
| config, base::BindOnce(&MojoDecryptorService::OnAudioDecoderInitialized, |
| weak_this_, std::move(callback))); |
| } |
| |
| void MojoDecryptorService::InitializeVideoDecoder( |
| const VideoDecoderConfig& config, |
| InitializeVideoDecoderCallback callback) { |
| DVLOG(2) << __func__; |
| decryptor_->InitializeVideoDecoder( |
| config, base::BindOnce(&MojoDecryptorService::OnVideoDecoderInitialized, |
| weak_this_, std::move(callback))); |
| } |
| |
| void MojoDecryptorService::DecryptAndDecodeAudio( |
| mojom::DecoderBufferPtr encrypted, |
| DecryptAndDecodeAudioCallback callback) { |
| DVLOG(3) << __func__; |
| |
| if (!audio_buffer_reader_) { |
| mojo::ReportBadMessage(kInvalidStateMessage); |
| return; |
| } |
| |
| audio_buffer_reader_->ReadDecoderBuffer( |
| std::move(encrypted), base::BindOnce(&MojoDecryptorService::OnAudioRead, |
| weak_this_, std::move(callback))); |
| } |
| |
| void MojoDecryptorService::DecryptAndDecodeVideo( |
| mojom::DecoderBufferPtr encrypted, |
| DecryptAndDecodeVideoCallback callback) { |
| DVLOG(3) << __func__; |
| |
| if (!video_buffer_reader_) { |
| mojo::ReportBadMessage(kInvalidStateMessage); |
| return; |
| } |
| |
| video_buffer_reader_->ReadDecoderBuffer( |
| std::move(encrypted), base::BindOnce(&MojoDecryptorService::OnVideoRead, |
| weak_this_, std::move(callback))); |
| } |
| |
| void MojoDecryptorService::ResetDecoder(StreamType stream_type) { |
| DVLOG(2) << __func__ << ": stream_type = " << stream_type; |
| |
| // Reset the reader so that pending decodes will be dispatched first. |
| if (!GetBufferReader(stream_type)) |
| return; |
| |
| GetBufferReader(stream_type) |
| ->Flush(base::BindOnce(&MojoDecryptorService::OnReaderFlushDone, |
| weak_this_, stream_type)); |
| } |
| |
| void MojoDecryptorService::DeinitializeDecoder(StreamType stream_type) { |
| DVLOG(2) << __func__; |
| DCHECK(!GetBufferReader(stream_type)->HasPendingReads()) |
| << "The decoder should be fully flushed before deinitialized."; |
| |
| decryptor_->DeinitializeDecoder(stream_type); |
| } |
| |
| void MojoDecryptorService::OnReadDone(StreamType stream_type, |
| DecryptCallback callback, |
| scoped_refptr<DecoderBuffer> buffer) { |
| if (!buffer) { |
| std::move(callback).Run(Status::kError, nullptr); |
| return; |
| } |
| |
| decryptor_->Decrypt(stream_type, std::move(buffer), |
| base::BindOnce(&MojoDecryptorService::OnDecryptDone, |
| weak_this_, std::move(callback))); |
| } |
| |
| void MojoDecryptorService::OnDecryptDone(DecryptCallback callback, |
| Status status, |
| scoped_refptr<DecoderBuffer> buffer) { |
| DVLOG_IF(1, status != Status::kSuccess) << __func__ << "(" << status << ")"; |
| DVLOG_IF(3, status == Status::kSuccess) << __func__; |
| |
| if (!buffer) { |
| std::move(callback).Run(status, nullptr); |
| return; |
| } |
| |
| mojom::DecoderBufferPtr mojo_buffer = |
| decrypted_buffer_writer_->WriteDecoderBuffer(std::move(buffer)); |
| if (!mojo_buffer) { |
| std::move(callback).Run(Status::kError, nullptr); |
| return; |
| } |
| |
| std::move(callback).Run(status, std::move(mojo_buffer)); |
| } |
| |
| void MojoDecryptorService::OnAudioDecoderInitialized( |
| InitializeAudioDecoderCallback callback, |
| bool success) { |
| DVLOG(2) << __func__ << "(" << success << ")"; |
| std::move(callback).Run(success); |
| } |
| |
| void MojoDecryptorService::OnVideoDecoderInitialized( |
| InitializeVideoDecoderCallback callback, |
| bool success) { |
| DVLOG(2) << __func__ << "(" << success << ")"; |
| std::move(callback).Run(success); |
| } |
| |
| void MojoDecryptorService::OnAudioRead(DecryptAndDecodeAudioCallback callback, |
| scoped_refptr<DecoderBuffer> buffer) { |
| if (!buffer) { |
| std::move(callback).Run(Status::kError, |
| std::vector<mojom::AudioBufferPtr>()); |
| return; |
| } |
| |
| decryptor_->DecryptAndDecodeAudio( |
| std::move(buffer), base::BindOnce(&MojoDecryptorService::OnAudioDecoded, |
| weak_this_, std::move(callback))); |
| } |
| |
| void MojoDecryptorService::OnVideoRead(DecryptAndDecodeVideoCallback callback, |
| scoped_refptr<DecoderBuffer> buffer) { |
| if (!buffer) { |
| std::move(callback).Run(Status::kError, nullptr, mojo::NullRemote()); |
| return; |
| } |
| |
| decryptor_->DecryptAndDecodeVideo( |
| std::move(buffer), base::BindOnce(&MojoDecryptorService::OnVideoDecoded, |
| weak_this_, std::move(callback))); |
| } |
| |
| void MojoDecryptorService::OnReaderFlushDone(StreamType stream_type) { |
| DVLOG(2) << __func__ << ": stream_type = " << stream_type; |
| decryptor_->ResetDecoder(stream_type); |
| } |
| |
| void MojoDecryptorService::OnAudioDecoded( |
| DecryptAndDecodeAudioCallback callback, |
| Status status, |
| const media::Decryptor::AudioFrames& frames) { |
| DVLOG_IF(1, status != Status::kSuccess) << __func__ << "(" << status << ")"; |
| DVLOG_IF(3, status == Status::kSuccess) << __func__; |
| |
| // Note that the audio data is sent over the mojo pipe. This could be |
| // improved to use shared memory (http://crbug.com/593896). |
| std::vector<mojom::AudioBufferPtr> audio_buffers; |
| for (const auto& frame : frames) |
| audio_buffers.push_back(mojom::AudioBuffer::From(*frame)); |
| |
| std::move(callback).Run(status, std::move(audio_buffers)); |
| } |
| |
| void MojoDecryptorService::OnVideoDecoded( |
| DecryptAndDecodeVideoCallback callback, |
| Status status, |
| scoped_refptr<VideoFrame> frame) { |
| DVLOG_IF(1, status != Status::kSuccess) |
| << __func__ << ": status = " << status; |
| DVLOG_IF(3, status == Status::kSuccess) << __func__; |
| |
| if (!frame) { |
| DCHECK_NE(status, Status::kSuccess); |
| std::move(callback).Run(status, nullptr, mojo::NullRemote()); |
| return; |
| } |
| |
| // If |frame| has shared memory that will be passed back, keep the reference |
| // to it until the other side is done with the memory. |
| mojo::PendingRemote<mojom::FrameResourceReleaser> releaser; |
| if (frame->storage_type() == VideoFrame::STORAGE_MOJO_SHARED_BUFFER) { |
| mojo::MakeSelfOwnedReceiver( |
| std::make_unique<FrameResourceReleaserImpl>(frame), |
| releaser.InitWithNewPipeAndPassReceiver()); |
| } |
| |
| std::move(callback).Run(status, std::move(frame), std::move(releaser)); |
| } |
| |
| MojoDecoderBufferReader* MojoDecryptorService::GetBufferReader( |
| StreamType stream_type) const { |
| switch (stream_type) { |
| case StreamType::kAudio: |
| return audio_buffer_reader_.get(); |
| case StreamType::kVideo: |
| return video_buffer_reader_.get(); |
| } |
| |
| NOTREACHED() << "Unexpected stream_type: " << stream_type; |
| return nullptr; |
| } |
| |
| } // namespace media |