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

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

Issue 151192: Cleanup resources allocated by FFmpeg to avoid memory and threads leaks... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 5 months 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) 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
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
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
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
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
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