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

Unified Diff: media/filters/ffmpeg_demuxer.cc

Issue 39295: Checking in media::FFmpegDemuxer and tests. (Closed)
Patch Set: Fixes Created 11 years, 9 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/filters/ffmpeg_demuxer.h ('k') | media/filters/ffmpeg_demuxer_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/filters/ffmpeg_demuxer.cc
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..43441473ded5234dc511328ae1a5797119974090
--- /dev/null
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -0,0 +1,281 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/string_util.h"
+#include "base/time.h"
+#include "media/base/filter_host.h"
+#include "media/filters/ffmpeg_common.h"
+#include "media/filters/ffmpeg_demuxer.h"
+#include "media/filters/ffmpeg_glue.h"
+
+namespace media {
+
+//
+// AVPacketBuffer
+//
+class AVPacketBuffer : public Buffer {
+ public:
+ AVPacketBuffer(AVPacket* packet, const base::TimeDelta& timestamp,
+ const base::TimeDelta& duration)
+ : packet_(packet) {
+ DCHECK(packet);
+ SetTimestamp(timestamp);
+ SetDuration(duration);
+ }
+
+ virtual ~AVPacketBuffer() {
+ av_free_packet(packet_.get());
+ }
+
+ // Buffer implementation.
+ virtual const char* GetData() const {
+ return reinterpret_cast<const char*>(packet_->data);
+ }
+
+ virtual size_t GetDataSize() const {
+ return static_cast<size_t>(packet_->size);
+ }
+
+ private:
+ scoped_ptr<AVPacket> packet_;
+
+ DISALLOW_COPY_AND_ASSIGN(AVPacketBuffer);
+};
+
+
+//
+// FFmpegDemuxerStream
+//
+FFmpegDemuxerStream::FFmpegDemuxerStream(FFmpegDemuxer* demuxer,
+ const AVStream& stream)
+ : demuxer_(demuxer) {
+ DCHECK(demuxer_);
+
+ // Determine our media format.
+ switch (stream.codec->codec_type) {
+ case CODEC_TYPE_AUDIO:
+ media_format_.SetAsString(MediaFormat::kMimeType,
+ mime_type::kFFmpegAudio);
+ media_format_.SetAsInteger(MediaFormat::kChannels,
+ stream.codec->channels);
+ media_format_.SetAsInteger(MediaFormat::kSampleRate,
+ stream.codec->sample_rate);
+ break;
+ case CODEC_TYPE_VIDEO:
+ media_format_.SetAsString(MediaFormat::kMimeType,
+ mime_type::kFFmpegVideo);
+ media_format_.SetAsInteger(MediaFormat::kHeight,
+ stream.codec->height);
+ media_format_.SetAsInteger(MediaFormat::kWidth,
+ stream.codec->width);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ int codec_id = static_cast<int>(stream.codec->codec_id);
+ media_format_.SetAsInteger(kFFmpegCodecID, codec_id);
+
+ // Calculate the time base and duration in microseconds.
+ int64 time_base_us = static_cast<int64>(av_q2d(stream.time_base) *
+ base::Time::kMicrosecondsPerSecond);
+ int64 duration_us = static_cast<int64>(time_base_us * stream.duration);
+ time_base_ = base::TimeDelta::FromMicroseconds(time_base_us);
+ duration_ = base::TimeDelta::FromMicroseconds(duration_us);
+}
+
+FFmpegDemuxerStream::~FFmpegDemuxerStream() {
+ // Since |input_queue_| and |output_queue_| use scoped_refptr everything
+ // should get released.
+}
+
+bool FFmpegDemuxerStream::HasPendingReads() {
+ AutoLock auto_lock(lock_);
+ return !output_queue_.empty();
+}
+
+void FFmpegDemuxerStream::EnqueuePacket(AVPacket* packet) {
+ base::TimeDelta timestamp = time_base_ * packet->pts;
+ base::TimeDelta duration = time_base_ * packet->duration;
+ Buffer* buffer = new AVPacketBuffer(packet, timestamp, duration);
+ DCHECK(buffer);
+ {
+ AutoLock auto_lock(lock_);
+ input_queue_.push_back(buffer);
+ }
+ FulfillPendingReads();
+}
+
+const MediaFormat* FFmpegDemuxerStream::GetMediaFormat() {
+ return &media_format_;
+}
+
+void FFmpegDemuxerStream::Read(Assignable<Buffer>* buffer) {
+ DCHECK(buffer);
+ {
+ AutoLock auto_lock(lock_);
+ output_queue_.push_back(scoped_refptr< Assignable<Buffer> >(buffer));
+ }
+ if (FulfillPendingReads()) {
+ demuxer_->ScheduleDemux();
+ }
+}
+
+bool FFmpegDemuxerStream::FulfillPendingReads() {
+ bool pending_reads = false;
+ while (true) {
+ scoped_refptr<Buffer> buffer_in;
+ scoped_refptr< Assignable<Buffer> > buffer_out;
+ {
+ AutoLock auto_lock(lock_);
+ pending_reads = !output_queue_.empty();
+ if (input_queue_.empty() || output_queue_.empty()) {
+ break;
+ }
+ buffer_in = input_queue_.front();
+ buffer_out = output_queue_.front();
+ input_queue_.pop_front();
+ output_queue_.pop_front();
+ }
+ buffer_out->SetBuffer(buffer_in);
+ buffer_out->OnAssignment();
+ }
+ return pending_reads;
+}
+
+
+//
+// FFmpegDemuxer
+//
+FFmpegDemuxer::FFmpegDemuxer()
+ : demuxing_(false),
+ format_context_(NULL) {
+}
+
+FFmpegDemuxer::~FFmpegDemuxer() {
+ if (format_context_) {
+ av_free(format_context_);
+ }
+ while (!streams_.empty()) {
+ delete streams_.back();
+ streams_.pop_back();
+ }
+}
+
+void FFmpegDemuxer::ScheduleDemux() {
+ if (!demuxing_) {
+ demuxing_ = true;
+ host_->PostTask(NewRunnableMethod(this, &FFmpegDemuxer::Demux));
+ }
+}
+
+void FFmpegDemuxer::Stop() {
+ // TODO(scherkus): implement Stop().
+ NOTIMPLEMENTED();
+}
+
+bool FFmpegDemuxer::Initialize(DataSource* data_source) {
+ // In order to get FFmpeg to use |data_source| for file IO we must transfer
+ // ownership via FFmpegGlue. We'll add |data_source| to FFmpegGlue and pass
+ // the resulting key to FFmpeg. FFmpeg will pass the key to FFmpegGlue which
+ // will take care of attaching |data_source| to an FFmpeg context. After
+ // we finish initializing the FFmpeg context we can remove |data_source| from
+ // FFmpegGlue.
+ //
+ // Refer to media/filters/ffmpeg_glue.h for details.
+
+ // Add our data source and get our unique key.
+ std::string key = FFmpegGlue::get()->AddDataSource(data_source);
+
+ // Open FFmpeg AVFormatContext.
+ DCHECK(!format_context_);
+ int result = av_open_input_file(&format_context_, key.c_str(), NULL, 0, NULL);
+
+ // Remove our data source.
+ FFmpegGlue::get()->RemoveDataSource(data_source);
+
+ if (result < 0) {
+ host_->Error(DEMUXER_ERROR_COULD_NOT_OPEN);
+ return false;
+ }
+
+ // Fully initialize AVFormatContext by parsing the stream a little.
+ result = av_find_stream_info(format_context_);
+ if (result < 0) {
+ host_->Error(DEMUXER_ERROR_COULD_NOT_PARSE);
+ return false;
+ }
+
+ // Create demuxer streams for all supported streams.
+ base::TimeDelta max_duration;
+ for (size_t i = 0; i < format_context_->nb_streams; ++i) {
+ CodecType codec_type = format_context_->streams[i]->codec->codec_type;
+ if (codec_type == CODEC_TYPE_AUDIO || codec_type == CODEC_TYPE_VIDEO) {
+ AVStream* stream = format_context_->streams[i];
+ FFmpegDemuxerStream* demuxer_stream
+ = new FFmpegDemuxerStream(this, *stream);
+ DCHECK(demuxer_stream);
+ streams_.push_back(demuxer_stream);
+ max_duration = std::max(max_duration, demuxer_stream->duration());
+ }
+ }
+ if (streams_.empty()) {
+ host_->Error(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
+ return false;
+ }
+
+ // We have at least one supported stream, set the duration and notify we're
+ // done initializing.
+ host_->SetDuration(max_duration);
+ host_->InitializationComplete();
+ return true;
+}
+
+size_t FFmpegDemuxer::GetNumberOfStreams() {
+ return streams_.size();
+}
+
+DemuxerStream* FFmpegDemuxer::GetStream(int stream) {
+ DCHECK(stream >= 0);
+ DCHECK(stream < static_cast<int>(streams_.size()));
+ return streams_[stream];
+}
+
+void FFmpegDemuxer::Demux() {
+ DCHECK(demuxing_);
+
+ // Loop until we've satisfied every stream.
+ while (StreamsHavePendingReads()) {
+ // Allocate and read an AVPacket from the media.
+ scoped_ptr<AVPacket> packet(new AVPacket());
+ int result = av_read_frame(format_context_, packet.get());
+ if (result < 0) {
+ // TODO(scherkus): handle end of stream by marking Buffer with the end of
+ // stream flag.
+ NOTIMPLEMENTED();
+ break;
+ }
+
+ // Queue the packet with the appropriate stream.
+ DCHECK(packet->stream_index >= 0);
+ DCHECK(packet->stream_index < static_cast<int>(streams_.size()));
+ FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index];
+ demuxer_stream->EnqueuePacket(packet.release());
+ }
+
+ // Finished demuxing.
+ demuxing_ = false;
+}
+
+bool FFmpegDemuxer::StreamsHavePendingReads() {
+ StreamVector::iterator iter;
+ for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
+ if ((*iter)->HasPendingReads()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace media
« 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