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