OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/cdm/ppapi/ffmpeg_cdm_audio_decoder.h" | 5 #include "media/cdm/ppapi/ffmpeg_cdm_audio_decoder.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "media/base/audio_bus.h" | 10 #include "media/base/audio_bus.h" |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
73 memcpy(codec_context->extradata, config.extra_data, | 73 memcpy(codec_context->extradata, config.extra_data, |
74 config.extra_data_size); | 74 config.extra_data_size); |
75 memset(codec_context->extradata + config.extra_data_size, '\0', | 75 memset(codec_context->extradata + config.extra_data_size, '\0', |
76 FF_INPUT_BUFFER_PADDING_SIZE); | 76 FF_INPUT_BUFFER_PADDING_SIZE); |
77 } else { | 77 } else { |
78 codec_context->extradata = NULL; | 78 codec_context->extradata = NULL; |
79 codec_context->extradata_size = 0; | 79 codec_context->extradata_size = 0; |
80 } | 80 } |
81 } | 81 } |
82 | 82 |
83 cdm::AudioFormat AVSampleFormatToCdmAudioFormat( | |
84 AVSampleFormat sample_format) { | |
85 switch (sample_format) { | |
86 case AV_SAMPLE_FMT_U8: | |
87 return cdm::kAudioFormatU8; | |
88 case AV_SAMPLE_FMT_S16: | |
89 return cdm::kAudioFormatS16; | |
90 case AV_SAMPLE_FMT_S32: | |
91 return cdm::kAudioFormatS32; | |
92 case AV_SAMPLE_FMT_FLT: | |
93 return cdm::kAudioFormatF32; | |
94 case AV_SAMPLE_FMT_S16P: | |
95 return cdm::kAudioFormatPlanarS16; | |
96 case AV_SAMPLE_FMT_FLTP: | |
97 return cdm::kAudioFormatPlanarF32; | |
98 default: | |
99 DVLOG(1) << "Unknown AVSampleFormat: " << sample_format; | |
100 } | |
101 return cdm::kUnknownAudioFormat; | |
102 } | |
103 | |
83 FFmpegCdmAudioDecoder::FFmpegCdmAudioDecoder(cdm::Host* host) | 104 FFmpegCdmAudioDecoder::FFmpegCdmAudioDecoder(cdm::Host* host) |
84 : is_initialized_(false), | 105 : is_initialized_(false), |
85 host_(host), | 106 host_(host), |
86 bits_per_channel_(0), | |
87 samples_per_second_(0), | 107 samples_per_second_(0), |
88 channels_(0), | 108 channels_(0), |
89 av_sample_format_(0), | 109 av_sample_format_(0), |
90 bytes_per_frame_(0), | 110 bytes_per_frame_(0), |
91 last_input_timestamp_(kNoTimestamp()), | 111 last_input_timestamp_(kNoTimestamp()), |
92 output_bytes_to_drop_(0) { | 112 output_bytes_to_drop_(0) { |
93 } | 113 } |
94 | 114 |
95 FFmpegCdmAudioDecoder::~FFmpegCdmAudioDecoder() { | 115 FFmpegCdmAudioDecoder::~FFmpegCdmAudioDecoder() { |
96 ReleaseFFmpegResources(); | 116 ReleaseFFmpegResources(); |
97 } | 117 } |
98 | 118 |
99 bool FFmpegCdmAudioDecoder::Initialize(const cdm::AudioDecoderConfig& config) { | 119 bool FFmpegCdmAudioDecoder::Initialize(const cdm::AudioDecoderConfig& config) { |
100 DVLOG(1) << "Initialize()"; | 120 DVLOG(1) << "Initialize()"; |
101 | |
102 if (!IsValidConfig(config)) { | 121 if (!IsValidConfig(config)) { |
103 LOG(ERROR) << "Initialize(): invalid audio decoder configuration."; | 122 LOG(ERROR) << "Initialize(): invalid audio decoder configuration."; |
104 return false; | 123 return false; |
105 } | 124 } |
106 | 125 |
107 if (is_initialized_) { | 126 if (is_initialized_) { |
108 LOG(ERROR) << "Initialize(): Already initialized."; | 127 LOG(ERROR) << "Initialize(): Already initialized."; |
109 return false; | 128 return false; |
110 } | 129 } |
111 | 130 |
(...skipping 12 matching lines...) Expand all Loading... | |
124 return false; | 143 return false; |
125 } | 144 } |
126 | 145 |
127 // Ensure avcodec_open2() respected our format request. | 146 // Ensure avcodec_open2() respected our format request. |
128 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) { | 147 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) { |
129 DLOG(ERROR) << "Unable to configure a supported sample format: " | 148 DLOG(ERROR) << "Unable to configure a supported sample format: " |
130 << codec_context_->sample_fmt; | 149 << codec_context_->sample_fmt; |
131 return false; | 150 return false; |
132 } | 151 } |
133 | 152 |
134 // Some codecs will only output float data, so we need to convert to integer | |
135 // before returning the decoded buffer. | |
136 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP || | |
137 codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) { | |
138 // Preallocate the AudioBus for float conversions. We can treat interleaved | |
139 // float data as a single planar channel since our output is expected in an | |
140 // interleaved format anyways. | |
141 int channels = codec_context_->channels; | |
142 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) | |
143 channels = 1; | |
144 converter_bus_ = AudioBus::CreateWrapper(channels); | |
145 } | |
146 | |
147 // Success! | 153 // Success! |
148 av_frame_.reset(avcodec_alloc_frame()); | 154 av_frame_.reset(avcodec_alloc_frame()); |
149 bits_per_channel_ = config.bits_per_channel; | |
150 samples_per_second_ = config.samples_per_second; | 155 samples_per_second_ = config.samples_per_second; |
151 bytes_per_frame_ = codec_context_->channels * bits_per_channel_ / 8; | 156 bytes_per_frame_ = codec_context_->channels * config.bits_per_channel / 8; |
152 output_timestamp_helper_.reset( | 157 output_timestamp_helper_.reset( |
153 new AudioTimestampHelper(config.samples_per_second)); | 158 new AudioTimestampHelper(config.samples_per_second)); |
154 serialized_audio_frames_.reserve(bytes_per_frame_ * samples_per_second_); | 159 serialized_audio_frames_.reserve(bytes_per_frame_ * samples_per_second_); |
DaleCurtis
2013/10/14 19:04:45
Removed since this allocates 172kb and most output
| |
155 is_initialized_ = true; | 160 is_initialized_ = true; |
156 | 161 |
157 // Store initial values to guard against midstream configuration changes. | 162 // Store initial values to guard against midstream configuration changes. |
158 channels_ = codec_context_->channels; | 163 channels_ = codec_context_->channels; |
159 av_sample_format_ = codec_context_->sample_fmt; | 164 av_sample_format_ = codec_context_->sample_fmt; |
160 | 165 |
161 return true; | 166 return true; |
162 } | 167 } |
163 | 168 |
164 void FFmpegCdmAudioDecoder::Deinitialize() { | 169 void FFmpegCdmAudioDecoder::Deinitialize() { |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
219 | 224 |
220 last_input_timestamp_ = timestamp; | 225 last_input_timestamp_ = timestamp; |
221 } | 226 } |
222 } | 227 } |
223 | 228 |
224 AVPacket packet; | 229 AVPacket packet; |
225 av_init_packet(&packet); | 230 av_init_packet(&packet); |
226 packet.data = const_cast<uint8_t*>(compressed_buffer); | 231 packet.data = const_cast<uint8_t*>(compressed_buffer); |
227 packet.size = compressed_buffer_size; | 232 packet.size = compressed_buffer_size; |
228 | 233 |
234 // Tell the CDM what AudioFormat we're using. | |
235 const cdm::AudioFormat cdm_format = AVSampleFormatToCdmAudioFormat( | |
236 static_cast<AVSampleFormat>(av_sample_format_)); | |
237 DCHECK_NE(cdm_format, cdm::kUnknownAudioFormat); | |
238 decoded_frames->SetFormat(cdm_format); | |
239 | |
229 // Each audio packet may contain several frames, so we must call the decoder | 240 // Each audio packet may contain several frames, so we must call the decoder |
230 // until we've exhausted the packet. Regardless of the packet size we always | 241 // until we've exhausted the packet. Regardless of the packet size we always |
231 // want to hand it to the decoder at least once, otherwise we would end up | 242 // want to hand it to the decoder at least once, otherwise we would end up |
232 // skipping end of stream packets since they have a size of zero. | 243 // skipping end of stream packets since they have a size of zero. |
233 do { | 244 do { |
234 // Reset frame to default values. | 245 // Reset frame to default values. |
235 avcodec_get_frame_defaults(av_frame_.get()); | 246 avcodec_get_frame_defaults(av_frame_.get()); |
236 | 247 |
237 int frame_decoded = 0; | 248 int frame_decoded = 0; |
238 int result = avcodec_decode_audio4( | 249 int result = avcodec_decode_audio4( |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
282 << ", Channels: " << av_frame_->channels << " vs " | 293 << ", Channels: " << av_frame_->channels << " vs " |
283 << channels_ | 294 << channels_ |
284 << ", Sample Format: " << av_frame_->format << " vs " | 295 << ", Sample Format: " << av_frame_->format << " vs " |
285 << av_sample_format_; | 296 << av_sample_format_; |
286 return cdm::kDecodeError; | 297 return cdm::kDecodeError; |
287 } | 298 } |
288 | 299 |
289 decoded_audio_size = av_samples_get_buffer_size( | 300 decoded_audio_size = av_samples_get_buffer_size( |
290 NULL, codec_context_->channels, av_frame_->nb_samples, | 301 NULL, codec_context_->channels, av_frame_->nb_samples, |
291 codec_context_->sample_fmt, 1); | 302 codec_context_->sample_fmt, 1); |
292 // If we're decoding into float, adjust audio size. | |
293 if (converter_bus_ && bits_per_channel_ / 8 != sizeof(float)) { | |
294 DCHECK(codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT || | |
295 codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP); | |
296 decoded_audio_size *= | |
297 static_cast<float>(bits_per_channel_ / 8) / sizeof(float); | |
298 } | |
299 } | 303 } |
300 | 304 |
301 int start_sample = 0; | 305 int start_sample = 0; |
302 if (decoded_audio_size > 0 && output_bytes_to_drop_ > 0) { | 306 if (decoded_audio_size > 0 && output_bytes_to_drop_ > 0) { |
303 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0) | 307 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0) |
304 << "Decoder didn't output full frames"; | 308 << "Decoder didn't output full frames"; |
305 | 309 |
306 int dropped_size = std::min(decoded_audio_size, output_bytes_to_drop_); | 310 int dropped_size = std::min(decoded_audio_size, output_bytes_to_drop_); |
307 start_sample = dropped_size / bytes_per_frame_; | 311 start_sample = dropped_size / bytes_per_frame_; |
308 decoded_audio_size -= dropped_size; | 312 decoded_audio_size -= dropped_size; |
309 output_bytes_to_drop_ -= dropped_size; | 313 output_bytes_to_drop_ -= dropped_size; |
310 } | 314 } |
311 | 315 |
312 scoped_refptr<DataBuffer> output; | |
313 if (decoded_audio_size > 0) { | 316 if (decoded_audio_size > 0) { |
314 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0) | 317 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0) |
315 << "Decoder didn't output full frames"; | 318 << "Decoder didn't output full frames"; |
316 | 319 |
317 // Convert float data using an AudioBus. | |
318 if (converter_bus_) { | |
319 // Setup the AudioBus as a wrapper of the AVFrame data and then use | |
320 // AudioBus::ToInterleaved() to convert the data as necessary. | |
321 int skip_frames = start_sample; | |
322 int total_frames = av_frame_->nb_samples; | |
323 int frames_to_interleave = decoded_audio_size / bytes_per_frame_; | |
324 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) { | |
325 DCHECK_EQ(converter_bus_->channels(), 1); | |
326 total_frames *= codec_context_->channels; | |
327 skip_frames *= codec_context_->channels; | |
328 frames_to_interleave *= codec_context_->channels; | |
329 } | |
330 | |
331 converter_bus_->set_frames(total_frames); | |
332 for (int i = 0; i < converter_bus_->channels(); ++i) { | |
333 converter_bus_->SetChannelData(i, reinterpret_cast<float*>( | |
334 av_frame_->extended_data[i])); | |
335 } | |
336 | |
337 output = new DataBuffer(decoded_audio_size); | |
338 output->set_data_size(decoded_audio_size); | |
339 | |
340 DCHECK_EQ(frames_to_interleave, converter_bus_->frames() - skip_frames); | |
341 converter_bus_->ToInterleavedPartial( | |
342 skip_frames, frames_to_interleave, bits_per_channel_ / 8, | |
343 output->writable_data()); | |
344 } else { | |
345 output = DataBuffer::CopyFrom( | |
346 av_frame_->extended_data[0] + start_sample * bytes_per_frame_, | |
347 decoded_audio_size); | |
348 } | |
349 | |
350 base::TimeDelta output_timestamp = | 320 base::TimeDelta output_timestamp = |
351 output_timestamp_helper_->GetTimestamp(); | 321 output_timestamp_helper_->GetTimestamp(); |
352 output_timestamp_helper_->AddFrames(decoded_audio_size / | 322 output_timestamp_helper_->AddFrames(decoded_audio_size / |
353 bytes_per_frame_); | 323 bytes_per_frame_); |
354 | 324 |
355 // Serialize the audio samples into |serialized_audio_frames_|. | 325 // If we've exhausted the packet in the first decode we can write directly |
356 SerializeInt64(output_timestamp.InMicroseconds()); | 326 // into the frame buffer instead of a multistep serialization approach. |
xhwang
2013/10/11 22:45:16
Hmm, how much improvement does this approach buy u
DaleCurtis
2013/10/12 01:51:05
Hard to tell since we don't have pipeline tests fo
DaleCurtis
2013/10/14 19:04:45
Actually this ended up more complicated since we n
| |
357 SerializeInt64(output->data_size()); | 327 uint8_t* output_buffer = NULL; |
358 serialized_audio_frames_.insert( | 328 if (serialized_audio_frames_.empty() && !packet.size) { |
359 serialized_audio_frames_.end(), | 329 const uint32_t buffer_size = decoded_audio_size + sizeof(int64) * 2; |
360 output->data(), | 330 decoded_frames->SetFrameBuffer(host_->Allocate(buffer_size)); |
361 output->data() + output->data_size()); | 331 if (!decoded_frames->FrameBuffer()) { |
332 LOG(ERROR) << "DecodeBuffer() cdm::Host::Allocate failed."; | |
333 return cdm::kDecodeError; | |
334 } | |
335 decoded_frames->FrameBuffer()->SetSize(buffer_size); | |
336 output_buffer = decoded_frames->FrameBuffer()->Data(); | |
337 | |
338 const int64 timestamp = output_timestamp.InMicroseconds(); | |
339 memcpy(output_buffer, ×tamp, sizeof(timestamp)); | |
340 output_buffer += sizeof(timestamp); | |
341 | |
342 const int64 output_size = decoded_audio_size; | |
343 memcpy(output_buffer, &output_size, sizeof(output_size)); | |
344 output_buffer += sizeof(output_size); | |
345 } else { | |
346 // Serialize the audio samples into |serialized_audio_frames_|. | |
347 SerializeInt64(output_timestamp.InMicroseconds()); | |
348 SerializeInt64(decoded_audio_size); | |
349 | |
350 const size_t previous_size = serialized_audio_frames_.size(); | |
351 serialized_audio_frames_.resize(previous_size + decoded_audio_size); | |
352 output_buffer = &serialized_audio_frames_[0] + previous_size; | |
353 } | |
354 | |
355 switch (cdm_format) { | |
356 case cdm::kAudioFormatU8: | |
357 case cdm::kAudioFormatS16: | |
358 case cdm::kAudioFormatS32: | |
359 case cdm::kAudioFormatF32: | |
360 memcpy(output_buffer, av_frame_->data[0], decoded_audio_size); | |
361 break; | |
362 case cdm::kAudioFormatPlanarS16: | |
363 case cdm::kAudioFormatPlanarF32: { | |
364 const int decoded_size_per_channel = | |
365 decoded_audio_size / av_frame_->channels; | |
xhwang
2013/10/11 22:45:16
shall we CHECK(decoded_audio_size % av_frame_->cha
DaleCurtis
2013/10/12 01:51:05
1. That wouldn't overflow since we'd always be rou
| |
366 for (int i = 0; i < av_frame_->channels; ++i) { | |
367 memcpy(output_buffer, | |
368 av_frame_->extended_data[i], | |
369 decoded_size_per_channel); | |
370 output_buffer += decoded_size_per_channel; | |
371 } | |
372 break; | |
373 } | |
374 default: | |
375 NOTREACHED() << "Unsupported CDM Audio Format!"; | |
376 memset(output_buffer, 0, decoded_audio_size); | |
377 } | |
362 } | 378 } |
363 } while (packet.size > 0); | 379 } while (packet.size > 0); |
364 | 380 |
381 // If the decode loop already wrote out the data, we're done. | |
382 if (decoded_frames->FrameBuffer()) | |
383 return cdm::kSuccess; | |
384 | |
365 if (!serialized_audio_frames_.empty()) { | 385 if (!serialized_audio_frames_.empty()) { |
366 decoded_frames->SetFrameBuffer( | 386 decoded_frames->SetFrameBuffer( |
367 host_->Allocate(serialized_audio_frames_.size())); | 387 host_->Allocate(serialized_audio_frames_.size())); |
368 if (!decoded_frames->FrameBuffer()) { | 388 if (!decoded_frames->FrameBuffer()) { |
369 LOG(ERROR) << "DecodeBuffer() cdm::Host::Allocate failed."; | 389 LOG(ERROR) << "DecodeBuffer() cdm::Host::Allocate failed."; |
370 return cdm::kDecodeError; | 390 return cdm::kDecodeError; |
371 } | 391 } |
372 memcpy(decoded_frames->FrameBuffer()->Data(), | 392 memcpy(decoded_frames->FrameBuffer()->Data(), |
373 &serialized_audio_frames_[0], | 393 &serialized_audio_frames_[0], |
374 serialized_audio_frames_.size()); | 394 serialized_audio_frames_.size()); |
(...skipping 19 matching lines...) Expand all Loading... | |
394 av_frame_.reset(); | 414 av_frame_.reset(); |
395 } | 415 } |
396 | 416 |
397 void FFmpegCdmAudioDecoder::SerializeInt64(int64 value) { | 417 void FFmpegCdmAudioDecoder::SerializeInt64(int64 value) { |
398 int previous_size = serialized_audio_frames_.size(); | 418 int previous_size = serialized_audio_frames_.size(); |
399 serialized_audio_frames_.resize(previous_size + sizeof(value)); | 419 serialized_audio_frames_.resize(previous_size + sizeof(value)); |
400 memcpy(&serialized_audio_frames_[0] + previous_size, &value, sizeof(value)); | 420 memcpy(&serialized_audio_frames_[0] + previous_size, &value, sizeof(value)); |
401 } | 421 } |
402 | 422 |
403 } // namespace media | 423 } // namespace media |
OLD | NEW |