Index: media/mf/mft_h264_decoder_example.cc |
=================================================================== |
--- media/mf/mft_h264_decoder_example.cc (revision 57106) |
+++ media/mf/mft_h264_decoder_example.cc (working copy) |
@@ -19,24 +19,27 @@ |
#include "base/scoped_comptr_win.h" |
#include "base/scoped_ptr.h" |
#include "base/time.h" |
+#include "media/base/data_buffer.h" |
#include "media/base/media.h" |
#include "media/base/video_frame.h" |
+#include "media/base/yuv_convert.h" |
#include "media/ffmpeg/ffmpeg_common.h" |
#include "media/ffmpeg/file_protocol.h" |
-#include "media/mf/basic_renderer.h" |
-#include "media/mf/d3d_util.h" |
#include "media/mf/file_reader_util.h" |
#include "media/mf/mft_h264_decoder.h" |
using base::AtExitManager; |
using base::Time; |
using base::TimeDelta; |
-using media::BasicRenderer; |
-using media::NullRenderer; |
+using media::Buffer; |
+using media::DataBuffer; |
using media::FFmpegFileReader; |
using media::MftH264Decoder; |
-using media::MftRenderer; |
+using media::VideoCodecConfig; |
+using media::VideoCodecInfo; |
+using media::VideoDecodeEngine; |
using media::VideoFrame; |
+using media::VideoStreamInfo; |
namespace { |
@@ -64,16 +67,6 @@ |
return true; |
} |
-bool InitComLibrary() { |
- HRESULT hr; |
- hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); |
- if (FAILED(hr)) { |
- LOG(ERROR) << "CoInit fail"; |
- return false; |
- } |
- return true; |
-} |
- |
// Creates a window with the given width and height. |
// Returns: A handle to the window on success, NULL otherwise. |
static HWND CreateDrawWindow(int width, int height) { |
@@ -103,6 +96,14 @@ |
LOG(ERROR) << "Failed to create window"; |
return NULL; |
} |
+ RECT rect; |
+ rect.left = 0; |
+ rect.right = width; |
+ rect.top = 0; |
+ rect.bottom = height; |
+ AdjustWindowRect(&rect, kWindowStyleFlags, FALSE); |
+ MoveWindow(window, 0, 0, rect.right - rect.left, rect.bottom - rect.top, |
+ TRUE); |
return window; |
} |
@@ -115,10 +116,8 @@ |
virtual void WillProcessMessage(const MSG& msg) { |
if (msg.message == WM_CHAR && msg.wParam == ' ') { |
- if (!decoder_->Flush()) { |
- LOG(ERROR) << "Flush failed"; |
- } |
// Seek forward 5 seconds. |
+ decoder_->Flush(); |
reader_->SeekForward(5000000); |
} |
} |
@@ -131,17 +130,150 @@ |
MftH264Decoder* decoder_; |
}; |
-static int Run(bool use_dxva, bool render, const std::string& input_file) { |
- // If we are not rendering, we need a window anyway to create a D3D device, |
- // so we will just use the desktop window. (?) |
- HWND window = GetDesktopWindow(); |
- if (render) { |
- window = CreateDrawWindow(640, 480); |
- if (window == NULL) { |
- LOG(ERROR) << "Failed to create window"; |
- return -1; |
+class MftH264DecoderHandler |
+ : public VideoDecodeEngine::EventHandler, |
+ public base::RefCountedThreadSafe<MftH264DecoderHandler> { |
+ public: |
+ MftH264DecoderHandler() : frames_read_(0), frames_decoded_(0) { |
+ memset(&info_, 0, sizeof(info_)); |
+ } |
+ virtual ~MftH264DecoderHandler() {} |
+ virtual void OnInitializeComplete(const VideoCodecInfo& info) { |
+ info_ = info; |
+ } |
+ virtual void OnUninitializeComplete() { |
+ } |
+ virtual void OnFlushComplete() { |
+ } |
+ virtual void OnSeekComplete() {} |
+ virtual void OnError() {} |
+ virtual void OnFormatChange(VideoStreamInfo stream_info) { |
+ info_.stream_info_ = stream_info; |
+ } |
+ virtual void OnEmptyBufferCallback(scoped_refptr<Buffer> buffer) { |
+ if (reader_ && decoder_.get()) { |
+ scoped_refptr<DataBuffer> input; |
+ reader_->Read(&input); |
+ if (!input->IsEndOfStream()) |
+ frames_read_++; |
+ decoder_->EmptyThisBuffer(input); |
} |
} |
+ virtual void OnFillBufferCallback(scoped_refptr<VideoFrame> frame) { |
+ if (frame.get()) { |
+ if (frame->format() != VideoFrame::EMPTY) { |
+ frames_decoded_++; |
+ } |
+ } |
+ } |
+ virtual void SetReader(FFmpegFileReader* reader) { |
+ reader_ = reader; |
+ } |
+ virtual void SetDecoder(scoped_refptr<MftH264Decoder> decoder) { |
+ decoder_ = decoder; |
+ } |
+ virtual void DecodeSingleFrame() { |
+ scoped_refptr<VideoFrame> frame; |
+ decoder_->FillThisBuffer(frame); |
+ } |
+ virtual void Start() { |
+ while (decoder_->state() != MftH264Decoder::kStopped) |
+ DecodeSingleFrame(); |
+ } |
+ |
+ VideoCodecInfo info_; |
+ int frames_read_; |
+ int frames_decoded_; |
+ FFmpegFileReader* reader_; |
+ scoped_refptr<MftH264Decoder> decoder_; |
+}; |
+ |
+class RenderToWindowHandler : public MftH264DecoderHandler { |
+ public: |
+ RenderToWindowHandler(HWND window, MessageLoop* loop) |
+ : MftH264DecoderHandler(), |
+ window_(window), |
+ loop_(loop), |
+ has_output_(false) { |
+ } |
+ virtual ~RenderToWindowHandler() {} |
+ virtual void OnFillBufferCallback(scoped_refptr<VideoFrame> frame) { |
+ has_output_ = true; |
+ if (frame.get()) { |
+ if (frame->format() != VideoFrame::EMPTY) { |
+ frames_decoded_++; |
+ loop_->PostDelayedTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, &RenderToWindowHandler::DecodeSingleFrame), |
+ frame->GetDuration().InMilliseconds()); |
+ |
+ int width = frame->width(); |
+ int height = frame->height(); |
+ |
+ // Assume height does not change. |
+ static uint8* rgb_frame = new uint8[height * frame->stride(0) * 4]; |
+ uint8* frame_y = static_cast<uint8*>(frame->data(VideoFrame::kYPlane)); |
+ uint8* frame_u = static_cast<uint8*>(frame->data(VideoFrame::kUPlane)); |
+ uint8* frame_v = static_cast<uint8*>(frame->data(VideoFrame::kVPlane)); |
+ media::ConvertYUVToRGB32(frame_y, frame_v, frame_u, rgb_frame, |
+ width, (height + 15) & ~15, |
+ frame->stride(0), frame->stride(1), |
+ 4 * frame->stride(0), media::YV12); |
+ PAINTSTRUCT ps; |
+ InvalidateRect(window_, NULL, TRUE); |
+ HDC hdc = BeginPaint(window_, &ps); |
+ BITMAPINFOHEADER hdr; |
+ hdr.biSize = sizeof(BITMAPINFOHEADER); |
+ hdr.biWidth = width; |
+ hdr.biHeight = -height; // minus means top-down bitmap |
+ hdr.biPlanes = 1; |
+ hdr.biBitCount = 32; |
+ hdr.biCompression = BI_RGB; // no compression |
+ hdr.biSizeImage = 0; |
+ hdr.biXPelsPerMeter = 1; |
+ hdr.biYPelsPerMeter = 1; |
+ hdr.biClrUsed = 0; |
+ hdr.biClrImportant = 0; |
+ int rv = StretchDIBits(hdc, 0, 0, width, height, 0, 0, width, height, |
+ rgb_frame, reinterpret_cast<BITMAPINFO*>(&hdr), |
+ DIB_RGB_COLORS, SRCCOPY); |
+ EndPaint(window_, &ps); |
+ if (!rv) { |
+ LOG(ERROR) << "StretchDIBits failed"; |
+ loop_->QuitNow(); |
+ } |
+ } else { // if frame is type EMPTY, there will be no more frames. |
+ loop_->QuitNow(); |
+ } |
+ } |
+ } |
+ virtual void DecodeSingleFrame() { |
+ if (decoder_->state() != MftH264Decoder::kStopped) { |
+ while (decoder_->state() != MftH264Decoder::kStopped && !has_output_) { |
+ scoped_refptr<VideoFrame> frame; |
+ decoder_->FillThisBuffer(frame); |
+ } |
+ if (decoder_->state() == MftH264Decoder::kStopped) |
+ loop_->QuitNow(); |
+ has_output_ = false; |
+ } else { |
+ loop_->QuitNow(); |
+ } |
+ } |
+ virtual void Start() { |
+ loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, &RenderToWindowHandler::DecodeSingleFrame)); |
+ loop_->Run(); |
+ } |
+ |
+ private: |
+ HWND window_; |
+ MessageLoop* loop_; |
+ bool has_output_; |
+}; |
+ |
+static int Run(bool use_dxva, bool render, const std::string& input_file) { |
scoped_ptr<FFmpegFileReader> reader(new FFmpegFileReader(input_file)); |
if (reader.get() == NULL || !reader->Initialize()) { |
LOG(ERROR) << "Failed to create/initialize reader"; |
@@ -151,86 +283,53 @@ |
if (!reader->GetWidth(&width) || !reader->GetHeight(&height)) { |
LOG(WARNING) << "Failed to get width/height from reader"; |
} |
- int aspect_ratio_num = 0, aspect_ratio_denom = 0; |
- if (!reader->GetAspectRatio(&aspect_ratio_num, &aspect_ratio_denom)) { |
- LOG(WARNING) << "Failed to get aspect ratio"; |
- } |
- int frame_rate_num = 0, frame_rate_denom = 0; |
- if (!reader->GetFrameRate(&frame_rate_num, &frame_rate_denom)) { |
- LOG(WARNING) << "Failed to get frame rate"; |
- } |
- ScopedComPtr<IDirect3D9> d3d9; |
- ScopedComPtr<IDirect3DDevice9> device; |
- ScopedComPtr<IDirect3DDeviceManager9> dev_manager; |
- if (use_dxva) { |
- dev_manager.Attach(media::CreateD3DDevManager(window, |
- d3d9.Receive(), |
- device.Receive())); |
- if (dev_manager.get() == NULL) { |
- LOG(ERROR) << "Cannot create D3D9 manager"; |
+ VideoCodecConfig config; |
+ config.width_ = width; |
+ config.height_ = height; |
+ HWND window = NULL; |
+ if (render) { |
+ window = CreateDrawWindow(width, height); |
+ if (window == NULL) { |
+ LOG(ERROR) << "Failed to create window"; |
return -1; |
} |
} |
+ |
scoped_refptr<MftH264Decoder> mft(new MftH264Decoder(use_dxva)); |
- scoped_refptr<MftRenderer> renderer; |
- if (render) { |
- renderer = new BasicRenderer(mft.get(), window, device); |
- } else { |
- renderer = new NullRenderer(mft.get()); |
- } |
- if (mft.get() == NULL) { |
- LOG(ERROR) << "Failed to create fake renderer / MFT"; |
+ if (!mft.get()) { |
+ LOG(ERROR) << "Failed to create fake MFT"; |
return -1; |
} |
- if (!mft->Init(dev_manager, |
- frame_rate_num, frame_rate_denom, |
- width, height, |
- aspect_ratio_num, aspect_ratio_denom, |
- NewCallback(reader.get(), &FFmpegFileReader::Read), |
- NewCallback(renderer.get(), &MftRenderer::ProcessFrame), |
- NewCallback(renderer.get(), |
- &MftRenderer::OnDecodeError))) { |
- LOG(ERROR) << "Failed to initialize mft"; |
+ |
+ scoped_refptr<MftH264DecoderHandler> handler; |
+ if (render) |
+ handler = new RenderToWindowHandler(window, MessageLoop::current()); |
+ else |
+ handler = new MftH264DecoderHandler(); |
+ handler->SetDecoder(mft); |
+ handler->SetReader(reader.get()); |
+ if (!handler.get()) { |
+ LOG(ERROR) << "FAiled to create handler"; |
return -1; |
} |
+ |
+ mft->Initialize(MessageLoop::current(), handler.get(), config); |
scoped_ptr<WindowObserver> observer; |
// If rendering, resize the window to fit the video frames. |
if (render) { |
- RECT rect; |
- rect.left = 0; |
- rect.right = mft->width(); |
- rect.top = 0; |
- rect.bottom = mft->height(); |
- AdjustWindowRect(&rect, kWindowStyleFlags, FALSE); |
- if (!MoveWindow(window, 0, 0, rect.right - rect.left, |
- rect.bottom - rect.top, TRUE)) { |
- LOG(WARNING) << "Warning: Failed to resize window"; |
- } |
observer.reset(new WindowObserver(reader.get(), mft.get())); |
MessageLoopForUI::current()->AddObserver(observer.get()); |
} |
- if (use_dxva) { |
- // Reset the device's back buffer dimensions to match the window's |
- // dimensions. |
- if (!media::AdjustD3DDeviceBackBufferDimensions(device.get(), |
- window, |
- mft->width(), |
- mft->height())) { |
- LOG(WARNING) << "Warning: Failed to reset device to have correct " |
- << "backbuffer dimension, scaling might occur"; |
- } |
- } |
+ |
Time decode_start(Time::Now()); |
- |
- MessageLoopForUI::current()->PostTask(FROM_HERE, |
- NewRunnableMethod(renderer.get(), &MftRenderer::StartPlayback)); |
- MessageLoopForUI::current()->Run(NULL); |
- |
+ handler->Start(); |
TimeDelta decode_time = Time::Now() - decode_start; |
printf("All done, frames read: %d, frames decoded: %d\n", |
- mft->frames_read(), mft->frames_decoded()); |
+ handler->frames_read_, handler->frames_decoded_); |
printf("Took %lldms\n", decode_time.InMilliseconds()); |
+ if (window) |
+ DestroyWindow(window); |
return 0; |
} |
@@ -266,10 +365,6 @@ |
LOG(ERROR) << "InitFFMpeg() failed"; |
return -1; |
} |
- if (!InitComLibrary()) { |
- LOG(ERROR) << "InitComLibraries() failed"; |
- return -1; |
- } |
int ret = Run(use_dxva, render, input_file); |
printf("Done\n"); |