2 * GStreamer DVB Media Sink
3 * Copyright 2006 Felix Domke <tmbinc@elitedvb.net>
5 * Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
6 * Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * Alternatively, the contents of this file may be used under the
27 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
28 * which case the following provisions apply instead of the ones
31 * This library is free software; you can redistribute it and/or
32 * modify it under the terms of the GNU Library General Public
33 * License as published by the Free Software Foundation; either
34 * version 2 of the License, or (at your option) any later version.
36 * This library is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
39 * Library General Public License for more details.
41 * You should have received a copy of the GNU Library General Public
42 * License along with this library; if not, write to the
43 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
44 * Boston, MA 02111-1307, USA.
48 * SECTION:element-plugin
51 * <title>Example launch line</title>
54 * gst-launch -v -m audiotestsrc ! plugin ! fakesink silent=TRUE
64 #include <sys/ioctl.h>
65 #include <sys/socket.h>
66 #include <linux/dvb/video.h>
72 #include "gstdvbvideosink.h"
75 #define VIDEO_GET_PTS _IOR('o', 57, gint64)
78 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
86 void bitstream_init(struct bitstream *bit, const void *buffer, gboolean wr)
88 bit->data = (guint8*) buffer;
95 bit->last = *bit->data++;
99 unsigned long bitstream_get(struct bitstream *bit, int bits)
106 bit->last = *bit->data++;
112 res|=(bit->last>>(bit->avail-d))&~(-1<<d);
119 void bitstream_put(struct bitstream *bit, unsigned long val, int bits)
123 bit->last |= ((val & (1 << (bits-1))) ? 1 : 0) << (7 - bit->avail);
124 if (++bit->avail == 8) {
125 *bit->data = bit->last;
135 /* We add a control socket as in fdsrc to make it shutdown quickly when it's blocking on the fd.
136 * Select is used to determine when the fd is ready for use. When the element state is changed,
137 * it happens from another thread while fdsink is select'ing on the fd. The state-change thread
138 * sends a control message, so fdsink wakes up and changes state immediately otherwise
139 * it would stay blocked until it receives some data. */
141 /* the select call is also performed on the control sockets, that way
142 * we can send special commands to unblock the select call */
143 #define CONTROL_STOP 'S' /* stop the select call */
144 #define CONTROL_SOCKETS(sink) sink->control_sock
145 #define WRITE_SOCKET(sink) sink->control_sock[1]
146 #define READ_SOCKET(sink) sink->control_sock[0]
148 #define SEND_COMMAND(sink, command) \
150 unsigned char c; c = command; \
151 write (WRITE_SOCKET(sink), &c, 1); \
154 #define READ_COMMAND(sink, command, res) \
156 res = read(READ_SOCKET(sink), &command, 1); \
159 GST_DEBUG_CATEGORY_STATIC (dvbvideosink_debug);
160 #define GST_CAT_DEFAULT dvbvideosink_debug
162 /* Filter signals and args */
173 #define COMMON_VIDEO_CAPS \
174 "width = (int) [ 16, 4096 ], " \
175 "height = (int) [ 16, 4096 ], " \
176 "framerate = (fraction) [ 0, MAX ]"
178 static GstStaticPadTemplate sink_factory =
179 GST_STATIC_PAD_TEMPLATE (
185 "mpegversion = (int) { 1, 2, 4 }, "
186 "systemstream = (boolean) false, "
187 COMMON_VIDEO_CAPS "; "
189 COMMON_VIDEO_CAPS "; "
191 COMMON_VIDEO_CAPS "; "
193 COMMON_VIDEO_CAPS ", divxversion = (int) [ 3, 5 ]; "
195 COMMON_VIDEO_CAPS "; ")
198 #define DEBUG_INIT(bla) \
199 GST_DEBUG_CATEGORY_INIT (dvbvideosink_debug, "dvbvideosink", 0, "dvbvideosink element");
201 GST_BOILERPLATE_FULL (GstDVBVideoSink, gst_dvbvideosink, GstBaseSink,
202 GST_TYPE_BASE_SINK, DEBUG_INIT);
204 static void gst_dvbvideosink_set_property (GObject *object, guint prop_id,
207 static void gst_dvbvideosink_get_property (GObject *object, guint prop_id,
211 static gboolean gst_dvbvideosink_start (GstBaseSink * sink);
212 static gboolean gst_dvbvideosink_stop (GstBaseSink * sink);
213 static gboolean gst_dvbvideosink_event (GstBaseSink * sink, GstEvent * event);
214 static GstFlowReturn gst_dvbvideosink_render (GstBaseSink * sink,
216 static gboolean gst_dvbvideosink_query (GstElement * element, GstQuery * query);
217 static gboolean gst_dvbvideosink_set_caps (GstBaseSink * sink, GstCaps * caps);
218 static gboolean gst_dvbvideosink_unlock (GstBaseSink * basesink);
221 gst_dvbvideosink_base_init (gpointer klass)
223 static GstElementDetails element_details = {
225 "Generic/DVBVideoSink",
226 "Output video PES / ES into a DVB video device for hardware playback",
227 "Felix Domke <tmbinc@elitedvb.net>"
229 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
231 gst_element_class_add_pad_template (element_class,
232 gst_static_pad_template_get (&sink_factory));
233 gst_element_class_set_details (element_class, &element_details);
236 /* initialize the plugin's class */
238 gst_dvbvideosink_class_init (GstDVBVideoSinkClass *klass)
240 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
241 GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
243 gobject_class->set_property = gst_dvbvideosink_set_property;
244 gobject_class->get_property = gst_dvbvideosink_get_property;
246 gobject_class = G_OBJECT_CLASS (klass);
247 g_object_class_install_property (gobject_class, ARG_SILENT,
248 g_param_spec_boolean ("silent", "Silent", "Produce verbose output ?",
249 FALSE, G_PARAM_READWRITE));
251 gstbasesink_class->get_times = NULL;
252 gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_dvbvideosink_start);
253 gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_dvbvideosink_stop);
254 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_dvbvideosink_render);
255 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_dvbvideosink_event);
256 gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_dvbvideosink_unlock);
257 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_dvbvideosink_set_caps);
258 GST_ELEMENT_CLASS (klass)->query = GST_DEBUG_FUNCPTR (gst_dvbvideosink_query);
261 /* initialize the new element
262 * instantiate pads and add them to element
264 * initialize structure
267 gst_dvbvideosink_init (GstDVBVideoSink *klass,
268 GstDVBVideoSinkClass * gclass)
270 klass->silent = FALSE;
271 klass->must_send_header = TRUE;
272 klass->codec_data = NULL;
273 klass->codec_type = CT_H264;
274 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
275 klass->must_pack_bitstream = 0;
276 klass->num_non_keyframes = 0;
277 klass->prev_frame = NULL;
279 GST_BASE_SINK (klass)->sync = FALSE;
283 gst_dvbvideosink_set_property (GObject *object, guint prop_id,
284 const GValue *value, GParamSpec *pspec)
286 GstDVBVideoSink *filter;
288 g_return_if_fail (GST_IS_DVBVIDEOSINK (object));
289 filter = GST_DVBVIDEOSINK (object);
294 filter->silent = g_value_get_boolean (value);
297 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
303 gst_dvbvideosink_get_property (GObject *object, guint prop_id,
304 GValue *value, GParamSpec *pspec)
306 GstDVBVideoSink *filter;
308 g_return_if_fail (GST_IS_DVBVIDEOSINK (object));
309 filter = GST_DVBVIDEOSINK (object);
313 g_value_set_boolean (value, filter->silent);
316 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
322 gst_dvbvideosink_query (GstElement * element, GstQuery * query)
324 GstDVBVideoSink *self = GST_DVBVIDEOSINK (element);
326 // printf("QUERY: type: %d (%d)\n", GST_QUERY_TYPE(query), GST_QUERY_POSITION);
328 switch (GST_QUERY_TYPE (query)) {
329 case GST_QUERY_POSITION:
334 gst_query_parse_position (query, &format, NULL);
336 if (format != GST_FORMAT_TIME)
339 ioctl(self->fd, VIDEO_GET_PTS, &cur);
340 // printf("PTS: %08llx", cur);
344 gst_query_set_position (query, format, cur);
346 GST_DEBUG_OBJECT (self, "position format %d", format);
351 return GST_ELEMENT_CLASS (parent_class)->query (element, query);
355 static gboolean gst_dvbvideosink_unlock (GstBaseSink * basesink)
357 GstDVBVideoSink *self = GST_DVBVIDEOSINK (basesink);
359 SEND_COMMAND (self, CONTROL_STOP);
365 gst_dvbvideosink_event (GstBaseSink * sink, GstEvent * event)
367 GstDVBVideoSink *self = GST_DVBVIDEOSINK (sink);
368 GST_DEBUG_OBJECT (self, "EVENT %s", gst_event_type_get_name(GST_EVENT_TYPE (event)));
370 switch (GST_EVENT_TYPE (event)) {
371 case GST_EVENT_FLUSH_START:
372 ioctl(self->fd, VIDEO_CLEAR_BUFFER);
373 self->must_send_header = TRUE;
375 case GST_EVENT_FLUSH_STOP:
376 ioctl(self->fd, VIDEO_CLEAR_BUFFER);
377 self->must_send_header = TRUE;
383 READ_COMMAND (self, command, res);
394 gst_dvbvideosink_render (GstBaseSink * sink, GstBuffer * buffer)
396 GstDVBVideoSink *self = GST_DVBVIDEOSINK (sink);
397 unsigned char *data = GST_BUFFER_DATA(buffer);
398 unsigned int data_len = GST_BUFFER_SIZE (buffer);
399 guint8 pes_header[2048];
400 unsigned int pes_header_len=0;
401 unsigned int payload_len=0;
408 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
409 gboolean commit_prev_frame_data = FALSE,
410 cache_prev_frame = FALSE;
413 // for (;i < (data_len > 0xF ? 0xF : data_len); ++i)
414 // printf("%02x ", data[i]);
415 // printf("%d bytes\n", data_len);
417 // printf("timestamp: %08llx\n", (long long)GST_BUFFER_TIMESTAMP(buffer));
420 FD_SET (READ_SOCKET (self), &readfds);
423 FD_SET (self->fd, &writefds);
426 FD_SET (self->fd, &priofds);
429 GST_DEBUG_OBJECT (self, "going into select, have %d bytes to write",
431 retval = select (FD_SETSIZE, &readfds, &writefds, &priofds, NULL);
432 } while ((retval == -1 && errno == EINTR));
437 if (FD_ISSET (READ_SOCKET (self), &readfds)) {
438 /* read all stop commands */
443 READ_COMMAND (self, command, res);
445 GST_LOG_OBJECT (self, "no more commands");
446 /* no more commands */
453 if (FD_ISSET (self->fd, &priofds))
458 struct video_event evt;
459 if (ioctl(self->fd, VIDEO_GET_EVENT, &evt) < 0)
460 g_warning ("failed to ioctl VIDEO_GET_EVENT!");
463 if (evt.type == VIDEO_EVENT_SIZE_CHANGED)
465 s = gst_structure_new ("eventSizeChanged",
466 "aspect_ratio", G_TYPE_INT, evt.u.size.aspect_ratio == 0 ? 2 : 3,
467 "width", G_TYPE_INT, evt.u.size.w,
468 "height", G_TYPE_INT, evt.u.size.h, NULL);
469 msg = gst_message_new_element (GST_OBJECT (sink), s);
470 gst_element_post_message (GST_ELEMENT (sink), msg);
472 else if (evt.type == VIDEO_EVENT_FRAME_RATE_CHANGED)
474 s = gst_structure_new ("eventFrameRateChanged",
475 "frame_rate", G_TYPE_INT, evt.u.frame_rate, NULL);
476 msg = gst_message_new_element (GST_OBJECT (sink), s);
477 gst_element_post_message (GST_ELEMENT (sink), msg);
479 else if (evt.type == 16 /*VIDEO_EVENT_PROGRESSIVE_CHANGED*/)
481 s = gst_structure_new ("eventProgressiveChanged",
482 "progressive", G_TYPE_INT, evt.u.frame_rate, NULL);
483 msg = gst_message_new_element (GST_OBJECT (sink), s);
484 gst_element_post_message (GST_ELEMENT (sink), msg);
487 g_warning ("unhandled DVBAPI Video Event %d", evt.type);
494 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
495 if (self->must_pack_bitstream == 1) {
496 cache_prev_frame = TRUE;
497 unsigned int pos = 0;
498 while(pos < data_len) {
505 if (data[pos++] != 1)
507 if ((data[pos++] & 0xF0) == 0x20) { // we need time_inc_res
508 gboolean low_delay=FALSE;
509 unsigned int ver_id = 1, shape=0, time_inc_res=0, tmp=0;
510 struct bitstream bit;
511 bitstream_init(&bit, data+pos, 0);
512 bitstream_get(&bit, 9);
513 if (bitstream_get(&bit, 1)) {
514 ver_id = bitstream_get(&bit, 4); // ver_id
515 bitstream_get(&bit, 3);
517 if ((tmp=bitstream_get(&bit, 4)) == 15) { // Custom Aspect Ration
518 bitstream_get(&bit, 8); // skip AR width
519 bitstream_get(&bit, 8); // skip AR height
521 if (bitstream_get(&bit, 1)) {
522 bitstream_get(&bit, 2);
523 low_delay = bitstream_get(&bit, 1) ? TRUE : FALSE;
524 if (bitstream_get(&bit, 1)) {
525 bitstream_get(&bit, 32);
526 bitstream_get(&bit, 32);
527 bitstream_get(&bit, 15);
530 shape = bitstream_get(&bit, 2);
531 if (ver_id != 1 && shape == 3 /* Grayscale */)
532 bitstream_get(&bit, 4);
533 bitstream_get(&bit, 1);
534 time_inc_res = bitstream_get(&bit, 16);
535 self->time_inc_bits = 0;
536 while (time_inc_res) { // count bits
537 ++self->time_inc_bits;
540 // printf("%d time_inc_bits\n", self->time_inc_bits);
545 if (self->must_pack_bitstream == 1) {
547 unsigned char c1, c2;
548 unsigned int pos = 0;
549 while(pos < data_len) {
556 if (data[pos++] != 1)
558 if (data[pos++] != 0xB2)
560 if (data_len - pos < 13)
562 if (sscanf((char*)data+pos, "DivX%d%c%d%cp", &tmp1, &c1, &tmp2, &c2) == 4 && (c1 == 'b' || c1 == 'B') && (c2 == 'p' || c2 == 'P')) {
563 printf("%s seen... already packed!\n", (char*)data+pos);
564 self->must_pack_bitstream = 0;
566 // if (self->must_pack_bitstream)
567 // printf("pack needed\n");
569 // printf("no pack needed\n");
577 pes_header[3] = 0xE0;
579 /* do we have a timestamp? */
580 if (GST_BUFFER_TIMESTAMP(buffer) != GST_CLOCK_TIME_NONE) {
581 unsigned long long pts = GST_BUFFER_TIMESTAMP(buffer) * 9LL / 100000 /* convert ns to 90kHz */;
583 pes_header[6] = 0x80;
584 pes_header[7] = 0x80;
588 pes_header[9] = 0x21 | ((pts >> 29) & 0xE);
589 pes_header[10] = pts >> 22;
590 pes_header[11] = 0x01 | ((pts >> 14) & 0xFE);
591 pes_header[12] = pts >> 7;
592 pes_header[13] = 0x01 | ((pts << 1) & 0xFE);
596 if (self->codec_data) {
597 if (self->must_send_header) {
598 unsigned char *codec_data = GST_BUFFER_DATA (self->codec_data);
599 unsigned int codec_data_len = GST_BUFFER_SIZE (self->codec_data);
600 if (self->codec_type == CT_DIVX311)
601 write(self->fd, codec_data, codec_data_len);
603 memcpy(pes_header+pes_header_len, codec_data, codec_data_len);
604 pes_header_len += codec_data_len;
606 self->must_send_header = FALSE;
608 if (self->codec_type == CT_H264) { // MKV stuff
609 unsigned int pos = 0;
611 unsigned int pack_len = (data[pos] << 24) | (data[pos+1] << 16) | (data[pos+2] << 8) | data[pos+3];
612 // printf("patch %02x %02x %02x %02x\n",
617 memcpy(data+pos, "\x00\x00\x00\x01", 4);
618 // printf("pos %d, (%d) >= %d\n", pos, pos+pack_len, data_len);
620 if ((pos + pack_len) >= data_len)
625 else if (self->codec_type == CT_MPEG4_PART2) {
626 memcpy(pes_header+pes_header_len, "\x00\x00\x01", 3);
629 else if (self->codec_type == CT_DIVX311) {
630 memcpy(pes_header+pes_header_len, "\x00\x00\x01\xb6", 4);
636 // printf("no timestamp!\n");
637 pes_header[6] = 0x80;
638 pes_header[7] = 0x00;
643 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
644 if (self->must_pack_bitstream == 1) {
645 unsigned int pos = 0;
646 gboolean i_frame = FALSE;
647 // gboolean s_frame = FALSE;
648 while(pos < data_len) {
655 if (data[pos++] != 1)
657 if (data[pos++] != 0xB6)
659 switch ((data[pos] & 0xC0) >> 6) {
662 cache_prev_frame = FALSE;
667 if (self->prev_frame != buffer) {
668 struct bitstream bit;
669 gboolean store_frame=FALSE;
670 if (self->prev_frame) {
671 if (!self->num_non_keyframes) {
672 // printf("no non keyframes...immediate commit prev frame\n");
673 GstFlowReturn ret = gst_dvbvideosink_render(sink, self->prev_frame);
674 gst_buffer_unref(self->prev_frame);
675 self->prev_frame = NULL;
676 if (ret != GST_FLOW_OK)
682 pes_header[pes_header_len++] = 0;
683 pes_header[pes_header_len++] = 0;
684 pes_header[pes_header_len++] = 1;
685 pes_header[pes_header_len++] = 0xB6;
686 bitstream_init(&bit, pes_header+pes_header_len, 1);
687 bitstream_put(&bit, 1, 2);
688 bitstream_put(&bit, 0, 1);
689 bitstream_put(&bit, 1, 1);
690 bitstream_put(&bit, self->time_inc, self->time_inc_bits);
691 bitstream_put(&bit, 1, 1);
692 bitstream_put(&bit, 0, 1);
693 bitstream_put(&bit, 0x7F >> bit.avail, 8 - bit.avail);
694 // printf(" insert pack frame %d non keyframes, time_inc %d, time_inc_bits %d -",
695 // self->num_non_keyframes, self->time_inc, self->time_inc_bits);
696 // for (; i < (bit.data - (pes_header+pes_header_len)); ++i)
697 // printf(" %02x", pes_header[pes_header_len+i]);
698 // printf("\nset data_len to 0!\n");
700 pes_header_len += bit.data - (pes_header+pes_header_len);
701 cache_prev_frame = TRUE;
707 self->num_non_keyframes=0;
710 bitstream_init(&bit, data+pos, 0);
711 bitstream_get(&bit, 2); // skip coding_type
712 while(bitstream_get(&bit, 1));
713 bitstream_get(&bit, 1);
714 self->time_inc = bitstream_get(&bit, self->time_inc_bits);
715 // printf("\ntime_inc is %d\n", self->time_inc);
718 // printf("store frame\n");
719 self->prev_frame = buffer;
720 gst_buffer_ref (buffer);
725 cache_prev_frame = FALSE;
726 // printf(" I/P Frame without non key frame(s)!!\n");
735 if (++self->num_non_keyframes == 1 && self->prev_frame) {
736 // printf("send grouped with prev P!\n");
737 commit_prev_frame_data = TRUE;
742 printf("unhandled divx5/xvid frame type %d\n", (data[pos] & 0xC0) >> 6);
750 payload_len = data_len + pes_header_len - 6;
752 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
753 if (self->prev_frame && self->prev_frame != buffer) {
754 unsigned long long pts = GST_BUFFER_TIMESTAMP(self->prev_frame) * 9LL / 100000 /* convert ns to 90kHz */;
755 // printf("use prev timestamp: %08llx\n", (long long)GST_BUFFER_TIMESTAMP(self->prev_frame));
757 pes_header[9] = 0x21 | ((pts >> 29) & 0xE);
758 pes_header[10] = pts >> 22;
759 pes_header[11] = 0x01 | ((pts >> 14) & 0xFE);
760 pes_header[12] = pts >> 7;
761 pes_header[13] = 0x01 | ((pts << 1) & 0xFE);
764 if (commit_prev_frame_data)
765 payload_len += GST_BUFFER_SIZE (self->prev_frame);
768 if (payload_len <= 0xFFFF) {
769 pes_header[4] = payload_len >> 8;
770 pes_header[5] = payload_len & 0xFF;
777 write(self->fd, pes_header, pes_header_len);
779 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
780 if (commit_prev_frame_data) {
781 // printf("commit prev frame data\n");
782 write(self->fd, GST_BUFFER_DATA (self->prev_frame), GST_BUFFER_SIZE (self->prev_frame));
785 if (self->prev_frame && self->prev_frame != buffer) {
786 // printf("unref prev_frame buffer\n");
787 gst_buffer_unref(self->prev_frame);
788 self->prev_frame = NULL;
791 if (cache_prev_frame) {
792 // printf("cache prev frame\n");
793 gst_buffer_ref(buffer);
794 self->prev_frame = buffer;
798 write(self->fd, data, data_len);
803 GST_ELEMENT_ERROR (self, RESOURCE, READ, (NULL),
804 ("select on file descriptor: %s.", g_strerror (errno)));
805 GST_DEBUG_OBJECT (self, "Error during select");
806 return GST_FLOW_ERROR;
810 GST_DEBUG_OBJECT (self, "Select stopped");
811 ioctl(self->fd, VIDEO_CLEAR_BUFFER);
812 return GST_FLOW_WRONG_STATE;
817 gst_dvbvideosink_set_caps (GstBaseSink * basesink, GstCaps * caps)
819 GstDVBVideoSink *self = GST_DVBVIDEOSINK (basesink);
820 GstStructure *structure = gst_caps_get_structure (caps, 0);
821 const char *mimetype = gst_structure_get_name (structure);
824 if (!strcmp (mimetype, "video/mpeg")) {
826 gst_structure_get_int (structure, "mpegversion", &mpegversion);
827 switch (mpegversion) {
830 printf("MIMETYPE video/mpeg1 -> VIDEO_SET_STREAMTYPE, 6\n");
834 printf("MIMETYPE video/mpeg2 -> VIDEO_SET_STREAMTYPE, 0\n");
838 const GValue *codec_data = gst_structure_get_value (structure, "codec_data");
840 printf("MPEG4 have codec data\n");
841 self->codec_data = gst_value_get_buffer (codec_data);
842 self->codec_type = CT_MPEG4_PART2;
843 gst_buffer_ref (self->codec_data);
846 printf("MIMETYPE video/mpeg4 -> VIDEO_SET_STREAMTYPE, 4\n");
850 GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("unhandled mpeg version %i", mpegversion));
853 } else if (!strcmp (mimetype, "video/x-h264")) {
854 const GValue *cd_data = gst_structure_get_value (structure, "codec_data");
857 unsigned char tmp[2048];
858 unsigned int tmp_len = 0;
859 GstBuffer *codec_data = gst_value_get_buffer (cd_data);
860 unsigned char *data = GST_BUFFER_DATA (codec_data);
861 unsigned int cd_len = GST_BUFFER_SIZE (codec_data);
862 unsigned int cd_pos = 0;
863 printf("H264 have codec data..!\n");
866 unsigned short len = (data[6] << 8) | data[7];
867 // printf("2 %d bytes\n", len);
868 if (cd_len >= (len + 8)) {
871 memcpy(tmp, "\x00\x00\x00\x01", 4);
873 // printf("header part1 ");
874 // for (i = 0; i < len; ++i)
875 // printf("%02x ", data[8+i]);
877 memcpy(tmp+tmp_len, data+8, len);
878 if (!memcmp(tmp+tmp_len, "\x67\x64\x00", 3)) {
879 tmp[tmp_len+3] = 0x29; // hardcode h264 level 4.1
880 printf("h264 level patched!\n");
884 if (cd_len > (cd_pos + 2)) {
885 len = (data[cd_pos+1] << 8) | data[cd_pos+2];
886 // printf("4 %d bytes\n", len);
888 if (cd_len >= (cd_pos+len)) {
889 // printf("codec data ok!\n");
890 memcpy(tmp+tmp_len, "\x00\x00\x00\x01", 4);
892 // printf("header part2 %02x %02x %02x %02x ... %d bytes\n", data[cd_pos], data[cd_pos+1], data[cd_pos+2], data[cd_pos+3], len);
893 memcpy(tmp+tmp_len, data+cd_pos, len);
895 self->codec_data = gst_buffer_new_and_alloc(tmp_len);
896 memcpy(GST_BUFFER_DATA(self->codec_data), tmp, tmp_len);
899 printf("codec_data to short(4)\n");
902 printf("codec_data to short(3)!\n");
905 printf("codec_data to short(2)!\n");
908 printf("codec_data to short(1)!\n");
910 printf("MIMETYPE video/x-h264 VIDEO_SET_STREAMTYPE, 1\n");
911 } else if (!strcmp (mimetype, "video/x-h263")) {
913 printf("MIMETYPE video/x-h263 VIDEO_SET_STREAMTYPE, 2\n");
914 } else if (!strcmp (mimetype, "video/x-xvid")) {
916 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
917 self->must_pack_bitstream = 1;
919 printf("MIMETYPE video/x-xvid -> VIDEO_SET_STREAMTYPE, 10\n");
920 } else if (!strcmp (mimetype, "video/x-divx")) {
922 gst_structure_get_int (structure, "divxversion", &divxversion);
923 switch (divxversion) {
926 #define B_GET_BITS(w,e,b) (((w)>>(b))&(((unsigned)(-1))>>((sizeof(unsigned))*8-(e+1-b))))
927 #define B_SET_BITS(name,v,e,b) (((unsigned)(v))<<(b))
928 static const guint8 brcm_divx311_sequence_header[] = {
929 0x00, 0x00, 0x01, 0xE0, 0x00, 0x34, 0x80, 0x80, // PES HEADER
930 0x05, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
931 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, /* 0 .. 7 */
932 0x08, 0xC8, 0x0D, 0x40, 0x00, 0x53, 0x88, 0x40, /* 8 .. 15 */
933 0x0C, 0x40, 0x01, 0x90, 0x00, 0x97, 0x53, 0x0A, /* 16 .. 24 */
934 0x00, 0x00, 0x00, 0x00,
935 0x30, 0x7F, 0x00, 0x00, 0x01, 0xB2, 0x44, 0x69, /* 0 .. 7 */
936 0x76, 0x58, 0x33, 0x31, 0x31, 0x41, 0x4E, 0x44 /* 8 .. 15 */
938 self->codec_data = gst_buffer_new_and_alloc(63);
939 guint8 *data = GST_BUFFER_DATA(self->codec_data);
941 gst_structure_get_int (structure, "height", &height);
942 gst_structure_get_int (structure, "width", &width);
943 memcpy(data, brcm_divx311_sequence_header, 63);
945 data[0] = B_GET_BITS(width,11,4);
946 data[1] = B_SET_BITS("width [3..0]", B_GET_BITS(width,3,0), 7, 4) |
947 B_SET_BITS("'10'", 0x02, 3, 2) |
948 B_SET_BITS("height [11..10]", B_GET_BITS(height,11,10), 1, 0);
949 data[2] = B_GET_BITS(height,9,2);
950 data[3]= B_SET_BITS("height [1.0]", B_GET_BITS(height,1,0), 7, 6) |
951 B_SET_BITS("'100000'", 0x20, 5, 0);
953 self->codec_type = CT_DIVX311;
954 printf("MIMETYPE video/x-divx vers. 3 -> VIDEO_SET_STREAMTYPE, 13\n");
959 printf("MIMETYPE video/x-divx vers. 4 -> VIDEO_SET_STREAMTYPE, 14\n");
963 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
964 self->must_pack_bitstream = 1;
966 printf("MIMETYPE video/x-divx vers. 5 -> VIDEO_SET_STREAMTYPE, 15\n");
969 GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("unhandled divx version %i", divxversion));
973 if (streamtype != -1) {
974 if (ioctl(self->fd, VIDEO_SET_STREAMTYPE, streamtype) < 0 )
975 if ( streamtype != 0 && streamtype != 6 )
976 GST_ELEMENT_ERROR (self, STREAM, CODEC_NOT_FOUND, (NULL), ("hardware decoder can't handle streamtype %i", streamtype));
978 GST_ELEMENT_ERROR (self, STREAM, TYPE_NOT_FOUND, (NULL), ("unimplemented stream type %s", mimetype));
983 static int readMpegProc(char *str, int decoder)
987 sprintf(tmp, "/proc/stb/vmpeg/%d/%s", decoder, str);
988 FILE *f = fopen(tmp, "r");
991 fscanf(f, "%x", &val);
997 static int readApiSize(int fd, int *xres, int *yres, int *aspect)
1000 if (!ioctl(fd, VIDEO_GET_SIZE, &size))
1004 *aspect = size.aspect_ratio == 0 ? 2 : 3; // convert dvb api to etsi
1010 static int readApiFrameRate(int fd, int *framerate)
1013 if (!ioctl(fd, VIDEO_GET_FRAME_RATE, &frate))
1022 gst_dvbvideosink_start (GstBaseSink * basesink)
1024 GstDVBVideoSink *self = GST_DVBVIDEOSINK (basesink);
1025 self->fd = open("/dev/dvb/adapter0/video0", O_RDWR);
1026 // self->fd = open("/dump.pes", O_RDWR|O_CREAT|O_TRUNC, 0555);
1028 gint control_sock[2];
1030 if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
1033 READ_SOCKET (self) = control_sock[0];
1034 WRITE_SOCKET (self) = control_sock[1];
1036 fcntl (READ_SOCKET (self), F_SETFL, O_NONBLOCK);
1037 fcntl (WRITE_SOCKET (self), F_SETFL, O_NONBLOCK);
1041 GstStructure *s = 0;
1042 GstMessage *msg = 0;
1043 int aspect = -1, width = -1, height = -1, framerate = -1,
1044 progressive = readMpegProc("progressive", 0);
1046 if (readApiSize(self->fd, &width, &height, &aspect) == -1)
1048 aspect = readMpegProc("aspect", 0);
1049 width = readMpegProc("xres", 0);
1050 height = readMpegProc("yres", 0);
1053 aspect = aspect == 0 ? 2 : 3; // dvb api to etsi
1054 if (readApiFrameRate(self->fd, &framerate) == -1)
1055 framerate = readMpegProc("framerate", 0);
1057 s = gst_structure_new ("eventSizeAvail",
1058 "aspect_ratio", G_TYPE_INT, aspect == 0 ? 2 : 3,
1059 "width", G_TYPE_INT, width,
1060 "height", G_TYPE_INT, height, NULL);
1061 msg = gst_message_new_element (GST_OBJECT (basesink), s);
1062 gst_element_post_message (GST_ELEMENT (basesink), msg);
1064 s = gst_structure_new ("eventFrameRateAvail",
1065 "frame_rate", G_TYPE_INT, framerate, NULL);
1066 msg = gst_message_new_element (GST_OBJECT (basesink), s);
1067 gst_element_post_message (GST_ELEMENT (basesink), msg);
1069 s = gst_structure_new ("eventProgressiveAvail",
1070 "progressive", G_TYPE_INT, progressive, NULL);
1071 msg = gst_message_new_element (GST_OBJECT (basesink), s);
1072 gst_element_post_message (GST_ELEMENT (basesink), msg);
1074 ioctl(self->fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY);
1075 ioctl(self->fd, VIDEO_PLAY);
1082 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, (NULL),
1089 gst_dvbvideosink_stop (GstBaseSink * basesink)
1091 GstDVBVideoSink *self = GST_DVBVIDEOSINK (basesink);
1094 ioctl(self->fd, VIDEO_STOP);
1095 ioctl(self->fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX);
1099 close (READ_SOCKET (self));
1100 close (WRITE_SOCKET (self));
1102 if (self->codec_data)
1103 gst_buffer_unref(self->codec_data);
1105 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
1106 if (self->prev_frame)
1107 gst_buffer_unref(self->prev_frame);
1113 /* entry point to initialize the plug-in
1114 * initialize the plug-in itself
1115 * register the element factories and pad templates
1116 * register the features
1118 * exchange the string 'plugin' with your elemnt name
1121 plugin_init (GstPlugin *plugin)
1123 return gst_element_register (plugin, "dvbvideosink",
1125 GST_TYPE_DVBVIDEOSINK);
1128 /* this is the structure that gstreamer looks for to register plugins
1130 * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
1142 "http://gstreamer.net/"