| 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 "webkit/renderer/media/crypto/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" |
| 11 #include "media/base/audio_timestamp_helper.h" | 11 #include "media/base/audio_timestamp_helper.h" |
| 12 #include "media/base/buffers.h" | 12 #include "media/base/buffers.h" |
| 13 #include "media/base/data_buffer.h" | 13 #include "media/base/data_buffer.h" |
| 14 #include "media/base/limits.h" | 14 #include "media/base/limits.h" |
| 15 #include "webkit/renderer/media/crypto/ppapi/cdm/content_decryption_module.h" | |
| 16 | 15 |
| 17 // Include FFmpeg header files. | 16 // Include FFmpeg header files. |
| 18 extern "C" { | 17 extern "C" { |
| 19 // Temporarily disable possible loss of data warning. | 18 // Temporarily disable possible loss of data warning. |
| 20 MSVC_PUSH_DISABLE_WARNING(4244); | 19 MSVC_PUSH_DISABLE_WARNING(4244); |
| 21 #include <libavcodec/avcodec.h> | 20 #include <libavcodec/avcodec.h> |
| 22 MSVC_POP_WARNING(); | 21 MSVC_POP_WARNING(); |
| 23 } // extern "C" | 22 } // extern "C" |
| 24 | 23 |
| 25 namespace webkit_media { | 24 namespace media { |
| 26 | 25 |
| 27 // Maximum number of channels with defined layout in src/media. | 26 // Maximum number of channels with defined layout in src/media. |
| 28 static const int kMaxChannels = 8; | 27 static const int kMaxChannels = 8; |
| 29 | 28 |
| 30 static AVCodecID CdmAudioCodecToCodecID( | 29 static AVCodecID CdmAudioCodecToCodecID( |
| 31 cdm::AudioDecoderConfig::AudioCodec audio_codec) { | 30 cdm::AudioDecoderConfig::AudioCodec audio_codec) { |
| 32 switch (audio_codec) { | 31 switch (audio_codec) { |
| 33 case cdm::AudioDecoderConfig::kCodecVorbis: | 32 case cdm::AudioDecoderConfig::kCodecVorbis: |
| 34 return AV_CODEC_ID_VORBIS; | 33 return AV_CODEC_ID_VORBIS; |
| 35 case cdm::AudioDecoderConfig::kCodecAac: | 34 case cdm::AudioDecoderConfig::kCodecAac: |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 83 FFmpegCdmAudioDecoder::FFmpegCdmAudioDecoder(cdm::Host* host) | 82 FFmpegCdmAudioDecoder::FFmpegCdmAudioDecoder(cdm::Host* host) |
| 84 : is_initialized_(false), | 83 : is_initialized_(false), |
| 85 host_(host), | 84 host_(host), |
| 86 codec_context_(NULL), | 85 codec_context_(NULL), |
| 87 av_frame_(NULL), | 86 av_frame_(NULL), |
| 88 bits_per_channel_(0), | 87 bits_per_channel_(0), |
| 89 samples_per_second_(0), | 88 samples_per_second_(0), |
| 90 channels_(0), | 89 channels_(0), |
| 91 av_sample_format_(0), | 90 av_sample_format_(0), |
| 92 bytes_per_frame_(0), | 91 bytes_per_frame_(0), |
| 93 last_input_timestamp_(media::kNoTimestamp()), | 92 last_input_timestamp_(kNoTimestamp()), |
| 94 output_bytes_to_drop_(0) { | 93 output_bytes_to_drop_(0) { |
| 95 } | 94 } |
| 96 | 95 |
| 97 FFmpegCdmAudioDecoder::~FFmpegCdmAudioDecoder() { | 96 FFmpegCdmAudioDecoder::~FFmpegCdmAudioDecoder() { |
| 98 ReleaseFFmpegResources(); | 97 ReleaseFFmpegResources(); |
| 99 } | 98 } |
| 100 | 99 |
| 101 bool FFmpegCdmAudioDecoder::Initialize(const cdm::AudioDecoderConfig& config) { | 100 bool FFmpegCdmAudioDecoder::Initialize(const cdm::AudioDecoderConfig& config) { |
| 102 DVLOG(1) << "Initialize()"; | 101 DVLOG(1) << "Initialize()"; |
| 103 | 102 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 136 // Some codecs will only output float data, so we need to convert to integer | 135 // Some codecs will only output float data, so we need to convert to integer |
| 137 // before returning the decoded buffer. | 136 // before returning the decoded buffer. |
| 138 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP || | 137 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP || |
| 139 codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) { | 138 codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) { |
| 140 // Preallocate the AudioBus for float conversions. We can treat interleaved | 139 // Preallocate the AudioBus for float conversions. We can treat interleaved |
| 141 // float data as a single planar channel since our output is expected in an | 140 // float data as a single planar channel since our output is expected in an |
| 142 // interleaved format anyways. | 141 // interleaved format anyways. |
| 143 int channels = codec_context_->channels; | 142 int channels = codec_context_->channels; |
| 144 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) | 143 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) |
| 145 channels = 1; | 144 channels = 1; |
| 146 converter_bus_ = media::AudioBus::CreateWrapper(channels); | 145 converter_bus_ = AudioBus::CreateWrapper(channels); |
| 147 } | 146 } |
| 148 | 147 |
| 149 // Success! | 148 // Success! |
| 150 av_frame_ = avcodec_alloc_frame(); | 149 av_frame_ = avcodec_alloc_frame(); |
| 151 bits_per_channel_ = config.bits_per_channel; | 150 bits_per_channel_ = config.bits_per_channel; |
| 152 samples_per_second_ = config.samples_per_second; | 151 samples_per_second_ = config.samples_per_second; |
| 153 bytes_per_frame_ = codec_context_->channels * bits_per_channel_ / 8; | 152 bytes_per_frame_ = codec_context_->channels * bits_per_channel_ / 8; |
| 154 output_timestamp_helper_.reset( | 153 output_timestamp_helper_.reset( |
| 155 new media::AudioTimestampHelper(config.samples_per_second)); | 154 new AudioTimestampHelper(config.samples_per_second)); |
| 156 serialized_audio_frames_.reserve(bytes_per_frame_ * samples_per_second_); | 155 serialized_audio_frames_.reserve(bytes_per_frame_ * samples_per_second_); |
| 157 is_initialized_ = true; | 156 is_initialized_ = true; |
| 158 | 157 |
| 159 // Store initial values to guard against midstream configuration changes. | 158 // Store initial values to guard against midstream configuration changes. |
| 160 channels_ = codec_context_->channels; | 159 channels_ = codec_context_->channels; |
| 161 av_sample_format_ = codec_context_->sample_fmt; | 160 av_sample_format_ = codec_context_->sample_fmt; |
| 162 | 161 |
| 163 return true; | 162 return true; |
| 164 } | 163 } |
| 165 | 164 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 176 ResetTimestampState(); | 175 ResetTimestampState(); |
| 177 } | 176 } |
| 178 | 177 |
| 179 // static | 178 // static |
| 180 bool FFmpegCdmAudioDecoder::IsValidConfig( | 179 bool FFmpegCdmAudioDecoder::IsValidConfig( |
| 181 const cdm::AudioDecoderConfig& config) { | 180 const cdm::AudioDecoderConfig& config) { |
| 182 return config.codec != cdm::AudioDecoderConfig::kUnknownAudioCodec && | 181 return config.codec != cdm::AudioDecoderConfig::kUnknownAudioCodec && |
| 183 config.channel_count > 0 && | 182 config.channel_count > 0 && |
| 184 config.channel_count <= kMaxChannels && | 183 config.channel_count <= kMaxChannels && |
| 185 config.bits_per_channel > 0 && | 184 config.bits_per_channel > 0 && |
| 186 config.bits_per_channel <= media::limits::kMaxBitsPerSample && | 185 config.bits_per_channel <= limits::kMaxBitsPerSample && |
| 187 config.samples_per_second > 0 && | 186 config.samples_per_second > 0 && |
| 188 config.samples_per_second <= media::limits::kMaxSampleRate; | 187 config.samples_per_second <= limits::kMaxSampleRate; |
| 189 } | 188 } |
| 190 | 189 |
| 191 cdm::Status FFmpegCdmAudioDecoder::DecodeBuffer( | 190 cdm::Status FFmpegCdmAudioDecoder::DecodeBuffer( |
| 192 const uint8_t* compressed_buffer, | 191 const uint8_t* compressed_buffer, |
| 193 int32_t compressed_buffer_size, | 192 int32_t compressed_buffer_size, |
| 194 int64_t input_timestamp, | 193 int64_t input_timestamp, |
| 195 cdm::AudioFrames* decoded_frames) { | 194 cdm::AudioFrames* decoded_frames) { |
| 196 DVLOG(1) << "DecodeBuffer()"; | 195 DVLOG(1) << "DecodeBuffer()"; |
| 197 const bool is_end_of_stream = !compressed_buffer; | 196 const bool is_end_of_stream = !compressed_buffer; |
| 198 base::TimeDelta timestamp = | 197 base::TimeDelta timestamp = |
| 199 base::TimeDelta::FromMicroseconds(input_timestamp); | 198 base::TimeDelta::FromMicroseconds(input_timestamp); |
| 200 | 199 |
| 201 bool is_vorbis = codec_context_->codec_id == AV_CODEC_ID_VORBIS; | 200 bool is_vorbis = codec_context_->codec_id == AV_CODEC_ID_VORBIS; |
| 202 if (!is_end_of_stream) { | 201 if (!is_end_of_stream) { |
| 203 if (last_input_timestamp_ == media::kNoTimestamp()) { | 202 if (last_input_timestamp_ == kNoTimestamp()) { |
| 204 if (is_vorbis && timestamp < base::TimeDelta()) { | 203 if (is_vorbis && timestamp < base::TimeDelta()) { |
| 205 // Dropping frames for negative timestamps as outlined in section A.2 | 204 // Dropping frames for negative timestamps as outlined in section A.2 |
| 206 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html | 205 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html |
| 207 int frames_to_drop = floor( | 206 int frames_to_drop = floor( |
| 208 0.5 + -timestamp.InSecondsF() * samples_per_second_); | 207 0.5 + -timestamp.InSecondsF() * samples_per_second_); |
| 209 output_bytes_to_drop_ = bytes_per_frame_ * frames_to_drop; | 208 output_bytes_to_drop_ = bytes_per_frame_ * frames_to_drop; |
| 210 } else { | 209 } else { |
| 211 last_input_timestamp_ = timestamp; | 210 last_input_timestamp_ = timestamp; |
| 212 } | 211 } |
| 213 } else if (timestamp != media::kNoTimestamp()) { | 212 } else if (timestamp != kNoTimestamp()) { |
| 214 if (timestamp < last_input_timestamp_) { | 213 if (timestamp < last_input_timestamp_) { |
| 215 base::TimeDelta diff = timestamp - last_input_timestamp_; | 214 base::TimeDelta diff = timestamp - last_input_timestamp_; |
| 216 DVLOG(1) << "Input timestamps are not monotonically increasing! " | 215 DVLOG(1) << "Input timestamps are not monotonically increasing! " |
| 217 << " ts " << timestamp.InMicroseconds() << " us" | 216 << " ts " << timestamp.InMicroseconds() << " us" |
| 218 << " diff " << diff.InMicroseconds() << " us"; | 217 << " diff " << diff.InMicroseconds() << " us"; |
| 219 return cdm::kDecodeError; | 218 return cdm::kDecodeError; |
| 220 } | 219 } |
| 221 | 220 |
| 222 last_input_timestamp_ = timestamp; | 221 last_input_timestamp_ = timestamp; |
| 223 } | 222 } |
| (...skipping 29 matching lines...) Expand all Loading... |
| 253 << compressed_buffer_size << " bytes"; | 252 << compressed_buffer_size << " bytes"; |
| 254 | 253 |
| 255 return cdm::kDecodeError; | 254 return cdm::kDecodeError; |
| 256 } | 255 } |
| 257 | 256 |
| 258 // Update packet size and data pointer in case we need to call the decoder | 257 // Update packet size and data pointer in case we need to call the decoder |
| 259 // with the remaining bytes from this packet. | 258 // with the remaining bytes from this packet. |
| 260 packet.size -= result; | 259 packet.size -= result; |
| 261 packet.data += result; | 260 packet.data += result; |
| 262 | 261 |
| 263 if (output_timestamp_helper_->base_timestamp() == media::kNoTimestamp() && | 262 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() && |
| 264 !is_end_of_stream) { | 263 !is_end_of_stream) { |
| 265 DCHECK(timestamp != media::kNoTimestamp()); | 264 DCHECK(timestamp != kNoTimestamp()); |
| 266 if (output_bytes_to_drop_ > 0) { | 265 if (output_bytes_to_drop_ > 0) { |
| 267 // Currently Vorbis is the only codec that causes us to drop samples. | 266 // Currently Vorbis is the only codec that causes us to drop samples. |
| 268 // If we have to drop samples it always means the timeline starts at 0. | 267 // If we have to drop samples it always means the timeline starts at 0. |
| 269 DCHECK_EQ(codec_context_->codec_id, AV_CODEC_ID_VORBIS); | 268 DCHECK_EQ(codec_context_->codec_id, AV_CODEC_ID_VORBIS); |
| 270 output_timestamp_helper_->SetBaseTimestamp(base::TimeDelta()); | 269 output_timestamp_helper_->SetBaseTimestamp(base::TimeDelta()); |
| 271 } else { | 270 } else { |
| 272 output_timestamp_helper_->SetBaseTimestamp(timestamp); | 271 output_timestamp_helper_->SetBaseTimestamp(timestamp); |
| 273 } | 272 } |
| 274 } | 273 } |
| 275 | 274 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 304 if (decoded_audio_size > 0 && output_bytes_to_drop_ > 0) { | 303 if (decoded_audio_size > 0 && output_bytes_to_drop_ > 0) { |
| 305 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0) | 304 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0) |
| 306 << "Decoder didn't output full frames"; | 305 << "Decoder didn't output full frames"; |
| 307 | 306 |
| 308 int dropped_size = std::min(decoded_audio_size, output_bytes_to_drop_); | 307 int dropped_size = std::min(decoded_audio_size, output_bytes_to_drop_); |
| 309 start_sample = dropped_size / bytes_per_frame_; | 308 start_sample = dropped_size / bytes_per_frame_; |
| 310 decoded_audio_size -= dropped_size; | 309 decoded_audio_size -= dropped_size; |
| 311 output_bytes_to_drop_ -= dropped_size; | 310 output_bytes_to_drop_ -= dropped_size; |
| 312 } | 311 } |
| 313 | 312 |
| 314 scoped_refptr<media::DataBuffer> output; | 313 scoped_refptr<DataBuffer> output; |
| 315 if (decoded_audio_size > 0) { | 314 if (decoded_audio_size > 0) { |
| 316 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0) | 315 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0) |
| 317 << "Decoder didn't output full frames"; | 316 << "Decoder didn't output full frames"; |
| 318 | 317 |
| 319 // Convert float data using an AudioBus. | 318 // Convert float data using an AudioBus. |
| 320 if (converter_bus_) { | 319 if (converter_bus_) { |
| 321 // Setup the AudioBus as a wrapper of the AVFrame data and then use | 320 // Setup the AudioBus as a wrapper of the AVFrame data and then use |
| 322 // AudioBus::ToInterleaved() to convert the data as necessary. | 321 // AudioBus::ToInterleaved() to convert the data as necessary. |
| 323 int skip_frames = start_sample; | 322 int skip_frames = start_sample; |
| 324 int total_frames = av_frame_->nb_samples; | 323 int total_frames = av_frame_->nb_samples; |
| 325 int frames_to_interleave = decoded_audio_size / bytes_per_frame_; | 324 int frames_to_interleave = decoded_audio_size / bytes_per_frame_; |
| 326 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) { | 325 if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) { |
| 327 DCHECK_EQ(converter_bus_->channels(), 1); | 326 DCHECK_EQ(converter_bus_->channels(), 1); |
| 328 total_frames *= codec_context_->channels; | 327 total_frames *= codec_context_->channels; |
| 329 skip_frames *= codec_context_->channels; | 328 skip_frames *= codec_context_->channels; |
| 330 frames_to_interleave *= codec_context_->channels; | 329 frames_to_interleave *= codec_context_->channels; |
| 331 } | 330 } |
| 332 | 331 |
| 333 converter_bus_->set_frames(total_frames); | 332 converter_bus_->set_frames(total_frames); |
| 334 for (int i = 0; i < converter_bus_->channels(); ++i) { | 333 for (int i = 0; i < converter_bus_->channels(); ++i) { |
| 335 converter_bus_->SetChannelData(i, reinterpret_cast<float*>( | 334 converter_bus_->SetChannelData(i, reinterpret_cast<float*>( |
| 336 av_frame_->extended_data[i])); | 335 av_frame_->extended_data[i])); |
| 337 } | 336 } |
| 338 | 337 |
| 339 output = new media::DataBuffer(decoded_audio_size); | 338 output = new DataBuffer(decoded_audio_size); |
| 340 output->set_data_size(decoded_audio_size); | 339 output->set_data_size(decoded_audio_size); |
| 341 | 340 |
| 342 DCHECK_EQ(frames_to_interleave, converter_bus_->frames() - skip_frames); | 341 DCHECK_EQ(frames_to_interleave, converter_bus_->frames() - skip_frames); |
| 343 converter_bus_->ToInterleavedPartial( | 342 converter_bus_->ToInterleavedPartial( |
| 344 skip_frames, frames_to_interleave, bits_per_channel_ / 8, | 343 skip_frames, frames_to_interleave, bits_per_channel_ / 8, |
| 345 output->writable_data()); | 344 output->writable_data()); |
| 346 } else { | 345 } else { |
| 347 output = media::DataBuffer::CopyFrom( | 346 output = DataBuffer::CopyFrom( |
| 348 av_frame_->extended_data[0] + start_sample * bytes_per_frame_, | 347 av_frame_->extended_data[0] + start_sample * bytes_per_frame_, |
| 349 decoded_audio_size); | 348 decoded_audio_size); |
| 350 } | 349 } |
| 351 | 350 |
| 352 base::TimeDelta output_timestamp = | 351 base::TimeDelta output_timestamp = |
| 353 output_timestamp_helper_->GetTimestamp(); | 352 output_timestamp_helper_->GetTimestamp(); |
| 354 output_timestamp_helper_->AddFrames(decoded_audio_size / | 353 output_timestamp_helper_->AddFrames(decoded_audio_size / |
| 355 bytes_per_frame_); | 354 bytes_per_frame_); |
| 356 | 355 |
| 357 // Serialize the audio samples into |serialized_audio_frames_|. | 356 // Serialize the audio samples into |serialized_audio_frames_|. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 377 decoded_frames->FrameBuffer()->SetSize(serialized_audio_frames_.size()); | 376 decoded_frames->FrameBuffer()->SetSize(serialized_audio_frames_.size()); |
| 378 serialized_audio_frames_.clear(); | 377 serialized_audio_frames_.clear(); |
| 379 | 378 |
| 380 return cdm::kSuccess; | 379 return cdm::kSuccess; |
| 381 } | 380 } |
| 382 | 381 |
| 383 return cdm::kNeedMoreData; | 382 return cdm::kNeedMoreData; |
| 384 } | 383 } |
| 385 | 384 |
| 386 void FFmpegCdmAudioDecoder::ResetTimestampState() { | 385 void FFmpegCdmAudioDecoder::ResetTimestampState() { |
| 387 output_timestamp_helper_->SetBaseTimestamp(media::kNoTimestamp()); | 386 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); |
| 388 last_input_timestamp_ = media::kNoTimestamp(); | 387 last_input_timestamp_ = kNoTimestamp(); |
| 389 output_bytes_to_drop_ = 0; | 388 output_bytes_to_drop_ = 0; |
| 390 } | 389 } |
| 391 | 390 |
| 392 void FFmpegCdmAudioDecoder::ReleaseFFmpegResources() { | 391 void FFmpegCdmAudioDecoder::ReleaseFFmpegResources() { |
| 393 DVLOG(1) << "ReleaseFFmpegResources()"; | 392 DVLOG(1) << "ReleaseFFmpegResources()"; |
| 394 | 393 |
| 395 if (codec_context_) { | 394 if (codec_context_) { |
| 396 av_free(codec_context_->extradata); | 395 av_free(codec_context_->extradata); |
| 397 avcodec_close(codec_context_); | 396 avcodec_close(codec_context_); |
| 398 av_free(codec_context_); | 397 av_free(codec_context_); |
| 399 codec_context_ = NULL; | 398 codec_context_ = NULL; |
| 400 } | 399 } |
| 401 if (av_frame_) { | 400 if (av_frame_) { |
| 402 av_free(av_frame_); | 401 av_free(av_frame_); |
| 403 av_frame_ = NULL; | 402 av_frame_ = NULL; |
| 404 } | 403 } |
| 405 } | 404 } |
| 406 | 405 |
| 407 void FFmpegCdmAudioDecoder::SerializeInt64(int64 value) { | 406 void FFmpegCdmAudioDecoder::SerializeInt64(int64 value) { |
| 408 int previous_size = serialized_audio_frames_.size(); | 407 int previous_size = serialized_audio_frames_.size(); |
| 409 serialized_audio_frames_.resize(previous_size + sizeof(value)); | 408 serialized_audio_frames_.resize(previous_size + sizeof(value)); |
| 410 memcpy(&serialized_audio_frames_[0] + previous_size, &value, sizeof(value)); | 409 memcpy(&serialized_audio_frames_[0] + previous_size, &value, sizeof(value)); |
| 411 } | 410 } |
| 412 | 411 |
| 413 } // namespace webkit_media | 412 } // namespace media |
| OLD | NEW |