Andrew Top | 2a79646 | 2018-06-29 09:04:04 -0700 | [diff] [blame] | 1 | // Copyright 2018 The Cobalt Authors. All Rights Reserved. |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | // This file contains the explicit specialization of the AudioDecoderImpl class |
| 16 | // for the value 'FFMPEG'. |
| 17 | |
| 18 | #include "starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h" |
| 19 | |
| 20 | #include "starboard/audio_sink.h" |
Kaido Kert | b685599 | 2020-06-22 16:39:13 -0700 | [diff] [blame] | 21 | #include "starboard/common/format_string.h" |
Chad Duffin | ac9ac06 | 2019-07-23 10:06:45 -0700 | [diff] [blame] | 22 | #include "starboard/common/log.h" |
| 23 | #include "starboard/common/string.h" |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 24 | #include "starboard/memory.h" |
| 25 | #include "starboard/shared/starboard/media/media_util.h" |
| 26 | |
| 27 | namespace starboard { |
| 28 | namespace shared { |
| 29 | namespace ffmpeg { |
| 30 | |
| 31 | namespace { |
| 32 | |
| 33 | SbMediaAudioSampleType GetSupportedSampleType() { |
| 34 | if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) { |
| 35 | return kSbMediaAudioSampleTypeFloat32; |
| 36 | } |
| 37 | return kSbMediaAudioSampleTypeInt16Deprecated; |
| 38 | } |
| 39 | |
| 40 | AVCodecID GetFfmpegCodecIdByMediaCodec(SbMediaAudioCodec audio_codec) { |
| 41 | switch (audio_codec) { |
| 42 | case kSbMediaAudioCodecAac: |
| 43 | return AV_CODEC_ID_AAC; |
Kaido Kert | f585e26 | 2020-06-08 11:42:28 -0700 | [diff] [blame] | 44 | #if SB_API_VERSION >= 12 || SB_HAS(AC3_AUDIO) |
Andrew Top | 61a8495 | 2019-04-30 15:07:33 -0700 | [diff] [blame] | 45 | case kSbMediaAudioCodecAc3: |
Kaido Kert | 612c020 | 2020-01-22 10:28:42 -0800 | [diff] [blame] | 46 | return kSbHasAc3Audio ? AV_CODEC_ID_AC3 : AV_CODEC_ID_NONE; |
Andrew Top | 61a8495 | 2019-04-30 15:07:33 -0700 | [diff] [blame] | 47 | case kSbMediaAudioCodecEac3: |
Kaido Kert | 612c020 | 2020-01-22 10:28:42 -0800 | [diff] [blame] | 48 | return kSbHasAc3Audio ? AV_CODEC_ID_EAC3 : AV_CODEC_ID_NONE; |
Kaido Kert | f585e26 | 2020-06-08 11:42:28 -0700 | [diff] [blame] | 49 | #endif // SB_API_VERSION >= 12 || |
Kaido Kert | 612c020 | 2020-01-22 10:28:42 -0800 | [diff] [blame] | 50 | // SB_HAS(AC3_AUDIO) |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 51 | case kSbMediaAudioCodecOpus: |
| 52 | return AV_CODEC_ID_OPUS; |
| 53 | default: |
| 54 | return AV_CODEC_ID_NONE; |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | const bool g_registered = |
| 59 | FFMPEGDispatch::RegisterSpecialization(FFMPEG, |
| 60 | LIBAVCODEC_VERSION_MAJOR, |
| 61 | LIBAVFORMAT_VERSION_MAJOR, |
| 62 | LIBAVUTIL_VERSION_MAJOR); |
| 63 | |
| 64 | } // namespace |
| 65 | |
| 66 | AudioDecoderImpl<FFMPEG>::AudioDecoderImpl( |
| 67 | SbMediaAudioCodec audio_codec, |
Chad Duffin | ac9ac06 | 2019-07-23 10:06:45 -0700 | [diff] [blame] | 68 | const SbMediaAudioSampleInfo& audio_sample_info) |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 69 | : audio_codec_(audio_codec), |
| 70 | codec_context_(NULL), |
| 71 | av_frame_(NULL), |
| 72 | stream_ended_(false), |
Chad Duffin | ac9ac06 | 2019-07-23 10:06:45 -0700 | [diff] [blame] | 73 | audio_sample_info_(audio_sample_info) { |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 74 | SB_DCHECK(g_registered) << "Decoder Specialization registration failed."; |
| 75 | SB_DCHECK(GetFfmpegCodecIdByMediaCodec(audio_codec) != AV_CODEC_ID_NONE) |
| 76 | << "Unsupported audio codec " << audio_codec; |
| 77 | ffmpeg_ = FFMPEGDispatch::GetInstance(); |
| 78 | SB_DCHECK(ffmpeg_); |
| 79 | if ((ffmpeg_->specialization_version()) == FFMPEG) { |
| 80 | InitializeCodec(); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | AudioDecoderImpl<FFMPEG>::~AudioDecoderImpl() { |
| 85 | TeardownCodec(); |
| 86 | } |
| 87 | |
| 88 | // static |
| 89 | AudioDecoder* AudioDecoderImpl<FFMPEG>::Create( |
| 90 | SbMediaAudioCodec audio_codec, |
Chad Duffin | ac9ac06 | 2019-07-23 10:06:45 -0700 | [diff] [blame] | 91 | const SbMediaAudioSampleInfo& audio_sample_info) { |
| 92 | return new AudioDecoderImpl<FFMPEG>(audio_codec, audio_sample_info); |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 93 | } |
| 94 | |
| 95 | void AudioDecoderImpl<FFMPEG>::Initialize(const OutputCB& output_cb, |
| 96 | const ErrorCB& error_cb) { |
| 97 | SB_DCHECK(BelongsToCurrentThread()); |
| 98 | SB_DCHECK(output_cb); |
| 99 | SB_DCHECK(!output_cb_); |
| 100 | SB_DCHECK(error_cb); |
| 101 | SB_DCHECK(!error_cb_); |
| 102 | |
| 103 | output_cb_ = output_cb; |
| 104 | error_cb_ = error_cb; |
| 105 | } |
| 106 | |
| 107 | void AudioDecoderImpl<FFMPEG>::Decode( |
| 108 | const scoped_refptr<InputBuffer>& input_buffer, |
| 109 | const ConsumedCB& consumed_cb) { |
| 110 | SB_DCHECK(BelongsToCurrentThread()); |
| 111 | SB_DCHECK(input_buffer); |
| 112 | SB_DCHECK(output_cb_); |
| 113 | SB_CHECK(codec_context_ != NULL); |
| 114 | |
| 115 | Schedule(consumed_cb); |
| 116 | |
| 117 | if (stream_ended_) { |
| 118 | SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called."; |
| 119 | return; |
| 120 | } |
| 121 | |
| 122 | AVPacket packet; |
| 123 | ffmpeg_->av_init_packet(&packet); |
| 124 | packet.data = const_cast<uint8_t*>(input_buffer->data()); |
| 125 | packet.size = input_buffer->size(); |
| 126 | |
Kaido Kert | 612c020 | 2020-01-22 10:28:42 -0800 | [diff] [blame] | 127 | #if LIBAVUTIL_VERSION_INT < LIBAVUTIL_VERSION_52_8 |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 128 | ffmpeg_->avcodec_get_frame_defaults(av_frame_); |
Kaido Kert | 612c020 | 2020-01-22 10:28:42 -0800 | [diff] [blame] | 129 | #endif // LIBAVUTIL_VERSION_INT < LIBAVUTIL_VERSION_52_8 |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 130 | int frame_decoded = 0; |
| 131 | int result = ffmpeg_->avcodec_decode_audio4(codec_context_, av_frame_, |
| 132 | &frame_decoded, &packet); |
| 133 | if (result != input_buffer->size()) { |
| 134 | // TODO: Consider fill it with silence. |
| 135 | SB_DLOG(WARNING) << "avcodec_decode_audio4() failed with result: " << result |
| 136 | << " with input buffer size: " << input_buffer->size() |
| 137 | << " and frame decoded: " << frame_decoded; |
Andrew Top | 8b6b16e | 2018-07-25 17:44:41 -0700 | [diff] [blame] | 138 | error_cb_( |
| 139 | kSbPlayerErrorDecode, |
| 140 | FormatString("avcodec_decode_audio4() failed with result %d.", result)); |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 141 | return; |
| 142 | } |
| 143 | |
| 144 | if (frame_decoded != 1) { |
| 145 | // TODO: Adjust timestamp accordingly when decoding result is shifted. |
| 146 | SB_DCHECK(frame_decoded == 0); |
| 147 | SB_DLOG(WARNING) << "avcodec_decode_audio4() returns with 0 frames decoded"; |
| 148 | return; |
| 149 | } |
| 150 | |
| 151 | int decoded_audio_size = ffmpeg_->av_samples_get_buffer_size( |
| 152 | NULL, codec_context_->channels, av_frame_->nb_samples, |
| 153 | codec_context_->sample_fmt, 1); |
Chad Duffin | ac9ac06 | 2019-07-23 10:06:45 -0700 | [diff] [blame] | 154 | audio_sample_info_.samples_per_second = codec_context_->sample_rate; |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 155 | |
| 156 | if (decoded_audio_size > 0) { |
| 157 | scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio( |
| 158 | codec_context_->channels, GetSampleType(), GetStorageType(), |
| 159 | input_buffer->timestamp(), |
| 160 | codec_context_->channels * av_frame_->nb_samples * |
| 161 | starboard::media::GetBytesPerSample(GetSampleType())); |
| 162 | if (GetStorageType() == kSbMediaAudioFrameStorageTypeInterleaved) { |
| 163 | SbMemoryCopy(decoded_audio->buffer(), *av_frame_->extended_data, |
| 164 | decoded_audio->size()); |
| 165 | } else { |
| 166 | SB_DCHECK(GetStorageType() == kSbMediaAudioFrameStorageTypePlanar); |
| 167 | const int per_channel_size_in_bytes = |
| 168 | decoded_audio->size() / decoded_audio->channels(); |
| 169 | for (int i = 0; i < decoded_audio->channels(); ++i) { |
| 170 | SbMemoryCopy(decoded_audio->buffer() + per_channel_size_in_bytes * i, |
| 171 | av_frame_->extended_data[i], per_channel_size_in_bytes); |
| 172 | } |
| 173 | } |
| 174 | decoded_audios_.push(decoded_audio); |
| 175 | Schedule(output_cb_); |
| 176 | } else { |
| 177 | // TODO: Consider fill it with silence. |
| 178 | SB_LOG(ERROR) << "Decoded audio frame is empty."; |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | void AudioDecoderImpl<FFMPEG>::WriteEndOfStream() { |
| 183 | SB_DCHECK(BelongsToCurrentThread()); |
| 184 | SB_DCHECK(output_cb_); |
| 185 | |
| 186 | // AAC has no dependent frames so we needn't flush the decoder. Set the flag |
| 187 | // to ensure that Decode() is not called when the stream is ended. |
| 188 | stream_ended_ = true; |
| 189 | // Put EOS into the queue. |
| 190 | decoded_audios_.push(new DecodedAudio); |
| 191 | |
| 192 | Schedule(output_cb_); |
| 193 | } |
| 194 | |
| 195 | scoped_refptr<AudioDecoderImpl<FFMPEG>::DecodedAudio> |
Andrew Top | 63c7ad4 | 2019-11-25 16:10:13 -0800 | [diff] [blame] | 196 | AudioDecoderImpl<FFMPEG>::Read(int* samples_per_second) { |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 197 | SB_DCHECK(BelongsToCurrentThread()); |
| 198 | SB_DCHECK(output_cb_); |
| 199 | SB_DCHECK(!decoded_audios_.empty()); |
| 200 | |
| 201 | scoped_refptr<DecodedAudio> result; |
| 202 | if (!decoded_audios_.empty()) { |
| 203 | result = decoded_audios_.front(); |
| 204 | decoded_audios_.pop(); |
| 205 | } |
Andrew Top | 63c7ad4 | 2019-11-25 16:10:13 -0800 | [diff] [blame] | 206 | *samples_per_second = audio_sample_info_.samples_per_second; |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 207 | return result; |
| 208 | } |
| 209 | |
| 210 | void AudioDecoderImpl<FFMPEG>::Reset() { |
| 211 | SB_DCHECK(BelongsToCurrentThread()); |
| 212 | |
| 213 | stream_ended_ = false; |
| 214 | while (!decoded_audios_.empty()) { |
| 215 | decoded_audios_.pop(); |
| 216 | } |
| 217 | |
| 218 | CancelPendingJobs(); |
| 219 | } |
| 220 | |
| 221 | bool AudioDecoderImpl<FFMPEG>::is_valid() const { |
| 222 | return (ffmpeg_ != NULL) && ffmpeg_->is_valid() && (codec_context_ != NULL); |
| 223 | } |
| 224 | |
| 225 | SbMediaAudioSampleType AudioDecoderImpl<FFMPEG>::GetSampleType() const { |
| 226 | SB_DCHECK(BelongsToCurrentThread()); |
| 227 | |
| 228 | if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 || |
| 229 | codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) { |
| 230 | return kSbMediaAudioSampleTypeInt16Deprecated; |
| 231 | } else if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT || |
| 232 | codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) { |
| 233 | return kSbMediaAudioSampleTypeFloat32; |
| 234 | } |
| 235 | |
| 236 | SB_NOTREACHED(); |
| 237 | |
| 238 | return kSbMediaAudioSampleTypeFloat32; |
| 239 | } |
| 240 | |
| 241 | SbMediaAudioFrameStorageType AudioDecoderImpl<FFMPEG>::GetStorageType() const { |
| 242 | SB_DCHECK(BelongsToCurrentThread()); |
| 243 | |
| 244 | if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 || |
| 245 | codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) { |
| 246 | return kSbMediaAudioFrameStorageTypeInterleaved; |
| 247 | } |
| 248 | if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P || |
| 249 | codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) { |
| 250 | return kSbMediaAudioFrameStorageTypePlanar; |
| 251 | } |
| 252 | |
| 253 | SB_NOTREACHED(); |
| 254 | return kSbMediaAudioFrameStorageTypeInterleaved; |
| 255 | } |
| 256 | |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 257 | void AudioDecoderImpl<FFMPEG>::InitializeCodec() { |
| 258 | codec_context_ = ffmpeg_->avcodec_alloc_context3(NULL); |
| 259 | |
| 260 | if (codec_context_ == NULL) { |
| 261 | SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context"; |
| 262 | return; |
| 263 | } |
| 264 | |
| 265 | codec_context_->codec_type = AVMEDIA_TYPE_AUDIO; |
| 266 | codec_context_->codec_id = GetFfmpegCodecIdByMediaCodec(audio_codec_); |
| 267 | // Request_sample_fmt is set by us, but sample_fmt is set by the decoder. |
| 268 | if (GetSupportedSampleType() == kSbMediaAudioSampleTypeInt16Deprecated) { |
| 269 | codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16; |
| 270 | } else { |
| 271 | codec_context_->request_sample_fmt = AV_SAMPLE_FMT_FLT; |
| 272 | } |
| 273 | |
Chad Duffin | ac9ac06 | 2019-07-23 10:06:45 -0700 | [diff] [blame] | 274 | codec_context_->channels = audio_sample_info_.number_of_channels; |
| 275 | codec_context_->sample_rate = audio_sample_info_.samples_per_second; |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 276 | codec_context_->extradata = NULL; |
| 277 | codec_context_->extradata_size = 0; |
| 278 | |
| 279 | if (codec_context_->codec_id == AV_CODEC_ID_OPUS && |
Chad Duffin | ac9ac06 | 2019-07-23 10:06:45 -0700 | [diff] [blame] | 280 | audio_sample_info_.audio_specific_config_size > 0) { |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 281 | // AV_INPUT_BUFFER_PADDING_SIZE is not defined in ancient avcodec.h. Use a |
| 282 | // large enough padding here explicitly. |
| 283 | const int kAvInputBufferPaddingSize = 256; |
Chad Duffin | ac9ac06 | 2019-07-23 10:06:45 -0700 | [diff] [blame] | 284 | codec_context_->extradata_size = |
| 285 | audio_sample_info_.audio_specific_config_size; |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 286 | codec_context_->extradata = static_cast<uint8_t*>(ffmpeg_->av_malloc( |
| 287 | codec_context_->extradata_size + kAvInputBufferPaddingSize)); |
| 288 | SB_DCHECK(codec_context_->extradata); |
Chad Duffin | ac9ac06 | 2019-07-23 10:06:45 -0700 | [diff] [blame] | 289 | SbMemoryCopy(codec_context_->extradata, |
| 290 | audio_sample_info_.audio_specific_config, |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 291 | codec_context_->extradata_size); |
| 292 | SbMemorySet(codec_context_->extradata + codec_context_->extradata_size, 0, |
| 293 | kAvInputBufferPaddingSize); |
| 294 | } |
| 295 | |
| 296 | AVCodec* codec = ffmpeg_->avcodec_find_decoder(codec_context_->codec_id); |
| 297 | |
| 298 | if (codec == NULL) { |
| 299 | SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context"; |
| 300 | TeardownCodec(); |
| 301 | return; |
| 302 | } |
| 303 | |
| 304 | int rv = ffmpeg_->OpenCodec(codec_context_, codec); |
| 305 | if (rv < 0) { |
| 306 | SB_LOG(ERROR) << "Unable to open codec"; |
| 307 | TeardownCodec(); |
| 308 | return; |
| 309 | } |
| 310 | |
Kaido Kert | 612c020 | 2020-01-22 10:28:42 -0800 | [diff] [blame] | 311 | #if LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8 |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 312 | av_frame_ = ffmpeg_->av_frame_alloc(); |
Kaido Kert | 612c020 | 2020-01-22 10:28:42 -0800 | [diff] [blame] | 313 | #else // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8 |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 314 | av_frame_ = ffmpeg_->avcodec_alloc_frame(); |
Kaido Kert | 612c020 | 2020-01-22 10:28:42 -0800 | [diff] [blame] | 315 | #endif // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8 |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 316 | if (av_frame_ == NULL) { |
| 317 | SB_LOG(ERROR) << "Unable to allocate audio frame"; |
| 318 | TeardownCodec(); |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | void AudioDecoderImpl<FFMPEG>::TeardownCodec() { |
| 323 | if (codec_context_) { |
| 324 | ffmpeg_->CloseCodec(codec_context_); |
Kaido Kert | 612c020 | 2020-01-22 10:28:42 -0800 | [diff] [blame] | 325 | ffmpeg_->FreeContext(&codec_context_); |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 326 | } |
Kaido Kert | 612c020 | 2020-01-22 10:28:42 -0800 | [diff] [blame] | 327 | ffmpeg_->FreeFrame(&av_frame_); |
Mike Fleming | 3933d92 | 2018-04-02 10:53:08 -0700 | [diff] [blame] | 328 | } |
| 329 | |
| 330 | } // namespace ffmpeg |
| 331 | } // namespace shared |
| 332 | } // namespace starboard |