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_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/base64.h" | 10 #include "base/base64.h" |
(...skipping 24 matching lines...) Expand all Loading... |
35 // | 35 // |
36 // FFmpegDemuxerStream | 36 // FFmpegDemuxerStream |
37 // | 37 // |
38 FFmpegDemuxerStream::FFmpegDemuxerStream( | 38 FFmpegDemuxerStream::FFmpegDemuxerStream( |
39 FFmpegDemuxer* demuxer, | 39 FFmpegDemuxer* demuxer, |
40 AVStream* stream) | 40 AVStream* stream) |
41 : demuxer_(demuxer), | 41 : demuxer_(demuxer), |
42 message_loop_(base::MessageLoopProxy::current()), | 42 message_loop_(base::MessageLoopProxy::current()), |
43 stream_(stream), | 43 stream_(stream), |
44 type_(UNKNOWN), | 44 type_(UNKNOWN), |
45 stopped_(false), | |
46 end_of_stream_(false), | 45 end_of_stream_(false), |
47 last_packet_timestamp_(kNoTimestamp()), | 46 last_packet_timestamp_(kNoTimestamp()), |
48 bitstream_converter_enabled_(false) { | 47 bitstream_converter_enabled_(false) { |
49 DCHECK(demuxer_); | 48 DCHECK(demuxer_); |
50 | 49 |
51 bool is_encrypted = false; | 50 bool is_encrypted = false; |
52 | 51 |
53 // Determine our media format. | 52 // Determine our media format. |
54 switch (stream->codec->codec_type) { | 53 switch (stream->codec->codec_type) { |
55 case AVMEDIA_TYPE_AUDIO: | 54 case AVMEDIA_TYPE_AUDIO: |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
90 return; | 89 return; |
91 | 90 |
92 encryption_key_id_.assign(enc_key_id); | 91 encryption_key_id_.assign(enc_key_id); |
93 demuxer_->FireNeedKey(kWebMEncryptInitDataType, enc_key_id); | 92 demuxer_->FireNeedKey(kWebMEncryptInitDataType, enc_key_id); |
94 } | 93 } |
95 } | 94 } |
96 | 95 |
97 void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) { | 96 void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) { |
98 DCHECK(message_loop_->BelongsToCurrentThread()); | 97 DCHECK(message_loop_->BelongsToCurrentThread()); |
99 | 98 |
100 if (stopped_ || end_of_stream_) { | 99 if (!demuxer_ || end_of_stream_) { |
101 NOTREACHED() << "Attempted to enqueue packet on a stopped stream"; | 100 NOTREACHED() << "Attempted to enqueue packet on a stopped stream"; |
102 return; | 101 return; |
103 } | 102 } |
104 | 103 |
105 // Convert the packet if there is a bitstream filter. | 104 // Convert the packet if there is a bitstream filter. |
106 if (packet->data && bitstream_converter_enabled_ && | 105 if (packet->data && bitstream_converter_enabled_ && |
107 !bitstream_converter_->ConvertPacket(packet.get())) { | 106 !bitstream_converter_->ConvertPacket(packet.get())) { |
108 LOG(ERROR) << "Format conversion failed."; | 107 LOG(ERROR) << "Format conversion failed."; |
109 } | 108 } |
110 | 109 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 last_packet_timestamp_ = kNoTimestamp(); | 154 last_packet_timestamp_ = kNoTimestamp(); |
156 } | 155 } |
157 | 156 |
158 void FFmpegDemuxerStream::Stop() { | 157 void FFmpegDemuxerStream::Stop() { |
159 DCHECK(message_loop_->BelongsToCurrentThread()); | 158 DCHECK(message_loop_->BelongsToCurrentThread()); |
160 buffer_queue_.Clear(); | 159 buffer_queue_.Clear(); |
161 if (!read_cb_.is_null()) { | 160 if (!read_cb_.is_null()) { |
162 base::ResetAndReturn(&read_cb_).Run( | 161 base::ResetAndReturn(&read_cb_).Run( |
163 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); | 162 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
164 } | 163 } |
165 stopped_ = true; | 164 demuxer_ = NULL; |
| 165 stream_ = NULL; |
166 end_of_stream_ = true; | 166 end_of_stream_ = true; |
167 } | 167 } |
168 | 168 |
169 base::TimeDelta FFmpegDemuxerStream::duration() { | 169 base::TimeDelta FFmpegDemuxerStream::duration() { |
170 return duration_; | 170 return duration_; |
171 } | 171 } |
172 | 172 |
173 DemuxerStream::Type FFmpegDemuxerStream::type() { | 173 DemuxerStream::Type FFmpegDemuxerStream::type() { |
174 DCHECK(message_loop_->BelongsToCurrentThread()); | 174 DCHECK(message_loop_->BelongsToCurrentThread()); |
175 return type_; | 175 return type_; |
176 } | 176 } |
177 | 177 |
178 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) { | 178 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) { |
179 DCHECK(message_loop_->BelongsToCurrentThread()); | 179 DCHECK(message_loop_->BelongsToCurrentThread()); |
180 CHECK(read_cb_.is_null()) << "Overlapping reads are not supported"; | 180 CHECK(read_cb_.is_null()) << "Overlapping reads are not supported"; |
181 read_cb_ = BindToCurrentLoop(read_cb); | 181 read_cb_ = BindToCurrentLoop(read_cb); |
182 | 182 |
183 // Don't accept any additional reads if we've been told to stop. | 183 // Don't accept any additional reads if we've been told to stop. |
184 // The |demuxer_| may have been destroyed in the pipeline thread. | 184 // The |demuxer_| may have been destroyed in the pipeline thread. |
185 // | 185 // |
186 // TODO(scherkus): it would be cleaner to reply with an error message. | 186 // TODO(scherkus): it would be cleaner to reply with an error message. |
187 if (stopped_) { | 187 if (!demuxer_) { |
188 base::ResetAndReturn(&read_cb_).Run( | 188 base::ResetAndReturn(&read_cb_).Run( |
189 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); | 189 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
190 return; | 190 return; |
191 } | 191 } |
192 | 192 |
193 SatisfyPendingRead(); | 193 SatisfyPendingRead(); |
194 } | 194 } |
195 | 195 |
196 void FFmpegDemuxerStream::EnableBitstreamConverter() { | 196 void FFmpegDemuxerStream::EnableBitstreamConverter() { |
197 DCHECK(message_loop_->BelongsToCurrentThread()); | 197 DCHECK(message_loop_->BelongsToCurrentThread()); |
198 CHECK(bitstream_converter_.get()); | 198 CHECK(bitstream_converter_.get()); |
199 bitstream_converter_enabled_ = true; | 199 bitstream_converter_enabled_ = true; |
200 } | 200 } |
201 | 201 |
202 const AudioDecoderConfig& FFmpegDemuxerStream::audio_decoder_config() { | 202 const AudioDecoderConfig& FFmpegDemuxerStream::audio_decoder_config() { |
203 DCHECK(message_loop_->BelongsToCurrentThread()); | 203 DCHECK(message_loop_->BelongsToCurrentThread()); |
204 CHECK_EQ(type_, AUDIO); | 204 CHECK_EQ(type_, AUDIO); |
205 return audio_config_; | 205 return audio_config_; |
206 } | 206 } |
207 | 207 |
208 const VideoDecoderConfig& FFmpegDemuxerStream::video_decoder_config() { | 208 const VideoDecoderConfig& FFmpegDemuxerStream::video_decoder_config() { |
209 DCHECK(message_loop_->BelongsToCurrentThread()); | 209 DCHECK(message_loop_->BelongsToCurrentThread()); |
210 CHECK_EQ(type_, VIDEO); | 210 CHECK_EQ(type_, VIDEO); |
211 return video_config_; | 211 return video_config_; |
212 } | 212 } |
213 | 213 |
214 FFmpegDemuxerStream::~FFmpegDemuxerStream() { | 214 FFmpegDemuxerStream::~FFmpegDemuxerStream() { |
215 DCHECK(stopped_); | 215 DCHECK(!demuxer_); |
216 DCHECK(read_cb_.is_null()); | 216 DCHECK(read_cb_.is_null()); |
217 DCHECK(buffer_queue_.IsEmpty()); | 217 DCHECK(buffer_queue_.IsEmpty()); |
218 } | 218 } |
219 | 219 |
220 base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const { | 220 base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const { |
221 return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts); | 221 return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts); |
222 } | 222 } |
223 | 223 |
224 Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const { | 224 Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const { |
225 return buffered_ranges_; | 225 return buffered_ranges_; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
265 | 265 |
266 // | 266 // |
267 // FFmpegDemuxer | 267 // FFmpegDemuxer |
268 // | 268 // |
269 FFmpegDemuxer::FFmpegDemuxer( | 269 FFmpegDemuxer::FFmpegDemuxer( |
270 const scoped_refptr<base::MessageLoopProxy>& message_loop, | 270 const scoped_refptr<base::MessageLoopProxy>& message_loop, |
271 const scoped_refptr<DataSource>& data_source, | 271 const scoped_refptr<DataSource>& data_source, |
272 const FFmpegNeedKeyCB& need_key_cb) | 272 const FFmpegNeedKeyCB& need_key_cb) |
273 : host_(NULL), | 273 : host_(NULL), |
274 message_loop_(message_loop), | 274 message_loop_(message_loop), |
| 275 weak_factory_(this), |
275 blocking_thread_("FFmpegDemuxer"), | 276 blocking_thread_("FFmpegDemuxer"), |
276 pending_read_(false), | 277 pending_read_(false), |
277 pending_seek_(false), | 278 pending_seek_(false), |
278 data_source_(data_source), | 279 data_source_(data_source), |
279 bitrate_(0), | 280 bitrate_(0), |
280 start_time_(kNoTimestamp()), | 281 start_time_(kNoTimestamp()), |
281 audio_disabled_(false), | 282 audio_disabled_(false), |
282 duration_known_(false), | 283 duration_known_(false), |
283 url_protocol_(data_source, BindToLoop(message_loop_, base::Bind( | 284 url_protocol_(data_source, BindToLoop(message_loop_, base::Bind( |
284 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))), | 285 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))), |
285 need_key_cb_(need_key_cb) { | 286 need_key_cb_(need_key_cb) { |
286 DCHECK(message_loop_); | 287 DCHECK(message_loop_); |
287 DCHECK(data_source_); | 288 DCHECK(data_source_); |
288 } | 289 } |
289 | 290 |
290 FFmpegDemuxer::~FFmpegDemuxer() {} | 291 FFmpegDemuxer::~FFmpegDemuxer() {} |
291 | 292 |
292 void FFmpegDemuxer::Stop(const base::Closure& callback) { | 293 void FFmpegDemuxer::Stop(const base::Closure& callback) { |
293 DCHECK(message_loop_->BelongsToCurrentThread()); | 294 DCHECK(message_loop_->BelongsToCurrentThread()); |
294 url_protocol_.Abort(); | 295 url_protocol_.Abort(); |
295 data_source_->Stop(BindToCurrentLoop(base::Bind( | 296 data_source_->Stop(BindToCurrentLoop(base::Bind( |
296 &FFmpegDemuxer::OnDataSourceStopped, this, BindToCurrentLoop(callback)))); | 297 &FFmpegDemuxer::OnDataSourceStopped, weak_this_, |
| 298 BindToCurrentLoop(callback)))); |
297 } | 299 } |
298 | 300 |
299 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { | 301 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { |
300 DCHECK(message_loop_->BelongsToCurrentThread()); | 302 DCHECK(message_loop_->BelongsToCurrentThread()); |
301 CHECK(!pending_seek_); | 303 CHECK(!pending_seek_); |
302 | 304 |
303 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, | 305 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, |
304 // otherwise we can end up waiting for a pre-seek read to complete even though | 306 // otherwise we can end up waiting for a pre-seek read to complete even though |
305 // we know we're going to drop it on the floor. | 307 // we know we're going to drop it on the floor. |
306 | 308 |
307 // Always seek to a timestamp less than or equal to the desired timestamp. | 309 // Always seek to a timestamp less than or equal to the desired timestamp. |
308 int flags = AVSEEK_FLAG_BACKWARD; | 310 int flags = AVSEEK_FLAG_BACKWARD; |
309 | 311 |
310 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg | 312 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg |
311 // will attempt to use the lowest-index video stream, if present, followed by | 313 // will attempt to use the lowest-index video stream, if present, followed by |
312 // the lowest-index audio stream. | 314 // the lowest-index audio stream. |
313 pending_seek_ = true; | 315 pending_seek_ = true; |
314 base::PostTaskAndReplyWithResult( | 316 base::PostTaskAndReplyWithResult( |
315 blocking_thread_.message_loop_proxy(), FROM_HERE, | 317 blocking_thread_.message_loop_proxy(), FROM_HERE, |
316 base::Bind(&av_seek_frame, glue_->format_context(), -1, | 318 base::Bind(&av_seek_frame, glue_->format_context(), -1, |
317 time.InMicroseconds(), flags), | 319 time.InMicroseconds(), flags), |
318 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, this, cb)); | 320 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_this_, cb)); |
319 } | 321 } |
320 | 322 |
321 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { | 323 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { |
322 DCHECK(message_loop_->BelongsToCurrentThread()); | 324 DCHECK(message_loop_->BelongsToCurrentThread()); |
323 data_source_->SetPlaybackRate(playback_rate); | 325 data_source_->SetPlaybackRate(playback_rate); |
324 } | 326 } |
325 | 327 |
326 void FFmpegDemuxer::OnAudioRendererDisabled() { | 328 void FFmpegDemuxer::OnAudioRendererDisabled() { |
327 DCHECK(message_loop_->BelongsToCurrentThread()); | 329 DCHECK(message_loop_->BelongsToCurrentThread()); |
328 audio_disabled_ = true; | 330 audio_disabled_ = true; |
329 StreamVector::iterator iter; | 331 StreamVector::iterator iter; |
330 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 332 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
331 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { | 333 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { |
332 (*iter)->Stop(); | 334 (*iter)->Stop(); |
333 } | 335 } |
334 } | 336 } |
335 } | 337 } |
336 | 338 |
337 void FFmpegDemuxer::Initialize(DemuxerHost* host, | 339 void FFmpegDemuxer::Initialize(DemuxerHost* host, |
338 const PipelineStatusCB& status_cb) { | 340 const PipelineStatusCB& status_cb) { |
339 DCHECK(message_loop_->BelongsToCurrentThread()); | 341 DCHECK(message_loop_->BelongsToCurrentThread()); |
340 host_ = host; | 342 host_ = host; |
| 343 weak_this_ = weak_factory_.GetWeakPtr(); |
341 | 344 |
342 // TODO(scherkus): DataSource should have a host by this point, | 345 // TODO(scherkus): DataSource should have a host by this point, |
343 // see http://crbug.com/122071 | 346 // see http://crbug.com/122071 |
344 data_source_->set_host(host); | 347 data_source_->set_host(host); |
345 | 348 |
346 glue_.reset(new FFmpegGlue(&url_protocol_)); | 349 glue_.reset(new FFmpegGlue(&url_protocol_)); |
347 AVFormatContext* format_context = glue_->format_context(); | 350 AVFormatContext* format_context = glue_->format_context(); |
348 | 351 |
349 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we | 352 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we |
350 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is | 353 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is |
351 // available, so add a metadata entry to ensure some is always present. | 354 // available, so add a metadata entry to ensure some is always present. |
352 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); | 355 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); |
353 | 356 |
354 // Open the AVFormatContext using our glue layer. | 357 // Open the AVFormatContext using our glue layer. |
355 CHECK(blocking_thread_.Start()); | 358 CHECK(blocking_thread_.Start()); |
356 base::PostTaskAndReplyWithResult( | 359 base::PostTaskAndReplyWithResult( |
357 blocking_thread_.message_loop_proxy(), FROM_HERE, | 360 blocking_thread_.message_loop_proxy(), FROM_HERE, |
358 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), | 361 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), |
359 base::Bind(&FFmpegDemuxer::OnOpenContextDone, this, status_cb)); | 362 base::Bind(&FFmpegDemuxer::OnOpenContextDone, weak_this_, status_cb)); |
360 } | 363 } |
361 | 364 |
362 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( | 365 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( |
363 DemuxerStream::Type type) { | 366 DemuxerStream::Type type) { |
364 DCHECK(message_loop_->BelongsToCurrentThread()); | 367 DCHECK(message_loop_->BelongsToCurrentThread()); |
365 return GetFFmpegStream(type); | 368 return GetFFmpegStream(type); |
366 } | 369 } |
367 | 370 |
368 scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream( | 371 scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream( |
369 DemuxerStream::Type type) const { | 372 DemuxerStream::Type type) const { |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
428 if (!result) { | 431 if (!result) { |
429 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); | 432 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); |
430 return; | 433 return; |
431 } | 434 } |
432 | 435 |
433 // Fully initialize AVFormatContext by parsing the stream a little. | 436 // Fully initialize AVFormatContext by parsing the stream a little. |
434 base::PostTaskAndReplyWithResult( | 437 base::PostTaskAndReplyWithResult( |
435 blocking_thread_.message_loop_proxy(), FROM_HERE, | 438 blocking_thread_.message_loop_proxy(), FROM_HERE, |
436 base::Bind(&avformat_find_stream_info, glue_->format_context(), | 439 base::Bind(&avformat_find_stream_info, glue_->format_context(), |
437 static_cast<AVDictionary**>(NULL)), | 440 static_cast<AVDictionary**>(NULL)), |
438 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, this, status_cb)); | 441 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, weak_this_, status_cb)); |
439 } | 442 } |
440 | 443 |
441 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, | 444 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, |
442 int result) { | 445 int result) { |
443 DCHECK(message_loop_->BelongsToCurrentThread()); | 446 DCHECK(message_loop_->BelongsToCurrentThread()); |
444 if (!blocking_thread_.IsRunning()) { | 447 if (!blocking_thread_.IsRunning()) { |
445 status_cb.Run(PIPELINE_ERROR_ABORT); | 448 status_cb.Run(PIPELINE_ERROR_ABORT); |
446 return; | 449 return; |
447 } | 450 } |
448 | 451 |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
579 // Allocate and read an AVPacket from the media. Save |packet_ptr| since | 582 // Allocate and read an AVPacket from the media. Save |packet_ptr| since |
580 // evaluation order of packet.get() and base::Passed(&packet) is | 583 // evaluation order of packet.get() and base::Passed(&packet) is |
581 // undefined. | 584 // undefined. |
582 ScopedAVPacket packet(new AVPacket()); | 585 ScopedAVPacket packet(new AVPacket()); |
583 AVPacket* packet_ptr = packet.get(); | 586 AVPacket* packet_ptr = packet.get(); |
584 | 587 |
585 pending_read_ = true; | 588 pending_read_ = true; |
586 base::PostTaskAndReplyWithResult( | 589 base::PostTaskAndReplyWithResult( |
587 blocking_thread_.message_loop_proxy(), FROM_HERE, | 590 blocking_thread_.message_loop_proxy(), FROM_HERE, |
588 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), | 591 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), |
589 base::Bind(&FFmpegDemuxer::OnReadFrameDone, this, base::Passed(&packet))); | 592 base::Bind(&FFmpegDemuxer::OnReadFrameDone, weak_this_, |
| 593 base::Passed(&packet))); |
590 } | 594 } |
591 | 595 |
592 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { | 596 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { |
593 DCHECK(message_loop_->BelongsToCurrentThread()); | 597 DCHECK(message_loop_->BelongsToCurrentThread()); |
594 DCHECK(pending_read_); | 598 DCHECK(pending_read_); |
595 pending_read_ = false; | 599 pending_read_ = false; |
596 | 600 |
597 if (!blocking_thread_.IsRunning() || pending_seek_) { | 601 if (!blocking_thread_.IsRunning() || pending_seek_) { |
598 return; | 602 return; |
599 } | 603 } |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
730 } | 734 } |
731 for (size_t i = 0; i < buffered.size(); ++i) | 735 for (size_t i = 0; i < buffered.size(); ++i) |
732 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 736 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
733 } | 737 } |
734 | 738 |
735 void FFmpegDemuxer::OnDataSourceError() { | 739 void FFmpegDemuxer::OnDataSourceError() { |
736 host_->OnDemuxerError(PIPELINE_ERROR_READ); | 740 host_->OnDemuxerError(PIPELINE_ERROR_READ); |
737 } | 741 } |
738 | 742 |
739 } // namespace media | 743 } // namespace media |
OLD | NEW |