| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/filters/ffmpeg_audio_decoder.h" | 5 #include "media/filters/ffmpeg_audio_decoder.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "media/base/audio_decoder_config.h" | 8 #include "media/base/audio_decoder_config.h" |
| 9 #include "media/base/data_buffer.h" | 9 #include "media/base/data_buffer.h" |
| 10 #include "media/base/decoder_buffer.h" | 10 #include "media/base/decoder_buffer.h" |
| 11 #include "media/base/demuxer.h" | 11 #include "media/base/demuxer.h" |
| 12 #include "media/base/pipeline.h" | 12 #include "media/base/pipeline.h" |
| 13 #include "media/ffmpeg/ffmpeg_common.h" | 13 #include "media/ffmpeg/ffmpeg_common.h" |
| 14 | 14 |
| 15 namespace media { | 15 namespace media { |
| 16 | 16 |
| 17 // Returns true if the decode result was an error. | |
| 18 static bool IsErrorResult(int result, int decoded_size) { | |
| 19 return result < 0 || | |
| 20 decoded_size < 0 || | |
| 21 decoded_size > AVCODEC_MAX_AUDIO_FRAME_SIZE; | |
| 22 } | |
| 23 | |
| 24 // Returns true if the decode result produced audio samples. | |
| 25 static bool ProducedAudioSamples(int decoded_size) { | |
| 26 return decoded_size > 0; | |
| 27 } | |
| 28 | |
| 29 // Returns true if the decode result was a timestamp packet and not actual audio | 17 // Returns true if the decode result was a timestamp packet and not actual audio |
| 30 // data. | 18 // data. |
| 31 static bool IsTimestampMarkerPacket(int result, Buffer* input) { | 19 static inline bool IsTimestampMarkerPacket(int result, Buffer* input) { |
| 32 // We can get a positive result but no decoded data. This is ok because this | 20 // We can get a positive result but no decoded data. This is ok because this |
| 33 // this can be a marker packet that only contains timestamp. | 21 // this can be a marker packet that only contains timestamp. |
| 34 return result > 0 && !input->IsEndOfStream() && | 22 return result > 0 && !input->IsEndOfStream() && |
| 35 input->GetTimestamp() != kNoTimestamp() && | 23 input->GetTimestamp() != kNoTimestamp() && |
| 36 input->GetDuration() != kNoTimestamp(); | 24 input->GetDuration() != kNoTimestamp(); |
| 37 } | 25 } |
| 38 | 26 |
| 39 // Returns true if the decode result was end of stream. | 27 // Returns true if the decode result was end of stream. |
| 40 static bool IsEndOfStream(int result, int decoded_size, Buffer* input) { | 28 static inline bool IsEndOfStream(int result, int decoded_size, Buffer* input) { |
| 41 // Three conditions to meet to declare end of stream for this decoder: | 29 // Three conditions to meet to declare end of stream for this decoder: |
| 42 // 1. FFmpeg didn't read anything. | 30 // 1. FFmpeg didn't read anything. |
| 43 // 2. FFmpeg didn't output anything. | 31 // 2. FFmpeg didn't output anything. |
| 44 // 3. An end of stream buffer is received. | 32 // 3. An end of stream buffer is received. |
| 45 return result == 0 && decoded_size == 0 && input->IsEndOfStream(); | 33 return result == 0 && decoded_size == 0 && input->IsEndOfStream(); |
| 46 } | 34 } |
| 47 | 35 |
| 48 | 36 |
| 49 FFmpegAudioDecoder::FFmpegAudioDecoder( | 37 FFmpegAudioDecoder::FFmpegAudioDecoder( |
| 50 const base::Callback<MessageLoop*()>& message_loop_cb) | 38 const base::Callback<MessageLoop*()>& message_loop_cb) |
| 51 : message_loop_factory_cb_(message_loop_cb), | 39 : message_loop_factory_cb_(message_loop_cb), |
| 52 message_loop_(NULL), | 40 message_loop_(NULL), |
| 53 codec_context_(NULL), | 41 codec_context_(NULL), |
| 54 bits_per_channel_(0), | 42 bits_per_channel_(0), |
| 55 channel_layout_(CHANNEL_LAYOUT_NONE), | 43 channel_layout_(CHANNEL_LAYOUT_NONE), |
| 56 samples_per_second_(0), | 44 samples_per_second_(0), |
| 57 decoded_audio_size_(AVCODEC_MAX_AUDIO_FRAME_SIZE), | 45 av_frame_(NULL) { |
| 58 decoded_audio_(static_cast<uint8*>(av_malloc(decoded_audio_size_))) { | |
| 59 } | 46 } |
| 60 | 47 |
| 61 void FFmpegAudioDecoder::Initialize( | 48 void FFmpegAudioDecoder::Initialize( |
| 62 const scoped_refptr<DemuxerStream>& stream, | 49 const scoped_refptr<DemuxerStream>& stream, |
| 63 const PipelineStatusCB& status_cb, | 50 const PipelineStatusCB& status_cb, |
| 64 const StatisticsCB& statistics_cb) { | 51 const StatisticsCB& statistics_cb) { |
| 65 if (!message_loop_) { | 52 if (!message_loop_) { |
| 66 message_loop_ = message_loop_factory_cb_.Run(); | 53 message_loop_ = message_loop_factory_cb_.Run(); |
| 67 message_loop_factory_cb_.Reset(); | 54 message_loop_factory_cb_.Reset(); |
| 68 } else { | 55 } else { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 94 int FFmpegAudioDecoder::samples_per_second() { | 81 int FFmpegAudioDecoder::samples_per_second() { |
| 95 return samples_per_second_; | 82 return samples_per_second_; |
| 96 } | 83 } |
| 97 | 84 |
| 98 void FFmpegAudioDecoder::Reset(const base::Closure& closure) { | 85 void FFmpegAudioDecoder::Reset(const base::Closure& closure) { |
| 99 message_loop_->PostTask(FROM_HERE, base::Bind( | 86 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 100 &FFmpegAudioDecoder::DoReset, this, closure)); | 87 &FFmpegAudioDecoder::DoReset, this, closure)); |
| 101 } | 88 } |
| 102 | 89 |
| 103 FFmpegAudioDecoder::~FFmpegAudioDecoder() { | 90 FFmpegAudioDecoder::~FFmpegAudioDecoder() { |
| 104 av_free(decoded_audio_); | |
| 105 | |
| 106 // TODO(scherkus): should we require Stop() to be called? this might end up | 91 // TODO(scherkus): should we require Stop() to be called? this might end up |
| 107 // getting called on a random thread due to refcounting. | 92 // getting called on a random thread due to refcounting. |
| 108 if (codec_context_) { | 93 if (codec_context_) { |
| 109 av_free(codec_context_->extradata); | 94 av_free(codec_context_->extradata); |
| 110 avcodec_close(codec_context_); | 95 avcodec_close(codec_context_); |
| 111 av_free(codec_context_); | 96 av_free(codec_context_); |
| 112 } | 97 } |
| 98 |
| 99 if (av_frame_) { |
| 100 av_free(av_frame_); |
| 101 av_frame_ = NULL; |
| 102 } |
| 113 } | 103 } |
| 114 | 104 |
| 115 void FFmpegAudioDecoder::DoInitialize( | 105 void FFmpegAudioDecoder::DoInitialize( |
| 116 const scoped_refptr<DemuxerStream>& stream, | 106 const scoped_refptr<DemuxerStream>& stream, |
| 117 const PipelineStatusCB& status_cb, | 107 const PipelineStatusCB& status_cb, |
| 118 const StatisticsCB& statistics_cb) { | 108 const StatisticsCB& statistics_cb) { |
| 119 demuxer_stream_ = stream; | 109 demuxer_stream_ = stream; |
| 120 const AudioDecoderConfig& config = stream->audio_decoder_config(); | 110 const AudioDecoderConfig& config = stream->audio_decoder_config(); |
| 121 statistics_cb_ = statistics_cb; | 111 statistics_cb_ = statistics_cb; |
| 122 | 112 |
| 123 // TODO(scherkus): this check should go in Pipeline prior to creating | 113 // TODO(scherkus): this check should go in Pipeline prior to creating |
| 124 // decoder objects. | 114 // decoder objects. |
| 125 if (!config.IsValidConfig()) { | 115 if (!config.IsValidConfig()) { |
| 126 DLOG(ERROR) << "Invalid audio stream -" | 116 DLOG(ERROR) << "Invalid audio stream -" |
| 127 << " codec: " << config.codec() | 117 << " codec: " << config.codec() |
| 128 << " channel layout: " << config.channel_layout() | 118 << " channel layout: " << config.channel_layout() |
| 129 << " bits per channel: " << config.bits_per_channel() | 119 << " bits per channel: " << config.bits_per_channel() |
| 130 << " samples per second: " << config.samples_per_second(); | 120 << " samples per second: " << config.samples_per_second(); |
| 131 | 121 |
| 132 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | 122 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
| 133 return; | 123 return; |
| 134 } | 124 } |
| 135 | 125 |
| 136 // Initialize AVCodecContext structure. | 126 // Initialize AVCodecContext structure. |
| 137 codec_context_ = avcodec_alloc_context(); | 127 codec_context_ = avcodec_alloc_context3(NULL); |
| 138 AudioDecoderConfigToAVCodecContext(config, codec_context_); | 128 AudioDecoderConfigToAVCodecContext(config, codec_context_); |
| 139 | 129 |
| 140 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); | 130 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); |
| 141 if (!codec || avcodec_open2(codec_context_, codec, NULL) < 0) { | 131 if (!codec || avcodec_open2(codec_context_, codec, NULL) < 0) { |
| 142 DLOG(ERROR) << "Could not initialize audio decoder: " | 132 DLOG(ERROR) << "Could not initialize audio decoder: " |
| 143 << codec_context_->codec_id; | 133 << codec_context_->codec_id; |
| 144 | 134 |
| 145 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | 135 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
| 146 return; | 136 return; |
| 147 } | 137 } |
| 148 | 138 |
| 149 // Success! | 139 // Success! |
| 140 av_frame_ = avcodec_alloc_frame(); |
| 150 bits_per_channel_ = config.bits_per_channel(); | 141 bits_per_channel_ = config.bits_per_channel(); |
| 151 channel_layout_ = config.channel_layout(); | 142 channel_layout_ = config.channel_layout(); |
| 152 samples_per_second_ = config.samples_per_second(); | 143 samples_per_second_ = config.samples_per_second(); |
| 153 | 144 |
| 154 status_cb.Run(PIPELINE_OK); | 145 status_cb.Run(PIPELINE_OK); |
| 155 } | 146 } |
| 156 | 147 |
| 157 void FFmpegAudioDecoder::DoReset(const base::Closure& closure) { | 148 void FFmpegAudioDecoder::DoReset(const base::Closure& closure) { |
| 158 avcodec_flush_buffers(codec_context_); | 149 avcodec_flush_buffers(codec_context_); |
| 159 estimated_next_timestamp_ = kNoTimestamp(); | 150 estimated_next_timestamp_ = kNoTimestamp(); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 191 } | 182 } |
| 192 | 183 |
| 193 AVPacket packet; | 184 AVPacket packet; |
| 194 av_init_packet(&packet); | 185 av_init_packet(&packet); |
| 195 packet.data = const_cast<uint8*>(input->GetData()); | 186 packet.data = const_cast<uint8*>(input->GetData()); |
| 196 packet.size = input->GetDataSize(); | 187 packet.size = input->GetDataSize(); |
| 197 | 188 |
| 198 PipelineStatistics statistics; | 189 PipelineStatistics statistics; |
| 199 statistics.audio_bytes_decoded = input->GetDataSize(); | 190 statistics.audio_bytes_decoded = input->GetDataSize(); |
| 200 | 191 |
| 201 int decoded_audio_size = decoded_audio_size_; | 192 // Reset frame to default values. |
| 202 int result = avcodec_decode_audio3( | 193 avcodec_get_frame_defaults(av_frame_); |
| 203 codec_context_, reinterpret_cast<int16_t*>(decoded_audio_), | |
| 204 &decoded_audio_size, &packet); | |
| 205 | 194 |
| 206 if (IsErrorResult(result, decoded_audio_size)) { | 195 int frame_decoded = 0; |
| 196 int result = avcodec_decode_audio4( |
| 197 codec_context_, av_frame_, &frame_decoded, &packet); |
| 198 |
| 199 if (result < 0) { |
| 207 DCHECK(!input->IsEndOfStream()) | 200 DCHECK(!input->IsEndOfStream()) |
| 208 << "End of stream buffer produced an error! " | 201 << "End of stream buffer produced an error! " |
| 209 << "This is quite possibly a bug in the audio decoder not handling " | 202 << "This is quite possibly a bug in the audio decoder not handling " |
| 210 << "end of stream AVPackets correctly."; | 203 << "end of stream AVPackets correctly."; |
| 211 | 204 |
| 212 DLOG(ERROR) << "Error decoding an audio frame with timestamp: " | 205 DLOG(ERROR) << "Error decoding an audio frame with timestamp: " |
| 213 << input->GetTimestamp().InMicroseconds() << " us, duration: " | 206 << input->GetTimestamp().InMicroseconds() << " us, duration: " |
| 214 << input->GetDuration().InMicroseconds() << " us, packet size: " | 207 << input->GetDuration().InMicroseconds() << " us, packet size: " |
| 215 << input->GetDataSize() << " bytes"; | 208 << input->GetDataSize() << " bytes"; |
| 216 | 209 |
| 217 ReadFromDemuxerStream(); | 210 ReadFromDemuxerStream(); |
| 218 return; | 211 return; |
| 219 } | 212 } |
| 220 | 213 |
| 214 int decoded_audio_size = 0; |
| 215 if (frame_decoded) { |
| 216 decoded_audio_size = av_samples_get_buffer_size( |
| 217 NULL, codec_context_->channels, av_frame_->nb_samples, |
| 218 codec_context_->sample_fmt, 1); |
| 219 } |
| 220 |
| 221 scoped_refptr<DataBuffer> output; | 221 scoped_refptr<DataBuffer> output; |
| 222 | 222 |
| 223 if (ProducedAudioSamples(decoded_audio_size)) { | 223 if (decoded_audio_size > 0) { |
| 224 // Copy the audio samples into an output buffer. | 224 // Copy the audio samples into an output buffer. |
| 225 output = new DataBuffer(decoded_audio_size); | 225 output = new DataBuffer(decoded_audio_size); |
| 226 output->SetDataSize(decoded_audio_size); | 226 output->SetDataSize(decoded_audio_size); |
| 227 uint8* data = output->GetWritableData(); | 227 uint8* data = output->GetWritableData(); |
| 228 memcpy(data, decoded_audio_, decoded_audio_size); | 228 memcpy(data, av_frame_->data[0], decoded_audio_size); |
| 229 | 229 |
| 230 UpdateDurationAndTimestamp(input, output); | 230 UpdateDurationAndTimestamp(input, output); |
| 231 } else if (IsTimestampMarkerPacket(result, input)) { | 231 } else if (IsTimestampMarkerPacket(result, input)) { |
| 232 // Nothing else to do here but update our estimation. | 232 // Nothing else to do here but update our estimation. |
| 233 estimated_next_timestamp_ = input->GetTimestamp() + input->GetDuration(); | 233 estimated_next_timestamp_ = input->GetTimestamp() + input->GetDuration(); |
| 234 } else if (IsEndOfStream(result, decoded_audio_size, input)) { | 234 } else if (IsEndOfStream(result, decoded_audio_size, input)) { |
| 235 // Create an end of stream output buffer. | 235 // Create an end of stream output buffer. |
| 236 output = new DataBuffer(0); | 236 output = new DataBuffer(0); |
| 237 output->SetTimestamp(input->GetTimestamp()); | 237 output->SetTimestamp(input->GetTimestamp()); |
| 238 output->SetDuration(input->GetDuration()); | 238 output->SetDuration(input->GetDuration()); |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 } | 292 } |
| 293 | 293 |
| 294 void FFmpegAudioDecoder::DeliverSamples(const scoped_refptr<Buffer>& samples) { | 294 void FFmpegAudioDecoder::DeliverSamples(const scoped_refptr<Buffer>& samples) { |
| 295 // Reset the callback before running to protect against reentrancy. | 295 // Reset the callback before running to protect against reentrancy. |
| 296 ReadCB read_cb = read_cb_; | 296 ReadCB read_cb = read_cb_; |
| 297 read_cb_.Reset(); | 297 read_cb_.Reset(); |
| 298 read_cb.Run(samples); | 298 read_cb.Run(samples); |
| 299 } | 299 } |
| 300 | 300 |
| 301 } // namespace media | 301 } // namespace media |
| OLD | NEW |