blob: 8b7285cad13cade5e74d10b5e28c0218dd428326 [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 "src/media/playback/mediaplayer/ffmpeg/ffmpeg_audio_decoder.h"
#include "lib/media/cpp/timeline_rate.h"
#include "src/lib/syslog/cpp/logger.h"
namespace media_player {
// static
std::shared_ptr<Processor> FfmpegAudioDecoder::Create(AvCodecContextPtr av_codec_context) {
return std::make_shared<FfmpegAudioDecoder>(std::move(av_codec_context));
}
FfmpegAudioDecoder::FfmpegAudioDecoder(AvCodecContextPtr av_codec_context)
: FfmpegDecoderBase(std::move(av_codec_context)) {
FX_DCHECK(context());
FX_DCHECK(context()->channels > 0);
std::unique_ptr<StreamType> stream_type = output_stream_type();
FX_DCHECK(stream_type);
FX_DCHECK(stream_type->audio());
set_pts_rate(media::TimelineRate(stream_type->audio()->frames_per_second(), 1));
stream_type_ = std::move(stream_type);
if (av_sample_fmt_is_planar(context()->sample_fmt)) {
// Prepare for interleaving.
lpcm_util_ = LpcmUtil::Create(*stream_type_->audio());
copy_or_interleave_ = true;
}
// Codec-specific code goes here.
switch (context()->codec_id) {
case AV_CODEC_ID_OPUS:
// The opus decoder allocates buffers six times as large as the resulting payload. We need
// to copy the payloads, so we don't use up all the buffer space available to the renderer.
copy_or_interleave_ = true;
break;
default:
break;
}
}
FfmpegAudioDecoder::~FfmpegAudioDecoder() {}
void FfmpegAudioDecoder::ConfigureConnectors() {
ConfigureInputToUseLocalMemory(0, 2);
// TODO(dalesat): Real numbers here. How big are packets?
// We're OK for now, because the audio renderer asks for a single VMO that's
// big enough to handle any packet we want to produce.
ConfigureOutputToUseLocalMemory(0, 1, 1);
}
void FfmpegAudioDecoder::OnNewInputPacket(const PacketPtr& packet) {
if (next_pts() == Packet::kNoPts) {
set_next_pts(packet->GetPts(pts_rate()));
}
context()->reordered_opaque = packet->discontinuity() ? 1 : 0;
context()->pkt_timebase.num = pts_rate().reference_delta();
context()->pkt_timebase.den = pts_rate().subject_delta();
}
int FfmpegAudioDecoder::BuildAVFrame(const AVCodecContext& av_codec_context, AVFrame* av_frame) {
FX_DCHECK(av_frame);
AVSampleFormat av_sample_format = static_cast<AVSampleFormat>(av_frame->format);
int buffer_size = av_samples_get_buffer_size(&av_frame->linesize[0], av_codec_context.channels,
av_frame->nb_samples, av_sample_format,
FfmpegAudioDecoder::kChannelAlign);
if (buffer_size < 0) {
FX_LOGS(WARNING) << "av_samples_get_buffer_size failed";
return buffer_size;
}
// Get the right payload buffer. If we need to copy or interleave later, we just get
// a buffer allocated using malloc. If not, we ask the stage for a buffer.
fbl::RefPtr<PayloadBuffer> buffer = copy_or_interleave_
? PayloadBuffer::CreateWithMalloc(buffer_size)
: AllocatePayloadBuffer(buffer_size);
if (!buffer) {
// TODO(dalesat): Record/report packet drop.
return -1;
}
// Check that the allocator has met the common alignment requirements and
// that those requirements are good enough for the decoder.
FX_DCHECK(PayloadBuffer::IsAligned(buffer->data()));
FX_DCHECK(PayloadBuffer::kByteAlignment >= kChannelAlign);
if (!av_sample_fmt_is_planar(av_sample_format)) {
// Samples are interleaved. There's just one buffer.
av_frame->data[0] = reinterpret_cast<uint8_t*>(buffer->data());
} else {
// Samples are not interleaved. There's one buffer per channel.
int channels = av_codec_context.channels;
int bytes_per_channel = buffer_size / channels;
uint8_t* channel_buffer = reinterpret_cast<uint8_t*>(buffer->data());
FX_DCHECK(buffer || bytes_per_channel == 0);
if (channels <= AV_NUM_DATA_POINTERS) {
// The buffer pointers will fit in av_frame->data.
FX_DCHECK(av_frame->extended_data == av_frame->data);
for (int channel = 0; channel < channels; ++channel) {
av_frame->data[channel] = channel_buffer;
channel_buffer += bytes_per_channel;
}
} else {
// Too many channels for av_frame->data. We have to use
// av_frame->extended_data
av_frame->extended_data =
static_cast<uint8_t**>(av_malloc(channels * sizeof(*av_frame->extended_data)));
// The first AV_NUM_DATA_POINTERS go in both data and extended_data.
int channel = 0;
for (; channel < AV_NUM_DATA_POINTERS; ++channel) {
av_frame->extended_data[channel] = av_frame->data[channel] = channel_buffer;
channel_buffer += bytes_per_channel;
}
// The rest go only in extended_data.
for (; channel < channels; ++channel) {
av_frame->extended_data[channel] = channel_buffer;
channel_buffer += bytes_per_channel;
}
}
}
av_frame->buf[0] = CreateAVBuffer(std::move(buffer));
av_frame->reordered_opaque = av_codec_context.reordered_opaque;
return 0;
}
PacketPtr FfmpegAudioDecoder::CreateOutputPacket(const AVFrame& av_frame,
fbl::RefPtr<PayloadBuffer> payload_buffer) {
FX_DCHECK(av_frame.buf[0]);
FX_DCHECK(payload_buffer);
// We infer the PTS for a packet based on the assumption that the decoder
// produces an uninterrupted stream of frames. The PTS value in av_frame is
// often bogus, and we get bad results if we try to use it. This approach is
// consistent with the way Chromium deals with the ffmpeg audio decoders.
int64_t pts = next_pts();
if (pts != Packet::kNoPts) {
set_next_pts(pts + av_frame.nb_samples);
}
FX_DCHECK(stream_type_);
FX_DCHECK(stream_type_->audio());
uint64_t payload_size = stream_type_->audio()->min_buffer_size(av_frame.nb_samples);
if (copy_or_interleave_) {
// We need to copy or interleave. The original frames are in
// |payload_buffer|, which was allocated from system memory. That buffer
// will get released later in ReleaseBufferForAvFrame. We need a new
// buffer for the output payload, which we get from the stage.
auto new_payload_buffer = AllocatePayloadBuffer(payload_size);
if (!new_payload_buffer) {
// TODO(dalesat): Record/report packet drop.
return nullptr;
}
if (lpcm_util_) {
lpcm_util_->Interleave(payload_buffer->data(),
av_frame.linesize[0] * stream_type_->audio()->channels(),
new_payload_buffer->data(), av_frame.nb_samples);
} else {
memcpy(new_payload_buffer->data(), payload_buffer->data(), payload_size);
}
// |new_payload_buffer| is the buffer we want to attach to the |Packet|.
// This assignment drops the reference to the original |payload_buffer|, so
// it will be recycled once the |AVBuffer| is released.
payload_buffer = std::move(new_payload_buffer);
}
// Create the output packet. We set the discontinuity bit on the packet if
// the corresponding input packet had one.
return Packet::Create(pts, pts_rate(),
false, // Not a keyframe
av_frame.reordered_opaque != 0, // discontinuity
false, // Not end-of-stream. The base class handles end-of-stream.
payload_size, std::move(payload_buffer));
}
const char* FfmpegAudioDecoder::label() const { return "audio_decoder"; }
} // namespace media_player