OLD | NEW |
(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 |
OLD | NEW |