OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 "base/scoped_ptr.h" | 5 #include "base/scoped_ptr.h" |
6 #include "base/stl_util-inl.h" | 6 #include "base/stl_util-inl.h" |
7 #include "base/string_util.h" | 7 #include "base/string_util.h" |
8 #include "base/time.h" | 8 #include "base/time.h" |
9 #include "media/base/filter_host.h" | 9 #include "media/base/filter_host.h" |
10 #include "media/filters/ffmpeg_common.h" | 10 #include "media/filters/ffmpeg_common.h" |
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
208 base::TimeDelta FFmpegDemuxerStream::ConvertTimestamp(int64 timestamp) { | 208 base::TimeDelta FFmpegDemuxerStream::ConvertTimestamp(int64 timestamp) { |
209 AVRational time_base = { 1, base::Time::kMicrosecondsPerSecond }; | 209 AVRational time_base = { 1, base::Time::kMicrosecondsPerSecond }; |
210 int64 microseconds = av_rescale_q(timestamp, stream_->time_base, time_base); | 210 int64 microseconds = av_rescale_q(timestamp, stream_->time_base, time_base); |
211 return base::TimeDelta::FromMicroseconds(microseconds); | 211 return base::TimeDelta::FromMicroseconds(microseconds); |
212 } | 212 } |
213 | 213 |
214 // | 214 // |
215 // FFmpegDemuxer | 215 // FFmpegDemuxer |
216 // | 216 // |
217 FFmpegDemuxer::FFmpegDemuxer() | 217 FFmpegDemuxer::FFmpegDemuxer() |
218 : thread_id_(NULL) { | 218 : format_context_(NULL), |
| 219 thread_id_(NULL) { |
219 } | 220 } |
220 | 221 |
221 FFmpegDemuxer::~FFmpegDemuxer() { | 222 FFmpegDemuxer::~FFmpegDemuxer() { |
222 DCHECK(!format_context_.get()); | 223 // In this destructor, we clean up resources held by FFmpeg. It is ugly to |
223 // TODO(scherkus): I believe we need to use av_close_input_file() here | 224 // close the codec contexts here because the corresponding codecs are opened |
224 // instead of scoped_ptr_malloc calling av_free(). | 225 // in the decoder filters. By reaching this point, all filters should have |
225 // | 226 // stopped, so this is the only safe place to do the global clean up. |
226 // Note that av_close_input_file() doesn't close the codecs so we need to | 227 // TODO(hclam): close the codecs in the corresponding decoders. |
227 // figure out who's responsible for closing the them. | 228 AutoLock auto_lock(FFmpegLock::get()->lock()); |
| 229 if (!format_context_) |
| 230 return; |
| 231 |
| 232 // Iterate each stream and destroy each one of them. |
| 233 int streams = format_context_->nb_streams; |
| 234 for (int i = 0; i < streams; ++i) { |
| 235 AVStream* stream = format_context_->streams[i]; |
| 236 |
| 237 // The conditions for calling avcodec_close(): |
| 238 // 1. AVStream is alive. |
| 239 // 2. AVCodecContext in AVStream is alive. |
| 240 // 3. AVCodec in AVCodecContext is alive. |
| 241 // Notice that closing a codec context without prior avcodec_open() will |
| 242 // result in a crash in FFmpeg. |
| 243 if (stream && stream->codec && stream->codec->codec) { |
| 244 stream->discard = AVDISCARD_ALL; |
| 245 avcodec_close(stream->codec); |
| 246 } |
| 247 } |
| 248 |
| 249 // Then finally cleanup the format context. |
| 250 av_close_input_file(format_context_); |
| 251 format_context_ = NULL; |
228 } | 252 } |
229 | 253 |
230 void FFmpegDemuxer::PostDemuxTask() { | 254 void FFmpegDemuxer::PostDemuxTask() { |
231 message_loop_->PostTask(FROM_HERE, | 255 message_loop_->PostTask(FROM_HERE, |
232 NewRunnableMethod(this, &FFmpegDemuxer::DemuxTask)); | 256 NewRunnableMethod(this, &FFmpegDemuxer::DemuxTask)); |
233 } | 257 } |
234 | 258 |
235 void FFmpegDemuxer::Stop() { | 259 void FFmpegDemuxer::Stop() { |
236 // Post a task to notify the streams to stop as well. | 260 // Post a task to notify the streams to stop as well. |
237 message_loop_->PostTask(FROM_HERE, | 261 message_loop_->PostTask(FROM_HERE, |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
274 // Refer to media/filters/ffmpeg_glue.h for details. | 298 // Refer to media/filters/ffmpeg_glue.h for details. |
275 | 299 |
276 // Grab the thread id for debugging. | 300 // Grab the thread id for debugging. |
277 DCHECK(!thread_id_); | 301 DCHECK(!thread_id_); |
278 thread_id_ = PlatformThread::CurrentId(); | 302 thread_id_ = PlatformThread::CurrentId(); |
279 | 303 |
280 // Add our data source and get our unique key. | 304 // Add our data source and get our unique key. |
281 std::string key = FFmpegGlue::get()->AddDataSource(data_source); | 305 std::string key = FFmpegGlue::get()->AddDataSource(data_source); |
282 | 306 |
283 // Open FFmpeg AVFormatContext. | 307 // Open FFmpeg AVFormatContext. |
284 DCHECK(!format_context_.get()); | 308 DCHECK(!format_context_); |
285 AVFormatContext* context = NULL; | 309 AVFormatContext* context = NULL; |
286 int result = av_open_input_file(&context, key.c_str(), NULL, 0, NULL); | 310 int result = av_open_input_file(&context, key.c_str(), NULL, 0, NULL); |
287 | 311 |
288 // Remove our data source. | 312 // Remove our data source. |
289 FFmpegGlue::get()->RemoveDataSource(data_source); | 313 FFmpegGlue::get()->RemoveDataSource(data_source); |
290 | 314 |
291 if (result < 0) { | 315 if (result < 0) { |
292 host_->Error(DEMUXER_ERROR_COULD_NOT_OPEN); | 316 host_->Error(DEMUXER_ERROR_COULD_NOT_OPEN); |
293 return; | 317 return; |
294 } | 318 } |
295 | 319 |
296 // Assign to our scoped_ptr_malloc. | |
297 DCHECK(context); | 320 DCHECK(context); |
298 format_context_.reset(context); | 321 format_context_ = context; |
299 | 322 |
300 // Serialize calls to av_find_stream_info(). | 323 // Serialize calls to av_find_stream_info(). |
301 { | 324 { |
302 AutoLock auto_lock(FFmpegLock::get()->lock()); | 325 AutoLock auto_lock(FFmpegLock::get()->lock()); |
303 | 326 |
304 // Fully initialize AVFormatContext by parsing the stream a little. | 327 // Fully initialize AVFormatContext by parsing the stream a little. |
305 result = av_find_stream_info(format_context_.get()); | 328 result = av_find_stream_info(format_context_); |
306 if (result < 0) { | 329 if (result < 0) { |
307 host_->Error(DEMUXER_ERROR_COULD_NOT_PARSE); | 330 host_->Error(DEMUXER_ERROR_COULD_NOT_PARSE); |
308 return; | 331 return; |
309 } | 332 } |
310 } | 333 } |
311 | 334 |
312 // Create demuxer streams for all supported streams. | 335 // Create demuxer streams for all supported streams. |
313 base::TimeDelta max_duration; | 336 base::TimeDelta max_duration; |
314 for (size_t i = 0; i < format_context_->nb_streams; ++i) { | 337 for (size_t i = 0; i < format_context_->nb_streams; ++i) { |
315 CodecType codec_type = format_context_->streams[i]->codec->codec_type; | 338 CodecType codec_type = format_context_->streams[i]->codec->codec_type; |
(...skipping 27 matching lines...) Expand all Loading... |
343 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 366 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
344 (*iter)->FlushBuffers(); | 367 (*iter)->FlushBuffers(); |
345 } | 368 } |
346 | 369 |
347 // Seek backwards if requested timestamp is behind FFmpeg's current time. | 370 // Seek backwards if requested timestamp is behind FFmpeg's current time. |
348 int flags = 0; | 371 int flags = 0; |
349 if (time <= current_timestamp_) { | 372 if (time <= current_timestamp_) { |
350 flags |= AVSEEK_FLAG_BACKWARD; | 373 flags |= AVSEEK_FLAG_BACKWARD; |
351 } | 374 } |
352 | 375 |
353 if (av_seek_frame(format_context_.get(), -1, time.InMicroseconds(), | 376 if (av_seek_frame(format_context_, -1, time.InMicroseconds(), |
354 flags) < 0) { | 377 flags) < 0) { |
355 // TODO(scherkus): signal error. | 378 // TODO(scherkus): signal error. |
356 NOTIMPLEMENTED(); | 379 NOTIMPLEMENTED(); |
357 } | 380 } |
358 } | 381 } |
359 | 382 |
360 void FFmpegDemuxer::DemuxTask() { | 383 void FFmpegDemuxer::DemuxTask() { |
361 DCHECK_EQ(PlatformThread::CurrentId(), thread_id_); | 384 DCHECK_EQ(PlatformThread::CurrentId(), thread_id_); |
362 | 385 |
363 // Make sure we have work to do before demuxing. | 386 // Make sure we have work to do before demuxing. |
364 if (!StreamsHavePendingReads()) { | 387 if (!StreamsHavePendingReads()) { |
365 return; | 388 return; |
366 } | 389 } |
367 | 390 |
368 // Allocate and read an AVPacket from the media. | 391 // Allocate and read an AVPacket from the media. |
369 scoped_ptr<AVPacket> packet(new AVPacket()); | 392 scoped_ptr<AVPacket> packet(new AVPacket()); |
370 int result = av_read_frame(format_context_.get(), packet.get()); | 393 int result = av_read_frame(format_context_, packet.get()); |
371 if (result < 0) { | 394 if (result < 0) { |
372 // If we have reached the end of stream, tell the downstream filters about | 395 // If we have reached the end of stream, tell the downstream filters about |
373 // the event. | 396 // the event. |
374 StreamHasEnded(); | 397 StreamHasEnded(); |
375 return; | 398 return; |
376 } | 399 } |
377 | 400 |
378 // Queue the packet with the appropriate stream. | 401 // Queue the packet with the appropriate stream. |
379 // TODO(scherkus): should we post this back to the pipeline thread? I'm | 402 // TODO(scherkus): should we post this back to the pipeline thread? I'm |
380 // worried about downstream filters (i.e., decoders) executing on this | 403 // worried about downstream filters (i.e., decoders) executing on this |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
413 PostDemuxTask(); | 436 PostDemuxTask(); |
414 } | 437 } |
415 } | 438 } |
416 | 439 |
417 void FFmpegDemuxer::StopTask() { | 440 void FFmpegDemuxer::StopTask() { |
418 DCHECK_EQ(PlatformThread::CurrentId(), thread_id_); | 441 DCHECK_EQ(PlatformThread::CurrentId(), thread_id_); |
419 StreamVector::iterator iter; | 442 StreamVector::iterator iter; |
420 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 443 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
421 (*iter)->Stop(); | 444 (*iter)->Stop(); |
422 } | 445 } |
423 | |
424 // Free our AVFormatContext. | |
425 format_context_.reset(); | |
426 } | 446 } |
427 | 447 |
428 bool FFmpegDemuxer::StreamsHavePendingReads() { | 448 bool FFmpegDemuxer::StreamsHavePendingReads() { |
429 DCHECK_EQ(PlatformThread::CurrentId(), thread_id_); | 449 DCHECK_EQ(PlatformThread::CurrentId(), thread_id_); |
430 StreamVector::iterator iter; | 450 StreamVector::iterator iter; |
431 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 451 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
432 if ((*iter)->HasPendingReads()) { | 452 if ((*iter)->HasPendingReads()) { |
433 return true; | 453 return true; |
434 } | 454 } |
435 } | 455 } |
436 return false; | 456 return false; |
437 } | 457 } |
438 | 458 |
439 void FFmpegDemuxer::StreamHasEnded() { | 459 void FFmpegDemuxer::StreamHasEnded() { |
440 DCHECK_EQ(PlatformThread::CurrentId(), thread_id_); | 460 DCHECK_EQ(PlatformThread::CurrentId(), thread_id_); |
441 StreamVector::iterator iter; | 461 StreamVector::iterator iter; |
442 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 462 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
443 AVPacket* packet = new AVPacket(); | 463 AVPacket* packet = new AVPacket(); |
444 memset(packet, 0, sizeof(*packet)); | 464 memset(packet, 0, sizeof(*packet)); |
445 (*iter)->EnqueuePacket(packet); | 465 (*iter)->EnqueuePacket(packet); |
446 } | 466 } |
447 } | 467 } |
448 | 468 |
449 } // namespace media | 469 } // namespace media |
OLD | NEW |