| // Copyright 2016 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 "base/logging.h" | 
 | #include "services/media/framework_ffmpeg/av_codec_context.h" | 
 | #include "services/media/framework_ffmpeg/ffmpeg_decoder_base.h" | 
 |  | 
 | namespace mojo { | 
 | namespace media { | 
 |  | 
 | FfmpegDecoderBase::FfmpegDecoderBase(AvCodecContextPtr av_codec_context) | 
 |     : av_codec_context_(std::move(av_codec_context)), | 
 |       av_frame_ptr_(ffmpeg::AvFrame::Create()) { | 
 |   DCHECK(av_codec_context_); | 
 | } | 
 |  | 
 | FfmpegDecoderBase::~FfmpegDecoderBase() {} | 
 |  | 
 | std::unique_ptr<StreamType> FfmpegDecoderBase::output_stream_type() { | 
 |   return AvCodecContext::GetStreamType(*av_codec_context_); | 
 | } | 
 |  | 
 | void FfmpegDecoderBase::Flush() { | 
 |   DCHECK(av_codec_context_); | 
 |   avcodec_flush_buffers(av_codec_context_.get()); | 
 | } | 
 |  | 
 | bool FfmpegDecoderBase::TransformPacket(const PacketPtr& input, | 
 |                                         bool new_input, | 
 |                                         PayloadAllocator* allocator, | 
 |                                         PacketPtr* output) { | 
 |   DCHECK(input); | 
 |   DCHECK(allocator); | 
 |   DCHECK(output); | 
 |  | 
 |   *output = nullptr; | 
 |  | 
 |   if (new_input) { | 
 |     PrepareInputPacket(input); | 
 |   } | 
 |  | 
 |   bool frame_decoded = false; | 
 |   int input_bytes_used = | 
 |       Decode(av_packet_, av_frame_ptr_, allocator, &frame_decoded); | 
 |   if (input_bytes_used < 0) { | 
 |     // Decode failed. | 
 |     return UnprepareInputPacket(input, output); | 
 |   } | 
 |  | 
 |   if (frame_decoded) { | 
 |     DCHECK(allocator); | 
 |     *output = CreateOutputPacket(*av_frame_ptr_, allocator); | 
 |     av_frame_unref(av_frame_ptr_.get()); | 
 |   } | 
 |  | 
 |   CHECK(input_bytes_used <= av_packet_.size) | 
 |       << "Ffmpeg decoder read beyond end of packet"; | 
 |   av_packet_.size -= input_bytes_used; | 
 |   av_packet_.data += input_bytes_used; | 
 |  | 
 |   if (av_packet_.size != 0 || (input->end_of_stream() && frame_decoded)) { | 
 |     // The input packet is only partially decoded, or it's an end-of-stream | 
 |     // packet and we're still draining. Let the caller know we want to see the | 
 |     // input packet again. | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Used up the whole input packet, and, if we were draining, we're done with | 
 |   // that too. | 
 |   return UnprepareInputPacket(input, output); | 
 | } | 
 |  | 
 | void FfmpegDecoderBase::PrepareInputPacket(const PacketPtr& input) { | 
 |   av_init_packet(&av_packet_); | 
 |   av_packet_.data = reinterpret_cast<uint8_t*>(input->payload()); | 
 |   av_packet_.size = input->size(); | 
 |   av_packet_.pts = input->pts(); | 
 | } | 
 |  | 
 | bool FfmpegDecoderBase::UnprepareInputPacket(const PacketPtr& input, | 
 |                                              PacketPtr* output) { | 
 |   if (input->end_of_stream()) { | 
 |     // Indicate end of stream. This happens when we're draining for the last | 
 |     // time, so there should be no output packet yet. | 
 |     DCHECK(*output == nullptr); | 
 |     *output = CreateOutputEndOfStreamPacket(); | 
 |   } | 
 |  | 
 |   av_packet_.size = 0; | 
 |   av_packet_.data = nullptr; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | }  // namespace media | 
 | }  // namespace mojo |