diff options
Diffstat (limited to 'gst/mpeg1sys')
-rw-r--r-- | gst/mpeg1sys/.gitignore | 7 | ||||
-rw-r--r-- | gst/mpeg1sys/Makefile.am | 13 | ||||
-rw-r--r-- | gst/mpeg1sys/buffer.c | 482 | ||||
-rw-r--r-- | gst/mpeg1sys/buffer.h | 141 | ||||
-rw-r--r-- | gst/mpeg1sys/gstmpeg1systemencode.c | 572 | ||||
-rw-r--r-- | gst/mpeg1sys/gstmpeg1systemencode.h | 110 | ||||
-rw-r--r-- | gst/mpeg1sys/main.h | 140 | ||||
-rw-r--r-- | gst/mpeg1sys/systems.c | 290 |
8 files changed, 1755 insertions, 0 deletions
diff --git a/gst/mpeg1sys/.gitignore b/gst/mpeg1sys/.gitignore new file mode 100644 index 00000000..08f5ed37 --- /dev/null +++ b/gst/mpeg1sys/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs diff --git a/gst/mpeg1sys/Makefile.am b/gst/mpeg1sys/Makefile.am new file mode 100644 index 00000000..5e28e2be --- /dev/null +++ b/gst/mpeg1sys/Makefile.am @@ -0,0 +1,13 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgstmpeg1systemencode.la + +libgstmpeg1systemencode_la_SOURCES = gstmpeg1systemencode.c \ + buffer.c \ + systems.c + +noinst_HEADERS = gstmpeg1systemencode.h \ + main.h \ + buffer.h + +libsystem_encode_la_CFLAGS = -O2 $(FOMIT_FRAME_POINTER) -funroll-all-loops -finline-functions -ffast-math $(GST_CFLAGS) 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; +} diff --git a/gst/mpeg1sys/buffer.h b/gst/mpeg1sys/buffer.h new file mode 100644 index 00000000..f3eba4f7 --- /dev/null +++ b/gst/mpeg1sys/buffer.h @@ -0,0 +1,141 @@ +/* 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. + */ + + +#ifndef __BUFFER_H__ +#define __BUFFER_H__ + +#include <gst/gst.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MPEG1MUX_BUFFER_QUEUED(mb) (g_list_length((mb)->timecode_list)) +#define MPEG1MUX_BUFFER_SPACE(mb) ((mb)->length) +#define MPEG1MUX_BUFFER_DATA(mb) ((mb)->buffer) +#define MPEG1MUX_BUFFER_TYPE(mb) ((mb)->buffer) +#define MPEG1MUX_BUFFER_FIRST_TIMECODE(mb) (g_list_first((mb)->timecode_list)->data) + +#define BUFFER_TYPE_VIDEO 1 +#define BUFFER_TYPE_AUDIO 2 + +#define FRAME_TYPE_IFRAME 1 +#define FRAME_TYPE_BFRAME 2 +#define FRAME_TYPE_PFRAME 3 +#define FRAME_TYPE_AUDIO 4 + +typedef struct _Mpeg1MuxBuffer Mpeg1MuxBuffer; +typedef struct _Mpeg1MuxTimecode Mpeg1MuxTimecode; + +typedef struct video_struc /* Informationen ueber Video Stream */ +{ + unsigned int stream_length ; + unsigned int num_sequence ; + unsigned int num_seq_end ; + unsigned int num_pictures ; + unsigned int num_groups ; + unsigned int num_frames[4] ; + unsigned int avg_frames[4] ; + + unsigned int horizontal_size; + unsigned int vertical_size ; + unsigned int aspect_ratio ; + unsigned int picture_rate ; + unsigned int bit_rate ; + unsigned int comp_bit_rate ; + unsigned int vbv_buffer_size; + unsigned int CSPF ; + + guint64 PTS; + guint64 DTS; + + guint64 current_PTS; + guint64 current_DTS; + guchar current_type; + + double secs_per_frame; + gulong group_order, decoding_order; +} Video_struc; + +typedef struct audio_struc /* Informationen ueber Audio Stream */ +{ + unsigned int stream_length ; + unsigned int num_syncword ; + unsigned int num_frames [2] ; + unsigned int framesize ; + unsigned int layer ; + unsigned int protection ; + unsigned int bit_rate ; + unsigned int frequency ; + unsigned int mode ; + unsigned int mode_extension ; + unsigned int copyright ; + unsigned int original_copy ; + unsigned int emphasis ; + + guint64 PTS; + + guint64 current_PTS; + + double samples_per_second; + gulong decoding_order; +} Audio_struc; + +struct _Mpeg1MuxTimecode { + gulong length; + gulong original_length; + guchar frame_type; + guint64 PTS; + guint64 DTS; +}; + +struct _Mpeg1MuxBuffer { + unsigned char *buffer; + gulong length; + gulong base; + gulong scan_pos; + gulong last_pos; + gulong current_start; + guchar buffer_type; + guchar stream_id; + gboolean new_frame; + guint64 next_frame_time; + + union { + Video_struc video; + Audio_struc audio; + } info; + + GList *timecode_list; + GList *queued_list; +}; + +Mpeg1MuxBuffer *mpeg1mux_buffer_new(guchar type, guchar id); + +void mpeg1mux_buffer_queue(Mpeg1MuxBuffer *mb, GstBuffer *buf); +void mpeg1mux_buffer_shrink(Mpeg1MuxBuffer *mb, gulong size); +gulong mpeg1mux_buffer_update_queued(Mpeg1MuxBuffer *mb, guint64 scr); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __BUFFER_H__ */ diff --git a/gst/mpeg1sys/gstmpeg1systemencode.c b/gst/mpeg1sys/gstmpeg1systemencode.c new file mode 100644 index 00000000..d8927d0f --- /dev/null +++ b/gst/mpeg1sys/gstmpeg1systemencode.c @@ -0,0 +1,572 @@ +/* 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 <stdlib.h> +#include <string.h> +#include <math.h> + +/*#define DEBUG_ENABLED */ +#include "gstmpeg1systemencode.h" +#include "main.h" + +/*#define GST_DEBUG(a, b...) g_print (##b) */ + +/* elementfactory information */ +static GstElementDetails system_encode_details = { + "MPEG1 Multiplexer", + "Filter/Multiplexer/System", + "Multiplexes MPEG-1 Streams", + VERSION, + "Wim Taymans <wim.taymans@chello.be>", + "(C) 2000", +}; + +/* GstMPEG1SystemEncode signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + /* FILL ME */ +}; + +GST_PADTEMPLATE_FACTORY (src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "src_video", + "video/mpeg", + "mpegversion", GST_PROPS_INT (1), + "systemstream", GST_PROPS_BOOLEAN (TRUE) + ) +) +GST_PADTEMPLATE_FACTORY (video_sink_factory, + "video_%02d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_CAPS_NEW ( + "sink_video", + "video/mpeg", + "mpegversion", GST_PROPS_INT (1), + "systemstream", GST_PROPS_BOOLEAN (FALSE) + ) +) + +GST_PADTEMPLATE_FACTORY (audio_sink_factory, + "audio_%02d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_CAPS_NEW ( + "sink_audio", + "audio/mp3", + NULL + ) +) + +static void gst_system_encode_class_init (GstMPEG1SystemEncodeClass *klass); +static void gst_system_encode_init (GstMPEG1SystemEncode *system_encode); + +static GstPad* gst_system_encode_request_new_pad (GstElement *element, GstPadTemplate *templ, + const gchar *unused); +static void gst_system_encode_chain (GstPad *pad, GstBuffer *buf); + +static void gst_system_encode_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec); +static void gst_system_encode_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec); + +static GstElementClass *parent_class = NULL; +/*static guint gst_system_encode_signals[LAST_SIGNAL] = { 0 }; */ + +GType +gst_mpeg1_system_encode_get_type (void) +{ + static GType system_encode_type = 0; + + if (!system_encode_type) { + static const GTypeInfo system_encode_info = { + sizeof(GstMPEG1SystemEncodeClass), + NULL, + NULL, + (GClassInitFunc)gst_system_encode_class_init, + NULL, + NULL, + sizeof(GstMPEG1SystemEncode), + 0, + (GInstanceInitFunc)gst_system_encode_init, + NULL + }; + system_encode_type = g_type_register_static(GST_TYPE_ELEMENT, "GstMPEG1SystemEncode", &system_encode_info, 0); + } + return system_encode_type; +} + +static void +gst_system_encode_class_init (GstMPEG1SystemEncodeClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + gobject_class->set_property = gst_system_encode_set_property; + gobject_class->get_property = gst_system_encode_get_property; + + gstelement_class->request_new_pad = gst_system_encode_request_new_pad; +} + +static void +gst_system_encode_init (GstMPEG1SystemEncode *system_encode) +{ + system_encode->srcpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (src_factory), "src"); + gst_element_add_pad (GST_ELEMENT (system_encode), system_encode->srcpad); + + system_encode->video_buffer = mpeg1mux_buffer_new (BUFFER_TYPE_VIDEO, 0xE0); + system_encode->audio_buffer = mpeg1mux_buffer_new (BUFFER_TYPE_AUDIO, 0xC0); + system_encode->have_setup = FALSE; + system_encode->mta = NULL; + system_encode->packet_size = 2048; + system_encode->lock = g_mutex_new(); + system_encode->current_pack = system_encode->packets_per_pack = 3; + system_encode->video_delay_ms = 0; + system_encode->audio_delay_ms = 0; + system_encode->sectors_delay = 0; + system_encode->startup_delay = ~1; + system_encode->which_streams = 0; + system_encode->num_audio_pads = 0; + system_encode->num_video_pads = 0; + system_encode->pack = g_malloc (sizeof (Pack_struc)); + system_encode->sys_header = g_malloc (sizeof (Sys_header_struc)); + system_encode->sector = g_malloc (sizeof (Sector_struc)); + +} + +static GstPad* +gst_system_encode_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *unused) +{ + GstMPEG1SystemEncode *system_encode; + gchar *name = NULL; + GstPad *newpad; + + g_return_val_if_fail (templ != NULL, NULL); + + if (templ->direction != GST_PAD_SINK) { + g_warning ("system_encode: request pad that is not a SINK pad\n"); + return NULL; + } + system_encode = GST_SYSTEM_ENCODE (element); + + if (templ == GST_PADTEMPLATE_GET (audio_sink_factory)) { + name = g_strdup_printf ("audio_%02d", system_encode->num_audio_pads); + g_print ("%s\n", name); + newpad = gst_pad_new_from_template (templ, name); + gst_pad_set_element_private (newpad, GINT_TO_POINTER (system_encode->num_audio_pads)); + + system_encode->audio_pad[system_encode->num_audio_pads] = newpad; + system_encode->num_audio_pads++; + system_encode->which_streams |= STREAMS_AUDIO; + } + else if (templ == GST_PADTEMPLATE_GET (video_sink_factory)) { + name = g_strdup_printf ("video_%02d", system_encode->num_video_pads); + g_print ("%s\n", name); + newpad = gst_pad_new_from_template (templ, name); + gst_pad_set_element_private (newpad, GINT_TO_POINTER (system_encode->num_video_pads)); + + system_encode->video_pad[system_encode->num_video_pads] = newpad; + system_encode->num_video_pads++; + system_encode->which_streams |= STREAMS_VIDEO; + } + else { + g_warning ("system_encode: this is not our template!\n"); + return NULL; + } + + gst_pad_set_chain_function (newpad, gst_system_encode_chain); + gst_element_add_pad (GST_ELEMENT (system_encode), newpad); + + return newpad; +} + +/* return a list of all the highest prioripty streams */ +static GList* +gst_system_encode_pick_streams (GList *mta, GstMPEG1SystemEncode *system_encode) +{ + guint64 lowest = ~1; + + GST_DEBUG (0, "pick_streams: %lld, %lld\n", system_encode->video_buffer->next_frame_time, + system_encode->audio_buffer->next_frame_time); + + if (system_encode->which_streams & STREAMS_VIDEO) { + if (system_encode->video_buffer->next_frame_time < lowest-system_encode->video_delay) { + lowest = system_encode->video_buffer->next_frame_time; + } + } + if (system_encode->which_streams & STREAMS_AUDIO) { + if (system_encode->audio_buffer->next_frame_time < lowest-system_encode->audio_delay) { + lowest = system_encode->audio_buffer->next_frame_time; + } + } + + if (system_encode->which_streams & STREAMS_VIDEO) { + if (system_encode->video_buffer->next_frame_time == lowest) { + mta = g_list_append(mta, system_encode->video_buffer); + } + } + if (system_encode->which_streams & STREAMS_AUDIO) { + if (system_encode->audio_buffer->next_frame_time == lowest) { + mta = g_list_append(mta, system_encode->audio_buffer); + } + } + return mta; +} + +static gboolean +gst_system_encode_have_data (GstMPEG1SystemEncode *system_encode) +{ + + if (system_encode->which_streams == (STREAMS_VIDEO | STREAMS_AUDIO)) { + if (MPEG1MUX_BUFFER_QUEUED(system_encode->audio_buffer) > 2 && + MPEG1MUX_BUFFER_SPACE(system_encode->audio_buffer) > system_encode->packet_size*2 && + MPEG1MUX_BUFFER_QUEUED(system_encode->video_buffer) > 2 && + MPEG1MUX_BUFFER_SPACE(system_encode->video_buffer) > system_encode->packet_size*2) { + return TRUE; + } + } + if (system_encode->which_streams == STREAMS_VIDEO) { + if (MPEG1MUX_BUFFER_QUEUED(system_encode->video_buffer) > 2 && + MPEG1MUX_BUFFER_SPACE(system_encode->video_buffer) > system_encode->packet_size*2) { + return TRUE; + } + } + if (system_encode->which_streams == STREAMS_VIDEO) { + if (MPEG1MUX_BUFFER_QUEUED(system_encode->audio_buffer) > 2 && + MPEG1MUX_BUFFER_SPACE(system_encode->audio_buffer) > system_encode->packet_size*2) { + return TRUE; + } + } + + return FALSE; +} + +static GList* +gst_system_encode_update_mta (GstMPEG1SystemEncode *system_encode, GList *mta, gulong size) +{ + GList *streams = g_list_first(mta); + Mpeg1MuxBuffer *mb = (Mpeg1MuxBuffer *)streams->data; + + GST_DEBUG (0,"system_encode::multiplex: update mta\n"); + + mpeg1mux_buffer_shrink(mb, size); + + mta = g_list_remove(mta, mb); + + return mta; +} + +static void +gst_system_setup_multiplex (GstMPEG1SystemEncode *system_encode) +{ + Mpeg1MuxTimecode *video_tc, *audio_tc; + + system_encode->audio_buffer_size = 4*1024; + system_encode->video_buffer_size = 46*1024; + system_encode->bytes_output = 0; + system_encode->min_packet_data = system_encode->packet_size - PACK_HEADER_SIZE - SYS_HEADER_SIZE - + PACKET_HEADER_SIZE - AFTER_PACKET_LENGTH; + system_encode->max_packet_data = system_encode->packet_size - PACKET_HEADER_SIZE - AFTER_PACKET_LENGTH; + + if (system_encode->which_streams & STREAMS_VIDEO) { + system_encode->video_rate = system_encode->video_buffer->info.video.bit_rate * 50; + } + else system_encode->video_rate = 0; + if (system_encode->which_streams & STREAMS_AUDIO) + system_encode->audio_rate = system_encode->audio_buffer->info.audio.bit_rate * 128; + else system_encode->audio_rate = 0; + + system_encode->data_rate = system_encode->video_rate + system_encode->audio_rate; + + system_encode->dmux_rate = ceil((double)(system_encode->data_rate) * + ((double)(system_encode->packet_size)/(double)(system_encode->min_packet_data) + + ((double)(system_encode->packet_size)/(double)(system_encode->max_packet_data) * + (double)(system_encode->packets_per_pack-1.))) / (double)(system_encode->packets_per_pack) ); + system_encode->data_rate = ceil(system_encode->dmux_rate/50.)*50; + + GST_DEBUG (0,"system_encode::multiplex: data_rate %u, video_rate: %u, audio_rate: %u\n", system_encode->data_rate, + system_encode->video_rate, system_encode->audio_rate); + + system_encode->video_delay = (double)system_encode->video_delay_ms*(double)(CLOCKS/1000); + system_encode->audio_delay = (double)system_encode->audio_delay_ms*(double)(CLOCKS/1000); + + system_encode->mux_rate = ceil(system_encode->dmux_rate/50.); + system_encode->dmux_rate= system_encode->mux_rate * 50.; + + video_tc = MPEG1MUX_BUFFER_FIRST_TIMECODE(system_encode->video_buffer); + audio_tc = MPEG1MUX_BUFFER_FIRST_TIMECODE(system_encode->audio_buffer); + + GST_DEBUG (0,"system_encode::video tc %lld, audio tc %lld:\n", video_tc->DTS, audio_tc->DTS); + + system_encode->delay = ((double)system_encode->sectors_delay + + ceil((double)video_tc->length/(double)system_encode->min_packet_data) + + ceil((double)video_tc->length/(double)system_encode->min_packet_data )) * + (double)system_encode->packet_size/system_encode->dmux_rate*(double)CLOCKS; + + system_encode->audio_delay += system_encode->delay; + system_encode->video_delay += system_encode->delay; + + system_encode->audio_delay = 0; + system_encode->video_delay = 0; + system_encode->delay = 0; + + GST_DEBUG (0,"system_encode::multiplex: delay %g, mux_rate: %lu\n", system_encode->delay, system_encode->mux_rate); +} + +static void +gst_system_encode_multiplex(GstMPEG1SystemEncode *system_encode) +{ + GList *streams; + Mpeg1MuxBuffer *mb = (Mpeg1MuxBuffer *)streams->data; + guchar timestamps; + guchar buffer_scale; + GstBuffer *outbuf; + Pack_struc *pack; + Sys_header_struc *sys_header; + Mpeg1MuxTimecode *tc; + gulong buffer_size, non_scaled_buffer_size, total_queued; + guint64 PTS, DTS; + + g_mutex_lock(system_encode->lock); + + while (gst_system_encode_have_data(system_encode)) { + GST_DEBUG (0,"system_encode::multiplex: multiplexing\n"); + + if (!system_encode->have_setup) { + gst_system_setup_multiplex(system_encode); + system_encode->have_setup = TRUE; + } + + if (system_encode->mta == NULL) { + system_encode->mta = gst_system_encode_pick_streams(system_encode->mta, system_encode); + } + if (system_encode->mta == NULL) break; + + + system_encode->SCR = (guint64)(system_encode->bytes_output+LAST_SCR_BYTE_IN_PACK)*CLOCKS/system_encode->dmux_rate; + + + streams = g_list_first(system_encode->mta); + mb = (Mpeg1MuxBuffer *)streams->data; + + if (system_encode->current_pack == system_encode->packets_per_pack) { + create_pack(system_encode->pack, system_encode->SCR, system_encode->mux_rate); + create_sys_header (system_encode->sys_header, system_encode->mux_rate, 1, 1, 1, 1, 1, 1, + AUDIO_STR_0, 0, system_encode->audio_buffer_size/128, + VIDEO_STR_0, 1, system_encode->video_buffer_size/1024, system_encode->which_streams ); + system_encode->current_pack = 0; + pack = system_encode->pack; + sys_header = system_encode->sys_header; + } + else { + system_encode->current_pack++; + pack = NULL; + sys_header = NULL; + } + + tc = MPEG1MUX_BUFFER_FIRST_TIMECODE(mb); + if (mb->new_frame) { + GST_DEBUG (0,"system_encode::multiplex: new frame\n"); + if (tc->frame_type == FRAME_TYPE_AUDIO || tc->frame_type == FRAME_TYPE_IFRAME || tc->frame_type == FRAME_TYPE_PFRAME) { + timestamps = TIMESTAMPS_PTS; + } + else { + timestamps = TIMESTAMPS_PTS_DTS; + } + } + else { + timestamps = TIMESTAMPS_NO; + } + + if (tc->frame_type != FRAME_TYPE_AUDIO) { + if (tc->PTS<system_encode->startup_delay) + system_encode->startup_delay = tc->PTS; + } + + if (tc->frame_type == FRAME_TYPE_AUDIO) { + buffer_scale = 0; + non_scaled_buffer_size = system_encode->audio_buffer_size; + buffer_size = system_encode->audio_buffer_size/128; + PTS = tc->PTS + system_encode->audio_delay + system_encode->startup_delay; + DTS = tc->PTS + system_encode->audio_delay + system_encode->startup_delay; + } + else { + buffer_scale = 1; + non_scaled_buffer_size = system_encode->video_buffer_size; + buffer_size = system_encode->video_buffer_size/1024; + PTS = tc->PTS + system_encode->video_delay; + DTS = tc->DTS + system_encode->video_delay; + } + + total_queued = mpeg1mux_buffer_update_queued(mb, system_encode->SCR); + + if (non_scaled_buffer_size - total_queued >= system_encode->packet_size) { + + /* write the pack/packet here */ + create_sector (system_encode->sector, pack, sys_header, + system_encode->packet_size, + MPEG1MUX_BUFFER_DATA(mb), mb->stream_id, buffer_scale, + buffer_size, TRUE, PTS, DTS, + timestamps, system_encode->which_streams); + /* update mta */ + system_encode->mta = gst_system_encode_update_mta(system_encode, system_encode->mta, + system_encode->sector->length_of_packet_data); + } + else { + /* write a padding packet */ + create_sector (system_encode->sector, pack, sys_header, + system_encode->packet_size, NULL, PADDING_STR, 0, + 0, FALSE, 0, 0, + TIMESTAMPS_NO, system_encode->which_streams); + } + + outbuf = gst_buffer_new(); + GST_BUFFER_DATA(outbuf) = g_malloc(system_encode->sector->length_of_sector); + GST_BUFFER_SIZE(outbuf) = system_encode->sector->length_of_sector; + memcpy(GST_BUFFER_DATA(outbuf),system_encode->sector->buf, system_encode->sector->length_of_sector); + system_encode->bytes_output += GST_BUFFER_SIZE(outbuf); + gst_pad_push(system_encode->srcpad,outbuf); + + GST_DEBUG (0,"system_encode::multiplex: writing %02x\n", mb->stream_id); + + } + gst_info("system_encode::multiplex: data left in video buffer %lu\n", MPEG1MUX_BUFFER_SPACE(system_encode->video_buffer)); + gst_info("system_encode::multiplex: data left in audio buffer %lu\n", MPEG1MUX_BUFFER_SPACE(system_encode->audio_buffer)); + + g_mutex_unlock(system_encode->lock); +} + +static void +gst_system_encode_chain (GstPad *pad, GstBuffer *buf) +{ + GstMPEG1SystemEncode *system_encode; + guchar *data; + gulong size; + const gchar *padname; + gint channel; + + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + g_return_if_fail(buf != NULL); + + system_encode = GST_SYSTEM_ENCODE (GST_OBJECT_PARENT (pad)); + data = GST_BUFFER_DATA(buf); + size = GST_BUFFER_SIZE(buf); + + GST_DEBUG (0,"system_encode::chain: system_encode: have buffer of size %lu\n",size); + padname = GST_OBJECT_NAME (pad); + + if (strncmp(padname, "audio_", 6) == 0) { + channel = atoi(&padname[6]); + GST_DEBUG (0,"gst_system_encode_chain: got audio buffer in from audio channel %02d\n", channel); + + mpeg1mux_buffer_queue(system_encode->audio_buffer, buf); + } + else if (strncmp(padname, "video_", 6) == 0) { + channel = atoi(&padname[6]); + GST_DEBUG (0,"gst_system_encode_chain: got video buffer in from video channel %02d\n", channel); + + mpeg1mux_buffer_queue(system_encode->video_buffer, buf); + + } + else { + g_assert_not_reached (); + } + gst_system_encode_multiplex(system_encode); + + gst_buffer_unref(buf); +} + +static void +gst_system_encode_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstMPEG1SystemEncode *system_encode; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_SYSTEM_ENCODE(object)); + system_encode = GST_SYSTEM_ENCODE(object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_system_encode_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstMPEG1SystemEncode *src; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_SYSTEM_ENCODE(object)); + src = GST_SYSTEM_ENCODE(object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + /* this filter needs the getbits functions */ + if (!gst_library_load("gstgetbits")) { + gst_info("system_encode:: could not load support library: 'gstgetbits'\n"); + return FALSE; + } + + /* create an elementfactory for the system_encode element */ + factory = gst_elementfactory_new("system_encode",GST_TYPE_SYSTEM_ENCODE, + &system_encode_details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (src_factory)); + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (audio_sink_factory)); + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (video_sink_factory)); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "system_encode", + plugin_init +}; diff --git a/gst/mpeg1sys/gstmpeg1systemencode.h b/gst/mpeg1sys/gstmpeg1systemencode.h new file mode 100644 index 00000000..bb24f01d --- /dev/null +++ b/gst/mpeg1sys/gstmpeg1systemencode.h @@ -0,0 +1,110 @@ +/* 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. + */ + + +#ifndef __SYSTEM_ENCODE_H__ +#define __SYSTEM_ENCODE_H__ + + +#include <config.h> +#include <gst/gst.h> +#include <libs/getbits/gstgetbits.h> + +#include "buffer.h" +#include "main.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_SYSTEM_ENCODE \ + (gst_mpeg1_system_encode_get_type()) +#define GST_SYSTEM_ENCODE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SYSTEM_ENCODE,GstMPEG1SystemEncode)) +#define GST_SYSTEM_ENCODE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SYSTEM_ENCODE,GstMPEG1SystemEncode)) +#define GST_IS_SYSTEM_ENCODE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SYSTEM_ENCODE)) +#define GST_IS_SYSTEM_ENCODE_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SYSTEM_ENCODE)) + +typedef struct _GstMPEG1SystemEncode GstMPEG1SystemEncode; +typedef struct _GstMPEG1SystemEncodeClass GstMPEG1SystemEncodeClass; + +struct _GstMPEG1SystemEncode { + GstElement element; + + GstPad *srcpad; + + gboolean have_setup; + + GMutex *lock; + + guint num_audio_pads; + guint num_video_pads; + + Mpeg1MuxBuffer *audio_buffer; + Mpeg1MuxBuffer *video_buffer; + + Pack_struc *pack; + Sys_header_struc *sys_header; + Sector_struc *sector; + + guint data_rate, video_rate, audio_rate; + gdouble delay, audio_delay, video_delay; + gdouble clock_cycles; + gulong sectors_delay, video_delay_ms, audio_delay_ms; + gulong startup_delay; + gulong audio_buffer_size; + gulong video_buffer_size; + gulong mux_rate, dmux_rate; + guint64 SCR; + gint which_streams; + + gint current_pack; + gulong min_packet_data; + gulong max_packet_data; + gint packets_per_pack; + gulong packet_size; + gulong bytes_output; + + GList *mta; + + /* stream input pads */ + GstPad *private_1_pad[8]; /* up to 8 ac3 audio tracks <grumble> */ + GstPad *private_2_pad; + GstPad *video_pad[16]; + GstPad *audio_pad[32]; +}; + +struct _GstMPEG1SystemEncodeClass { + GstElementClass parent_class; +}; + +GType gst_mpeg1_system_encode_get_type(void); + +/* multplex.c */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __SYSTEM_ENCODE_H__ */ diff --git a/gst/mpeg1sys/main.h b/gst/mpeg1sys/main.h new file mode 100644 index 00000000..434f57f5 --- /dev/null +++ b/gst/mpeg1sys/main.h @@ -0,0 +1,140 @@ +/************************************************************************* +* Generating a MPEG/SYSTEMS * +* MULTIPLEXED VIDEO/AUDIO STREAM * +* from two MPEG source streams * +* Christoph Moar * +* SIEMENS CORPORATE RESEARCH AND DEVELOPMENT ST SN 11 / T SN 6 * +* (C) 1994 1995 * +************************************************************************** +* Restrictions apply. Will not support the whole MPEG/SYSTEM Standard. * +* Basically, will generate Constrained System Parameter Files. * +* Mixes only one audio and/or one video stream. Might be expanded. * +*************************************************************************/ + +/************************************************************************* +* mplex - MPEG/SYSTEMS multiplexer * +* Copyright (C) 1994 1995 Christoph Moar * +* Siemens ZFE ST SN 11 / T SN 6 * +* * +* moar@informatik.tu-muenchen.de * +* (Christoph Moar) * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program 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 General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * +*************************************************************************/ + + +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include <glib.h> + +#define PACK_START 0x000001ba +#define SYS_HEADER_START 0x000001bb +#define ISO11172_END 0x000001b9 +#define PACKET_START 0x000001 + +#define CLOCKS 90000.0 /* System Clock Hertz */ + +#define AFTER_PACKET_LENGTH 15 /* No of non-data-bytes */ + /* following the packet */ + /* length field */ +#define LAST_SCR_BYTE_IN_PACK 9 /* No of bytes in pack */ + /* preceding, and */ + /* including, the SCR */ + +/* The following values for sys_header_length & size are only valid for */ +/* System streams consisting of two basic streams. When wrapping around */ +/* the system layer on a single video or a single audio stream, those */ +/* values get decreased by 3. */ + +#define SYS_HEADER_LENGTH 12 /* length of Sys Header */ + /* after start code and */ + /* length field */ + +#define SYS_HEADER_SIZE 18 /* incl. start code and */ + /* length field */ +#define PACK_HEADER_SIZE 12 + +#define PACKET_HEADER_SIZE 6 + +#define MAX_SECTOR_SIZE 0x20000 /* Max Sektor Groesse */ + +#define STREAMS_VIDEO 1 +#define STREAMS_AUDIO 2 +#define STREAMS_BOTH 3 + +#define AUDIO_STREAMS 0xb8 /* Marker Audio Streams */ +#define VIDEO_STREAMS 0xb9 /* Marker Video Streams */ +#define AUDIO_STR_0 0xc0 /* Marker Audio Stream0 */ +#define VIDEO_STR_0 0xe0 /* Marker Video Stream0 */ +#define PADDING_STR 0xbe /* Marker Padding Stream*/ + +#define ZERO_STUFFING_BYTE 0 +#define STUFFING_BYTE 0xff +#define RESERVED_BYTE 0xff +#define TIMESTAMPS_NO 0 /* Flag NO timestamps */ +#define TIMESTAMPS_PTS 1 /* Flag PTS timestamp */ +#define TIMESTAMPS_PTS_DTS 2 /* Flag BOTH timestamps */ + +#define MARKER_SCR 2 /* Marker SCR */ +#define MARKER_JUST_PTS 2 /* Marker only PTS */ +#define MARKER_PTS 3 /* Marker PTS */ +#define MARKER_DTS 1 /* Marker DTS */ +#define MARKER_NO_TIMESTAMPS 0x0f /* Marker NO timestamps */ + +#define STATUS_AUDIO_END 0 /* Statusmessage A end */ +#define STATUS_VIDEO_END 1 /* Statusmessage V end */ +#define STATUS_AUDIO_TIME_OUT 2 /* Statusmessage A out */ +#define STATUS_VIDEO_TIME_OUT 3 /* Statusmessage V out */ + +/************************************************************************* + Typ- und Strukturdefinitionen +*************************************************************************/ + +typedef struct sector_struc /* A sector, can contain pack, sys header */ + /* and packet. */ +{ unsigned char buf [MAX_SECTOR_SIZE] ; + unsigned int length_of_sector ; + unsigned int length_of_packet_data ; + guint64 TS ; +} Sector_struc; + +typedef struct pack_struc /* Pack Info */ +{ unsigned char buf [PACK_HEADER_SIZE]; + guint64 SCR; +} Pack_struc; + +typedef struct sys_header_struc /* System Header Info */ +{ unsigned char buf [SYS_HEADER_SIZE]; +} Sys_header_struc; + +/************************************************************************* + Funktionsprototypen, keine Argumente, K&R Style +*************************************************************************/ + +/* systems.c */ +void create_sector (Sector_struc *sector, Pack_struc *pack, Sys_header_struc *sys_header, + unsigned int packet_size, unsigned char *inputbuffer, unsigned char type, unsigned char buffer_scale, + unsigned int buffer_size, unsigned char buffers, guint64 PTS, guint64 DTS, + unsigned char timestamps, unsigned int which_streams); + +void create_pack (Pack_struc *pack, guint64 SCR, unsigned int mux_rate); + +void create_sys_header (Sys_header_struc *sys_header, unsigned int rate_bound, unsigned char audio_bound, + unsigned char fixed, unsigned char CSPS, unsigned char audio_lock, unsigned char video_lock, + unsigned char video_bound, unsigned char stream1, unsigned char buffer1_scale, unsigned int buffer1_size, + unsigned char stream2, unsigned char buffer2_scale, unsigned int buffer2_size, unsigned int which_streams); + +#endif diff --git a/gst/mpeg1sys/systems.c b/gst/mpeg1sys/systems.c new file mode 100644 index 00000000..d0b3388a --- /dev/null +++ b/gst/mpeg1sys/systems.c @@ -0,0 +1,290 @@ +#include <string.h> + +#include "main.h" + +static void buffer_timecode (timecode, marker, buffer) +guint64 timecode; +unsigned char marker; +unsigned char **buffer; + +{ + unsigned char temp; + + temp = (marker << 4) | ((timecode>>29) & 0x38) | + ((timecode >> 29) & 0x6) | 1; + *((*buffer)++)=temp; + temp = (timecode & 0x3fc00000) >> 22; + *((*buffer)++)=temp; + temp = ((timecode & 0x003f8000) >> 14) | 1; + *((*buffer)++)=temp; + temp = (timecode & 0x7f80) >> 7; + *((*buffer)++)=temp; + temp = ((timecode & 0x007f) << 1) | 1; + *((*buffer)++)=temp; +} + +/************************************************************************* + creates a complete sector. + Also copies Pack and Sys_Header informations into the + sector buffer, then reads a packet full of data from + the input stream into the sector buffer. +*************************************************************************/ + + +void create_sector (sector, pack, sys_header, + packet_size, inputbuffer, type, + buffer_scale, buffer_size, buffers, + PTS, DTS, timestamps, which_streams ) + +Sector_struc *sector; +Pack_struc *pack; +Sys_header_struc *sys_header; +unsigned int packet_size; +unsigned char *inputbuffer; + +unsigned char type; +unsigned char buffer_scale; +unsigned int buffer_size; +unsigned char buffers; +guint64 PTS; +guint64 DTS; +unsigned char timestamps; +unsigned int which_streams; + +{ + int i,j,tmp; + unsigned char *index; + unsigned char *size_offset; + + /* printf("creating sector\n"); */ + + index = sector->buf; + sector->length_of_sector=0; + +/* Should we copy Pack Header information ? */ + + if (pack != NULL) + { + i = sizeof(pack->buf); + bcopy (pack->buf, index, i); + index += i; + sector->length_of_sector += i; + } + +/* Should we copy System Header information ? */ + + if (sys_header != NULL) + { + i = sizeof(sys_header->buf); + + /* only one stream? 3 bytes less in sys header */ + if (which_streams != STREAMS_BOTH) i -= 3; + + bcopy (sys_header->buf, index, i); + index += i; + sector->length_of_sector += i; + } + + /* write constant packet header data */ + + *(index++) = (unsigned char)(PACKET_START)>>16; + *(index++) = (unsigned char)(PACKET_START & 0x00ffff)>>8; + *(index++) = (unsigned char)(PACKET_START & 0x0000ff); + *(index++) = type; + + /* we remember this offset in case we will have to shrink this packet */ + + size_offset = index; + *(index++) = (unsigned char)((packet_size - PACKET_HEADER_SIZE)>>8); + *(index++) = (unsigned char)((packet_size - PACKET_HEADER_SIZE)&0xff); + + *(index++) = STUFFING_BYTE; + *(index++) = STUFFING_BYTE; + *(index++) = STUFFING_BYTE; + + i = 0; + + if (!buffers) i +=2; + if (timestamps == TIMESTAMPS_NO) i+=9; + else if (timestamps == TIMESTAMPS_PTS) i+=5; + + /* printf("%i stuffing %d\n", i, timestamps); */ + + for (j=0; j<i; j++) + *(index++) = STUFFING_BYTE; + + /* should we write buffer info ? */ + + if (buffers) + { + *(index++) = (unsigned char) (0x40 | + (buffer_scale << 5) | (buffer_size >> 8)); + *(index++) = (unsigned char) (buffer_size & 0xff); + } + + /* should we write PTS, PTS & DTS or nothing at all ? */ + + switch (timestamps) + { + case TIMESTAMPS_NO: + *(index++) = MARKER_NO_TIMESTAMPS; + break; + case TIMESTAMPS_PTS: + buffer_timecode (PTS, MARKER_JUST_PTS, &index); + sector->TS = PTS; + break; + case TIMESTAMPS_PTS_DTS: + buffer_timecode (PTS, MARKER_PTS, &index); + buffer_timecode (DTS, MARKER_DTS, &index); + sector->TS = DTS; + break; + } + +/* read in packet data */ + + i = (packet_size - PACKET_HEADER_SIZE - AFTER_PACKET_LENGTH); + + if (type == PADDING_STR) + { + for (j=0; j<i; j++) + *(index++)=(unsigned char) STUFFING_BYTE; + tmp = i; + } + else + { + /*tmp = fread (index, sizeof (unsigned char), i, inputstream);*/ + memcpy(index, inputbuffer, i); tmp = i; + index += tmp; + + /* if we did not get enough data bytes, shorten the Packet length */ + + if (tmp != i) + { + packet_size -= (i-tmp); + *(size_offset++) = (unsigned char)((packet_size - PACKET_HEADER_SIZE)>>8); + *(size_offset++) = (unsigned char)((packet_size - PACKET_HEADER_SIZE)&0xff); + +/* zero byte stuffing in the last Packet of a stream */ +/* we don't need this any more, since we shortenend the packet */ +/* for (j=tmp; j<i; j++) */ +/* *(index++)=(unsigned char) ZERO_STUFFING_BYTE; */ + } + } + + + /* write other struct data */ + + sector->length_of_sector += packet_size; + sector->length_of_packet_data = tmp; + +} + +/************************************************************************* + writes specifical pack header information into a buffer + later this will be copied from the sector routine into + the sector buffer +*************************************************************************/ + +void create_pack (pack, SCR, mux_rate) + +Pack_struc *pack; +unsigned int mux_rate; +guint64 SCR; + +{ + unsigned char *index; + + index = pack->buf; + + *(index++) = (unsigned char)((PACK_START)>>24); + *(index++) = (unsigned char)((PACK_START & 0x00ff0000)>>16); + *(index++) = (unsigned char)((PACK_START & 0x0000ff00)>>8); + *(index++) = (unsigned char)(PACK_START & 0x000000ff); + buffer_timecode (SCR, MARKER_SCR, &index); + *(index++) = (unsigned char)(0x80 | (mux_rate >>15)); + *(index++) = (unsigned char)(0xff & (mux_rate >> 7)); + *(index++) = (unsigned char)(0x01 | ((mux_rate & 0x7f)<<1)); + pack->SCR = SCR; +} + + +/************************************************************************* + writes specifical system header information into a buffer + later this will be copied from the sector routine into + the sector buffer +*************************************************************************/ + +void create_sys_header (sys_header, rate_bound, audio_bound, + fixed, CSPS, audio_lock, video_lock, + video_bound, + stream1, buffer1_scale, buffer1_size, + stream2, buffer2_scale, buffer2_size, + which_streams) + +Sys_header_struc *sys_header; +unsigned int rate_bound; +unsigned char audio_bound; +unsigned char fixed; +unsigned char CSPS; +unsigned char audio_lock; +unsigned char video_lock; +unsigned char video_bound; + +unsigned char stream1; +unsigned char buffer1_scale; +unsigned int buffer1_size; +unsigned char stream2; +unsigned char buffer2_scale; +unsigned int buffer2_size; +unsigned int which_streams; + +{ + unsigned char *index; + + index = sys_header->buf; + + /* if we are not using both streams, we should clear some + options here */ + + if (!(which_streams & STREAMS_AUDIO)) + audio_bound = 0; + if (!(which_streams & STREAMS_VIDEO)) + video_bound = 0; + + *(index++) = (unsigned char)((SYS_HEADER_START)>>24); + *(index++) = (unsigned char)((SYS_HEADER_START & 0x00ff0000)>>16); + *(index++) = (unsigned char)((SYS_HEADER_START & 0x0000ff00)>>8); + *(index++) = (unsigned char)(SYS_HEADER_START & 0x000000ff); + + if (which_streams == STREAMS_BOTH) { + *(index++) = (unsigned char)(SYS_HEADER_LENGTH >> 8); + *(index++) = (unsigned char)(SYS_HEADER_LENGTH & 0xff); + } else { + *(index++) = (unsigned char)((SYS_HEADER_LENGTH-3) >> 8); + *(index++) = (unsigned char)((SYS_HEADER_LENGTH-3) & 0xff); + } + + *(index++) = (unsigned char)(0x80 | (rate_bound >>15)); + *(index++) = (unsigned char)(0xff & (rate_bound >> 7)); + *(index++) = (unsigned char)(0x01 | ((rate_bound & 0x7f)<<1)); + *(index++) = (unsigned char)((audio_bound << 2)|(fixed << 1)|CSPS); + *(index++) = (unsigned char)((audio_lock << 7)| + (video_lock << 6)|0x20|video_bound); + + *(index++) = (unsigned char)RESERVED_BYTE; + + if (which_streams & STREAMS_AUDIO) { + *(index++) = stream1; + *(index++) = (unsigned char) (0xc0 | + (buffer1_scale << 5) | (buffer1_size >> 8)); + *(index++) = (unsigned char) (buffer1_size & 0xff); + } + + if (which_streams & STREAMS_VIDEO) { + *(index++) = stream2; + *(index++) = (unsigned char) (0xc0 | + (buffer2_scale << 5) | (buffer2_size >> 8)); + *(index++) = (unsigned char) (buffer2_size & 0xff); + } + +} |