1/*
2 * GStreamer
3 * Copyright (C) 2009 Julien Isorce <julien.isorce@gmail.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include <Cocoa/Cocoa.h>
22#include <gst/gst.h>
23#include <gst/video/videooverlay.h>
24
25#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
26#define NSEventMaskAny                       NSAnyEventMask
27#define NSWindowStyleMaskTitled              NSTitledWindowMask
28#define NSWindowStyleMaskClosable            NSClosableWindowMask
29#define NSWindowStyleMaskResizable           NSResizableWindowMask
30#define NSWindowStyleMaskMiniaturizable      NSMiniaturizableWindowMask
31#endif
32
33/* ============================================================= */
34/*                                                               */
35/*                          MainWindow                           */
36/*                                                               */
37/* ============================================================= */
38
39@interface MainWindow: NSWindow <NSApplicationDelegate> {
40  GMainLoop *m_loop;
41  GstElement *m_pipeline;
42  gboolean m_isClosed;
43}
44- (id) initWithContentRect:(NSRect) contentRect Loop:(GMainLoop*)loop Pipeline:(GstElement*)pipeline;
45- (GMainLoop*) loop;
46- (GstElement*) pipeline;
47- (gboolean) isClosed;
48@end
49
50@implementation MainWindow
51
52- (id) initWithContentRect:(NSRect)contentRect Loop:(GMainLoop*)loop Pipeline:(GstElement*)pipeline
53{
54  m_loop = loop;
55  m_pipeline = pipeline;
56  m_isClosed = FALSE;
57
58  self = [super initWithContentRect: contentRect
59    styleMask: (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
60                NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable)
61    backing: NSBackingStoreBuffered defer: NO screen: nil];
62
63  [self setReleasedWhenClosed:NO];
64  [[NSApplication sharedApplication] setDelegate:self];
65
66  [self setTitle:@"gst-plugins-gl implements videooverlay interface"];
67
68  return self;
69}
70
71- (GMainLoop*) loop {
72  return m_loop;
73}
74
75- (GstElement*) pipeline {
76  return m_pipeline;
77}
78
79- (gboolean) isClosed {
80  return m_isClosed;
81}
82
83- (void) customClose {
84  m_isClosed = TRUE;
85}
86
87- (BOOL) windowShouldClose:(id)sender {
88  gst_element_send_event (m_pipeline, gst_event_new_eos ());
89  return YES;
90}
91
92- (void) applicationDidFinishLaunching: (NSNotification *) not {
93  [self makeMainWindow];
94  [self center];
95  [self orderFront:self];
96}
97
98- (BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
99  return NO;
100}
101
102@end
103
104
105/* ============================================================= */
106/*                                                               */
107/*                   gstreamer callbacks                         */
108/*                                                               */
109/* ============================================================= */
110
111
112static GstBusSyncReply create_window (GstBus* bus, GstMessage* message, MainWindow* window)
113{
114  // ignore anything but 'prepare-window-handle' element messages
115  if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
116    return GST_BUS_PASS;
117
118  if (!gst_is_video_overlay_prepare_window_handle_message (message))
119    return GST_BUS_PASS;
120
121  g_print ("setting window handle %lud\n", (gulong) window);
122
123  gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message)), (guintptr) [window contentView]);
124
125  gst_message_unref (message);
126
127  return GST_BUS_DROP;
128}
129
130
131static void end_stream_cb(GstBus* bus, GstMessage* message, MainWindow* window)
132{
133  g_print ("end of stream\n");
134
135  gst_element_set_state ([window pipeline], GST_STATE_NULL);
136  gst_object_unref ([window pipeline]);
137  g_main_loop_quit ([window loop]);
138
139  [window performSelectorOnMainThread:@selector(customClose) withObject:nil waitUntilDone:YES];
140}
141
142static gpointer thread_func (MainWindow* window)
143{
144  g_main_loop_run ([window loop]);
145
146  return NULL;
147}
148
149
150/* ============================================================= */
151/*                                                               */
152/*                         application                           */
153/*                                                               */
154/* ============================================================= */
155
156int main(int argc, char **argv)
157{
158	int width = 640;
159  int height = 480;
160
161  GMainLoop *loop = NULL;
162  GstElement *pipeline = NULL;
163
164  GstElement *videosrc  = NULL;
165  GstElement *videosink = NULL;
166  GstCaps *caps=NULL;
167  gboolean ok=FALSE;
168  GstBus *bus=NULL;
169  GThread *loop_thread=NULL;
170  NSRect rect;
171  MainWindow *window=nil;
172
173  [NSApplication sharedApplication];
174
175  g_print("app created\n");
176
177  gst_init (&argc, &argv);
178
179  loop = g_main_loop_new (NULL, FALSE);
180  pipeline = gst_pipeline_new ("pipeline");
181
182  videosrc  = gst_element_factory_make ("videotestsrc", "videotestsrc");
183  videosink = gst_element_factory_make ("glimagesink", "glimagesink");
184
185  g_object_set(G_OBJECT(videosrc), "num-buffers", 500, NULL);
186
187  gst_bin_add_many (GST_BIN (pipeline), videosrc, videosink, NULL);
188
189  caps = gst_caps_new_simple("video/x-raw",
190    "width", G_TYPE_INT, width,
191    "height", G_TYPE_INT, height,
192    "framerate", GST_TYPE_FRACTION, 25, 1,
193    "format", G_TYPE_STRING, "I420",
194    NULL);
195
196  ok = gst_element_link_filtered(videosrc, videosink, caps);
197  gst_caps_unref(caps);
198  if (!ok)
199    g_warning("could not link videosrc to videosink\n");
200
201  rect.origin.x = 0; rect.origin.y = 0;
202  rect.size.width = width; rect.size.height = height;
203
204  window = [[MainWindow alloc] initWithContentRect:rect Loop:loop Pipeline:pipeline];
205
206  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
207  gst_bus_add_signal_watch (bus);
208  /* NOTE: window is not bridge_retained because its lifetime is just this function */
209  g_signal_connect(bus, "message::error", G_CALLBACK(end_stream_cb), (__bridge gpointer)window);
210  g_signal_connect(bus, "message::warning", G_CALLBACK(end_stream_cb), (__bridge gpointer)window);
211  g_signal_connect(bus, "message::eos", G_CALLBACK(end_stream_cb), (__bridge gpointer)window);
212  gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, (__bridge gpointer)window, NULL);
213  gst_object_unref (bus);
214
215  loop_thread = g_thread_new (NULL,
216      (GThreadFunc) thread_func, (__bridge gpointer)window);
217
218  gst_element_set_state (pipeline, GST_STATE_PLAYING);
219
220  [window orderFront:window];
221
222  while (![window isClosed]) {
223    NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny
224      untilDate:[NSDate dateWithTimeIntervalSinceNow:1]
225      inMode:NSDefaultRunLoopMode dequeue:YES];
226    if (event)
227      [NSApp sendEvent:event];
228  }
229
230  g_thread_join (loop_thread);
231
232  return 0;
233}
234