Chromium Code Reviews| 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 |