Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(412)

Side by Side Diff: media/filters/ffmpeg_audio_decoder.cc

Issue 10540067: Switch to using avcodec_decode_audio4, avcodec_alloc_context3. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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(
scherkus (not reviewing) 2012/06/08 00:51:22 the old code looked for decoded_audio_size < 0 --
DaleCurtis 2012/06/08 01:00:01 Technically that's still happening, it's just that
217 NULL, codec_context_->channels, av_frame_->nb_samples,
218 codec_context_->sample_fmt, 1);
scherkus (not reviewing) 2012/06/08 00:51:22 OOC using the default value "0" doesn't work here?
DaleCurtis 2012/06/08 01:00:01 Per the docs: "align: buffer size alignment (0 = d
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698