summaryrefslogtreecommitdiffstats
path: root/media/player
diff options
context:
space:
mode:
authorfbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-28 01:20:14 +0000
committerfbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-28 01:20:14 +0000
commit3df393b3ff90f86df612eb995511c81851e088b2 (patch)
treeabad903b52dc73a47df23be3e99ff17db30713fc /media/player
parent41886ac8cee8bf449559aff1bd2de89bc73958a4 (diff)
downloadchromium_src-3df393b3ff90f86df612eb995511c81851e088b2.zip
chromium_src-3df393b3ff90f86df612eb995511c81851e088b2.tar.gz
chromium_src-3df393b3ff90f86df612eb995511c81851e088b2.tar.bz2
media player view window for painting frames to the display with YUV conversion and scaling.
Review URL: http://codereview.chromium.org/100075 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@14701 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/player')
-rw-r--r--media/player/view.h392
1 files changed, 392 insertions, 0 deletions
diff --git a/media/player/view.h b/media/player/view.h
new file mode 100644
index 0000000..fb4c5b4
--- /dev/null
+++ b/media/player/view.h
@@ -0,0 +1,392 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+// View.h : Paints the current movie frame (with scaling) to the display.
+// TODO(fbarchard): Consider rewriting as view.cc and view.h
+
+#ifndef MEDIA_PLAYER_VIEW_H_
+#define MEDIA_PLAYER_VIEW_H_
+
+// Enable timing code by turning on TESTING macro.
+// #define TESTING 1
+
+#ifdef TESTING
+#define _CRT_SECURE_NO_WARNINGS
+#include <windows.h>
+#include <stdio.h>
+#include <process.h>
+#include <string.h>
+#endif
+
+#include <atlscrl.h>
+
+#include "base/basictypes.h"
+#include "media/base/buffers.h"
+#include "media/base/factory.h"
+#include "media/base/filters.h"
+#include "media/base/yuv_convert.h"
+#include "media/base/yuv_scale.h"
+#include "media/player/wtl_renderer.h"
+
+#include "media/player/movie.h"
+
+#ifdef TESTING
+// Fetch current time as milliseconds.
+// Return as double for high duration and precision.
+static inline double GetTime() {
+ LARGE_INTEGER perf_time, perf_hz;
+ QueryPerformanceFrequency(&perf_hz); // May change with speed step.
+ QueryPerformanceCounter(&perf_time);
+ return perf_time.QuadPart * 1000.0 / perf_hz.QuadPart; // Convert to ms.
+}
+#endif
+
+extern bool g_enableswscaler;
+extern bool g_enabledraw;
+extern bool g_enabledump_yuv_file;
+extern int g_view_size;
+
+class WtlVideoWindow : public CScrollWindowImpl<WtlVideoWindow> {
+ public:
+ DECLARE_WND_CLASS_EX(NULL, 0, -1)
+
+ BEGIN_MSG_MAP(WtlVideoWindow)
+ MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
+ CHAIN_MSG_MAP(CScrollWindowImpl<WtlVideoWindow>);
+ END_MSG_MAP()
+
+ WtlVideoWindow() {
+ size_.cx = 0;
+ size_.cy = 0;
+ renderer_ = new WtlVideoRenderer(this);
+ last_frame_ = NULL;
+ }
+
+ BOOL PreTranslateMessage(MSG* /*msg*/) {
+ return FALSE;
+ }
+
+ void AllocateVideoBitmap(CDCHandle dc) {
+ // See note on SetSize for why we check size_.cy.
+ if (bmp_.IsNull() && size_.cy > 0) {
+ BITMAPINFO bmi;
+ bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
+ bmi.bmiHeader.biWidth = size_.cx;
+ bmi.bmiHeader.biHeight = size_.cy;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = 0;
+ bmi.bmiHeader.biXPelsPerMeter = 100;
+ bmi.bmiHeader.biYPelsPerMeter = 100;
+ bmi.bmiHeader.biClrUsed = 0;
+ bmi.bmiHeader.biClrImportant = 0;
+ void* pBits;
+ bmp_.CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &pBits, NULL, 0);
+ SetScrollOffset(0, 0, FALSE);
+ SetScrollSize(size_);
+ }
+ }
+
+ // Called on the video renderer's thread.
+ // Note that AllocateVideoBitmap examines the size_.cy value to determine
+ // if a bitmap should be allocated, so we set it last to avoid a race
+ // condition.
+ void SetSize(int cx, int cy) {
+ size_.cx = cx;
+ size_.cy = cy;
+ }
+
+ void Reset() {
+ if (!bmp_.IsNull()) {
+ bmp_.DeleteObject();
+ }
+ size_.cx = 0;
+ size_.cy = 0;
+ // TODO(frank): get rid of renderer at reset too.
+ }
+
+ LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/,
+ BOOL& /*bHandled*/) {
+ CDCHandle dc = reinterpret_cast<HDC>(wParam);
+ AllocateVideoBitmap(dc);
+ RECT rect;
+ GetClientRect(&rect);
+ int x = 0;
+ int y = 0;
+ if (!bmp_.IsNull()) {
+ x = size_.cx + 1;
+ y = size_.cy + 1;
+ }
+ if (rect.right > m_sizeAll.cx) {
+ RECT rectRight = rect;
+ rectRight.left = x;
+ rectRight.bottom = y;
+ dc.FillRect(&rectRight, COLOR_WINDOW);
+ }
+ if (rect.bottom > m_sizeAll.cy) {
+ RECT rectBottom = rect;
+ rectBottom.top = y;
+ dc.FillRect(&rectBottom, COLOR_WINDOW);
+ }
+ if (!bmp_.IsNull()) {
+ dc.MoveTo(size_.cx, 0);
+ dc.LineTo(size_.cx, size_.cy);
+ dc.LineTo(0, size_.cy);
+ }
+ return 0;
+ }
+
+ // Convert the video frame to RGB and Blit.
+ void ConvertFrame(media::VideoFrame * video_frame) {
+ media::VideoSurface frame_in;
+ bool lock_result = video_frame->Lock(&frame_in);
+ DCHECK(lock_result);
+ BITMAP bm;
+ bmp_.GetBitmap(&bm);
+ int dibwidth = bm.bmWidth;
+ int dibheight = bm.bmHeight;
+
+ uint8 *movie_dib_bits = reinterpret_cast<uint8 *>(bm.bmBits) +
+ bm.bmWidthBytes * (bm.bmHeight - 1);
+ int dibrowbytes = -bm.bmWidthBytes;
+ int clipped_width = frame_in.width;
+ if (dibwidth < clipped_width) {
+ clipped_width = dibwidth;
+ }
+ int clipped_height = frame_in.height;
+ if (dibheight < clipped_height) {
+ clipped_height = dibheight;
+ }
+
+ int scaled_width = clipped_width;
+ int scaled_height = clipped_height;
+ switch (g_view_size) {
+ case 0:
+ scaled_width = clipped_width / 2;
+ scaled_height = clipped_height / 2;
+ break;
+
+ case 1:
+ default: // Assume 1:1 for stray view sizes
+ scaled_width = clipped_width;
+ scaled_height = clipped_height;
+ break;
+
+ case 2:
+ scaled_width = clipped_width;
+ scaled_height = clipped_height;
+ clipped_width = scaled_width / 2;
+ clipped_height = scaled_height / 2;
+ break;
+ }
+
+ // Append each frame to end of file.
+ if (g_enabledump_yuv_file) {
+ DumpYUV(frame_in);
+ }
+
+#ifdef TESTING
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
+ double yuvtimestart = GetTime(); // Start timer.
+#endif
+
+ if (g_enabledraw) {
+ if (g_enableswscaler) {
+ uint8* data_out[3];
+ int stride_out[3];
+ data_out[0] = movie_dib_bits;
+ data_out[1] = NULL;
+ data_out[2] = NULL;
+ stride_out[0] = dibrowbytes;
+ stride_out[1] = 0;
+ stride_out[2] = 0;
+
+ /*
+ if (!sws_context_) {
+ DCHECK(frame_in.format == VideoSurface::YV12);
+ int outtype = bm.bmBitsPixel == 32 ? PIX_FMT_RGB32 : PIX_FMT_RGB24;
+ sws_context_ = sws_getContext(frame_in.width, frame_in.height,
+ PIX_FMT_YUV420P, width_, height_,
+ outtype, SWS_FAST_BILINEAR,
+ NULL, NULL, NULL);
+ DCHECK(sws_context_);
+ }
+
+ sws_scale(sws_context_, frame_in.data, frame_in.strides, 0,
+ height_, data_out, stride_out);
+ */
+ } else {
+ DCHECK(bm.bmBitsPixel == 32);
+ DrawYUV(frame_in,
+ movie_dib_bits,
+ dibrowbytes,
+ clipped_width,
+ clipped_height,
+ scaled_width,
+ scaled_height);
+ }
+ }
+#ifdef TESTING
+ double yuvtimeend = GetTime(); // Start timer.
+ SSetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
+ static int yuvtimecount = 0;
+ static double yuvtimesum = 0;
+ yuvtimesum += yuvtimeend - yuvtimestart;
+ ++yuvtimecount;
+
+ char outputbuf[512];
+ snprintf(outputbuf, sizeof(outputbuf), "yuv %.2fms avg %.2fms\n",
+ yuvtimeend - yuvtimestart, yuvtimesum / yuvtimecount);
+ OutputDebugStringA(outputbuf);
+#endif
+ }
+
+ void DoPaint(CDCHandle dc) {
+ AllocateVideoBitmap(dc);
+ if (!bmp_.IsNull()) {
+ scoped_refptr<media::VideoFrame> frame;
+ renderer_->GetCurrentFrame(&frame);
+ if (frame.get()) {
+ base::TimeDelta frame_timestamp = frame->GetTimestamp();
+ if (frame != last_frame_ || frame_timestamp != last_timestamp_) {
+ last_frame_ = frame;
+ last_timestamp_ = frame_timestamp;
+ ConvertFrame(frame);
+ }
+ frame = NULL;
+ }
+
+#ifdef TESTING
+ double paint_time_start = GetTime();
+ static double paint_time_previous = 0;
+ if (!paint_time_previous)
+ paint_time_previous = paint_time_start;
+#endif
+ CDC dcMem;
+ dcMem.CreateCompatibleDC(dc);
+ HBITMAP hBmpOld = dcMem.SelectBitmap(bmp_);
+ dc.BitBlt(0, 0, size_.cx, size_.cy, dcMem, 0, 0, SRCCOPY);
+ dcMem.SelectBitmap(hBmpOld);
+#ifdef TESTING
+ double paint_time_end = GetTime();
+ static int paint_count = 0;
+ static double paint_time_sum = 0;
+ paint_time_sum += paint_time_end-paint_time_start;
+ ++paint_count;
+ char outputbuf[512];
+ snprintf(outputbuf, sizeof(outputbuf),
+ "paint time %5.2fms blit %5.2fms avg %5.2fms\n",
+ paint_time_start-paint_time_previous,
+ paint_time_end-paint_time_start,
+ paint_time_sum/paint_count);
+ OutputDebugStringA(outputbuf);
+
+ paint_time_previous = paint_time_start;
+#endif
+ }
+ } // End of DoPaint function.
+
+ CBitmap bmp_; // Used by mainfrm.h.
+ SIZE size_; // Used by WtlVideoWindow.
+ scoped_refptr<WtlVideoRenderer> renderer_; // Used by WtlVideoWindow.
+
+ private:
+
+ // Draw a frame of YUV to an RGB buffer with scaling.
+ // Handles different YUV formats.
+ void DrawYUV(const media::VideoSurface &frame_in,
+ uint8 *movie_dib_bits,
+ int dibrowbytes,
+ int clipped_width,
+ int clipped_height,
+ int scaled_width,
+ int scaled_height) {
+ // Normal size
+ if (g_view_size == 1) {
+ if (frame_in.format == media::VideoSurface::YV16) {
+ // Temporary cast, til we use uint8 for VideoFrame.
+ media::ConvertYV16ToRGB32((const uint8*)frame_in.data[0],
+ (const uint8*)frame_in.data[1],
+ (const uint8*)frame_in.data[2],
+ movie_dib_bits,
+ clipped_width, clipped_height,
+ frame_in.strides[0],
+ frame_in.strides[1],
+ dibrowbytes);
+ } else {
+ // Temporary cast, til we use uint8 for VideoFrame.
+ media::ConvertYV12ToRGB32((const uint8*)frame_in.data[0],
+ (const uint8*)frame_in.data[1],
+ (const uint8*)frame_in.data[2],
+ movie_dib_bits,
+ clipped_width, clipped_height,
+ frame_in.strides[0],
+ frame_in.strides[1],
+ dibrowbytes);
+ }
+ } else {
+ if (frame_in.format == media::VideoSurface::YV16) {
+ // Temporary cast, til we use uint8 for VideoFrame.
+ media::ScaleYV16ToRGB32((const uint8*)frame_in.data[0],
+ (const uint8*)frame_in.data[1],
+ (const uint8*)frame_in.data[2],
+ movie_dib_bits,
+ clipped_width, clipped_height,
+ scaled_width, scaled_height,
+ frame_in.strides[0],
+ frame_in.strides[1],
+ dibrowbytes);
+ } else {
+ // Temporary cast, til we use uint8 for VideoFrame.
+ media::ScaleYV12ToRGB32((const uint8*)frame_in.data[0],
+ (const uint8*)frame_in.data[1],
+ (const uint8*)frame_in.data[2],
+ movie_dib_bits,
+ clipped_width, clipped_height,
+ scaled_width, scaled_height,
+ frame_in.strides[0],
+ frame_in.strides[1],
+ dibrowbytes);
+ }
+ }
+ }
+
+ // Diagnostic function to write out YUV in format compatible with PYUV tool.
+ void DumpYUV(const media::VideoSurface &frame_in) {
+ FILE * file_yuv = fopen("raw.yuv", "ab+"); // Open for append binary.
+ if (file_yuv != NULL) {
+ fseek(file_yuv, 0, SEEK_END);
+ const size_t frame_size = frame_in.width * frame_in.height;
+ for (size_t y = 0; y < frame_in.height; ++y)
+ fwrite(frame_in.data[0]+frame_in.strides[0]*y,
+ frame_in.width, sizeof(uint8), file_yuv);
+ for (size_t y = 0; y < frame_in.height/2; ++y)
+ fwrite(frame_in.data[1]+frame_in.strides[1]*y,
+ frame_in.width/2, sizeof(uint8), file_yuv);
+ for (size_t y = 0; y < frame_in.height/2; ++y)
+ fwrite(frame_in.data[2]+frame_in.strides[2]*y,
+ frame_in.width/2, sizeof(uint8), file_yuv);
+ fclose(file_yuv);
+
+#if TESTING
+ static int frame_dump_count = 0;
+ char outputbuf[512];
+ snprintf(outputbuf, sizeof(outputbuf), "yuvdump %4d %dx%d stride %d\n",
+ frame_dump_count, frame_in.width, frame_in.height,
+ frame_in.strides[0]);
+ OutputDebugStringA(outputbuf);
+ ++frame_dump_count;
+#endif
+ }
+ }
+
+ media::VideoFrame* last_frame_;
+ base::TimeDelta last_timestamp_;
+
+ DISALLOW_COPY_AND_ASSIGN(WtlVideoWindow);
+};
+
+#endif // MEDIA_PLAYER_VIEW_H_
+