summaryrefslogtreecommitdiffstats
path: root/gst/mpeg1sys/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/mpeg1sys/buffer.c')
-rw-r--r--gst/mpeg1sys/buffer.c482
1 files changed, 482 insertions, 0 deletions
diff --git a/gst/mpeg1sys/buffer.c b/gst/mpeg1sys/buffer.c
new file mode 100644
index 00000000..933b76e6
--- /dev/null
+++ b/gst/mpeg1sys/buffer.c
@@ -0,0 +1,482 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+/*#define DEBUG_ENABLED */
+#include <gst/gst.h>
+#include <libs/getbits/gstgetbits.h>
+
+#include "buffer.h"
+
+#define SEQUENCE_HEADER 0x000001b3
+#define SEQUENCE_END 0x000001b7
+#define PICTURE_START 0x00000100
+#define GROUP_START 0x000001b8
+#define SYNCWORD_START 0x000001
+
+#define AUDIO_SYNCWORD 0xfff
+
+#define CLOCKS 90000.0
+
+#define DEBUG(a, b...) g_print (##b)
+
+/* This must match decoder and encoder tables */
+static double picture_rates [16] =
+{
+ 0.0,
+ 24000.0/1001.,
+ 24.0,
+ 25.0,
+ 30000.0/1001.,
+ 30.0,
+ 50.0,
+ 60000.0/1001.,
+ 60.0,
+
+ 1,
+ 5,
+ 10,
+ 12,
+ 15,
+ 0,
+ 0
+};
+
+static double ratio [16] = { 0., 1., 0.6735, 0.7031, 0.7615, 0.8055,
+ 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575,
+ 1.2015, 0.};
+
+static char picture_types [4][3] =
+ { "I", "P", "B", "D" };
+
+static int bitrate_index[2][3][16] =
+{ { {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, },
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, },
+ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, } },
+ { {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, },
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, },
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, } },
+};
+
+static long frequency[9] =
+{44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000};
+
+static double dfrequency[9] =
+{44.1, 48, 32, 22.05, 24, 16, 11.025, 12, 8};
+
+static unsigned int samples [4] = {192, 384, 1152, 1152};
+
+static char mode [4][15] =
+ { "stereo", "joint stereo", "dual channel", "single channel" };
+static char copyright [2][20] =
+ { "no copyright","copyright protected" };
+static char original [2][10] =
+ { "copy","original" };
+static char emphasis [4][20] =
+ { "none", "50/15 microseconds", "reserved", "CCITT J.17" };
+
+static void mpeg1mux_buffer_update_video_info(Mpeg1MuxBuffer *mb);
+static void mpeg1mux_buffer_update_audio_info(Mpeg1MuxBuffer *mb);
+
+Mpeg1MuxBuffer *mpeg1mux_buffer_new(guchar type, guchar id) {
+ Mpeg1MuxBuffer *new = g_malloc(sizeof(Mpeg1MuxBuffer));
+
+ new->buffer = NULL;
+ new->length = 0;
+ new->base = 0;
+ new->buffer_type = type;
+ new->stream_id = id;
+ new->scan_pos = 0;
+ new->new_frame = TRUE;
+ new->current_start = 0;
+ new->timecode_list = NULL;
+ new->queued_list = NULL;
+ new->next_frame_time = 0;
+
+ return new;
+}
+
+void mpeg1mux_buffer_queue(Mpeg1MuxBuffer *mb, GstBuffer *buf) {
+
+ if (mb->buffer == NULL) {
+ mb->buffer = g_malloc(GST_BUFFER_SIZE(buf));
+ mb->length = GST_BUFFER_SIZE(buf);
+ memcpy(mb->buffer, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf));
+ }
+ else {
+ mb->buffer = g_realloc(mb->buffer, mb->length + GST_BUFFER_SIZE(buf));
+ memcpy(mb->buffer+mb->length, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf));
+ mb->length += GST_BUFFER_SIZE(buf);
+ }
+
+ GST_DEBUG (0,"queuing buffer %lu\n", mb->length);
+ if (mb->buffer_type == BUFFER_TYPE_VIDEO) {
+ mpeg1mux_buffer_update_video_info(mb);
+ }
+ else {
+ mpeg1mux_buffer_update_audio_info(mb);
+ }
+}
+
+gulong mpeg1mux_buffer_update_queued(Mpeg1MuxBuffer *mb, guint64 scr) {
+ GList *queued_list;
+ Mpeg1MuxTimecode *tc;
+ gulong total_queued = 0;
+
+ GST_DEBUG (0,"queued in buffer on SCR=%llu\n", scr);
+ queued_list = g_list_first(mb->queued_list);
+
+ while (queued_list) {
+ tc = (Mpeg1MuxTimecode *) queued_list->data;
+ if (tc->DTS < scr) {
+ /* this buffer should be sent out */
+ mb->queued_list = g_list_remove(mb->queued_list, tc);
+ queued_list = g_list_first(mb->queued_list);
+ }
+ else {
+ GST_DEBUG (0,"queued in buffer %ld, %llu\n", tc->original_length, tc->DTS);
+ total_queued += tc->original_length;
+ queued_list = g_list_next(queued_list);
+ }
+ }
+ GST_DEBUG (0,"queued in buffer %lu\n", total_queued);
+
+ return total_queued;
+}
+
+void mpeg1mux_buffer_shrink(Mpeg1MuxBuffer *mb, gulong size) {
+ GList *timecode_list;
+ Mpeg1MuxTimecode *tc;
+ gulong consumed = 0;
+ gulong count;
+
+ GST_DEBUG (0,"shrinking buffer %lu\n", size);
+
+ g_assert(mb->length >= size);
+
+ memcpy(mb->buffer, mb->buffer+size, mb->length-size);
+ mb->buffer = g_realloc(mb->buffer, mb->length-size);
+
+ mb->length -= size;
+ mb->scan_pos -= size;
+ mb->current_start -= size;
+
+ timecode_list = g_list_first(mb->timecode_list);
+ tc = (Mpeg1MuxTimecode *) timecode_list->data;
+
+ if (tc->length > size) {
+ tc->length -= size;
+ mb->new_frame = FALSE;
+ }
+ else {
+ consumed += tc->length;
+ while (size >= consumed) {
+ GST_DEBUG (0,"removing timecode: %llu %llu %lu %lu\n", tc->DTS, tc->PTS, tc->length, consumed);
+ mb->timecode_list = g_list_remove_link(mb->timecode_list, timecode_list);
+ mb->queued_list = g_list_append(mb->queued_list, tc);
+ timecode_list = g_list_first(mb->timecode_list);
+ tc = (Mpeg1MuxTimecode *) timecode_list->data;
+ consumed += tc->length;
+ GST_DEBUG (0,"next timecode: %llu %llu %lu %lu\n", tc->DTS, tc->PTS, tc->length, consumed);
+ }
+ mb->new_frame = TRUE;
+ GST_DEBUG (0,"leftover frame size from %lu to %lu \n", tc->length, consumed-size);
+ tc->length = consumed - size;
+ }
+
+ if (mb->buffer_type == BUFFER_TYPE_VIDEO) {
+ mb->info.video.DTS = tc->DTS;
+ mb->info.video.PTS = tc->PTS;
+ mb->next_frame_time = tc->DTS;
+ }
+ else {
+ mb->info.audio.PTS = tc->PTS;
+ mb->next_frame_time = tc->PTS;
+ }
+ GST_DEBUG (0,"next frame time timecode: %llu %lu\n", mb->next_frame_time, tc->length);
+
+ /* check buffer consistency */
+ timecode_list = g_list_first(mb->timecode_list);
+ count = 0;
+
+ while (timecode_list) {
+ tc = (Mpeg1MuxTimecode *) timecode_list->data;
+ count += tc->length;
+
+ timecode_list = g_list_next(timecode_list);
+ }
+
+ if (count != mb->current_start) g_print("********** error %lu != %lu\n", count, mb->current_start);
+
+ mb->base += size;
+}
+
+static void mpeg1mux_buffer_update_video_info(Mpeg1MuxBuffer *mb) {
+ gboolean have_sync = FALSE;
+ guchar *data = mb->buffer;
+ gulong offset = mb->scan_pos;
+ guint sync_zeros = 0;
+ gulong id=0;
+ guint temporal_reference, temp;
+ gst_getbits_t gb;
+
+
+ GST_DEBUG (0,"mpeg1mux::update_video_info %lu %lu\n", mb->base, mb->scan_pos);
+ if (mb->base == 0 && mb->scan_pos == 0) {
+ if ((SYNCWORD_START<<8)+*(mb->buffer+3) == SEQUENCE_HEADER) {
+
+ gst_getbits_init(&gb, NULL, NULL);
+ gst_getbits_newbuf(&gb, data+4, mb->length);
+ mb->info.video.horizontal_size = gst_getbits12(&gb);
+ mb->info.video.vertical_size = gst_getbits12(&gb);
+ mb->info.video.aspect_ratio = gst_getbits4(&gb);
+ mb->info.video.picture_rate = gst_getbits4(&gb);
+ mb->info.video.bit_rate = gst_getbits18(&gb);
+ if (gst_getbits1(&gb) != 1) {
+ g_print("mpeg1mux::update_video_info: marker bit error\n");
+ }
+ mb->info.video.vbv_buffer_size = gst_getbits10(&gb);
+ mb->info.video.CSPF = gst_getbits1(&gb);
+
+ mb->info.video.secs_per_frame = 1. / picture_rates[mb->info.video.picture_rate];
+ mb->info.video.decoding_order=0;
+ mb->info.video.group_order=0;
+ GST_DEBUG (0,"mpeg1mux::update_video_info: secs per frame %g\n", mb->info.video.secs_per_frame);
+ }
+ else {
+ g_print("mpeg1mux::update_video_info: Invalid MPEG Video header\n");
+ }
+ }
+ while (offset < mb->length-6) {
+ if (!have_sync) {
+ guchar byte = *(data+offset);
+ /*GST_DEBUG (0,"mpeg1mux::update_video_info: found #%d at %lu\n",byte,offset); */
+ offset++;
+ /* if it's zero, increment the zero count */
+ if (byte == 0) {
+ sync_zeros++;
+ /*GST_DEBUG (0,"mpeg1mux::update_video_info: found zero #%d at %lu\n",sync_zeros,offset-1); */
+ }
+ /* if it's a one and we have two previous zeros, we have sync */
+ else if ((byte == 1) && (sync_zeros >= 2)) {
+ GST_DEBUG (0,"mpeg1mux::update_video_info: synced at %lu\n",offset-1);
+ have_sync = TRUE;
+ sync_zeros = 0;
+ }
+ /* if it's anything else, we've lost it completely */
+ else sync_zeros = 0;
+ /* then snag the chunk ID */
+ } else if (id == 0) {
+ id = *(data+offset);
+ GST_DEBUG (0,"mpeg1mux::update_video_info: got id 0x%02lX\n",id);
+ id = (SYNCWORD_START<<8)+id;
+ switch (id) {
+ case SEQUENCE_HEADER:
+ GST_DEBUG (0,"mpeg1mux::update_video_info: sequence header\n");
+ break;
+ case GROUP_START:
+ GST_DEBUG (0,"mpeg1mux::update_video_info: group start\n");
+ mb->info.video.group_order=0;
+ break;
+ case PICTURE_START:
+ /* skip the first access unit */
+ if (mb->info.video.decoding_order != 0) {
+ Mpeg1MuxTimecode *tc;
+ GST_DEBUG (0,"mpeg1mux::update_video_info: PTS %llu, DTS %llu, length %lu\n", mb->info.video.current_PTS,
+ mb->info.video.current_DTS, offset - mb->current_start-3);
+
+ tc = (Mpeg1MuxTimecode *) g_malloc(sizeof(Mpeg1MuxTimecode));
+ tc->length = offset - mb->current_start-3;
+ tc->original_length = tc->length;
+ tc->frame_type = mb->info.video.current_type;
+ tc->DTS = mb->info.video.current_DTS;
+ tc->PTS = mb->info.video.current_PTS;
+
+ mb->timecode_list = g_list_append(mb->timecode_list, tc);
+
+ if (mb->info.video.decoding_order == 0) {
+ mb->next_frame_time = tc->DTS;
+ }
+
+ mb->current_start = offset-3;
+ }
+
+ temp= (*(data+offset+1)<<8)+*(data+offset+2);
+ temporal_reference = (temp & 0xffc0) >> 6;
+ mb->info.video.current_type = (temp & 0x0038) >> 3;
+ GST_DEBUG (0,"mpeg1mux::update_video_info: picture start temporal_ref:%d type:%s Frame\n", temporal_reference,
+ picture_types[mb->info.video.current_type-1]);
+
+ mb->info.video.current_DTS = mb->info.video.decoding_order * mb->info.video.secs_per_frame * CLOCKS;
+ mb->info.video.current_PTS = (temporal_reference - mb->info.video.group_order + 1 +
+ mb->info.video.decoding_order) *mb->info.video.secs_per_frame*CLOCKS;
+
+ mb->info.video.decoding_order++;
+ mb->info.video.group_order++;
+
+
+ offset++;
+ break;
+ case SEQUENCE_END:
+ GST_DEBUG (0,"mpeg1mux::update_video_info: sequence end\n");
+ break;
+ }
+ /* prepare for next sync */
+ offset++;
+ have_sync = FALSE;
+ id = 0;
+ sync_zeros = 0;
+ }
+ }
+ mb->scan_pos = offset;
+}
+
+static void mpeg1mux_buffer_update_audio_info(Mpeg1MuxBuffer *mb) {
+ guchar *data = mb->buffer;
+ gulong offset = mb->scan_pos;
+ gulong id=0;
+ guint padding_bit;
+ gst_getbits_t gb;
+ guint startup_delay = 0;
+ int layer_index,lsf,samplerate_index,padding;
+ long bpf;
+ Mpeg1MuxTimecode *tc;
+
+
+ GST_DEBUG (0,"mpeg1mux::update_audio_info %lu %lu\n", mb->base, mb->scan_pos);
+ if (mb->base == 0 && mb->scan_pos == 0) {
+ id = GULONG_FROM_BE(*((gulong *)(data)));
+
+ printf("MPEG audio id = %08lx\n", id);
+ if ((id & 0xfff00000) == AUDIO_SYNCWORD<<20) {
+
+ /*mpegver = (header >> 19) & 0x3; // don't need this for bpf */
+ layer_index = (id >> 17) & 0x3;
+ mb->info.audio.layer = 4 - layer_index;
+ lsf = (id & (1 << 20)) ? ((id & (1 << 19)) ? 0 : 1) : 1;
+ mb->info.audio.bit_rate = bitrate_index[lsf][mb->info.audio.layer - 1][((id >> 12) & 0xf)];
+ samplerate_index = (id >> 10) & 0x3;
+ padding = (id >> 9) & 0x1;
+
+ if (mb->info.audio.layer == 1) {
+ bpf = mb->info.audio.bit_rate * 12000;
+ bpf /= frequency[samplerate_index];
+ bpf = ((bpf + padding) << 2);
+ } else {
+ bpf = mb->info.audio.bit_rate * 144000;
+ bpf /= frequency[samplerate_index];
+ bpf += padding;
+ }
+ mb->info.audio.framesize = bpf;
+
+ GST_DEBUG (0,"mpeg1mux::update_audio_info: samples per second %d\n", samplerate_index);
+
+ gst_getbits_init(&gb, NULL, NULL);
+ gst_getbits_newbuf(&gb, data, mb->length);
+
+ gst_flushbitsn(&gb, 12);
+ if (gst_getbits1(&gb) != 1) {
+ g_print("mpeg1mux::update_audio_info: marker bit error\n");
+ }
+ gst_flushbitsn(&gb, 2);
+ mb->info.audio.protection = gst_getbits1(&gb);
+ gst_flushbitsn(&gb, 4);
+ mb->info.audio.frequency = gst_getbits2(&gb);
+ padding_bit = gst_getbits1(&gb);
+ gst_flushbitsn(&gb, 1);
+ mb->info.audio.mode = gst_getbits2(&gb);
+ mb->info.audio.mode_extension = gst_getbits2(&gb);
+ mb->info.audio.copyright = gst_getbits1(&gb);
+ mb->info.audio.original_copy = gst_getbits1(&gb);
+ mb->info.audio.emphasis = gst_getbits2(&gb);
+
+ GST_DEBUG (0,"mpeg1mux::update_audio_info: layer %d\n", mb->info.audio.layer);
+ GST_DEBUG (0,"mpeg1mux::update_audio_info: bit_rate %d\n", mb->info.audio.bit_rate);
+ GST_DEBUG (0,"mpeg1mux::update_audio_info: frequency %d\n", mb->info.audio.frequency);
+
+ mb->info.audio.samples_per_second = (double)dfrequency [mb->info.audio.frequency];
+
+ GST_DEBUG (0,"mpeg1mux::update_audio_info: samples per second %g\n", mb->info.audio.samples_per_second);
+
+ mb->info.audio.decoding_order=0;
+
+ tc = (Mpeg1MuxTimecode *) g_malloc(sizeof(Mpeg1MuxTimecode));
+ tc->length = mb->info.audio.framesize;
+ tc->original_length = tc->length;
+ tc->frame_type = FRAME_TYPE_AUDIO;
+
+ mb->info.audio.current_PTS = mb->info.audio.decoding_order * samples [mb->info.audio.layer] /
+ mb->info.audio.samples_per_second * 90. + startup_delay;
+
+ GST_DEBUG (0,"mpeg1mux::update_audio_info: PTS %llu, length %u\n", mb->info.audio.current_PTS, mb->info.audio.framesize);
+ tc->PTS = mb->info.audio.current_PTS;
+ tc->DTS = mb->info.audio.current_PTS;
+ mb->timecode_list = g_list_append(mb->timecode_list, tc);
+
+ mb->next_frame_time = tc->PTS;
+
+ mb->info.audio.decoding_order++;
+ offset += tc->length;
+ }
+ else {
+ g_print("mpeg1mux::update_audio_info: Invalid MPEG Video header\n");
+ }
+ }
+ while (offset < mb->length-4) {
+ id = GULONG_FROM_BE(*((gulong *)(data+offset)));
+
+ /*mpegver = (header >> 19) & 0x3; // don't need this for bpf */
+ layer_index = (id >> 17) & 0x3;
+ mb->info.audio.layer = 4 - layer_index;
+ lsf = (id & (1 << 20)) ? ((id & (1 << 19)) ? 0 : 1) : 1;
+ mb->info.audio.bit_rate = bitrate_index[lsf][mb->info.audio.layer - 1][((id >> 12) & 0xf)];
+ samplerate_index = (id >> 10) & 0x3;
+ padding = (id >> 9) & 0x1;
+
+ if (mb->info.audio.layer == 1) {
+ bpf = mb->info.audio.bit_rate * 12000;
+ bpf /= frequency[samplerate_index];
+ bpf = ((bpf + padding) << 2);
+ } else {
+ bpf = mb->info.audio.bit_rate * 144000;
+ bpf /= frequency[samplerate_index];
+ bpf += padding;
+ }
+ tc = (Mpeg1MuxTimecode *) g_malloc(sizeof(Mpeg1MuxTimecode));
+ tc->length = bpf;
+ tc->original_length = tc->length;
+ tc->frame_type = FRAME_TYPE_AUDIO;
+
+ mb->current_start = offset + bpf;
+
+ mb->info.audio.samples_per_second = (double)dfrequency [mb->info.audio.frequency];
+
+ mb->info.audio.current_PTS = (mb->info.audio.decoding_order * samples [mb->info.audio.layer]) /
+ mb->info.audio.samples_per_second * 90. ;
+
+ tc->DTS = tc->PTS = mb->info.audio.current_PTS;
+ GST_DEBUG (0,"mpeg1mux::update_audio_info: PTS %llu, %llu length %lu\n", mb->info.audio.current_PTS, tc->PTS, tc->length);
+ mb->timecode_list = g_list_append(mb->timecode_list, tc);
+
+ mb->info.audio.decoding_order++;
+ offset += tc->length;
+ }
+ mb->scan_pos = offset;
+}