3 * v4lmjpegsink_calls.c: functions for hardware MJPEG video sink
5 * Copyright (C) 2001-2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
28 #include <sys/types.h>
31 #include <sys/ioctl.h>
35 #include "v4lmjpegsink_calls.h"
37 /* On some systems MAP_FAILED seems to be missing */
39 #define MAP_FAILED ( (caddr_t) -1 )
42 GST_DEBUG_CATEGORY_EXTERN (v4lmjpegsink_debug);
43 #define GST_CAT_DEFAULT v4lmjpegsink_debug
45 /******************************************************
46 * gst_v4lmjpegsink_sync_thread()
47 * thread keeps track of played frames
48 ******************************************************/
51 gst_v4lmjpegsink_sync_thread (void *arg)
53 GstV4lMjpegSink *v4lmjpegsink = GST_V4LMJPEGSINK (arg);
54 gint frame = 0; /* frame that we're currently syncing on */
56 GST_DEBUG_OBJECT (v4lmjpegsink, "starting sync thread");
59 /* Allow easy shutting down by other processes... */
60 pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);
61 pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
65 g_mutex_lock (v4lmjpegsink->mutex_queued_frames);
66 if (!v4lmjpegsink->isqueued_queued_frames[frame]) {
67 g_cond_wait (v4lmjpegsink->cond_queued_frames[frame],
68 v4lmjpegsink->mutex_queued_frames);
70 if (v4lmjpegsink->isqueued_queued_frames[frame] != 1) {
71 g_mutex_unlock (v4lmjpegsink->mutex_queued_frames);
74 g_mutex_unlock (v4lmjpegsink->mutex_queued_frames);
76 GST_DEBUG_OBJECT (v4lmjpegsink, "thread-syncing on next frame");
77 if (ioctl (GST_V4LELEMENT (v4lmjpegsink)->video_fd, MJPIOC_SYNC,
78 &(v4lmjpegsink->bsync)) < 0) {
79 GST_ELEMENT_ERROR (v4lmjpegsink, RESOURCE, SYNC, (NULL),
80 ("Failed to sync on frame %d: %s", frame, g_strerror (errno)));
81 g_mutex_lock (v4lmjpegsink->mutex_queued_frames);
82 v4lmjpegsink->isqueued_queued_frames[frame] = -1;
83 g_cond_broadcast (v4lmjpegsink->cond_queued_frames[frame]);
84 g_mutex_unlock (v4lmjpegsink->mutex_queued_frames);
87 /* be sure that we're not confusing */
88 if (frame != v4lmjpegsink->bsync.frame) {
89 GST_ELEMENT_ERROR (v4lmjpegsink, CORE, TOO_LAZY, (NULL),
90 ("Internal error: frame number confusion"));
93 g_mutex_lock (v4lmjpegsink->mutex_queued_frames);
94 v4lmjpegsink->isqueued_queued_frames[frame] = 0;
95 g_cond_broadcast (v4lmjpegsink->cond_queued_frames[frame]);
96 g_mutex_unlock (v4lmjpegsink->mutex_queued_frames);
99 frame = (frame + 1) % v4lmjpegsink->breq.count;
103 GST_DEBUG_OBJECT (v4lmjpegsink, "Sync thread got signalled to exit");
104 g_thread_exit (NULL);
109 /******************************************************
110 * gst_v4lmjpegsink_queue_frame()
111 * queue a frame for playback
112 * return value: TRUE on success, FALSE on error
113 ******************************************************/
116 gst_v4lmjpegsink_queue_frame (GstV4lMjpegSink * v4lmjpegsink, gint num)
118 GST_DEBUG_OBJECT (v4lmjpegsink, "queueing frame %d", num);
120 /* queue on this frame */
121 if (ioctl (GST_V4LELEMENT (v4lmjpegsink)->video_fd, MJPIOC_QBUF_PLAY,
123 GST_ELEMENT_ERROR (v4lmjpegsink, RESOURCE, WRITE, (NULL),
124 ("Failed to queue frame %d: %s", num, g_strerror (errno)));
128 g_mutex_lock (v4lmjpegsink->mutex_queued_frames);
129 v4lmjpegsink->isqueued_queued_frames[num] = 1;
130 g_cond_broadcast (v4lmjpegsink->cond_queued_frames[num]);
131 g_mutex_unlock (v4lmjpegsink->mutex_queued_frames);
137 /******************************************************
138 * gst_v4lmjpegsink_sync_frame()
139 * wait for a frame to be finished playing
140 * return value: TRUE on success, FALSE on error
141 ******************************************************/
144 gst_v4lmjpegsink_sync_frame (GstV4lMjpegSink * v4lmjpegsink, gint * num)
146 GST_DEBUG_OBJECT (v4lmjpegsink, "syncing on next frame");
148 /* calculate next frame */
149 v4lmjpegsink->current_frame =
150 (v4lmjpegsink->current_frame + 1) % v4lmjpegsink->breq.count;
151 *num = v4lmjpegsink->current_frame;
153 g_mutex_lock (v4lmjpegsink->mutex_queued_frames);
154 if (v4lmjpegsink->isqueued_queued_frames[*num] == 1) {
155 g_cond_wait (v4lmjpegsink->cond_queued_frames[*num],
156 v4lmjpegsink->mutex_queued_frames);
158 if (v4lmjpegsink->isqueued_queued_frames[*num] != 0) {
159 g_mutex_unlock (v4lmjpegsink->mutex_queued_frames);
162 g_mutex_unlock (v4lmjpegsink->mutex_queued_frames);
168 /******************************************************
169 * gst_v4lmjpegsink_set_buffer()
171 * return value: TRUE on success, FALSE on error
172 ******************************************************/
175 gst_v4lmjpegsink_set_buffer (GstV4lMjpegSink * v4lmjpegsink,
176 gint numbufs, gint bufsize)
178 GST_DEBUG_OBJECT (v4lmjpegsink,
179 "setting buffer info to numbufs = %d, bufsize = %d KB", numbufs, bufsize);
180 GST_V4L_CHECK_OPEN (GST_V4LELEMENT (v4lmjpegsink));
181 GST_V4L_CHECK_NOT_ACTIVE (GST_V4LELEMENT (v4lmjpegsink));
183 v4lmjpegsink->breq.size = bufsize * 1024;
184 v4lmjpegsink->breq.count = numbufs;
190 /******************************************************
191 * gst_v4lmjpegsink_set_playback()
192 * set playback options (video, interlacing, etc.)
193 * return value: TRUE on success, FALSE on error
194 ******************************************************/
197 gst_v4lmjpegsink_set_playback (GstV4lMjpegSink * v4lmjpegsink,
199 gint height, gint x_offset, gint y_offset, gint norm, gint interlacing)
202 struct mjpeg_params bparm;
204 GST_DEBUG_OBJECT (v4lmjpegsink,
205 "setting size=%dx%d, X/Y offsets=%d/%d, norm=%d, interlacing=%d\n",
206 width, height, x_offset, y_offset, norm, interlacing);
207 GST_V4L_CHECK_OPEN (GST_V4LELEMENT (v4lmjpegsink));
208 /*GST_V4L_CHECK_NOT_ACTIVE(GST_V4LELEMENT(v4lmjpegsink)); */
210 if (ioctl (GST_V4LELEMENT (v4lmjpegsink)->video_fd, MJPIOC_G_PARAMS,
212 GST_ELEMENT_ERROR (v4lmjpegsink, RESOURCE, SETTINGS, (NULL),
219 bparm.decimation = 0; /* we'll set proper values later on */
221 /* maxwidth is broken on marvel cards */
222 mw = GST_V4LELEMENT (v4lmjpegsink)->vcap.maxwidth;
223 if (mw != 768 && mw != 640)
225 mh = (norm == VIDEO_MODE_NTSC ? 480 : 576);
227 if (width > mw || height > mh) {
228 GST_ELEMENT_ERROR (v4lmjpegsink, RESOURCE, TOO_LAZY, (NULL),
229 ("Video dimensions (%dx%d) are larger than device max (%dx%d)",
230 width, height, mw, mh));
236 else if (width <= mw / 2)
241 /* TODO: add proper interlacing handling */
243 if (interlacing != INTERLACING_NOT_INTERLACED) {
244 bparm.field_per_buff = 2;
247 if (height <= mh / 2)
254 if (height > mh / 2) {
255 GST_ELEMENT_ERROR (v4lmjpegsink, RESOURCE, TOO_LAZY, (NULL),
256 ("Video dimensions (%dx%d) too large for non-interlaced playback (%dx%d)",
257 width, height, mw, mh / 2));
261 bparm.field_per_buff = 1;
264 if (height <= mh / 4)
270 /* TODO: add proper interlacing handling */
272 bparm.odd_even = (interlacing == INTERLACING_TOP_FIRST);
276 bparm.img_width = bparm.HorDcm * width;
277 bparm.img_height = bparm.VerDcm * height / bparm.field_per_buff;
279 /* image X/Y offset on device */
281 bparm.img_x = (mw - bparm.img_width) / 2;
283 if (x_offset + bparm.img_width > mw)
284 bparm.img_x = mw - bparm.img_width;
286 bparm.img_x = x_offset;
290 bparm.img_y = (mh / 2 - bparm.img_height) / 2;
292 if (y_offset + bparm.img_height * 2 > mh)
293 bparm.img_y = mh / 2 - bparm.img_height;
295 bparm.img_y = y_offset / 2;
298 if (ioctl (GST_V4LELEMENT (v4lmjpegsink)->video_fd, MJPIOC_S_PARAMS,
300 GST_ELEMENT_ERROR (v4lmjpegsink, RESOURCE, SETTINGS, (NULL),
309 /******************************************************
310 * gst_v4lmjpegsink_playback_init()
311 * initialize playback system, set up buffer, etc.
312 * return value: TRUE on success, FALSE on error
313 ******************************************************/
316 gst_v4lmjpegsink_playback_init (GstV4lMjpegSink * v4lmjpegsink)
320 GST_DEBUG_OBJECT (v4lmjpegsink, "initting playback subsystem");
321 GST_V4L_CHECK_OPEN (GST_V4LELEMENT (v4lmjpegsink));
322 GST_V4L_CHECK_NOT_ACTIVE (GST_V4LELEMENT (v4lmjpegsink));
324 /* Request buffers */
325 if (ioctl (GST_V4LELEMENT (v4lmjpegsink)->video_fd, MJPIOC_REQBUFS,
326 &(v4lmjpegsink->breq)) < 0) {
327 GST_ELEMENT_ERROR (v4lmjpegsink, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
331 GST_INFO_OBJECT (v4lmjpegsink, "Got %ld buffers of size %ld KB",
332 v4lmjpegsink->breq.count, v4lmjpegsink->breq.size / 1024);
334 /* Map the buffers */
335 GST_V4LELEMENT (v4lmjpegsink)->buffer = mmap (0,
336 v4lmjpegsink->breq.count * v4lmjpegsink->breq.size,
337 PROT_READ | PROT_WRITE, MAP_SHARED,
338 GST_V4LELEMENT (v4lmjpegsink)->video_fd, 0);
339 if (GST_V4LELEMENT (v4lmjpegsink)->buffer == MAP_FAILED) {
340 GST_ELEMENT_ERROR (v4lmjpegsink, RESOURCE, TOO_LAZY, (NULL),
341 ("Error mapping video buffers: %s", g_strerror (errno)));
342 GST_V4LELEMENT (v4lmjpegsink)->buffer = NULL;
346 /* allocate/init the GThread thingies */
347 v4lmjpegsink->mutex_queued_frames = g_mutex_new ();
348 v4lmjpegsink->isqueued_queued_frames = (gint8 *)
349 malloc (sizeof (gint8) * v4lmjpegsink->breq.count);
350 if (!v4lmjpegsink->isqueued_queued_frames) {
351 GST_ELEMENT_ERROR (v4lmjpegsink, RESOURCE, TOO_LAZY, (NULL),
352 ("Failed to create queue tracker: %s", g_strerror (errno)));
355 v4lmjpegsink->cond_queued_frames = (GCond **)
356 malloc (sizeof (GCond *) * v4lmjpegsink->breq.count);
357 if (!v4lmjpegsink->cond_queued_frames) {
358 GST_ELEMENT_ERROR (v4lmjpegsink, RESOURCE, TOO_LAZY, (NULL),
359 ("Failed to create queue condition holders: %s", g_strerror (errno)));
362 for (n = 0; n < v4lmjpegsink->breq.count; n++)
363 v4lmjpegsink->cond_queued_frames[n] = g_cond_new ();
369 /******************************************************
370 * gst_v4lmjpegsink_playback_start()
371 * start playback system
372 * return value: TRUE on success, FALSE on error
373 ******************************************************/
376 gst_v4lmjpegsink_playback_start (GstV4lMjpegSink * v4lmjpegsink)
381 GST_DEBUG_OBJECT (v4lmjpegsink, "starting playback");
382 GST_V4L_CHECK_OPEN (GST_V4LELEMENT (v4lmjpegsink));
383 GST_V4L_CHECK_ACTIVE (GST_V4LELEMENT (v4lmjpegsink));
385 /* mark all buffers as unqueued */
386 for (n = 0; n < v4lmjpegsink->breq.count; n++)
387 v4lmjpegsink->isqueued_queued_frames[n] = 0;
389 v4lmjpegsink->current_frame = -1;
391 /* create sync() thread */
392 v4lmjpegsink->thread_queued_frames =
393 g_thread_create (gst_v4lmjpegsink_sync_thread, (void *) v4lmjpegsink,
395 if (!v4lmjpegsink->thread_queued_frames) {
396 GST_ELEMENT_ERROR (v4lmjpegsink, RESOURCE, TOO_LAZY, (NULL),
397 ("Failed to create sync thread: %s", error->message));
405 /******************************************************
406 * gst_v4lmjpegsink_get_buffer()
407 * get address of a buffer
408 * return value: buffer's address or NULL
409 ******************************************************/
412 gst_v4lmjpegsink_get_buffer (GstV4lMjpegSink * v4lmjpegsink, gint num)
414 /*GST_DEBUG_OBJECT (v4lmjpegsink, gst_v4lmjpegsink_get_buffer(), num = %d", num); */
416 if (!GST_V4L_IS_ACTIVE (GST_V4LELEMENT (v4lmjpegsink)) ||
417 !GST_V4L_IS_OPEN (GST_V4LELEMENT (v4lmjpegsink)))
420 if (num < 0 || num >= v4lmjpegsink->breq.count)
423 return GST_V4LELEMENT (v4lmjpegsink)->buffer +
424 (v4lmjpegsink->breq.size * num);
428 /******************************************************
429 * gst_v4lmjpegsink_play_frame()
431 * return value: TRUE on success, FALSE on error
432 ******************************************************/
435 gst_v4lmjpegsink_play_frame (GstV4lMjpegSink * v4lmjpegsink, gint num)
437 GST_DEBUG_OBJECT (v4lmjpegsink, "playing frame %d", num);
438 GST_V4L_CHECK_OPEN (GST_V4LELEMENT (v4lmjpegsink));
439 GST_V4L_CHECK_ACTIVE (GST_V4LELEMENT (v4lmjpegsink));
441 if (!gst_v4lmjpegsink_queue_frame (v4lmjpegsink, num))
448 /******************************************************
449 * gst_v4lmjpegsink_wait_frame()
450 * wait for buffer to be actually played
451 * return value: TRUE on success, FALSE on error
452 ******************************************************/
455 gst_v4lmjpegsink_wait_frame (GstV4lMjpegSink * v4lmjpegsink, gint * num)
457 GST_DEBUG_OBJECT (v4lmjpegsink,
458 "waiting for next frame to be finished playing");
459 GST_V4L_CHECK_OPEN (GST_V4LELEMENT (v4lmjpegsink));
460 GST_V4L_CHECK_ACTIVE (GST_V4LELEMENT (v4lmjpegsink));
462 if (!gst_v4lmjpegsink_sync_frame (v4lmjpegsink, num))
469 /******************************************************
470 * gst_v4lmjpegsink_playback_stop()
471 * stop playback system and sync on remaining frames
472 * return value: TRUE on success, FALSE on error
473 ******************************************************/
476 gst_v4lmjpegsink_playback_stop (GstV4lMjpegSink * v4lmjpegsink)
480 GST_DEBUG_OBJECT (v4lmjpegsink, "stopping playback");
481 GST_V4L_CHECK_OPEN (GST_V4LELEMENT (v4lmjpegsink));
482 GST_V4L_CHECK_ACTIVE (GST_V4LELEMENT (v4lmjpegsink));
484 /* mark next buffer as wrong */
485 if (!gst_v4lmjpegsink_sync_frame (v4lmjpegsink, &num) ||
486 !gst_v4lmjpegsink_queue_frame (v4lmjpegsink, num)) {
490 /* .. and wait for all buffers to be queued on */
491 g_thread_join (v4lmjpegsink->thread_queued_frames);
497 /******************************************************
498 * gst_v4lmjpegsink_playback_deinit()
499 * deinitialize the playback system and unmap buffer
500 * return value: TRUE on success, FALSE on error
501 ******************************************************/
504 gst_v4lmjpegsink_playback_deinit (GstV4lMjpegSink * v4lmjpegsink)
508 GST_DEBUG_OBJECT (v4lmjpegsink, "quitting playback subsystem");
509 GST_V4L_CHECK_OPEN (GST_V4LELEMENT (v4lmjpegsink));
510 GST_V4L_CHECK_ACTIVE (GST_V4LELEMENT (v4lmjpegsink));
512 /* free GThread thingies */
513 g_mutex_free (v4lmjpegsink->mutex_queued_frames);
514 for (n = 0; n < v4lmjpegsink->breq.count; n++)
515 g_cond_free (v4lmjpegsink->cond_queued_frames[n]);
516 free (v4lmjpegsink->cond_queued_frames);
517 free (v4lmjpegsink->isqueued_queued_frames);
519 /* unmap the buffer */
520 munmap (GST_V4LELEMENT (v4lmjpegsink)->buffer,
521 v4lmjpegsink->breq.size * v4lmjpegsink->breq.count);
522 GST_V4LELEMENT (v4lmjpegsink)->buffer = NULL;