blob: 80bbe4f33f8372bdfa8f2d8ba0de4cdbff48379a [file] [log] [blame]
// 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/ffmpeg_decoder_base.h"
#include "services/media/framework_ffmpeg/ffmpeg_type_converters.h"
namespace mojo {
namespace media {
FfmpegDecoderBase::FfmpegDecoderBase(AvCodecContextPtr av_codec_context) :
av_codec_context_(std::move(av_codec_context)),
av_frame_ptr_(av_frame_alloc()) {
DCHECK(av_codec_context_);
}
FfmpegDecoderBase::~FfmpegDecoderBase() {}
std::unique_ptr<StreamType> FfmpegDecoderBase::output_stream_type() {
return StreamTypeFromAVCodecContext(*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