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

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

Issue 11411332: Replace trampolining in FFmpegDemuxer with explicit thread calling convention checks. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 8 years 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
« no previous file with comments | « media/filters/ffmpeg_demuxer.h ('k') | media/filters/ffmpeg_demuxer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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_demuxer.h" 5 #include "media/filters/ffmpeg_demuxer.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <string> 8 #include <string>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/callback.h" 11 #include "base/callback.h"
12 #include "base/callback_helpers.h"
12 #include "base/command_line.h" 13 #include "base/command_line.h"
13 #include "base/memory/scoped_ptr.h" 14 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop.h" 15 #include "base/message_loop.h"
15 #include "base/stl_util.h" 16 #include "base/stl_util.h"
16 #include "base/string_util.h" 17 #include "base/string_util.h"
17 #include "base/task_runner_util.h" 18 #include "base/task_runner_util.h"
18 #include "base/time.h" 19 #include "base/time.h"
19 #include "media/base/audio_decoder_config.h" 20 #include "media/base/audio_decoder_config.h"
20 #include "media/base/bind_to_loop.h" 21 #include "media/base/bind_to_loop.h"
21 #include "media/base/decoder_buffer.h" 22 #include "media/base/decoder_buffer.h"
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
93 stream_->time_base, packet->duration)); 94 stream_->time_base, packet->duration));
94 if (buffer->GetTimestamp() != kNoTimestamp() && 95 if (buffer->GetTimestamp() != kNoTimestamp() &&
95 last_packet_timestamp_ != kNoTimestamp() && 96 last_packet_timestamp_ != kNoTimestamp() &&
96 last_packet_timestamp_ < buffer->GetTimestamp()) { 97 last_packet_timestamp_ < buffer->GetTimestamp()) {
97 buffered_ranges_.Add(last_packet_timestamp_, buffer->GetTimestamp()); 98 buffered_ranges_.Add(last_packet_timestamp_, buffer->GetTimestamp());
98 demuxer_->NotifyBufferingChanged(); 99 demuxer_->NotifyBufferingChanged();
99 } 100 }
100 last_packet_timestamp_ = buffer->GetTimestamp(); 101 last_packet_timestamp_ = buffer->GetTimestamp();
101 102
102 buffer_queue_.Push(buffer); 103 buffer_queue_.Push(buffer);
103 SatisfyPendingReads(); 104 SatisfyPendingRead();
104 } 105 }
105 106
106 void FFmpegDemuxerStream::SetEndOfStream() { 107 void FFmpegDemuxerStream::SetEndOfStream() {
107 DCHECK(message_loop_->BelongsToCurrentThread()); 108 DCHECK(message_loop_->BelongsToCurrentThread());
108 end_of_stream_ = true; 109 end_of_stream_ = true;
109 SatisfyPendingReads(); 110 SatisfyPendingRead();
110 } 111 }
111 112
112 void FFmpegDemuxerStream::FlushBuffers() { 113 void FFmpegDemuxerStream::FlushBuffers() {
113 DCHECK(message_loop_->BelongsToCurrentThread()); 114 DCHECK(message_loop_->BelongsToCurrentThread());
114 DCHECK(read_queue_.empty()) << "Read requests should be empty"; 115 DCHECK(read_cb_.is_null()) << "There should be no pending read";
115 buffer_queue_.Clear(); 116 buffer_queue_.Clear();
116 end_of_stream_ = false; 117 end_of_stream_ = false;
117 last_packet_timestamp_ = kNoTimestamp(); 118 last_packet_timestamp_ = kNoTimestamp();
118 } 119 }
119 120
120 void FFmpegDemuxerStream::Stop() { 121 void FFmpegDemuxerStream::Stop() {
121 DCHECK(message_loop_->BelongsToCurrentThread()); 122 DCHECK(message_loop_->BelongsToCurrentThread());
122 buffer_queue_.Clear(); 123 buffer_queue_.Clear();
123 for (ReadQueue::iterator it = read_queue_.begin(); 124 if (!read_cb_.is_null()) {
124 it != read_queue_.end(); ++it) { 125 base::ResetAndReturn(&read_cb_).Run(
125 it->Run(DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); 126 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer());
126 } 127 }
127 read_queue_.clear();
128 stopped_ = true; 128 stopped_ = true;
129 end_of_stream_ = true; 129 end_of_stream_ = true;
130 } 130 }
131 131
132 base::TimeDelta FFmpegDemuxerStream::duration() { 132 base::TimeDelta FFmpegDemuxerStream::duration() {
133 return duration_; 133 return duration_;
134 } 134 }
135 135
136 DemuxerStream::Type FFmpegDemuxerStream::type() { 136 DemuxerStream::Type FFmpegDemuxerStream::type() {
137 DCHECK(message_loop_->BelongsToCurrentThread());
137 return type_; 138 return type_;
138 } 139 }
139 140
140 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) { 141 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) {
141 if (!message_loop_->BelongsToCurrentThread()) { 142 DCHECK(message_loop_->BelongsToCurrentThread());
142 message_loop_->PostTask(FROM_HERE, base::Bind( 143 CHECK(read_cb_.is_null()) << "Overlapping reads are not supported";
143 &FFmpegDemuxerStream::Read, this, read_cb)); 144 read_cb_ = BindToCurrentLoop(read_cb);
144 return;
145 }
146 145
147 // Don't accept any additional reads if we've been told to stop. 146 // Don't accept any additional reads if we've been told to stop.
148 // The |demuxer_| may have been destroyed in the pipeline thread. 147 // The |demuxer_| may have been destroyed in the pipeline thread.
149 // 148 //
150 // TODO(scherkus): it would be cleaner to reply with an error message. 149 // TODO(scherkus): it would be cleaner to reply with an error message.
151 if (stopped_) { 150 if (stopped_) {
152 read_cb.Run(DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); 151 base::ResetAndReturn(&read_cb_).Run(
152 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer());
153 return; 153 return;
154 } 154 }
155 155
156 read_queue_.push_back(read_cb); 156 SatisfyPendingRead();
157 SatisfyPendingReads();
158 } 157 }
159 158
160 void FFmpegDemuxerStream::EnableBitstreamConverter() { 159 void FFmpegDemuxerStream::EnableBitstreamConverter() {
161 if (!message_loop_->BelongsToCurrentThread()) { 160 DCHECK(message_loop_->BelongsToCurrentThread());
162 message_loop_->PostTask(FROM_HERE, base::Bind(
163 &FFmpegDemuxerStream::EnableBitstreamConverter, this));
164 return;
165 }
166 CHECK(bitstream_converter_.get()); 161 CHECK(bitstream_converter_.get());
167 bitstream_converter_enabled_ = true; 162 bitstream_converter_enabled_ = true;
168 } 163 }
169 164
170 const AudioDecoderConfig& FFmpegDemuxerStream::audio_decoder_config() { 165 const AudioDecoderConfig& FFmpegDemuxerStream::audio_decoder_config() {
166 DCHECK(message_loop_->BelongsToCurrentThread());
171 CHECK_EQ(type_, AUDIO); 167 CHECK_EQ(type_, AUDIO);
172 return audio_config_; 168 return audio_config_;
173 } 169 }
174 170
175 const VideoDecoderConfig& FFmpegDemuxerStream::video_decoder_config() { 171 const VideoDecoderConfig& FFmpegDemuxerStream::video_decoder_config() {
172 DCHECK(message_loop_->BelongsToCurrentThread());
176 CHECK_EQ(type_, VIDEO); 173 CHECK_EQ(type_, VIDEO);
177 return video_config_; 174 return video_config_;
178 } 175 }
179 176
180 FFmpegDemuxerStream::~FFmpegDemuxerStream() { 177 FFmpegDemuxerStream::~FFmpegDemuxerStream() {
181 DCHECK(stopped_); 178 DCHECK(stopped_);
182 DCHECK(read_queue_.empty()); 179 DCHECK(read_cb_.is_null());
183 DCHECK(buffer_queue_.IsEmpty()); 180 DCHECK(buffer_queue_.IsEmpty());
184 } 181 }
185 182
186 base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const { 183 base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const {
187 return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts); 184 return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts);
188 } 185 }
189 186
190 Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const { 187 Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const {
191 return buffered_ranges_; 188 return buffered_ranges_;
192 } 189 }
193 190
194 void FFmpegDemuxerStream::SatisfyPendingReads() { 191 void FFmpegDemuxerStream::SatisfyPendingRead() {
195 DCHECK(message_loop_->BelongsToCurrentThread()); 192 DCHECK(message_loop_->BelongsToCurrentThread());
196 while (!read_queue_.empty()) { 193 if (!read_cb_.is_null()) {
197 scoped_refptr<DecoderBuffer> buffer;
198
199 if (!buffer_queue_.IsEmpty()) { 194 if (!buffer_queue_.IsEmpty()) {
200 buffer = buffer_queue_.Pop(); 195 base::ResetAndReturn(&read_cb_).Run(
196 DemuxerStream::kOk, buffer_queue_.Pop());
201 } else if (end_of_stream_) { 197 } else if (end_of_stream_) {
202 buffer = DecoderBuffer::CreateEOSBuffer(); 198 base::ResetAndReturn(&read_cb_).Run(
203 } else { 199 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer());
204 break;
205 } 200 }
206
207 // Send buffer back on a new execution stack to avoid recursing.
208 ReadCB read_cb = read_queue_.front();
209 read_queue_.pop_front();
210 message_loop_->PostTask(FROM_HERE, base::Bind(
211 read_cb, DemuxerStream::kOk, buffer));
212 } 201 }
213 202
214 // Have capacity? Ask for more! 203 // Have capacity? Ask for more!
215 if (HasAvailableCapacity() && !end_of_stream_) { 204 if (HasAvailableCapacity() && !end_of_stream_) {
216 demuxer_->NotifyCapacityAvailable(); 205 demuxer_->NotifyCapacityAvailable();
217 } 206 }
218 } 207 }
219 208
220 bool FFmpegDemuxerStream::HasAvailableCapacity() { 209 bool FFmpegDemuxerStream::HasAvailableCapacity() {
221 // Try to have one second's worth of encoded data per stream. 210 // Try to have one second's worth of encoded data per stream.
(...skipping 28 matching lines...) Expand all
250 duration_known_(false), 239 duration_known_(false),
251 url_protocol_(data_source, BindToLoop(message_loop_, base::Bind( 240 url_protocol_(data_source, BindToLoop(message_loop_, base::Bind(
252 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))) { 241 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))) {
253 DCHECK(message_loop_); 242 DCHECK(message_loop_);
254 DCHECK(data_source_); 243 DCHECK(data_source_);
255 } 244 }
256 245
257 FFmpegDemuxer::~FFmpegDemuxer() {} 246 FFmpegDemuxer::~FFmpegDemuxer() {}
258 247
259 void FFmpegDemuxer::Stop(const base::Closure& callback) { 248 void FFmpegDemuxer::Stop(const base::Closure& callback) {
260 // Post a task to notify the streams to stop as well. 249 DCHECK(message_loop_->BelongsToCurrentThread());
261 message_loop_->PostTask(FROM_HERE, 250 url_protocol_.Abort();
262 base::Bind(&FFmpegDemuxer::StopTask, this, callback)); 251 data_source_->Stop(BindToCurrentLoop(base::Bind(
252 &FFmpegDemuxer::OnDataSourceStopped, this, BindToCurrentLoop(callback))));
263 } 253 }
264 254
265 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { 255 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
266 message_loop_->PostTask(FROM_HERE, 256 DCHECK(message_loop_->BelongsToCurrentThread());
267 base::Bind(&FFmpegDemuxer::SeekTask, this, time, cb)); 257 CHECK(!pending_seek_);
258
259 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|,
260 // otherwise we can end up waiting for a pre-seek read to complete even though
261 // we know we're going to drop it on the floor.
262
263 // Always seek to a timestamp less than or equal to the desired timestamp.
264 int flags = AVSEEK_FLAG_BACKWARD;
265
266 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg
267 // will attempt to use the lowest-index video stream, if present, followed by
268 // the lowest-index audio stream.
269 pending_seek_ = true;
270 base::PostTaskAndReplyWithResult(
271 blocking_thread_.message_loop_proxy(), FROM_HERE,
272 base::Bind(&av_seek_frame, glue_->format_context(), -1,
273 time.InMicroseconds(), flags),
274 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, this, cb));
268 } 275 }
269 276
270 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { 277 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) {
271 DCHECK(data_source_.get()); 278 DCHECK(message_loop_->BelongsToCurrentThread());
272 data_source_->SetPlaybackRate(playback_rate); 279 data_source_->SetPlaybackRate(playback_rate);
273 } 280 }
274 281
275 void FFmpegDemuxer::OnAudioRendererDisabled() { 282 void FFmpegDemuxer::OnAudioRendererDisabled() {
276 message_loop_->PostTask(FROM_HERE, base::Bind( 283 DCHECK(message_loop_->BelongsToCurrentThread());
277 &FFmpegDemuxer::DisableAudioStreamTask, this)); 284 audio_disabled_ = true;
285 StreamVector::iterator iter;
286 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
287 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) {
288 (*iter)->Stop();
289 }
290 }
278 } 291 }
279 292
280 void FFmpegDemuxer::Initialize(DemuxerHost* host, 293 void FFmpegDemuxer::Initialize(DemuxerHost* host,
281 const PipelineStatusCB& status_cb) { 294 const PipelineStatusCB& status_cb) {
282 message_loop_->PostTask(FROM_HERE, base::Bind( 295 DCHECK(message_loop_->BelongsToCurrentThread());
283 &FFmpegDemuxer::InitializeTask, this, host, status_cb)); 296 host_ = host;
297
298 // TODO(scherkus): DataSource should have a host by this point,
299 // see http://crbug.com/122071
300 data_source_->set_host(host);
301
302 glue_.reset(new FFmpegGlue(&url_protocol_));
303 AVFormatContext* format_context = glue_->format_context();
304
305 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we
306 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is
307 // available, so add a metadata entry to ensure some is always present.
308 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0);
309
310 // Open the AVFormatContext using our glue layer.
311 CHECK(blocking_thread_.Start());
312 base::PostTaskAndReplyWithResult(
313 blocking_thread_.message_loop_proxy(), FROM_HERE,
314 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())),
315 base::Bind(&FFmpegDemuxer::OnOpenContextDone, this, status_cb));
284 } 316 }
285 317
286 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( 318 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream(
287 DemuxerStream::Type type) { 319 DemuxerStream::Type type) {
320 DCHECK(message_loop_->BelongsToCurrentThread());
288 return GetFFmpegStream(type); 321 return GetFFmpegStream(type);
289 } 322 }
290 323
291 scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream( 324 scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream(
292 DemuxerStream::Type type) const { 325 DemuxerStream::Type type) const {
293 StreamVector::const_iterator iter; 326 StreamVector::const_iterator iter;
294 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { 327 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
295 if (*iter && (*iter)->type() == type) { 328 if (*iter && (*iter)->type() == type) {
296 return *iter; 329 return *iter;
297 } 330 }
298 } 331 }
299 return NULL; 332 return NULL;
300 } 333 }
301 334
302 base::TimeDelta FFmpegDemuxer::GetStartTime() const { 335 base::TimeDelta FFmpegDemuxer::GetStartTime() const {
336 DCHECK(message_loop_->BelongsToCurrentThread());
303 return start_time_; 337 return start_time_;
304 } 338 }
305 339
306 // Helper for calculating the bitrate of the media based on information stored 340 // Helper for calculating the bitrate of the media based on information stored
307 // in |format_context| or failing that the size and duration of the media. 341 // in |format_context| or failing that the size and duration of the media.
308 // 342 //
309 // Returns 0 if a bitrate could not be determined. 343 // Returns 0 if a bitrate could not be determined.
310 static int CalculateBitrate( 344 static int CalculateBitrate(
311 AVFormatContext* format_context, 345 AVFormatContext* format_context,
312 const base::TimeDelta& duration, 346 const base::TimeDelta& duration,
(...skipping 19 matching lines...) Expand all
332 return 0; 366 return 0;
333 } 367 }
334 368
335 // Do math in floating point as we'd overflow an int64 if the filesize was 369 // Do math in floating point as we'd overflow an int64 if the filesize was
336 // larger than ~1073GB. 370 // larger than ~1073GB.
337 double bytes = filesize_in_bytes; 371 double bytes = filesize_in_bytes;
338 double duration_us = duration.InMicroseconds(); 372 double duration_us = duration.InMicroseconds();
339 return bytes * 8000000.0 / duration_us; 373 return bytes * 8000000.0 / duration_us;
340 } 374 }
341 375
342 void FFmpegDemuxer::InitializeTask(DemuxerHost* host,
343 const PipelineStatusCB& status_cb) {
344 DCHECK(message_loop_->BelongsToCurrentThread());
345 host_ = host;
346
347 // TODO(scherkus): DataSource should have a host by this point,
348 // see http://crbug.com/122071
349 data_source_->set_host(host);
350
351 glue_.reset(new FFmpegGlue(&url_protocol_));
352 AVFormatContext* format_context = glue_->format_context();
353
354 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we
355 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is
356 // available, so add a metadata entry to ensure some is always present.
357 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0);
358
359 // Open the AVFormatContext using our glue layer.
360 CHECK(blocking_thread_.Start());
361 base::PostTaskAndReplyWithResult(
362 blocking_thread_.message_loop_proxy(), FROM_HERE,
363 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())),
364 base::Bind(&FFmpegDemuxer::OnOpenContextDone, this, status_cb));
365 }
366
367 void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb, 376 void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb,
368 bool result) { 377 bool result) {
369 DCHECK(message_loop_->BelongsToCurrentThread()); 378 DCHECK(message_loop_->BelongsToCurrentThread());
370 if (!blocking_thread_.IsRunning()) { 379 if (!blocking_thread_.IsRunning()) {
371 status_cb.Run(PIPELINE_ERROR_ABORT); 380 status_cb.Run(PIPELINE_ERROR_ABORT);
372 return; 381 return;
373 } 382 }
374 383
375 if (!result) { 384 if (!result) {
376 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); 385 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN);
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
471 480
472 int64 filesize_in_bytes = 0; 481 int64 filesize_in_bytes = 0;
473 url_protocol_.GetSize(&filesize_in_bytes); 482 url_protocol_.GetSize(&filesize_in_bytes);
474 bitrate_ = CalculateBitrate(format_context, max_duration, filesize_in_bytes); 483 bitrate_ = CalculateBitrate(format_context, max_duration, filesize_in_bytes);
475 if (bitrate_ > 0) 484 if (bitrate_ > 0)
476 data_source_->SetBitrate(bitrate_); 485 data_source_->SetBitrate(bitrate_);
477 486
478 status_cb.Run(PIPELINE_OK); 487 status_cb.Run(PIPELINE_OK);
479 } 488 }
480 489
481 void FFmpegDemuxer::SeekTask(base::TimeDelta time, const PipelineStatusCB& cb) {
482 DCHECK(message_loop_->BelongsToCurrentThread());
483 CHECK(!pending_seek_);
484
485 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|,
486 // otherwise we can end up waiting for a pre-seek read to complete even though
487 // we know we're going to drop it on the floor.
488
489 // Always seek to a timestamp less than or equal to the desired timestamp.
490 int flags = AVSEEK_FLAG_BACKWARD;
491
492 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg
493 // will attempt to use the lowest-index video stream, if present, followed by
494 // the lowest-index audio stream.
495 pending_seek_ = true;
496 base::PostTaskAndReplyWithResult(
497 blocking_thread_.message_loop_proxy(), FROM_HERE,
498 base::Bind(&av_seek_frame, glue_->format_context(), -1,
499 time.InMicroseconds(), flags),
500 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, this, cb));
501 }
502
503 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) { 490 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) {
504 DCHECK(message_loop_->BelongsToCurrentThread()); 491 DCHECK(message_loop_->BelongsToCurrentThread());
505 CHECK(pending_seek_); 492 CHECK(pending_seek_);
506 pending_seek_ = false; 493 pending_seek_ = false;
507 494
508 if (!blocking_thread_.IsRunning()) { 495 if (!blocking_thread_.IsRunning()) {
509 cb.Run(PIPELINE_ERROR_ABORT); 496 cb.Run(PIPELINE_ERROR_ABORT);
510 return; 497 return;
511 } 498 }
512 499
513 if (result < 0) { 500 if (result < 0) {
514 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being 501 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being
515 // captured from stdout and contaminates testing. 502 // captured from stdout and contaminates testing.
516 // TODO(scherkus): Implement this properly and signal error (BUG=23447). 503 // TODO(scherkus): Implement this properly and signal error (BUG=23447).
517 VLOG(1) << "Not implemented"; 504 VLOG(1) << "Not implemented";
518 } 505 }
519 506
520 // Tell streams to flush buffers due to seeking. 507 // Tell streams to flush buffers due to seeking.
521 StreamVector::iterator iter; 508 StreamVector::iterator iter;
522 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { 509 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
523 if (*iter) 510 if (*iter)
524 (*iter)->FlushBuffers(); 511 (*iter)->FlushBuffers();
525 } 512 }
526 513
527 // Resume demuxing until capacity. 514 // Resume reading until capacity.
528 DemuxTask(); 515 ReadFrameIfNeeded();
529 516
530 // Notify we're finished seeking. 517 // Notify we're finished seeking.
531 cb.Run(PIPELINE_OK); 518 cb.Run(PIPELINE_OK);
532 } 519 }
533 520
534 void FFmpegDemuxer::DemuxTask() { 521 void FFmpegDemuxer::ReadFrameIfNeeded() {
535 DCHECK(message_loop_->BelongsToCurrentThread()); 522 DCHECK(message_loop_->BelongsToCurrentThread());
536 523
537 // Make sure we have work to do before demuxing. 524 // Make sure we have work to do before reading.
538 if (!blocking_thread_.IsRunning() || !StreamsHaveAvailableCapacity() || 525 if (!blocking_thread_.IsRunning() || !StreamsHaveAvailableCapacity() ||
539 pending_read_ || pending_seek_) { 526 pending_read_ || pending_seek_) {
540 return; 527 return;
541 } 528 }
542 529
543 // Allocate and read an AVPacket from the media. Save |packet_ptr| since 530 // Allocate and read an AVPacket from the media. Save |packet_ptr| since
544 // evaluation order of packet.get() and base::Passed(&packet) is 531 // evaluation order of packet.get() and base::Passed(&packet) is
545 // undefined. 532 // undefined.
546 ScopedAVPacket packet(new AVPacket()); 533 ScopedAVPacket packet(new AVPacket());
547 AVPacket* packet_ptr = packet.get(); 534 AVPacket* packet_ptr = packet.get();
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
593 // Defend against ffmpeg giving us a bad stream index. 580 // Defend against ffmpeg giving us a bad stream index.
594 if (packet->stream_index >= 0 && 581 if (packet->stream_index >= 0 &&
595 packet->stream_index < static_cast<int>(streams_.size()) && 582 packet->stream_index < static_cast<int>(streams_.size()) &&
596 streams_[packet->stream_index] && 583 streams_[packet->stream_index] &&
597 (!audio_disabled_ || 584 (!audio_disabled_ ||
598 streams_[packet->stream_index]->type() != DemuxerStream::AUDIO)) { 585 streams_[packet->stream_index]->type() != DemuxerStream::AUDIO)) {
599 FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index]; 586 FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index];
600 demuxer_stream->EnqueuePacket(packet.Pass()); 587 demuxer_stream->EnqueuePacket(packet.Pass());
601 } 588 }
602 589
603 // Keep demuxing until we've reached capacity. 590 // Keep reading until we've reached capacity.
604 DemuxTask(); 591 ReadFrameIfNeeded();
605 }
606
607 void FFmpegDemuxer::StopTask(const base::Closure& callback) {
608 DCHECK(message_loop_->BelongsToCurrentThread());
609 url_protocol_.Abort();
610 data_source_->Stop(BindToLoop(message_loop_, base::Bind(
611 &FFmpegDemuxer::OnDataSourceStopped, this, callback)));
612 } 592 }
613 593
614 void FFmpegDemuxer::OnDataSourceStopped(const base::Closure& callback) { 594 void FFmpegDemuxer::OnDataSourceStopped(const base::Closure& callback) {
615 // This will block until all tasks complete. Note that after this returns it's 595 // This will block until all tasks complete. Note that after this returns it's
616 // possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this 596 // possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this
617 // thread. Each of the reply task methods must check whether we've stopped the 597 // thread. Each of the reply task methods must check whether we've stopped the
618 // thread and drop their results on the floor. 598 // thread and drop their results on the floor.
619 DCHECK(message_loop_->BelongsToCurrentThread()); 599 DCHECK(message_loop_->BelongsToCurrentThread());
620 blocking_thread_.Stop(); 600 blocking_thread_.Stop();
621 601
622 StreamVector::iterator iter; 602 StreamVector::iterator iter;
623 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { 603 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
624 if (*iter) 604 if (*iter)
625 (*iter)->Stop(); 605 (*iter)->Stop();
626 } 606 }
627 607
628 callback.Run(); 608 callback.Run();
629 } 609 }
630 610
631 void FFmpegDemuxer::DisableAudioStreamTask() {
632 DCHECK(message_loop_->BelongsToCurrentThread());
633 audio_disabled_ = true;
634 StreamVector::iterator iter;
635 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
636 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) {
637 (*iter)->Stop();
638 }
639 }
640 }
641
642 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() { 611 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() {
643 DCHECK(message_loop_->BelongsToCurrentThread()); 612 DCHECK(message_loop_->BelongsToCurrentThread());
644 StreamVector::iterator iter; 613 StreamVector::iterator iter;
645 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { 614 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
646 if (*iter && (*iter)->HasAvailableCapacity()) { 615 if (*iter && (*iter)->HasAvailableCapacity()) {
647 return true; 616 return true;
648 } 617 }
649 } 618 }
650 return false; 619 return false;
651 } 620 }
652 621
653 void FFmpegDemuxer::StreamHasEnded() { 622 void FFmpegDemuxer::StreamHasEnded() {
654 DCHECK(message_loop_->BelongsToCurrentThread()); 623 DCHECK(message_loop_->BelongsToCurrentThread());
655 StreamVector::iterator iter; 624 StreamVector::iterator iter;
656 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { 625 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
657 if (!*iter || 626 if (!*iter ||
658 (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) { 627 (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) {
659 continue; 628 continue;
660 } 629 }
661 (*iter)->SetEndOfStream(); 630 (*iter)->SetEndOfStream();
662 } 631 }
663 } 632 }
664 633
665 void FFmpegDemuxer::NotifyCapacityAvailable() { 634 void FFmpegDemuxer::NotifyCapacityAvailable() {
666 DCHECK(message_loop_->BelongsToCurrentThread()); 635 DCHECK(message_loop_->BelongsToCurrentThread());
667 DemuxTask(); 636 ReadFrameIfNeeded();
668 } 637 }
669 638
670 void FFmpegDemuxer::NotifyBufferingChanged() { 639 void FFmpegDemuxer::NotifyBufferingChanged() {
671 DCHECK(message_loop_->BelongsToCurrentThread()); 640 DCHECK(message_loop_->BelongsToCurrentThread());
672 Ranges<base::TimeDelta> buffered; 641 Ranges<base::TimeDelta> buffered;
673 scoped_refptr<FFmpegDemuxerStream> audio = 642 scoped_refptr<FFmpegDemuxerStream> audio =
674 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); 643 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO);
675 scoped_refptr<FFmpegDemuxerStream> video = 644 scoped_refptr<FFmpegDemuxerStream> video =
676 GetFFmpegStream(DemuxerStream::VIDEO); 645 GetFFmpegStream(DemuxerStream::VIDEO);
677 if (audio && video) { 646 if (audio && video) {
678 buffered = audio->GetBufferedRanges().IntersectionWith( 647 buffered = audio->GetBufferedRanges().IntersectionWith(
679 video->GetBufferedRanges()); 648 video->GetBufferedRanges());
680 } else if (audio) { 649 } else if (audio) {
681 buffered = audio->GetBufferedRanges(); 650 buffered = audio->GetBufferedRanges();
682 } else if (video) { 651 } else if (video) {
683 buffered = video->GetBufferedRanges(); 652 buffered = video->GetBufferedRanges();
684 } 653 }
685 for (size_t i = 0; i < buffered.size(); ++i) 654 for (size_t i = 0; i < buffered.size(); ++i)
686 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); 655 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i));
687 } 656 }
688 657
689 void FFmpegDemuxer::OnDataSourceError() { 658 void FFmpegDemuxer::OnDataSourceError() {
690 host_->OnDemuxerError(PIPELINE_ERROR_READ); 659 host_->OnDemuxerError(PIPELINE_ERROR_READ);
691 } 660 }
692 661
693 } // namespace media 662 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/ffmpeg_demuxer.h ('k') | media/filters/ffmpeg_demuxer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698