blob: a5cff77b9634aa155a2dceb1bb3910bdfadb6da6 [file] [log] [blame]
// Copyright 2018 The Cobalt Authors. 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.
// This file contains the explicit specialization of the AudioDecoderImpl class
// for the value 'FFMPEG'.
#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h"
#include <string>
#include "starboard/audio_sink.h"
#include "starboard/common/log.h"
#include "starboard/common/string.h"
#include "starboard/media.h"
#include "starboard/memory.h"
#include "starboard/shared/starboard/media/media_util.h"
namespace starboard {
namespace shared {
namespace ffmpeg {
namespace {
SbMediaAudioSampleType GetSupportedSampleType() {
if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
return kSbMediaAudioSampleTypeFloat32;
}
return kSbMediaAudioSampleTypeInt16Deprecated;
}
AVCodecID GetFfmpegCodecIdByMediaCodec(
starboard::media::AudioStreamInfo stream_info) {
SbMediaAudioCodec audio_codec = stream_info.codec;
switch (audio_codec) {
case kSbMediaAudioCodecAac:
return AV_CODEC_ID_AAC;
case kSbMediaAudioCodecAc3:
#if SB_API_VERSION < 15
return kSbHasAc3Audio ? AV_CODEC_ID_AC3 : AV_CODEC_ID_NONE;
#endif // SB_API_VERSION < 15
return AV_CODEC_ID_AC3;
case kSbMediaAudioCodecEac3:
#if SB_API_VERSION < 15
return kSbHasAc3Audio ? AV_CODEC_ID_EAC3 : AV_CODEC_ID_NONE;
#endif // SB_API_VERSION < 15
return AV_CODEC_ID_EAC3;
case kSbMediaAudioCodecOpus:
return AV_CODEC_ID_OPUS;
case kSbMediaAudioCodecVorbis:
return AV_CODEC_ID_VORBIS;
#if SB_API_VERSION >= 14
case kSbMediaAudioCodecMp3:
return AV_CODEC_ID_MP3;
case kSbMediaAudioCodecPcm:
if (stream_info.bits_per_sample == 16) {
return AV_CODEC_ID_PCM_S16LE;
} else {
SB_LOG(ERROR) << "PCM is only supported for 16-bit audio ("
<< stream_info.bits_per_sample
<< " bits per sample was requested)";
return AV_CODEC_ID_NONE;
}
case kSbMediaAudioCodecFlac:
if (stream_info.bits_per_sample == 16) {
return AV_CODEC_ID_FLAC;
} else {
SB_LOG(ERROR) << "FLAC is only supported for 16-bit audio ("
<< stream_info.bits_per_sample
<< " bits per sample was requested)";
return AV_CODEC_ID_NONE;
}
#endif // SB_API_VERSION >= 14
default:
return AV_CODEC_ID_NONE;
}
}
const bool g_registered =
FFMPEGDispatch::RegisterSpecialization(FFMPEG,
LIBAVCODEC_VERSION_MAJOR,
LIBAVFORMAT_VERSION_MAJOR,
LIBAVUTIL_VERSION_MAJOR);
} // namespace
AudioDecoderImpl<FFMPEG>::AudioDecoderImpl(
const AudioStreamInfo& audio_stream_info)
: codec_context_(NULL),
av_frame_(NULL),
stream_ended_(false),
audio_stream_info_(audio_stream_info) {
SB_DCHECK(g_registered) << "Decoder Specialization registration failed.";
SB_DCHECK(GetFfmpegCodecIdByMediaCodec(audio_stream_info_) !=
AV_CODEC_ID_NONE)
<< "Unsupported audio codec " << audio_stream_info_.codec;
ffmpeg_ = FFMPEGDispatch::GetInstance();
SB_DCHECK(ffmpeg_);
if ((ffmpeg_->specialization_version()) == FFMPEG) {
InitializeCodec();
}
}
AudioDecoderImpl<FFMPEG>::~AudioDecoderImpl() {
TeardownCodec();
}
// static
AudioDecoder* AudioDecoderImpl<FFMPEG>::Create(
const AudioStreamInfo& audio_stream_info) {
return new AudioDecoderImpl<FFMPEG>(audio_stream_info);
}
void AudioDecoderImpl<FFMPEG>::Initialize(const OutputCB& output_cb,
const ErrorCB& error_cb) {
SB_DCHECK(BelongsToCurrentThread());
SB_DCHECK(output_cb);
SB_DCHECK(!output_cb_);
SB_DCHECK(error_cb);
SB_DCHECK(!error_cb_);
output_cb_ = output_cb;
error_cb_ = error_cb;
}
void AudioDecoderImpl<FFMPEG>::Decode(const InputBuffers& input_buffers,
const ConsumedCB& consumed_cb) {
SB_DCHECK(BelongsToCurrentThread());
SB_DCHECK(input_buffers.size() == 1);
SB_DCHECK(input_buffers[0]);
SB_DCHECK(output_cb_);
SB_CHECK(codec_context_ != NULL);
Schedule(consumed_cb);
if (input_buffers.empty() || !input_buffers[0]) {
SB_LOG(ERROR) << "No input buffer to decode.";
return;
}
if (stream_ended_) {
SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called.";
return;
}
const auto& input_buffer = input_buffers[0];
AVPacket packet;
ffmpeg_->av_init_packet(&packet);
packet.data = const_cast<uint8_t*>(input_buffer->data());
packet.size = input_buffer->size();
#if LIBAVUTIL_VERSION_INT < LIBAVUTIL_VERSION_52_8
ffmpeg_->avcodec_get_frame_defaults(av_frame_);
#endif // LIBAVUTIL_VERSION_INT < LIBAVUTIL_VERSION_52_8
int result = 0;
if (ffmpeg_->avcodec_version() < kAVCodecHasUniformDecodeAPI) {
int frame_decoded = 0;
result = ffmpeg_->avcodec_decode_audio4(codec_context_, av_frame_,
&frame_decoded, &packet);
if (result != input_buffer->size()) {
// TODO: Consider fill it with silence.
SB_DLOG(WARNING) << "avcodec_decode_audio4() failed with result: "
<< result
<< " with input buffer size: " << input_buffer->size()
<< " and frame decoded: " << frame_decoded;
error_cb_(kSbPlayerErrorDecode,
FormatString("avcodec_decode_audio4() failed with result %d.",
result));
return;
}
if (frame_decoded != 1) {
// TODO: Adjust timestamp accordingly when decoding result is shifted.
SB_DCHECK(frame_decoded == 0);
SB_DLOG(WARNING) << "avcodec_decode_audio4()/avcodec_receive_frame() "
"returns with 0 frames decoded";
return;
}
ProcessDecodedFrame(*input_buffer, *av_frame_);
return;
}
// Newer decode API.
const int send_packet_result =
ffmpeg_->avcodec_send_packet(codec_context_, &packet);
if (send_packet_result != 0) {
const std::string error_message = FormatString(
"avcodec_send_packet() failed with result %d.", send_packet_result);
SB_DLOG(WARNING) << error_message;
error_cb_(kSbPlayerErrorDecode, error_message);
return;
}
// Keep receiving frames until the decoder has processed the entire packet.
for (;;) {
result = ffmpeg_->avcodec_receive_frame(codec_context_, av_frame_);
if (result != 0) {
// We either hit an error or are done processing packet.
break;
}
ProcessDecodedFrame(*input_buffer, *av_frame_);
}
// A return value of AVERROR(EAGAIN) signifies that the decoder needs
// another packet, so we are done processing the existing packet at that
// point.
if (result != AVERROR(EAGAIN)) {
SB_DLOG(WARNING) << "avcodec_receive_frame() failed with result: "
<< result;
error_cb_(
kSbPlayerErrorDecode,
FormatString("avcodec_receive_frame() failed with result %d.", result));
}
}
void AudioDecoderImpl<FFMPEG>::ProcessDecodedFrame(
const InputBuffer& input_buffer,
const AVFrame& av_frame) {
int decoded_audio_size = ffmpeg_->av_samples_get_buffer_size(
NULL, codec_context_->channels, av_frame.nb_samples,
codec_context_->sample_fmt, 1);
audio_stream_info_.samples_per_second = codec_context_->sample_rate;
if (decoded_audio_size <= 0) {
// TODO: Consider fill it with silence.
SB_LOG(ERROR) << "Decoded audio frame is empty.";
return;
}
scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
codec_context_->channels, GetSampleType(), GetStorageType(),
input_buffer.timestamp(),
codec_context_->channels * av_frame.nb_samples *
starboard::media::GetBytesPerSample(GetSampleType()));
if (GetStorageType() == kSbMediaAudioFrameStorageTypeInterleaved) {
memcpy(decoded_audio->data(), *av_frame.extended_data,
decoded_audio->size_in_bytes());
} else {
SB_DCHECK(GetStorageType() == kSbMediaAudioFrameStorageTypePlanar);
const int per_channel_size_in_bytes =
decoded_audio->size_in_bytes() / decoded_audio->channels();
for (int i = 0; i < decoded_audio->channels(); ++i) {
memcpy(decoded_audio->data() + per_channel_size_in_bytes * i,
av_frame.extended_data[i], per_channel_size_in_bytes);
}
decoded_audio = decoded_audio->SwitchFormatTo(
GetSampleType(), kSbMediaAudioFrameStorageTypeInterleaved);
}
decoded_audio->AdjustForDiscardedDurations(
audio_stream_info_.samples_per_second,
input_buffer.audio_sample_info().discarded_duration_from_front,
input_buffer.audio_sample_info().discarded_duration_from_back);
decoded_audios_.push(decoded_audio);
Schedule(output_cb_);
}
void AudioDecoderImpl<FFMPEG>::WriteEndOfStream() {
SB_DCHECK(BelongsToCurrentThread());
SB_DCHECK(output_cb_);
// AAC has no dependent frames so we needn't flush the decoder. Set the flag
// to ensure that Decode() is not called when the stream is ended.
stream_ended_ = true;
// Put EOS into the queue.
decoded_audios_.push(new DecodedAudio);
Schedule(output_cb_);
}
scoped_refptr<AudioDecoderImpl<FFMPEG>::DecodedAudio>
AudioDecoderImpl<FFMPEG>::Read(int* samples_per_second) {
SB_DCHECK(BelongsToCurrentThread());
SB_DCHECK(output_cb_);
SB_DCHECK(!decoded_audios_.empty());
scoped_refptr<DecodedAudio> result;
if (!decoded_audios_.empty()) {
result = decoded_audios_.front();
decoded_audios_.pop();
}
*samples_per_second = audio_stream_info_.samples_per_second;
return result;
}
void AudioDecoderImpl<FFMPEG>::Reset() {
SB_DCHECK(BelongsToCurrentThread());
TeardownCodec();
if ((ffmpeg_->specialization_version()) == FFMPEG) {
InitializeCodec();
}
stream_ended_ = false;
while (!decoded_audios_.empty()) {
decoded_audios_.pop();
}
CancelPendingJobs();
}
bool AudioDecoderImpl<FFMPEG>::is_valid() const {
return (ffmpeg_ != NULL) && ffmpeg_->is_valid() && (codec_context_ != NULL);
}
SbMediaAudioSampleType AudioDecoderImpl<FFMPEG>::GetSampleType() const {
SB_DCHECK(BelongsToCurrentThread());
if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 ||
codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) {
return kSbMediaAudioSampleTypeInt16Deprecated;
} else if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT ||
codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) {
return kSbMediaAudioSampleTypeFloat32;
}
SB_NOTREACHED();
return kSbMediaAudioSampleTypeFloat32;
}
SbMediaAudioFrameStorageType AudioDecoderImpl<FFMPEG>::GetStorageType() const {
SB_DCHECK(BelongsToCurrentThread());
if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 ||
codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) {
return kSbMediaAudioFrameStorageTypeInterleaved;
}
if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P ||
codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) {
return kSbMediaAudioFrameStorageTypePlanar;
}
SB_NOTREACHED();
return kSbMediaAudioFrameStorageTypeInterleaved;
}
void AudioDecoderImpl<FFMPEG>::InitializeCodec() {
codec_context_ = ffmpeg_->avcodec_alloc_context3(NULL);
if (codec_context_ == NULL) {
SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
return;
}
codec_context_->codec_type = AVMEDIA_TYPE_AUDIO;
codec_context_->codec_id = GetFfmpegCodecIdByMediaCodec(audio_stream_info_);
// Request_sample_fmt is set by us, but sample_fmt is set by the decoder.
if (GetSupportedSampleType() == kSbMediaAudioSampleTypeInt16Deprecated
#if SB_API_VERSION >= 14
// If we request FLT for 16-bit FLAC, FFmpeg will pick S32 as the closest
// option. Since the rest of this pipeline doesn't support S32, we should
// use S16 as the desired format.
|| audio_stream_info_.codec == kSbMediaAudioCodecFlac
#endif // SB_API_VERSION >= 14
) {
codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16;
} else {
codec_context_->request_sample_fmt = AV_SAMPLE_FMT_FLT;
}
codec_context_->channels = audio_stream_info_.number_of_channels;
codec_context_->sample_rate = audio_stream_info_.samples_per_second;
codec_context_->extradata = NULL;
codec_context_->extradata_size = 0;
if ((codec_context_->codec_id == AV_CODEC_ID_OPUS ||
codec_context_->codec_id == AV_CODEC_ID_VORBIS) &&
!audio_stream_info_.audio_specific_config.empty()) {
// AV_INPUT_BUFFER_PADDING_SIZE is not defined in ancient avcodec.h. Use a
// large enough padding here explicitly.
const int kAvInputBufferPaddingSize = 256;
codec_context_->extradata_size =
audio_stream_info_.audio_specific_config.size();
codec_context_->extradata = static_cast<uint8_t*>(ffmpeg_->av_malloc(
codec_context_->extradata_size + kAvInputBufferPaddingSize));
SB_DCHECK(codec_context_->extradata);
memcpy(codec_context_->extradata,
audio_stream_info_.audio_specific_config.data(),
codec_context_->extradata_size);
memset(codec_context_->extradata + codec_context_->extradata_size, 0,
kAvInputBufferPaddingSize);
}
AVCodec* codec = ffmpeg_->avcodec_find_decoder(codec_context_->codec_id);
if (codec == NULL) {
SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
TeardownCodec();
return;
}
int rv = ffmpeg_->OpenCodec(codec_context_, codec);
if (rv < 0) {
SB_LOG(ERROR) << "Unable to open codec";
TeardownCodec();
return;
}
if (ffmpeg_->avcodec_version() > kAVCodecSupportsAvFrameAlloc) {
av_frame_ = ffmpeg_->av_frame_alloc();
} else {
av_frame_ = ffmpeg_->avcodec_alloc_frame();
}
if (av_frame_ == NULL) {
SB_LOG(ERROR) << "Unable to allocate audio frame";
TeardownCodec();
}
}
void AudioDecoderImpl<FFMPEG>::TeardownCodec() {
if (codec_context_) {
ffmpeg_->CloseCodec(codec_context_);
ffmpeg_->FreeContext(&codec_context_);
}
ffmpeg_->FreeFrame(&av_frame_);
}
} // namespace ffmpeg
} // namespace shared
} // namespace starboard