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

Side by Side 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "base/string_util.h"
6 #include "base/time.h"
7 #include "media/base/filter_host.h"
8 #include "media/filters/ffmpeg_common.h"
9 #include "media/filters/ffmpeg_demuxer.h"
10 #include "media/filters/ffmpeg_glue.h"
11
12 namespace media {
13
14 //
15 // AVPacketBuffer
16 //
17 class AVPacketBuffer : public Buffer {
18 public:
19 AVPacketBuffer(AVPacket* packet, const base::TimeDelta& timestamp,
20 const base::TimeDelta& duration)
21 : packet_(packet) {
22 DCHECK(packet);
23 SetTimestamp(timestamp);
24 SetDuration(duration);
25 }
26
27 virtual ~AVPacketBuffer() {
28 av_free_packet(packet_.get());
29 }
30
31 // Buffer implementation.
32 virtual const char* GetData() const {
33 return reinterpret_cast<const char*>(packet_->data);
34 }
35
36 virtual size_t GetDataSize() const {
37 return static_cast<size_t>(packet_->size);
38 }
39
40 private:
41 scoped_ptr<AVPacket> packet_;
42
43 DISALLOW_COPY_AND_ASSIGN(AVPacketBuffer);
44 };
45
46
47 //
48 // FFmpegDemuxerStream
49 //
50 FFmpegDemuxerStream::FFmpegDemuxerStream(FFmpegDemuxer* demuxer,
51 const AVStream& stream)
52 : demuxer_(demuxer) {
53 DCHECK(demuxer_);
54
55 // Determine our media format.
56 switch (stream.codec->codec_type) {
57 case CODEC_TYPE_AUDIO:
58 media_format_.SetAsString(MediaFormat::kMimeType,
59 mime_type::kFFmpegAudio);
60 media_format_.SetAsInteger(MediaFormat::kChannels,
61 stream.codec->channels);
62 media_format_.SetAsInteger(MediaFormat::kSampleRate,
63 stream.codec->sample_rate);
64 break;
65 case CODEC_TYPE_VIDEO:
66 media_format_.SetAsString(MediaFormat::kMimeType,
67 mime_type::kFFmpegVideo);
68 media_format_.SetAsInteger(MediaFormat::kHeight,
69 stream.codec->height);
70 media_format_.SetAsInteger(MediaFormat::kWidth,
71 stream.codec->width);
72 break;
73 default:
74 NOTREACHED();
75 break;
76 }
77 int codec_id = static_cast<int>(stream.codec->codec_id);
78 media_format_.SetAsInteger(kFFmpegCodecID, codec_id);
79
80 // Calculate the time base and duration in microseconds.
81 int64 time_base_us = static_cast<int64>(av_q2d(stream.time_base) *
82 base::Time::kMicrosecondsPerSecond);
83 int64 duration_us = static_cast<int64>(time_base_us * stream.duration);
84 time_base_ = base::TimeDelta::FromMicroseconds(time_base_us);
85 duration_ = base::TimeDelta::FromMicroseconds(duration_us);
86 }
87
88 FFmpegDemuxerStream::~FFmpegDemuxerStream() {
89 // Since |input_queue_| and |output_queue_| use scoped_refptr everything
90 // should get released.
91 }
92
93 bool FFmpegDemuxerStream::HasPendingReads() {
94 AutoLock auto_lock(lock_);
95 return !output_queue_.empty();
96 }
97
98 void FFmpegDemuxerStream::EnqueuePacket(AVPacket* packet) {
99 base::TimeDelta timestamp = time_base_ * packet->pts;
100 base::TimeDelta duration = time_base_ * packet->duration;
101 Buffer* buffer = new AVPacketBuffer(packet, timestamp, duration);
102 DCHECK(buffer);
103 {
104 AutoLock auto_lock(lock_);
105 input_queue_.push_back(buffer);
106 }
107 FulfillPendingReads();
108 }
109
110 const MediaFormat* FFmpegDemuxerStream::GetMediaFormat() {
111 return &media_format_;
112 }
113
114 void FFmpegDemuxerStream::Read(Assignable<Buffer>* buffer) {
115 DCHECK(buffer);
116 {
117 AutoLock auto_lock(lock_);
118 output_queue_.push_back(scoped_refptr< Assignable<Buffer> >(buffer));
119 }
120 if (FulfillPendingReads()) {
121 demuxer_->ScheduleDemux();
122 }
123 }
124
125 bool FFmpegDemuxerStream::FulfillPendingReads() {
126 bool pending_reads = false;
127 while (true) {
128 scoped_refptr<Buffer> buffer_in;
129 scoped_refptr< Assignable<Buffer> > buffer_out;
130 {
131 AutoLock auto_lock(lock_);
132 pending_reads = !output_queue_.empty();
133 if (input_queue_.empty() || output_queue_.empty()) {
134 break;
135 }
136 buffer_in = input_queue_.front();
137 buffer_out = output_queue_.front();
138 input_queue_.pop_front();
139 output_queue_.pop_front();
140 }
141 buffer_out->SetBuffer(buffer_in);
142 buffer_out->OnAssignment();
143 }
144 return pending_reads;
145 }
146
147
148 //
149 // FFmpegDemuxer
150 //
151 FFmpegDemuxer::FFmpegDemuxer()
152 : demuxing_(false),
153 format_context_(NULL) {
154 }
155
156 FFmpegDemuxer::~FFmpegDemuxer() {
157 if (format_context_) {
158 av_free(format_context_);
159 }
160 while (!streams_.empty()) {
161 delete streams_.back();
162 streams_.pop_back();
163 }
164 }
165
166 void FFmpegDemuxer::ScheduleDemux() {
167 if (!demuxing_) {
168 demuxing_ = true;
169 host_->PostTask(NewRunnableMethod(this, &FFmpegDemuxer::Demux));
170 }
171 }
172
173 void FFmpegDemuxer::Stop() {
174 // TODO(scherkus): implement Stop().
175 NOTIMPLEMENTED();
176 }
177
178 bool FFmpegDemuxer::Initialize(DataSource* data_source) {
179 // In order to get FFmpeg to use |data_source| for file IO we must transfer
180 // ownership via FFmpegGlue. We'll add |data_source| to FFmpegGlue and pass
181 // the resulting key to FFmpeg. FFmpeg will pass the key to FFmpegGlue which
182 // will take care of attaching |data_source| to an FFmpeg context. After
183 // we finish initializing the FFmpeg context we can remove |data_source| from
184 // FFmpegGlue.
185 //
186 // Refer to media/filters/ffmpeg_glue.h for details.
187
188 // Add our data source and get our unique key.
189 std::string key = FFmpegGlue::get()->AddDataSource(data_source);
190
191 // Open FFmpeg AVFormatContext.
192 DCHECK(!format_context_);
193 int result = av_open_input_file(&format_context_, key.c_str(), NULL, 0, NULL);
194
195 // Remove our data source.
196 FFmpegGlue::get()->RemoveDataSource(data_source);
197
198 if (result < 0) {
199 host_->Error(DEMUXER_ERROR_COULD_NOT_OPEN);
200 return false;
201 }
202
203 // Fully initialize AVFormatContext by parsing the stream a little.
204 result = av_find_stream_info(format_context_);
205 if (result < 0) {
206 host_->Error(DEMUXER_ERROR_COULD_NOT_PARSE);
207 return false;
208 }
209
210 // Create demuxer streams for all supported streams.
211 base::TimeDelta max_duration;
212 for (size_t i = 0; i < format_context_->nb_streams; ++i) {
213 CodecType codec_type = format_context_->streams[i]->codec->codec_type;
214 if (codec_type == CODEC_TYPE_AUDIO || codec_type == CODEC_TYPE_VIDEO) {
215 AVStream* stream = format_context_->streams[i];
216 FFmpegDemuxerStream* demuxer_stream
217 = new FFmpegDemuxerStream(this, *stream);
218 DCHECK(demuxer_stream);
219 streams_.push_back(demuxer_stream);
220 max_duration = std::max(max_duration, demuxer_stream->duration());
221 }
222 }
223 if (streams_.empty()) {
224 host_->Error(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
225 return false;
226 }
227
228 // We have at least one supported stream, set the duration and notify we're
229 // done initializing.
230 host_->SetDuration(max_duration);
231 host_->InitializationComplete();
232 return true;
233 }
234
235 size_t FFmpegDemuxer::GetNumberOfStreams() {
236 return streams_.size();
237 }
238
239 DemuxerStream* FFmpegDemuxer::GetStream(int stream) {
240 DCHECK(stream >= 0);
241 DCHECK(stream < static_cast<int>(streams_.size()));
242 return streams_[stream];
243 }
244
245 void FFmpegDemuxer::Demux() {
246 DCHECK(demuxing_);
247
248 // Loop until we've satisfied every stream.
249 while (StreamsHavePendingReads()) {
250 // Allocate and read an AVPacket from the media.
251 scoped_ptr<AVPacket> packet(new AVPacket());
252 int result = av_read_frame(format_context_, packet.get());
253 if (result < 0) {
254 // TODO(scherkus): handle end of stream by marking Buffer with the end of
255 // stream flag.
256 NOTIMPLEMENTED();
257 break;
258 }
259
260 // Queue the packet with the appropriate stream.
261 DCHECK(packet->stream_index >= 0);
262 DCHECK(packet->stream_index < static_cast<int>(streams_.size()));
263 FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index];
264 demuxer_stream->EnqueuePacket(packet.release());
265 }
266
267 // Finished demuxing.
268 demuxing_ = false;
269 }
270
271 bool FFmpegDemuxer::StreamsHavePendingReads() {
272 StreamVector::iterator iter;
273 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
274 if ((*iter)->HasPendingReads()) {
275 return true;
276 }
277 }
278 return false;
279 }
280
281 } // 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