| 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 |