OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this |
| 2 // source code is governed by a BSD-style license that can be found in the |
| 3 // LICENSE file. |
| 4 |
| 5 |
| 6 #include "chrome/renderer/media/ipc_video_decoder.h" |
| 7 |
| 8 #include "base/task.h" |
| 9 #include "media/base/callback.h" |
| 10 #include "media/base/filters.h" |
| 11 #include "media/base/filter_host.h" |
| 12 #include "media/base/limits.h" |
| 13 #include "media/base/media_format.h" |
| 14 #include "media/base/video_frame.h" |
| 15 #include "media/ffmpeg/ffmpeg_common.h" |
| 16 #include "media/ffmpeg/ffmpeg_util.h" |
| 17 #include "media/filters/ffmpeg_interfaces.h" |
| 18 |
| 19 namespace media { |
| 20 |
| 21 IpcVideoDecoder::IpcVideoDecoder(MessageLoop* message_loop) |
| 22 : width_(0), |
| 23 height_(0), |
| 24 state_(kUnInitialized), |
| 25 pending_reads_(0), |
| 26 pending_requests_(0), |
| 27 renderer_thread_message_loop_(message_loop) { |
| 28 } |
| 29 |
| 30 IpcVideoDecoder::~IpcVideoDecoder() { |
| 31 } |
| 32 |
| 33 void IpcVideoDecoder::Initialize(DemuxerStream* demuxer_stream, |
| 34 FilterCallback* callback) { |
| 35 if (MessageLoop::current() != renderer_thread_message_loop_) { |
| 36 renderer_thread_message_loop_->PostTask( |
| 37 FROM_HERE, |
| 38 NewRunnableMethod(this, |
| 39 &IpcVideoDecoder::Initialize, |
| 40 demuxer_stream, |
| 41 callback)); |
| 42 return; |
| 43 } |
| 44 |
| 45 CHECK(!demuxer_stream_); |
| 46 demuxer_stream_ = demuxer_stream; |
| 47 initialize_callback_.reset(callback); |
| 48 |
| 49 // Get the AVStream by querying for the provider interface. |
| 50 AVStreamProvider* av_stream_provider; |
| 51 if (!demuxer_stream->QueryInterface(&av_stream_provider)) { |
| 52 GpuVideoDecoderInitDoneParam param; |
| 53 OnInitializeDone(false, param); |
| 54 return; |
| 55 } |
| 56 |
| 57 AVStream* av_stream = av_stream_provider->GetAVStream(); |
| 58 width_ = av_stream->codec->width; |
| 59 height_ = av_stream->codec->height; |
| 60 |
| 61 // Create hardware decoder instance. |
| 62 GpuVideoServiceHost* gpu_video_service_host = GpuVideoServiceHost::get(); |
| 63 gpu_video_decoder_host_ = gpu_video_service_host->CreateVideoDecoder(this); |
| 64 |
| 65 // Initialize hardware decoder. |
| 66 GpuVideoDecoderInitParam param; |
| 67 param.width_ = width_; |
| 68 param.height_ = height_; |
| 69 if (!gpu_video_decoder_host_->Initialize(param)) { |
| 70 GpuVideoDecoderInitDoneParam param; |
| 71 OnInitializeDone(false, param); |
| 72 } |
| 73 } |
| 74 |
| 75 void IpcVideoDecoder::OnInitializeDone( |
| 76 bool success, const GpuVideoDecoderInitDoneParam& param) { |
| 77 if (MessageLoop::current() != renderer_thread_message_loop_) { |
| 78 renderer_thread_message_loop_->PostTask( |
| 79 FROM_HERE, |
| 80 NewRunnableMethod(this, |
| 81 &IpcVideoDecoder::OnInitializeDone, |
| 82 success, |
| 83 param)); |
| 84 return; |
| 85 } |
| 86 |
| 87 AutoCallbackRunner done_runner(initialize_callback_.release()); |
| 88 |
| 89 if (success) { |
| 90 media_format_.SetAsString(MediaFormat::kMimeType, |
| 91 mime_type::kUncompressedVideo); |
| 92 media_format_.SetAsInteger(MediaFormat::kWidth, width_); |
| 93 media_format_.SetAsInteger(MediaFormat::kHeight, height_); |
| 94 media_format_.SetAsInteger(MediaFormat::kSurfaceType, |
| 95 static_cast<int>(param.surface_type_)); |
| 96 media_format_.SetAsInteger(MediaFormat::kSurfaceFormat, |
| 97 static_cast<int>(param.format_)); |
| 98 state_ = kPlaying; |
| 99 } else { |
| 100 LOG(ERROR) << "IpcVideoDecoder initialization failed!"; |
| 101 host()->SetError(PIPELINE_ERROR_DECODE); |
| 102 } |
| 103 } |
| 104 |
| 105 void IpcVideoDecoder::Stop(FilterCallback* callback) { |
| 106 if (MessageLoop::current() != renderer_thread_message_loop_) { |
| 107 renderer_thread_message_loop_->PostTask( |
| 108 FROM_HERE, |
| 109 NewRunnableMethod(this, |
| 110 &IpcVideoDecoder::Stop, |
| 111 callback)); |
| 112 return; |
| 113 } |
| 114 |
| 115 stop_callback_.reset(callback); |
| 116 if (!gpu_video_decoder_host_->Uninitialize()) { |
| 117 LOG(ERROR) << "gpu video decoder destroy failed"; |
| 118 IpcVideoDecoder::OnUninitializeDone(); |
| 119 } |
| 120 } |
| 121 |
| 122 void IpcVideoDecoder::OnUninitializeDone() { |
| 123 if (MessageLoop::current() != renderer_thread_message_loop_) { |
| 124 renderer_thread_message_loop_->PostTask( |
| 125 FROM_HERE, |
| 126 NewRunnableMethod(this, |
| 127 &IpcVideoDecoder::OnUninitializeDone)); |
| 128 return; |
| 129 } |
| 130 |
| 131 AutoCallbackRunner done_runner(stop_callback_.release()); |
| 132 |
| 133 state_ = kStopped; |
| 134 } |
| 135 |
| 136 void IpcVideoDecoder::Pause(FilterCallback* callback) { |
| 137 Flush(callback); // TODO(jiesun): move this to flush(). |
| 138 } |
| 139 |
| 140 void IpcVideoDecoder::Flush(FilterCallback* callback) { |
| 141 if (MessageLoop::current() != renderer_thread_message_loop_) { |
| 142 renderer_thread_message_loop_->PostTask( |
| 143 FROM_HERE, |
| 144 NewRunnableMethod(this, |
| 145 &IpcVideoDecoder::Flush, |
| 146 callback)); |
| 147 return; |
| 148 } |
| 149 |
| 150 state_ = kFlushing; |
| 151 |
| 152 flush_callback_.reset(callback); |
| 153 |
| 154 if (!gpu_video_decoder_host_->Flush()) { |
| 155 LOG(ERROR) << "gpu video decoder flush failed"; |
| 156 OnFlushDone(); |
| 157 } |
| 158 } |
| 159 |
| 160 void IpcVideoDecoder::OnFlushDone() { |
| 161 if (MessageLoop::current() != renderer_thread_message_loop_) { |
| 162 renderer_thread_message_loop_->PostTask( |
| 163 FROM_HERE, |
| 164 NewRunnableMethod(this, |
| 165 &IpcVideoDecoder::OnFlushDone)); |
| 166 return; |
| 167 } |
| 168 |
| 169 if (pending_reads_ == 0 && pending_requests_ == 0 && flush_callback_.get()) { |
| 170 flush_callback_->Run(); |
| 171 flush_callback_.reset(); |
| 172 } |
| 173 } |
| 174 |
| 175 void IpcVideoDecoder::Seek(base::TimeDelta time, FilterCallback* callback) { |
| 176 if (MessageLoop::current() != renderer_thread_message_loop_) { |
| 177 renderer_thread_message_loop_->PostTask( |
| 178 FROM_HERE, |
| 179 NewRunnableMethod(this, |
| 180 &IpcVideoDecoder::Seek, |
| 181 time, |
| 182 callback)); |
| 183 return; |
| 184 } |
| 185 |
| 186 OnSeekComplete(callback); |
| 187 } |
| 188 |
| 189 void IpcVideoDecoder::OnSeekComplete(FilterCallback* callback) { |
| 190 if (MessageLoop::current() != renderer_thread_message_loop_) { |
| 191 renderer_thread_message_loop_->PostTask( |
| 192 FROM_HERE, |
| 193 NewRunnableMethod(this, |
| 194 &IpcVideoDecoder::OnSeekComplete, |
| 195 callback)); |
| 196 return; |
| 197 } |
| 198 |
| 199 AutoCallbackRunner done_runner(callback); |
| 200 |
| 201 state_ = kPlaying; |
| 202 |
| 203 for (int i = 0; i < 20; ++i) { |
| 204 demuxer_stream_->Read( |
| 205 NewCallback(this, |
| 206 &IpcVideoDecoder::OnReadComplete)); |
| 207 ++pending_reads_; |
| 208 } |
| 209 } |
| 210 |
| 211 void IpcVideoDecoder::OnReadComplete(Buffer* buffer) { |
| 212 scoped_refptr<Buffer> buffer_ref = buffer; |
| 213 ReadCompleteTask(buffer_ref); |
| 214 } |
| 215 |
| 216 void IpcVideoDecoder::ReadCompleteTask(scoped_refptr<Buffer> buffer) { |
| 217 if (MessageLoop::current() != renderer_thread_message_loop_) { |
| 218 renderer_thread_message_loop_->PostTask( |
| 219 FROM_HERE, |
| 220 NewRunnableMethod(this, |
| 221 &IpcVideoDecoder::ReadCompleteTask, |
| 222 buffer)); |
| 223 return; |
| 224 } |
| 225 |
| 226 DCHECK_GT(pending_reads_, 0u); |
| 227 --pending_reads_; |
| 228 |
| 229 if (state_ == kStopped || state_ == kEnded) { |
| 230 // Just discard the input buffers |
| 231 return; |
| 232 } |
| 233 |
| 234 if (state_ == kFlushing) { |
| 235 if (pending_reads_ == 0 && pending_requests_ == 0) { |
| 236 CHECK(flush_callback_.get()); |
| 237 flush_callback_->Run(); |
| 238 flush_callback_.reset(); |
| 239 state_ = kPlaying; |
| 240 } |
| 241 return; |
| 242 } |
| 243 // Transition to kFlushCodec on the first end of input stream buffer. |
| 244 if (state_ == kPlaying && buffer->IsEndOfStream()) { |
| 245 state_ = kFlushCodec; |
| 246 } |
| 247 |
| 248 gpu_video_decoder_host_->EmptyThisBuffer(buffer); |
| 249 } |
| 250 |
| 251 void IpcVideoDecoder::FillThisBuffer(scoped_refptr<VideoFrame> video_frame) { |
| 252 if (MessageLoop::current() != renderer_thread_message_loop_) { |
| 253 renderer_thread_message_loop_->PostTask( |
| 254 FROM_HERE, |
| 255 NewRunnableMethod(this, |
| 256 &IpcVideoDecoder::FillThisBuffer, |
| 257 video_frame)); |
| 258 return; |
| 259 } |
| 260 |
| 261 // Synchronized flushing before stop should prevent this. |
| 262 CHECK_NE(state_, kStopped); |
| 263 |
| 264 // Notify decode engine the available of new frame. |
| 265 ++pending_requests_; |
| 266 gpu_video_decoder_host_->FillThisBuffer(video_frame); |
| 267 } |
| 268 |
| 269 void IpcVideoDecoder::OnFillBufferDone(scoped_refptr<VideoFrame> video_frame) { |
| 270 if (MessageLoop::current() != renderer_thread_message_loop_) { |
| 271 renderer_thread_message_loop_->PostTask( |
| 272 FROM_HERE, |
| 273 NewRunnableMethod(this, |
| 274 &IpcVideoDecoder::OnFillBufferDone, |
| 275 video_frame)); |
| 276 return; |
| 277 } |
| 278 |
| 279 if (video_frame.get()) { |
| 280 --pending_requests_; |
| 281 fill_buffer_done_callback()->Run(video_frame); |
| 282 if (state_ == kFlushing && pending_reads_ == 0 && pending_requests_ == 0) { |
| 283 CHECK(flush_callback_.get()); |
| 284 flush_callback_->Run(); |
| 285 flush_callback_.reset(); |
| 286 state_ = kPlaying; |
| 287 } |
| 288 |
| 289 } else { |
| 290 if (state_ == kFlushCodec) { |
| 291 // When in kFlushCodec, any errored decode, or a 0-lengthed frame, |
| 292 // is taken as a signal to stop decoding. |
| 293 state_ = kEnded; |
| 294 scoped_refptr<VideoFrame> video_frame; |
| 295 VideoFrame::CreateEmptyFrame(&video_frame); |
| 296 fill_buffer_done_callback()->Run(video_frame); |
| 297 } |
| 298 } |
| 299 } |
| 300 |
| 301 void IpcVideoDecoder::OnEmptyBufferDone(scoped_refptr<Buffer> buffer) { |
| 302 if (MessageLoop::current() != renderer_thread_message_loop_) { |
| 303 renderer_thread_message_loop_->PostTask( |
| 304 FROM_HERE, |
| 305 NewRunnableMethod(this, |
| 306 &IpcVideoDecoder::OnEmptyBufferDone, |
| 307 buffer)); |
| 308 return; |
| 309 } |
| 310 |
| 311 // TODO(jiesun): We haven't recycle input buffer yet. |
| 312 demuxer_stream_->Read(NewCallback(this, &IpcVideoDecoder::OnReadComplete)); |
| 313 ++pending_reads_; |
| 314 } |
| 315 |
| 316 void IpcVideoDecoder::OnDeviceError() { |
| 317 host()->SetError(PIPELINE_ERROR_DECODE); |
| 318 } |
| 319 |
| 320 bool IpcVideoDecoder::ProvidesBuffer() { |
| 321 return true; |
| 322 } |
| 323 |
| 324 // static |
| 325 FilterFactory* IpcVideoDecoder::CreateFactory(MessageLoop* message_loop) { |
| 326 return new FilterFactoryImpl1<IpcVideoDecoder, MessageLoop*>(message_loop); |
| 327 } |
| 328 |
| 329 // static |
| 330 bool IpcVideoDecoder::IsMediaFormatSupported(const MediaFormat& format) { |
| 331 std::string mime_type; |
| 332 if (!format.GetAsString(MediaFormat::kMimeType, &mime_type) && |
| 333 mime_type::kFFmpegVideo != mime_type) |
| 334 return false; |
| 335 |
| 336 // TODO(jiesun): Although we current only support H264 hardware decoding, |
| 337 // in the future, we should query GpuVideoService for capabilities. |
| 338 int codec_id; |
| 339 return format.GetAsInteger(MediaFormat::kFFmpegCodecID, &codec_id) && |
| 340 codec_id == CODEC_ID_H264; |
| 341 } |
| 342 |
| 343 } // namespace media |
| 344 |
OLD | NEW |