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 =
66      CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
67
68  window_ = XCreateWindow(display_,
69                          DefaultRootWindow(display_),
70                          x_pos,
71                          y_pos,
72                          width,
73                          height,
74                          0,
75                          visual_info.depth,
76                          InputOutput,
77                          visual_info.visual,
78                          attribute_mask,
79                          &window_attributes);
80
81  // Set window name.
82  XStoreName(display_, window_, name.c_str());
83  XSetIconName(display_, window_, name.c_str());
84
85  // Make x report events for mask.
86  XSelectInput(display_, window_, StructureNotifyMask);
87
88  // Map the window to the display.
89  XMapWindow(display_, window_);
90
91  // Wait for map event.
92  XEvent event;
93  do {
94    XNextEvent(display_, &event);
95  } while (event.type != MapNotify || event.xmap.event != window_);
96
97  gc_ = XCreateGC(display_, window_, 0, 0);
98
99  // create shared memory image
100  image_ = XShmCreateImage(
101      display_, CopyFromParent, 24, ZPixmap, NULL, &shminfo_, width, height);
102  shminfo_.shmid = shmget(
103      IPC_PRIVATE, (image_->bytes_per_line * image_->height), IPC_CREAT | 0777);
104  shminfo_.shmaddr = image_->data = (char*)shmat(shminfo_.shmid, 0, 0);
105  if (image_->data == reinterpret_cast<char*>(-1)) {
106    VLOG(1) << "XShmCreateImage failed";
107    NOTREACHED();
108  }
109  shminfo_.readOnly = false;
110
111  // Attach image to display.
112  if (!XShmAttach(display_, &shminfo_)) {
113    VLOG(1) << "XShmAttach failed";
114    NOTREACHED();
115  }
116  XSync(display_, false);
117}
118
119void LinuxOutputWindow::RenderFrame(
120    const scoped_refptr<media::VideoFrame>& video_frame) {
121  CHECK_LE(video_frame->coded_size().width(), image_->width);
122  CHECK_LE(video_frame->coded_size().height(), image_->height);
123  libyuv::I420ToARGB(video_frame->data(VideoFrame::kYPlane),
124                     video_frame->stride(VideoFrame::kYPlane),
125                     video_frame->data(VideoFrame::kUPlane),
126                     video_frame->stride(VideoFrame::kUPlane),
127                     video_frame->data(VideoFrame::kVPlane),
128                     video_frame->stride(VideoFrame::kVPlane),
129                     reinterpret_cast<uint8_t*>(image_->data),
130                     image_->bytes_per_line,
131                     video_frame->coded_size().width(),
132                     video_frame->coded_size().height());
133
134  // Place image in window.
135  XShmPutImage(display_,
136               window_,
137               gc_,
138               image_,
139               0,
140               0,
141               0,
142               0,
143               video_frame->coded_size().width(),
144               video_frame->coded_size().height(),
145               true);
146
147  // Very important for the image to update properly!
148  XSync(display_, false);
149}
150
151}  // namespace test
152}  // namespace cast
153}  // namespace media
154