diff options
author | fbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-28 01:20:14 +0000 |
---|---|---|
committer | fbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-28 01:20:14 +0000 |
commit | 3df393b3ff90f86df612eb995511c81851e088b2 (patch) | |
tree | abad903b52dc73a47df23be3e99ff17db30713fc /media/player | |
parent | 41886ac8cee8bf449559aff1bd2de89bc73958a4 (diff) | |
download | chromium_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.h | 392 |
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_ + |