| 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 |