1/*
2 * Copyright (C) 2000-2003 the xine project
3 *
4 * This file is part of xine, a free video player.
5 *
6 * xine is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * xine is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19 *
20 * This output driver makes use of xine's objective-c video_output
21 * classes located in the macosx folder.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include <string.h>
32
33#define LOG_MODULE "video_out_macosx"
34#define LOG_VERBOSE
35/*
36#define LOG
37*/
38
39#include "xine.h"
40#include "xine/video_out.h"
41#include "xine/vo_scale.h"
42#include "xine/xine_internal.h"
43#include "xine/xineutils.h"
44
45#include "macosx/video_window.h"
46
47typedef struct {
48  vo_frame_t            vo_frame;
49  int                   width;
50  int                   height;
51  double                ratio;
52  int                   format;
53  xine_t               *xine;
54} macosx_frame_t;
55
56typedef struct {
57  vo_driver_t           vo_driver;
58  config_values_t      *config;
59  int                   ratio;
60  xine_t               *xine;
61  id                    view;
62  alphablend_t          alphablend_extra_data;
63} macosx_driver_t;
64
65typedef struct {
66  video_driver_class_t  driver_class;
67  config_values_t      *config;
68  xine_t               *xine;
69} macosx_class_t;
70
71
72static void free_framedata(macosx_frame_t* frame) {
73  if(frame->vo_frame.base[0]) {
74    free(frame->vo_frame.base[0]);
75    frame->vo_frame.base[0] = NULL;
76    frame->vo_frame.base[1] = NULL;
77    frame->vo_frame.base[2] = NULL;
78  }
79}
80
81static void macosx_frame_dispose(vo_frame_t *vo_frame) {
82  macosx_frame_t *frame = (macosx_frame_t *)vo_frame;
83  free_framedata(frame);
84  free (frame);
85}
86
87static void macosx_frame_field(vo_frame_t *vo_frame, int which_field) {
88  /* do nothing */
89}
90
91static uint32_t macosx_get_capabilities(vo_driver_t *vo_driver) {
92  /* both styles, country and western */
93  return VO_CAP_YV12 | VO_CAP_YUY2 | VO_CAP_UNSCALED_OVERLAY;
94}
95
96static vo_frame_t *macosx_alloc_frame(vo_driver_t *vo_driver) {
97  /* macosx_driver_t *this = (macosx_driver_t *) vo_driver; */
98  macosx_frame_t  *frame;
99
100  frame = calloc(1, sizeof(macosx_frame_t));
101  if(!frame)
102    return NULL;
103
104  pthread_mutex_init(&frame->vo_frame.mutex, NULL);
105
106  frame->vo_frame.base[0] = NULL;
107  frame->vo_frame.base[1] = NULL;
108  frame->vo_frame.base[2] = NULL;
109
110  frame->vo_frame.proc_slice = NULL;
111  frame->vo_frame.proc_frame = NULL;
112  frame->vo_frame.field      = macosx_frame_field;
113  frame->vo_frame.dispose    = macosx_frame_dispose;
114  frame->vo_frame.driver     = vo_driver;
115
116  return (vo_frame_t *)frame;
117}
118
119static void macosx_update_frame_format(vo_driver_t *vo_driver, vo_frame_t *vo_frame,
120                                     uint32_t width, uint32_t height,
121                                     double ratio, int format, int flags) {
122  macosx_driver_t *this = (macosx_driver_t *) vo_driver;
123  macosx_frame_t  *frame = (macosx_frame_t *) vo_frame;
124
125  if((frame->width != width) || (frame->height != height) ||
126     (frame->format != format)) {
127
128    NSSize video_size = NSMakeSize(width, height);
129
130    free_framedata(frame);
131
132    frame->width  = width;
133    frame->height = height;
134    frame->format = format;
135
136    lprintf ("frame change, new height:%d width:%d (ratio:%lf) format:%d\n",
137             height, width, ratio, format);
138
139    switch(format) {
140
141    case XINE_IMGFMT_YV12:
142      {
143        int y_size, uv_size;
144
145        frame->vo_frame.pitches[0] = 8*((width + 7) / 8);
146        frame->vo_frame.pitches[1] = 8*((width + 15) / 16);
147        frame->vo_frame.pitches[2] = 8*((width + 15) / 16);
148
149        y_size  = frame->vo_frame.pitches[0] * height;
150        uv_size = frame->vo_frame.pitches[1] * ((height+1)/2);
151
152        frame->vo_frame.base[0] = malloc (y_size + 2*uv_size);
153        frame->vo_frame.base[1] = frame->vo_frame.base[0]+y_size+uv_size;
154        frame->vo_frame.base[2] = frame->vo_frame.base[0]+y_size;
155      }
156      break;
157
158    case XINE_IMGFMT_YUY2:
159      frame->vo_frame.pitches[0] = 8*((width + 3) / 4);
160      frame->vo_frame.base[0] = malloc(frame->vo_frame.pitches[0] * height);
161      frame->vo_frame.base[1] = NULL;
162      frame->vo_frame.base[2] = NULL;
163      break;
164
165    default:
166      xprintf (this->xine, XINE_VERBOSITY_DEBUG, "video_out_macosx: unknown frame format %04x)\n", format);
167      break;
168
169    }
170
171    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
172    [this->view setVideoSize:video_size];
173    [pool release];
174
175    if((format == XINE_IMGFMT_YV12
176        && (frame->vo_frame.base[0] == NULL
177            || frame->vo_frame.base[1] == NULL
178            || frame->vo_frame.base[2] == NULL))
179            || (format == XINE_IMGFMT_YUY2 && frame->vo_frame.base[0] == NULL)) {
180      xprintf (this->xine, XINE_VERBOSITY_DEBUG,
181               "video_out_macosx: error. (framedata allocation failed: out of memory)\n");
182      free_framedata(frame);
183    }
184  }
185
186  frame->ratio = ratio;
187}
188
189static void macosx_display_frame(vo_driver_t *vo_driver, vo_frame_t *vo_frame) {
190  macosx_driver_t  *driver = (macosx_driver_t *)vo_driver;
191  macosx_frame_t   *frame = (macosx_frame_t *)vo_frame;
192  char *texture_buffer;
193
194  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
195
196  if ((texture_buffer = [driver->view textureBuffer]) != NULL) {
197    switch (vo_frame->format) {
198      case XINE_IMGFMT_YV12:
199        yv12_to_yuy2 (vo_frame->base[0], vo_frame->pitches[0],
200                      vo_frame->base[1], vo_frame->pitches[1],
201                      vo_frame->base[2], vo_frame->pitches[2],
202                      (unsigned char *)texture_buffer,
203                      vo_frame->width * 2,
204                      vo_frame->width, vo_frame->height, 0);
205
206        [driver->view updateTexture];
207        break;
208      case XINE_IMGFMT_YUY2:
209        xine_fast_memcpy (texture_buffer, vo_frame->base[0],
210	                  vo_frame->pitches[0] * vo_frame->height);
211        [driver->view updateTexture];
212        break;
213      default:
214        /* unsupported frame format, do nothing. */
215        break;
216    }
217  }
218
219  frame->vo_frame.free(&frame->vo_frame);
220  [pool release];
221}
222
223static void macosx_overlay_blend (vo_driver_t *this_gen, vo_frame_t *frame_gen,
224                                  vo_overlay_t *overlay) {
225  macosx_driver_t *this = (macosx_driver_t *) this_gen;
226  macosx_frame_t *frame = (macosx_frame_t *) frame_gen;
227
228  this->alphablend_extra_data.offset_x = frame_gen->overlay_offset_x;
229  this->alphablend_extra_data.offset_y = frame_gen->overlay_offset_y;
230
231  /* TODO: should check here whether the overlay has changed or not: use a
232   * ovl_changed boolean variable similarly to video_out_xv */
233  if (overlay->rle) {
234    if (frame->format == XINE_IMGFMT_YV12)
235      /* TODO: It may be possible to accelerate the blending via Quartz
236       * Extreme ... */
237      _x_blend_yuv(frame->vo_frame.base, overlay,
238          frame->width, frame->height, frame->vo_frame.pitches,
239          &this->alphablend_extra_data);
240    else
241      _x_blend_yuy2(frame->vo_frame.base[0], overlay,
242          frame->width, frame->height, frame->vo_frame.pitches[0],
243          &this->alphablend_extra_data);
244  }
245}
246
247static int macosx_get_property(vo_driver_t *vo_driver, int property) {
248  macosx_driver_t  *driver = (macosx_driver_t *)vo_driver;
249
250  switch(property) {
251
252  case VO_PROP_ASPECT_RATIO:
253    return driver->ratio;
254    break;
255
256  default:
257    break;
258  }
259
260  return 0;
261}
262
263static int macosx_set_property(vo_driver_t *vo_driver, int property, int value) {
264  macosx_driver_t  *driver = (macosx_driver_t *)vo_driver;
265
266  switch(property) {
267
268  case VO_PROP_ASPECT_RATIO:
269    if(value >= XINE_VO_ASPECT_NUM_RATIOS)
270      value = XINE_VO_ASPECT_AUTO;
271
272    driver->ratio = value;
273    break;
274
275  default:
276    break;
277  }
278  return value;
279}
280
281static void macosx_get_property_min_max(vo_driver_t *vo_driver,
282                                        int property, int *min, int *max) {
283  *min = 0;
284  *max = 0;
285}
286
287static int macosx_gui_data_exchange(vo_driver_t *vo_driver, int data_type, void *data) {
288/*   macosx_driver_t     *this = (macosx_driver_t *) vo_driver; */
289
290  switch (data_type) {
291  case XINE_GUI_SEND_COMPLETION_EVENT:
292  case XINE_GUI_SEND_DRAWABLE_CHANGED:
293  case XINE_GUI_SEND_EXPOSE_EVENT:
294  case XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO:
295  case XINE_GUI_SEND_VIDEOWIN_VISIBLE:
296  case XINE_GUI_SEND_SELECT_VISUAL:
297  default:
298    lprintf("unknown GUI data type %d\n", data_type);
299    break;
300  }
301
302  return 0;
303}
304static void macosx_dispose(vo_driver_t *vo_driver) {
305  macosx_driver_t *this = (macosx_driver_t *) vo_driver;
306
307  _x_alphablend_free(&this->alphablend_extra_data);
308  [this->view releaseInMainThread];
309
310  free(this);
311}
312
313static int macosx_redraw_needed(vo_driver_t *vo_driver) {
314  return 0;
315}
316
317
318static vo_driver_t *open_plugin(video_driver_class_t *driver_class, const void *visual) {
319  macosx_class_t    *class = (macosx_class_t *) driver_class;
320  macosx_driver_t   *driver;
321  XineOpenGLView    *view = (XineOpenGLView *) visual;
322
323  driver = calloc(1, sizeof(macosx_driver_t));
324
325  driver->config = class->config;
326  driver->xine   = class->xine;
327  driver->ratio  = XINE_VO_ASPECT_AUTO;
328  driver->view   = [view retain];
329
330  driver->vo_driver.get_capabilities     = macosx_get_capabilities;
331  driver->vo_driver.alloc_frame          = macosx_alloc_frame;
332  driver->vo_driver.update_frame_format  = macosx_update_frame_format;
333  driver->vo_driver.overlay_begin        = NULL; /* not used */
334  driver->vo_driver.overlay_blend        = macosx_overlay_blend;
335  driver->vo_driver.overlay_end          = NULL; /* not used */
336  driver->vo_driver.display_frame        = macosx_display_frame;
337  driver->vo_driver.get_property         = macosx_get_property;
338  driver->vo_driver.set_property         = macosx_set_property;
339  driver->vo_driver.get_property_min_max = macosx_get_property_min_max;
340  driver->vo_driver.gui_data_exchange    = macosx_gui_data_exchange;
341  driver->vo_driver.dispose              = macosx_dispose;
342  driver->vo_driver.redraw_needed        = macosx_redraw_needed;
343
344  _x_alphablend_init(&driver->alphablend_extra_data, class->xine);
345
346  return &driver->vo_driver;
347}
348
349/*
350 * Class related functions.
351 */
352
353static void *init_class (xine_t *xine, void *visual) {
354  macosx_class_t        *this;
355
356  this = calloc(1, sizeof(macosx_class_t));
357
358  this->driver_class.open_plugin     = open_plugin;
359  this->driver_class.identifier      = "MacOSX";
360  this->driver_class.description     = N_("xine video output plugin for Mac OS X");
361  this->driver_class.dispose         = default_video_driver_class_dispose;
362
363  this->config                       = xine->config;
364  this->xine                         = xine;
365
366  return this;
367}
368
369static const vo_info_t vo_info_macosx = {
370  1,                        /* Priority    */
371  XINE_VISUAL_TYPE_MACOSX   /* Visual type */
372};
373
374plugin_info_t xine_plugin_info[] EXPORTED = {
375  /* type, API, "name", version, special_info, init_function */
376  /* work around the problem that dlclose() is not allowed to
377   * get rid of an image module which contains objective C code and simply
378   * crashes with a Trace/BPT trap when we try to do so */
379  { PLUGIN_VIDEO_OUT | PLUGIN_NO_UNLOAD, 22, "macosx", XINE_VERSION_CODE, &vo_info_macosx, init_class },
380  { PLUGIN_NONE, 0, NULL, 0, NULL, NULL }
381};
382
383