|  | // Copyright 2016 Google Inc. 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. | 
|  |  | 
|  | #include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.h" | 
|  |  | 
|  | #include "starboard/log.h" | 
|  |  | 
|  | namespace starboard { | 
|  | namespace shared { | 
|  | namespace ffmpeg { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // The required output format for the decoder is interleaved float.  However | 
|  | // the output of the ffmpeg decoder can be in other formats.  So libavresample | 
|  | // is used to convert the output into the required format. | 
|  | void ConvertToInterleavedFloat(int source_sample_format, | 
|  | int channel_layout, | 
|  | int sample_rate, | 
|  | int samples_per_channel, | 
|  | uint8_t** input_buffer, | 
|  | uint8_t* output_buffer) { | 
|  | AVAudioResampleContext* context = avresample_alloc_context(); | 
|  | SB_DCHECK(context != NULL); | 
|  |  | 
|  | av_opt_set_int(context, "in_channel_layout", channel_layout, 0); | 
|  | av_opt_set_int(context, "out_channel_layout", channel_layout, 0); | 
|  | av_opt_set_int(context, "in_sample_rate", sample_rate, 0); | 
|  | av_opt_set_int(context, "out_sample_rate", sample_rate, 0); | 
|  | av_opt_set_int(context, "in_sample_fmt", source_sample_format, 0); | 
|  | av_opt_set_int(context, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0); | 
|  | av_opt_set_int(context, "internal_sample_fmt", source_sample_format, 0); | 
|  |  | 
|  | int result = avresample_open(context); | 
|  | SB_DCHECK(!result); | 
|  |  | 
|  | int samples_resampled = | 
|  | avresample_convert(context, &output_buffer, 1024, samples_per_channel, | 
|  | input_buffer, 0, samples_per_channel); | 
|  | SB_DCHECK(samples_resampled == samples_per_channel); | 
|  |  | 
|  | avresample_close(context); | 
|  | av_free(context); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec, | 
|  | const SbMediaAudioHeader& audio_header) | 
|  | : codec_context_(NULL), | 
|  | av_frame_(NULL), | 
|  | stream_ended_(false), | 
|  | audio_header_(audio_header) { | 
|  | SB_DCHECK(audio_codec == kSbMediaAudioCodecAac); | 
|  |  | 
|  | InitializeCodec(); | 
|  | } | 
|  |  | 
|  | AudioDecoder::~AudioDecoder() { | 
|  | TeardownCodec(); | 
|  | } | 
|  |  | 
|  | void AudioDecoder::Decode(const InputBuffer& input_buffer, | 
|  | std::vector<float>* output) { | 
|  | SB_CHECK(output != NULL); | 
|  | SB_CHECK(codec_context_ != NULL); | 
|  |  | 
|  | if (stream_ended_) { | 
|  | SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called."; | 
|  | return; | 
|  | } | 
|  |  | 
|  | AVPacket packet; | 
|  | av_init_packet(&packet); | 
|  | packet.data = const_cast<uint8_t*>(input_buffer.data()); | 
|  | packet.size = input_buffer.size(); | 
|  |  | 
|  | avcodec_get_frame_defaults(av_frame_); | 
|  | int frame_decoded = 0; | 
|  | int result = | 
|  | avcodec_decode_audio4(codec_context_, av_frame_, &frame_decoded, &packet); | 
|  | if (result != input_buffer.size() || frame_decoded != 1) { | 
|  | // 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; | 
|  | output->clear(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | int decoded_audio_size = av_samples_get_buffer_size( | 
|  | NULL, codec_context_->channels, av_frame_->nb_samples, | 
|  | codec_context_->sample_fmt, 1); | 
|  | audio_header_.samples_per_second = codec_context_->sample_rate; | 
|  |  | 
|  | if (decoded_audio_size > 0) { | 
|  | output->resize(decoded_audio_size / sizeof(float)); | 
|  | ConvertToInterleavedFloat( | 
|  | codec_context_->sample_fmt, codec_context_->channel_layout, | 
|  | audio_header_.samples_per_second, av_frame_->nb_samples, | 
|  | av_frame_->extended_data, reinterpret_cast<uint8_t*>(&(*output)[0])); | 
|  | } else { | 
|  | // TODO: Consider fill it with silence. | 
|  | SB_LOG(ERROR) << "Decoded audio frame is empty."; | 
|  | output->clear(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AudioDecoder::WriteEndOfStream() { | 
|  | // 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; | 
|  | } | 
|  |  | 
|  | void AudioDecoder::Reset() { | 
|  | stream_ended_ = false; | 
|  | } | 
|  |  | 
|  | int AudioDecoder::GetSamplesPerSecond() { | 
|  | return audio_header_.samples_per_second; | 
|  | } | 
|  |  | 
|  | void AudioDecoder::InitializeCodec() { | 
|  | InitializeFfmpeg(); | 
|  |  | 
|  | codec_context_ = 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 = AV_CODEC_ID_AAC; | 
|  | // Request_sample_fmt is set by us, but sample_fmt is set by the decoder. | 
|  | codec_context_->request_sample_fmt = AV_SAMPLE_FMT_FLT;  // interleaved float | 
|  |  | 
|  | codec_context_->channels = audio_header_.number_of_channels; | 
|  | codec_context_->sample_rate = audio_header_.samples_per_second; | 
|  |  | 
|  | codec_context_->extradata = NULL; | 
|  | codec_context_->extradata_size = 0; | 
|  |  | 
|  | AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); | 
|  |  | 
|  | if (codec == NULL) { | 
|  | SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context"; | 
|  | TeardownCodec(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | int rv = avcodec_open2(codec_context_, codec, NULL); | 
|  | if (rv < 0) { | 
|  | SB_LOG(ERROR) << "Unable to open codec"; | 
|  | TeardownCodec(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | av_frame_ = avcodec_alloc_frame(); | 
|  | if (av_frame_ == NULL) { | 
|  | SB_LOG(ERROR) << "Unable to allocate audio frame"; | 
|  | TeardownCodec(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AudioDecoder::TeardownCodec() { | 
|  | if (codec_context_) { | 
|  | avcodec_close(codec_context_); | 
|  | av_free(codec_context_); | 
|  | codec_context_ = NULL; | 
|  | } | 
|  | if (av_frame_) { | 
|  | av_free(av_frame_); | 
|  | av_frame_ = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace ffmpeg | 
|  |  | 
|  | namespace starboard { | 
|  | namespace player { | 
|  |  | 
|  | // static | 
|  | AudioDecoder* AudioDecoder::Create(SbMediaAudioCodec audio_codec, | 
|  | const SbMediaAudioHeader& audio_header) { | 
|  | ffmpeg::AudioDecoder* decoder = | 
|  | new ffmpeg::AudioDecoder(audio_codec, audio_header); | 
|  | if (decoder->is_valid()) { | 
|  | return decoder; | 
|  | } | 
|  | delete decoder; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | }  // namespace player | 
|  | }  // namespace starboard | 
|  |  | 
|  | }  // namespace shared | 
|  | }  // namespace starboard |