1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_audio_decoder.h"
6
7 #include <stddef.h>
8
9 #include <algorithm>
10
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/callback_helpers.h"
14 #include "base/logging.h"
15 #include "media/base/audio_bus.h"
16 #include "media/base/audio_timestamp_helper.h"
17 #include "media/base/data_buffer.h"
18 #include "media/base/limits.h"
19 #include "media/cdm/library_cdm/cdm_host_proxy.h"
20 #include "media/ffmpeg/ffmpeg_common.h"
21 #include "media/ffmpeg/ffmpeg_decoding_loop.h"
22
23 namespace media {
24
25 namespace {
26
27 // Maximum number of channels with defined layout in src/media.
28 static const int kMaxChannels = 8;
29
IsValidConfig(const cdm::AudioDecoderConfig_2 & config)30 bool IsValidConfig(const cdm::AudioDecoderConfig_2& config) {
31 // No need to check |encryption_scheme| as the buffers will always be
32 // decrypted before calling DecodeBuffer().
33 return config.codec != cdm::kUnknownAudioCodec && config.channel_count > 0 &&
34 config.channel_count <= kMaxChannels && config.bits_per_channel > 0 &&
35 config.bits_per_channel <= limits::kMaxBitsPerSample &&
36 config.samples_per_second > 0 &&
37 config.samples_per_second <= limits::kMaxSampleRate;
38 }
39
CdmAudioCodecToCodecID(cdm::AudioCodec audio_codec)40 AVCodecID CdmAudioCodecToCodecID(cdm::AudioCodec audio_codec) {
41 switch (audio_codec) {
42 case cdm::kCodecVorbis:
43 return AV_CODEC_ID_VORBIS;
44 case cdm::kCodecAac:
45 return AV_CODEC_ID_AAC;
46 case cdm::kUnknownAudioCodec:
47 default:
48 NOTREACHED() << "Unsupported cdm::AudioCodec: " << audio_codec;
49 return AV_CODEC_ID_NONE;
50 }
51 }
52
CdmAudioDecoderConfigToAVCodecContext(const cdm::AudioDecoderConfig_2 & config,AVCodecContext * codec_context)53 void CdmAudioDecoderConfigToAVCodecContext(
54 const cdm::AudioDecoderConfig_2& config,
55 AVCodecContext* codec_context) {
56 codec_context->codec_type = AVMEDIA_TYPE_AUDIO;
57 codec_context->codec_id = CdmAudioCodecToCodecID(config.codec);
58
59 switch (config.bits_per_channel) {
60 case 8:
61 codec_context->sample_fmt = AV_SAMPLE_FMT_U8;
62 break;
63 case 16:
64 codec_context->sample_fmt = AV_SAMPLE_FMT_S16;
65 break;
66 case 32:
67 codec_context->sample_fmt = AV_SAMPLE_FMT_S32;
68 break;
69 default:
70 DVLOG(1) << "CdmAudioDecoderConfigToAVCodecContext() Unsupported bits "
71 "per channel: "
72 << config.bits_per_channel;
73 codec_context->sample_fmt = AV_SAMPLE_FMT_NONE;
74 }
75
76 codec_context->channels = config.channel_count;
77 codec_context->sample_rate = config.samples_per_second;
78
79 if (config.extra_data) {
80 codec_context->extradata_size = config.extra_data_size;
81 codec_context->extradata = reinterpret_cast<uint8_t*>(
82 av_malloc(config.extra_data_size + AV_INPUT_BUFFER_PADDING_SIZE));
83 memcpy(codec_context->extradata, config.extra_data, config.extra_data_size);
84 memset(codec_context->extradata + config.extra_data_size, '\0',
85 AV_INPUT_BUFFER_PADDING_SIZE);
86 } else {
87 codec_context->extradata = NULL;
88 codec_context->extradata_size = 0;
89 }
90 }
91
AVSampleFormatToCdmAudioFormat(AVSampleFormat sample_format)92 cdm::AudioFormat AVSampleFormatToCdmAudioFormat(AVSampleFormat sample_format) {
93 switch (sample_format) {
94 case AV_SAMPLE_FMT_U8:
95 return cdm::kAudioFormatU8;
96 case AV_SAMPLE_FMT_S16:
97 return cdm::kAudioFormatS16;
98 case AV_SAMPLE_FMT_S32:
99 return cdm::kAudioFormatS32;
100 case AV_SAMPLE_FMT_FLT:
101 return cdm::kAudioFormatF32;
102 case AV_SAMPLE_FMT_S16P:
103 return cdm::kAudioFormatPlanarS16;
104 case AV_SAMPLE_FMT_FLTP:
105 return cdm::kAudioFormatPlanarF32;
106 default:
107 DVLOG(1) << "Unknown AVSampleFormat: " << sample_format;
108 }
109 return cdm::kUnknownAudioFormat;
110 }
111
CopySamples(cdm::AudioFormat cdm_format,int decoded_audio_size,const AVFrame & av_frame,uint8_t * output_buffer)112 void CopySamples(cdm::AudioFormat cdm_format,
113 int decoded_audio_size,
114 const AVFrame& av_frame,
115 uint8_t* output_buffer) {
116 switch (cdm_format) {
117 case cdm::kAudioFormatU8:
118 case cdm::kAudioFormatS16:
119 case cdm::kAudioFormatS32:
120 case cdm::kAudioFormatF32:
121 memcpy(output_buffer, av_frame.data[0], decoded_audio_size);
122 break;
123 case cdm::kAudioFormatPlanarS16:
124 case cdm::kAudioFormatPlanarF32: {
125 const int decoded_size_per_channel =
126 decoded_audio_size / av_frame.channels;
127 for (int i = 0; i < av_frame.channels; ++i) {
128 memcpy(output_buffer, av_frame.extended_data[i],
129 decoded_size_per_channel);
130 output_buffer += decoded_size_per_channel;
131 }
132 break;
133 }
134 default:
135 NOTREACHED() << "Unsupported CDM Audio Format!";
136 memset(output_buffer, 0, decoded_audio_size);
137 }
138 }
139
140 } // namespace
141
FFmpegCdmAudioDecoder(CdmHostProxy * cdm_host_proxy)142 FFmpegCdmAudioDecoder::FFmpegCdmAudioDecoder(CdmHostProxy* cdm_host_proxy)
143 : cdm_host_proxy_(cdm_host_proxy) {}
144
~FFmpegCdmAudioDecoder()145 FFmpegCdmAudioDecoder::~FFmpegCdmAudioDecoder() {
146 ReleaseFFmpegResources();
147 }
148
Initialize(const cdm::AudioDecoderConfig_2 & config)149 bool FFmpegCdmAudioDecoder::Initialize(
150 const cdm::AudioDecoderConfig_2& config) {
151 DVLOG(1) << "Initialize()";
152 if (!IsValidConfig(config)) {
153 LOG(ERROR) << "Initialize(): invalid audio decoder configuration.";
154 return false;
155 }
156
157 if (is_initialized_) {
158 LOG(ERROR) << "Initialize(): Already initialized.";
159 return false;
160 }
161
162 // Initialize AVCodecContext structure.
163 codec_context_.reset(avcodec_alloc_context3(NULL));
164 CdmAudioDecoderConfigToAVCodecContext(config, codec_context_.get());
165
166 // MP3 decodes to S16P which we don't support, tell it to use S16 instead.
167 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P)
168 codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16;
169
170 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
171 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) {
172 DLOG(ERROR) << "Could not initialize audio decoder: "
173 << codec_context_->codec_id;
174 return false;
175 }
176
177 // Ensure avcodec_open2() respected our format request.
178 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) {
179 DLOG(ERROR) << "Unable to configure a supported sample format: "
180 << codec_context_->sample_fmt;
181 return false;
182 }
183
184 // Success!
185 decoding_loop_.reset(new FFmpegDecodingLoop(codec_context_.get()));
186 samples_per_second_ = config.samples_per_second;
187 bytes_per_frame_ = codec_context_->channels * config.bits_per_channel / 8;
188 output_timestamp_helper_.reset(
189 new AudioTimestampHelper(config.samples_per_second));
190 is_initialized_ = true;
191
192 // Store initial values to guard against midstream configuration changes.
193 channels_ = codec_context_->channels;
194 av_sample_format_ = codec_context_->sample_fmt;
195
196 return true;
197 }
198
Deinitialize()199 void FFmpegCdmAudioDecoder::Deinitialize() {
200 DVLOG(1) << "Deinitialize()";
201 ReleaseFFmpegResources();
202 is_initialized_ = false;
203 ResetTimestampState();
204 }
205
Reset()206 void FFmpegCdmAudioDecoder::Reset() {
207 DVLOG(1) << "Reset()";
208 avcodec_flush_buffers(codec_context_.get());
209 ResetTimestampState();
210 }
211
DecodeBuffer(const uint8_t * compressed_buffer,int32_t compressed_buffer_size,int64_t input_timestamp,cdm::AudioFrames * decoded_frames)212 cdm::Status FFmpegCdmAudioDecoder::DecodeBuffer(
213 const uint8_t* compressed_buffer,
214 int32_t compressed_buffer_size,
215 int64_t input_timestamp,
216 cdm::AudioFrames* decoded_frames) {
217 DVLOG(1) << "DecodeBuffer()";
218 const bool is_end_of_stream = !compressed_buffer;
219 base::TimeDelta timestamp =
220 base::TimeDelta::FromMicroseconds(input_timestamp);
221
222 if (!is_end_of_stream && timestamp != kNoTimestamp) {
223 if (last_input_timestamp_ != kNoTimestamp &&
224 timestamp < last_input_timestamp_) {
225 base::TimeDelta diff = timestamp - last_input_timestamp_;
226 DVLOG(1) << "Input timestamps are not monotonically increasing! "
227 << " ts " << timestamp.InMicroseconds() << " us"
228 << " diff " << diff.InMicroseconds() << " us";
229 return cdm::kDecodeError;
230 }
231 last_input_timestamp_ = timestamp;
232 }
233
234 size_t total_size = 0u;
235 std::vector<std::unique_ptr<AVFrame, ScopedPtrAVFreeFrame>> audio_frames;
236
237 AVPacket packet;
238 av_init_packet(&packet);
239 packet.data = const_cast<uint8_t*>(compressed_buffer);
240 packet.size = compressed_buffer_size;
241
242 switch (decoding_loop_->DecodePacket(
243 &packet, base::BindRepeating(&FFmpegCdmAudioDecoder::OnNewFrame,
244 base::Unretained(this), &total_size,
245 &audio_frames))) {
246 case FFmpegDecodingLoop::DecodeStatus::kSendPacketFailed:
247 return cdm::kDecodeError;
248 case FFmpegDecodingLoop::DecodeStatus::kFrameProcessingFailed:
249 NOTREACHED();
250 FALLTHROUGH;
251 case FFmpegDecodingLoop::DecodeStatus::kDecodeFrameFailed:
252 DLOG(WARNING) << " failed to decode an audio buffer: "
253 << timestamp.InMicroseconds();
254 break;
255 case FFmpegDecodingLoop::DecodeStatus::kOkay:
256 break;
257 }
258
259 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp &&
260 !is_end_of_stream) {
261 DCHECK(timestamp != kNoTimestamp);
262 output_timestamp_helper_->SetBaseTimestamp(timestamp);
263 }
264
265 if (audio_frames.empty())
266 return cdm::kNeedMoreData;
267
268 const size_t allocation_size = total_size + 2 * sizeof(int64_t);
269 decoded_frames->SetFrameBuffer(cdm_host_proxy_->Allocate(allocation_size));
270 if (!decoded_frames->FrameBuffer()) {
271 LOG(ERROR) << "DecodeBuffer() ClearKeyCdmHost::Allocate failed.";
272 return cdm::kDecodeError;
273 }
274 decoded_frames->FrameBuffer()->SetSize(allocation_size);
275
276 // Tell the CDM what AudioFormat we're using.
277 const cdm::AudioFormat cdm_format = AVSampleFormatToCdmAudioFormat(
278 static_cast<AVSampleFormat>(av_sample_format_));
279 DCHECK_NE(cdm_format, cdm::kUnknownAudioFormat);
280 decoded_frames->SetFormat(cdm_format);
281
282 uint8_t* output_buffer = decoded_frames->FrameBuffer()->Data();
283 SerializeInt64(output_timestamp_helper_->GetTimestamp().InMicroseconds(),
284 output_buffer);
285 output_buffer += sizeof(int64_t);
286 SerializeInt64(total_size, output_buffer);
287 output_buffer += sizeof(int64_t);
288 output_timestamp_helper_->AddFrames(total_size / bytes_per_frame_);
289
290 for (auto& frame : audio_frames) {
291 int decoded_audio_size = 0;
292 if (frame->sample_rate != samples_per_second_ ||
293 frame->channels != channels_ || frame->format != av_sample_format_) {
294 DLOG(ERROR) << "Unsupported midstream configuration change!"
295 << " Sample Rate: " << frame->sample_rate << " vs "
296 << samples_per_second_ << ", Channels: " << frame->channels
297 << " vs " << channels_ << ", Sample Format: " << frame->format
298 << " vs " << av_sample_format_;
299 return cdm::kDecodeError;
300 }
301
302 decoded_audio_size = av_samples_get_buffer_size(
303 nullptr, codec_context_->channels, frame->nb_samples,
304 codec_context_->sample_fmt, 1);
305 if (!decoded_audio_size)
306 continue;
307
308 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0)
309 << "Decoder didn't output full frames";
310
311 CopySamples(cdm_format, decoded_audio_size, *frame, output_buffer);
312 output_buffer += decoded_audio_size;
313 }
314
315 return cdm::kSuccess;
316 }
317
OnNewFrame(size_t * total_size,std::vector<std::unique_ptr<AVFrame,ScopedPtrAVFreeFrame>> * audio_frames,AVFrame * frame)318 bool FFmpegCdmAudioDecoder::OnNewFrame(
319 size_t* total_size,
320 std::vector<std::unique_ptr<AVFrame, ScopedPtrAVFreeFrame>>* audio_frames,
321 AVFrame* frame) {
322 *total_size += av_samples_get_buffer_size(nullptr, codec_context_->channels,
323 frame->nb_samples,
324 codec_context_->sample_fmt, 1);
325 audio_frames->emplace_back(av_frame_clone(frame));
326 return true;
327 }
328
ResetTimestampState()329 void FFmpegCdmAudioDecoder::ResetTimestampState() {
330 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp);
331 last_input_timestamp_ = kNoTimestamp;
332 }
333
ReleaseFFmpegResources()334 void FFmpegCdmAudioDecoder::ReleaseFFmpegResources() {
335 DVLOG(1) << "ReleaseFFmpegResources()";
336
337 decoding_loop_.reset();
338 codec_context_.reset();
339 }
340
SerializeInt64(int64_t value,uint8_t * dest)341 void FFmpegCdmAudioDecoder::SerializeInt64(int64_t value, uint8_t* dest) {
342 memcpy(dest, &value, sizeof(value));
343 }
344
345 } // namespace media
346