1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/cast/test/linux_output_window.h"
6
7#include "base/logging.h"
8#include "media/base/video_frame.h"
9#include "third_party/libyuv/include/libyuv/convert.h"
10#include "ui/gfx/size.h"
11
12namespace media {
13namespace cast {
14namespace test {
15
16LinuxOutputWindow::LinuxOutputWindow(int x_pos,
17                                     int y_pos,
18                                     int width,
19                                     int height,
20                                     const std::string& name) {
21  CreateWindow( x_pos, y_pos, width, height, name);
22}
23
24LinuxOutputWindow::~LinuxOutputWindow() {
25  if (display_ && window_) {
26    XUnmapWindow(display_, window_);
27    XDestroyWindow(display_, window_);
28    XSync(display_, false);
29    if (gc_)
30      XFreeGC(display_, gc_);
31    XCloseDisplay(display_);
32  }
33}
34
35void LinuxOutputWindow::CreateWindow(int x_pos,
36                                     int y_pos,
37                                     int width,
38                                     int height,
39                                     const std::string& name) {
40  display_ = XOpenDisplay(NULL);
41  if (display_ == NULL) {
42    // There's no point to continue if this happens: nothing will work anyway.
43    VLOG(1) << "Failed to connect to X server: X environment likely broken";
44    NOTREACHED();
45  }
46
47  int screen = DefaultScreen(display_);
48
49  // Try to establish a 24-bit TrueColor display.
50  // (our environment must allow this).
51  XVisualInfo visual_info;
52  if (XMatchVisualInfo(display_, screen, 24, TrueColor, &visual_info) == 0) {
53    VLOG(1) << "Failed to establish 24-bit TrueColor in X environment.";
54    NOTREACHED();
55  }
56
57  // Create suitable window attributes.
58  XSetWindowAttributes window_attributes;
59  window_attributes.colormap = XCreateColormap(
60      display_, DefaultRootWindow(display_), visual_info.visual, AllocNone);
61  window_attributes.event_mask = StructureNotifyMask | ExposureMask;
62  window_attributes.background_pixel = 0;
63  window_attributes.border_pixel = 0;
64
65  unsigned long attribute_mask = CWBackPixel | CWBorderPixel | CWColormap |
66      CWEventMask;
67
68  window_ = XCreateWindow(display_, DefaultRootWindow(display_), x_pos,
69                          y_pos, width, height, 0, visual_info.depth,
70                          InputOutput, visual_info.visual,
71                          attribute_mask, &window_attributes);
72
73  // Set window name.
74  XStoreName(display_, window_, name.c_str());
75  XSetIconName(display_, window_, name.c_str());
76
77  // Make x report events for mask.
78  XSelectInput(display_, window_, StructureNotifyMask);
79
80  // Map the window to the display.
81  XMapWindow(display_, window_);
82
83  // Wait for map event.
84  XEvent event;
85  do {
86    XNextEvent(display_, &event);
87  } while (event.type != MapNotify || event.xmap.event != window_);
88
89  gc_ = XCreateGC(display_, window_, 0, 0);
90
91  // create shared memory image
92  image_ = XShmCreateImage(display_, CopyFromParent, 24, ZPixmap, NULL,
93                           &shminfo_, width, height);
94  shminfo_.shmid = shmget(IPC_PRIVATE,
95                          (image_->bytes_per_line * image_->height),
96                          IPC_CREAT | 0777);
97  shminfo_.shmaddr = image_->data = (char*) shmat(shminfo_.shmid, 0, 0);
98  if (image_->data == reinterpret_cast<char*>(-1)) {
99    VLOG(1) << "XShmCreateImage failed";
100    NOTREACHED();
101  }
102  render_buffer_ = reinterpret_cast<uint8_t*>(image_->data);
103  shminfo_.readOnly = false;
104
105  // Attach image to display.
106  if (!XShmAttach(display_, &shminfo_)) {
107    VLOG(1) << "XShmAttach failed";
108    NOTREACHED();
109  }
110  XSync(display_, false);
111}
112
113void LinuxOutputWindow::RenderFrame(
114    const scoped_refptr<media::VideoFrame>& video_frame) {
115  libyuv::I420ToARGB(video_frame->data(VideoFrame::kYPlane),
116                     video_frame->stride(VideoFrame::kYPlane),
117                     video_frame->data(VideoFrame::kUPlane),
118                     video_frame->stride(VideoFrame::kUPlane),
119                     video_frame->data(VideoFrame::kVPlane),
120                     video_frame->stride(VideoFrame::kVPlane),
121                     render_buffer_,
122                     video_frame->coded_size().width() * 4,  // Stride.
123                     video_frame->coded_size().width(),
124                     video_frame->coded_size().height());
125
126  // Place image in window.
127  XShmPutImage(display_, window_, gc_, image_, 0, 0, 0, 0,
128               video_frame->coded_size().width(),
129               video_frame->coded_size().height(), true);
130
131  // Very important for the image to update properly!
132  XSync(display_, false);
133}
134
135}  // namespace test
136}  // namespace cast
137}  // namespace media