OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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 // View.h : Paints the current movie frame (with scaling) to the display. |
| 6 // TODO(fbarchard): Consider rewriting as view.cc and view.h |
| 7 |
| 8 #ifndef MEDIA_PLAYER_VIEW_H_ |
| 9 #define MEDIA_PLAYER_VIEW_H_ |
| 10 |
| 11 // Enable timing code by turning on TESTING macro. |
| 12 // #define TESTING 1 |
| 13 |
| 14 #ifdef TESTING |
| 15 #define _CRT_SECURE_NO_WARNINGS |
| 16 #include <windows.h> |
| 17 #include <stdio.h> |
| 18 #include <process.h> |
| 19 #include <string.h> |
| 20 #endif |
| 21 |
| 22 #include <atlscrl.h> |
| 23 |
| 24 #include "base/basictypes.h" |
| 25 #include "media/base/buffers.h" |
| 26 #include "media/base/factory.h" |
| 27 #include "media/base/filters.h" |
| 28 #include "media/base/yuv_convert.h" |
| 29 #include "media/base/yuv_scale.h" |
| 30 #include "media/player/wtl_renderer.h" |
| 31 |
| 32 #include "media/player/movie.h" |
| 33 |
| 34 #ifdef TESTING |
| 35 // Fetch current time as milliseconds. |
| 36 // Return as double for high duration and precision. |
| 37 static inline double GetTime() { |
| 38 LARGE_INTEGER perf_time, perf_hz; |
| 39 QueryPerformanceFrequency(&perf_hz); // May change with speed step. |
| 40 QueryPerformanceCounter(&perf_time); |
| 41 return perf_time.QuadPart * 1000.0 / perf_hz.QuadPart; // Convert to ms. |
| 42 } |
| 43 #endif |
| 44 |
| 45 extern bool g_enableswscaler; |
| 46 extern bool g_enabledraw; |
| 47 extern bool g_enabledump_yuv_file; |
| 48 extern int g_view_size; |
| 49 |
| 50 class WtlVideoWindow : public CScrollWindowImpl<WtlVideoWindow> { |
| 51 public: |
| 52 DECLARE_WND_CLASS_EX(NULL, 0, -1) |
| 53 |
| 54 BEGIN_MSG_MAP(WtlVideoWindow) |
| 55 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) |
| 56 CHAIN_MSG_MAP(CScrollWindowImpl<WtlVideoWindow>); |
| 57 END_MSG_MAP() |
| 58 |
| 59 WtlVideoWindow() { |
| 60 size_.cx = 0; |
| 61 size_.cy = 0; |
| 62 renderer_ = new WtlVideoRenderer(this); |
| 63 last_frame_ = NULL; |
| 64 } |
| 65 |
| 66 BOOL PreTranslateMessage(MSG* /*msg*/) { |
| 67 return FALSE; |
| 68 } |
| 69 |
| 70 void AllocateVideoBitmap(CDCHandle dc) { |
| 71 // See note on SetSize for why we check size_.cy. |
| 72 if (bmp_.IsNull() && size_.cy > 0) { |
| 73 BITMAPINFO bmi; |
| 74 bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); |
| 75 bmi.bmiHeader.biWidth = size_.cx; |
| 76 bmi.bmiHeader.biHeight = size_.cy; |
| 77 bmi.bmiHeader.biPlanes = 1; |
| 78 bmi.bmiHeader.biBitCount = 32; |
| 79 bmi.bmiHeader.biCompression = BI_RGB; |
| 80 bmi.bmiHeader.biSizeImage = 0; |
| 81 bmi.bmiHeader.biXPelsPerMeter = 100; |
| 82 bmi.bmiHeader.biYPelsPerMeter = 100; |
| 83 bmi.bmiHeader.biClrUsed = 0; |
| 84 bmi.bmiHeader.biClrImportant = 0; |
| 85 void* pBits; |
| 86 bmp_.CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &pBits, NULL, 0); |
| 87 SetScrollOffset(0, 0, FALSE); |
| 88 SetScrollSize(size_); |
| 89 } |
| 90 } |
| 91 |
| 92 // Called on the video renderer's thread. |
| 93 // Note that AllocateVideoBitmap examines the size_.cy value to determine |
| 94 // if a bitmap should be allocated, so we set it last to avoid a race |
| 95 // condition. |
| 96 void SetSize(int cx, int cy) { |
| 97 size_.cx = cx; |
| 98 size_.cy = cy; |
| 99 } |
| 100 |
| 101 void Reset() { |
| 102 if (!bmp_.IsNull()) { |
| 103 bmp_.DeleteObject(); |
| 104 } |
| 105 size_.cx = 0; |
| 106 size_.cy = 0; |
| 107 // TODO(frank): get rid of renderer at reset too. |
| 108 } |
| 109 |
| 110 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, |
| 111 BOOL& /*bHandled*/) { |
| 112 CDCHandle dc = reinterpret_cast<HDC>(wParam); |
| 113 AllocateVideoBitmap(dc); |
| 114 RECT rect; |
| 115 GetClientRect(&rect); |
| 116 int x = 0; |
| 117 int y = 0; |
| 118 if (!bmp_.IsNull()) { |
| 119 x = size_.cx + 1; |
| 120 y = size_.cy + 1; |
| 121 } |
| 122 if (rect.right > m_sizeAll.cx) { |
| 123 RECT rectRight = rect; |
| 124 rectRight.left = x; |
| 125 rectRight.bottom = y; |
| 126 dc.FillRect(&rectRight, COLOR_WINDOW); |
| 127 } |
| 128 if (rect.bottom > m_sizeAll.cy) { |
| 129 RECT rectBottom = rect; |
| 130 rectBottom.top = y; |
| 131 dc.FillRect(&rectBottom, COLOR_WINDOW); |
| 132 } |
| 133 if (!bmp_.IsNull()) { |
| 134 dc.MoveTo(size_.cx, 0); |
| 135 dc.LineTo(size_.cx, size_.cy); |
| 136 dc.LineTo(0, size_.cy); |
| 137 } |
| 138 return 0; |
| 139 } |
| 140 |
| 141 // Convert the video frame to RGB and Blit. |
| 142 void ConvertFrame(media::VideoFrame * video_frame) { |
| 143 media::VideoSurface frame_in; |
| 144 bool lock_result = video_frame->Lock(&frame_in); |
| 145 DCHECK(lock_result); |
| 146 BITMAP bm; |
| 147 bmp_.GetBitmap(&bm); |
| 148 int dibwidth = bm.bmWidth; |
| 149 int dibheight = bm.bmHeight; |
| 150 |
| 151 uint8 *movie_dib_bits = reinterpret_cast<uint8 *>(bm.bmBits) + |
| 152 bm.bmWidthBytes * (bm.bmHeight - 1); |
| 153 int dibrowbytes = -bm.bmWidthBytes; |
| 154 int clipped_width = frame_in.width; |
| 155 if (dibwidth < clipped_width) { |
| 156 clipped_width = dibwidth; |
| 157 } |
| 158 int clipped_height = frame_in.height; |
| 159 if (dibheight < clipped_height) { |
| 160 clipped_height = dibheight; |
| 161 } |
| 162 |
| 163 int scaled_width = clipped_width; |
| 164 int scaled_height = clipped_height; |
| 165 switch (g_view_size) { |
| 166 case 0: |
| 167 scaled_width = clipped_width / 2; |
| 168 scaled_height = clipped_height / 2; |
| 169 break; |
| 170 |
| 171 case 1: |
| 172 default: // Assume 1:1 for stray view sizes |
| 173 scaled_width = clipped_width; |
| 174 scaled_height = clipped_height; |
| 175 break; |
| 176 |
| 177 case 2: |
| 178 scaled_width = clipped_width; |
| 179 scaled_height = clipped_height; |
| 180 clipped_width = scaled_width / 2; |
| 181 clipped_height = scaled_height / 2; |
| 182 break; |
| 183 } |
| 184 |
| 185 // Append each frame to end of file. |
| 186 if (g_enabledump_yuv_file) { |
| 187 DumpYUV(frame_in); |
| 188 } |
| 189 |
| 190 #ifdef TESTING |
| 191 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); |
| 192 double yuvtimestart = GetTime(); // Start timer. |
| 193 #endif |
| 194 |
| 195 if (g_enabledraw) { |
| 196 if (g_enableswscaler) { |
| 197 uint8* data_out[3]; |
| 198 int stride_out[3]; |
| 199 data_out[0] = movie_dib_bits; |
| 200 data_out[1] = NULL; |
| 201 data_out[2] = NULL; |
| 202 stride_out[0] = dibrowbytes; |
| 203 stride_out[1] = 0; |
| 204 stride_out[2] = 0; |
| 205 |
| 206 /* |
| 207 if (!sws_context_) { |
| 208 DCHECK(frame_in.format == VideoSurface::YV12); |
| 209 int outtype = bm.bmBitsPixel == 32 ? PIX_FMT_RGB32 : PIX_FMT_RGB24; |
| 210 sws_context_ = sws_getContext(frame_in.width, frame_in.height, |
| 211 PIX_FMT_YUV420P, width_, height_, |
| 212 outtype, SWS_FAST_BILINEAR, |
| 213 NULL, NULL, NULL); |
| 214 DCHECK(sws_context_); |
| 215 } |
| 216 |
| 217 sws_scale(sws_context_, frame_in.data, frame_in.strides, 0, |
| 218 height_, data_out, stride_out); |
| 219 */ |
| 220 } else { |
| 221 DCHECK(bm.bmBitsPixel == 32); |
| 222 DrawYUV(frame_in, |
| 223 movie_dib_bits, |
| 224 dibrowbytes, |
| 225 clipped_width, |
| 226 clipped_height, |
| 227 scaled_width, |
| 228 scaled_height); |
| 229 } |
| 230 } |
| 231 #ifdef TESTING |
| 232 double yuvtimeend = GetTime(); // Start timer. |
| 233 SSetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); |
| 234 static int yuvtimecount = 0; |
| 235 static double yuvtimesum = 0; |
| 236 yuvtimesum += yuvtimeend - yuvtimestart; |
| 237 ++yuvtimecount; |
| 238 |
| 239 char outputbuf[512]; |
| 240 snprintf(outputbuf, sizeof(outputbuf), "yuv %.2fms avg %.2fms\n", |
| 241 yuvtimeend - yuvtimestart, yuvtimesum / yuvtimecount); |
| 242 OutputDebugStringA(outputbuf); |
| 243 #endif |
| 244 } |
| 245 |
| 246 void DoPaint(CDCHandle dc) { |
| 247 AllocateVideoBitmap(dc); |
| 248 if (!bmp_.IsNull()) { |
| 249 scoped_refptr<media::VideoFrame> frame; |
| 250 renderer_->GetCurrentFrame(&frame); |
| 251 if (frame.get()) { |
| 252 base::TimeDelta frame_timestamp = frame->GetTimestamp(); |
| 253 if (frame != last_frame_ || frame_timestamp != last_timestamp_) { |
| 254 last_frame_ = frame; |
| 255 last_timestamp_ = frame_timestamp; |
| 256 ConvertFrame(frame); |
| 257 } |
| 258 frame = NULL; |
| 259 } |
| 260 |
| 261 #ifdef TESTING |
| 262 double paint_time_start = GetTime(); |
| 263 static double paint_time_previous = 0; |
| 264 if (!paint_time_previous) |
| 265 paint_time_previous = paint_time_start; |
| 266 #endif |
| 267 CDC dcMem; |
| 268 dcMem.CreateCompatibleDC(dc); |
| 269 HBITMAP hBmpOld = dcMem.SelectBitmap(bmp_); |
| 270 dc.BitBlt(0, 0, size_.cx, size_.cy, dcMem, 0, 0, SRCCOPY); |
| 271 dcMem.SelectBitmap(hBmpOld); |
| 272 #ifdef TESTING |
| 273 double paint_time_end = GetTime(); |
| 274 static int paint_count = 0; |
| 275 static double paint_time_sum = 0; |
| 276 paint_time_sum += paint_time_end-paint_time_start; |
| 277 ++paint_count; |
| 278 char outputbuf[512]; |
| 279 snprintf(outputbuf, sizeof(outputbuf), |
| 280 "paint time %5.2fms blit %5.2fms avg %5.2fms\n", |
| 281 paint_time_start-paint_time_previous, |
| 282 paint_time_end-paint_time_start, |
| 283 paint_time_sum/paint_count); |
| 284 OutputDebugStringA(outputbuf); |
| 285 |
| 286 paint_time_previous = paint_time_start; |
| 287 #endif |
| 288 } |
| 289 } // End of DoPaint function. |
| 290 |
| 291 CBitmap bmp_; // Used by mainfrm.h. |
| 292 SIZE size_; // Used by WtlVideoWindow. |
| 293 scoped_refptr<WtlVideoRenderer> renderer_; // Used by WtlVideoWindow. |
| 294 |
| 295 private: |
| 296 |
| 297 // Draw a frame of YUV to an RGB buffer with scaling. |
| 298 // Handles different YUV formats. |
| 299 void DrawYUV(const media::VideoSurface &frame_in, |
| 300 uint8 *movie_dib_bits, |
| 301 int dibrowbytes, |
| 302 int clipped_width, |
| 303 int clipped_height, |
| 304 int scaled_width, |
| 305 int scaled_height) { |
| 306 // Normal size |
| 307 if (g_view_size == 1) { |
| 308 if (frame_in.format == media::VideoSurface::YV16) { |
| 309 // Temporary cast, til we use uint8 for VideoFrame. |
| 310 media::ConvertYV16ToRGB32((const uint8*)frame_in.data[0], |
| 311 (const uint8*)frame_in.data[1], |
| 312 (const uint8*)frame_in.data[2], |
| 313 movie_dib_bits, |
| 314 clipped_width, clipped_height, |
| 315 frame_in.strides[0], |
| 316 frame_in.strides[1], |
| 317 dibrowbytes); |
| 318 } else { |
| 319 // Temporary cast, til we use uint8 for VideoFrame. |
| 320 media::ConvertYV12ToRGB32((const uint8*)frame_in.data[0], |
| 321 (const uint8*)frame_in.data[1], |
| 322 (const uint8*)frame_in.data[2], |
| 323 movie_dib_bits, |
| 324 clipped_width, clipped_height, |
| 325 frame_in.strides[0], |
| 326 frame_in.strides[1], |
| 327 dibrowbytes); |
| 328 } |
| 329 } else { |
| 330 if (frame_in.format == media::VideoSurface::YV16) { |
| 331 // Temporary cast, til we use uint8 for VideoFrame. |
| 332 media::ScaleYV16ToRGB32((const uint8*)frame_in.data[0], |
| 333 (const uint8*)frame_in.data[1], |
| 334 (const uint8*)frame_in.data[2], |
| 335 movie_dib_bits, |
| 336 clipped_width, clipped_height, |
| 337 scaled_width, scaled_height, |
| 338 frame_in.strides[0], |
| 339 frame_in.strides[1], |
| 340 dibrowbytes); |
| 341 } else { |
| 342 // Temporary cast, til we use uint8 for VideoFrame. |
| 343 media::ScaleYV12ToRGB32((const uint8*)frame_in.data[0], |
| 344 (const uint8*)frame_in.data[1], |
| 345 (const uint8*)frame_in.data[2], |
| 346 movie_dib_bits, |
| 347 clipped_width, clipped_height, |
| 348 scaled_width, scaled_height, |
| 349 frame_in.strides[0], |
| 350 frame_in.strides[1], |
| 351 dibrowbytes); |
| 352 } |
| 353 } |
| 354 } |
| 355 |
| 356 // Diagnostic function to write out YUV in format compatible with PYUV tool. |
| 357 void DumpYUV(const media::VideoSurface &frame_in) { |
| 358 FILE * file_yuv = fopen("raw.yuv", "ab+"); // Open for append binary. |
| 359 if (file_yuv != NULL) { |
| 360 fseek(file_yuv, 0, SEEK_END); |
| 361 const size_t frame_size = frame_in.width * frame_in.height; |
| 362 for (size_t y = 0; y < frame_in.height; ++y) |
| 363 fwrite(frame_in.data[0]+frame_in.strides[0]*y, |
| 364 frame_in.width, sizeof(uint8), file_yuv); |
| 365 for (size_t y = 0; y < frame_in.height/2; ++y) |
| 366 fwrite(frame_in.data[1]+frame_in.strides[1]*y, |
| 367 frame_in.width/2, sizeof(uint8), file_yuv); |
| 368 for (size_t y = 0; y < frame_in.height/2; ++y) |
| 369 fwrite(frame_in.data[2]+frame_in.strides[2]*y, |
| 370 frame_in.width/2, sizeof(uint8), file_yuv); |
| 371 fclose(file_yuv); |
| 372 |
| 373 #if TESTING |
| 374 static int frame_dump_count = 0; |
| 375 char outputbuf[512]; |
| 376 snprintf(outputbuf, sizeof(outputbuf), "yuvdump %4d %dx%d stride %d\n", |
| 377 frame_dump_count, frame_in.width, frame_in.height, |
| 378 frame_in.strides[0]); |
| 379 OutputDebugStringA(outputbuf); |
| 380 ++frame_dump_count; |
| 381 #endif |
| 382 } |
| 383 } |
| 384 |
| 385 media::VideoFrame* last_frame_; |
| 386 base::TimeDelta last_timestamp_; |
| 387 |
| 388 DISALLOW_COPY_AND_ASSIGN(WtlVideoWindow); |
| 389 }; |
| 390 |
| 391 #endif // MEDIA_PLAYER_VIEW_H_ |
| 392 |
OLD | NEW |