summaryrefslogtreecommitdiff
path: root/core/multimedia/opieplayer/libmpeg3/mpeg3demux.c
Side-by-side diff
Diffstat (limited to 'core/multimedia/opieplayer/libmpeg3/mpeg3demux.c') (more/less context) (ignore whitespace changes)
-rw-r--r--core/multimedia/opieplayer/libmpeg3/mpeg3demux.c1849
1 files changed, 1849 insertions, 0 deletions
diff --git a/core/multimedia/opieplayer/libmpeg3/mpeg3demux.c b/core/multimedia/opieplayer/libmpeg3/mpeg3demux.c
new file mode 100644
index 0000000..cccc820
--- a/dev/null
+++ b/core/multimedia/opieplayer/libmpeg3/mpeg3demux.c
@@ -0,0 +1,1849 @@
+#include "libmpeg3.h"
+#include "mpeg3io.h"
+#include "mpeg3protos.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ABS(x) ((x) >= 0 ? (x) : -(x))
+
+/* Don't advance pointer */
+static inline unsigned char mpeg3packet_next_char(mpeg3_demuxer_t *demuxer)
+{
+ return demuxer->raw_data[demuxer->raw_offset];
+}
+
+unsigned char mpeg3packet_read_char(mpeg3_demuxer_t *demuxer)
+{
+ unsigned char result = demuxer->raw_data[demuxer->raw_offset++];
+ return result;
+}
+
+static inline unsigned int mpeg3packet_read_int16(mpeg3_demuxer_t *demuxer)
+{
+ unsigned int a, b, result;
+ a = demuxer->raw_data[demuxer->raw_offset++];
+ b = demuxer->raw_data[demuxer->raw_offset++];
+ result = (a << 8) | b;
+
+ return result;
+}
+
+static inline unsigned int mpeg3packet_next_int24(mpeg3_demuxer_t *demuxer)
+{
+ unsigned int a, b, c, result;
+ a = demuxer->raw_data[demuxer->raw_offset];
+ b = demuxer->raw_data[demuxer->raw_offset + 1];
+ c = demuxer->raw_data[demuxer->raw_offset + 2];
+ result = (a << 16) | (b << 8) | c;
+
+ return result;
+}
+
+static inline unsigned int mpeg3packet_read_int24(mpeg3_demuxer_t *demuxer)
+{
+ unsigned int a, b, c, result;
+ a = demuxer->raw_data[demuxer->raw_offset++];
+ b = demuxer->raw_data[demuxer->raw_offset++];
+ c = demuxer->raw_data[demuxer->raw_offset++];
+ result = (a << 16) | (b << 8) | c;
+
+ return result;
+}
+
+static inline unsigned int mpeg3packet_read_int32(mpeg3_demuxer_t *demuxer)
+{
+ unsigned int a, b, c, d, result;
+ a = demuxer->raw_data[demuxer->raw_offset++];
+ b = demuxer->raw_data[demuxer->raw_offset++];
+ c = demuxer->raw_data[demuxer->raw_offset++];
+ d = demuxer->raw_data[demuxer->raw_offset++];
+ result = (a << 24) | (b << 16) | (c << 8) | d;
+
+ return result;
+}
+
+static inline unsigned int mpeg3packet_skip(mpeg3_demuxer_t *demuxer, long length)
+{
+ demuxer->raw_offset += length;
+ return 0;
+}
+
+int mpeg3_get_adaptation_field(mpeg3_demuxer_t *demuxer)
+{
+ long length;
+ int pcr_flag;
+
+ demuxer->adaptation_fields++;
+/* get adaptation field length */
+ length = mpeg3packet_read_char(demuxer);
+/* get first byte */
+ pcr_flag = (mpeg3packet_read_char(demuxer) >> 4) & 1;
+
+ if(pcr_flag)
+ {
+ unsigned long clk_ref_base = mpeg3packet_read_int32(demuxer);
+ unsigned int clk_ref_ext = mpeg3packet_read_int16(demuxer);
+
+ if (clk_ref_base > 0x7fffffff)
+ { /* correct for invalid numbers */
+ clk_ref_base = 0; /* ie. longer than 32 bits when multiplied by 2 */
+ clk_ref_ext = 0; /* multiplied by 2 corresponds to shift left 1 (<<=1) */
+ }
+ else
+ {
+ clk_ref_base <<= 1; /* Create space for bit */
+ clk_ref_base |= (clk_ref_ext >> 15); /* Take bit */
+ clk_ref_ext &= 0x01ff; /* Only lower 9 bits */
+ }
+ demuxer->time = clk_ref_base + clk_ref_ext / 300;
+ if(length) mpeg3packet_skip(demuxer, length - 7);
+ }
+ else
+ mpeg3packet_skip(demuxer, length - 1);
+
+ return 0;
+}
+
+int mpeg3_get_program_association_table(mpeg3_demuxer_t *demuxer)
+{
+ demuxer->program_association_tables++;
+ demuxer->table_id = mpeg3packet_read_char(demuxer);
+ demuxer->section_length = mpeg3packet_read_int16(demuxer) & 0xfff;
+ demuxer->transport_stream_id = mpeg3packet_read_int16(demuxer);
+ mpeg3packet_skip(demuxer, demuxer->raw_size - demuxer->raw_offset);
+ return 0;
+}
+
+int mpeg3packet_get_data_buffer(mpeg3_demuxer_t *demuxer)
+{
+ while(demuxer->raw_offset < demuxer->raw_size && demuxer->data_size < demuxer->data_allocated)
+ {
+ demuxer->data_buffer[demuxer->data_size++] = demuxer->raw_data[demuxer->raw_offset++];
+ }
+ return 0;
+}
+
+int mpeg3_get_pes_packet_header(mpeg3_demuxer_t *demuxer, unsigned long *pts, unsigned long *dts)
+{
+ unsigned int pes_header_bytes = 0;
+ unsigned int pts_dts_flags;
+ int pes_header_data_length;
+
+/* drop first 8 bits */
+ mpeg3packet_read_char(demuxer);
+ pts_dts_flags = (mpeg3packet_read_char(demuxer) >> 6) & 0x3;
+ pes_header_data_length = mpeg3packet_read_char(demuxer);
+
+/* Get Presentation Time stamps and Decoding Time Stamps */
+ if(pts_dts_flags == 2)
+ {
+ *pts = (mpeg3packet_read_char(demuxer) >> 1) & 7; /* Only low 4 bits (7==1111) */
+ *pts <<= 15;
+ *pts |= (mpeg3packet_read_int16(demuxer) >> 1);
+ *pts <<= 15;
+ *pts |= (mpeg3packet_read_int16(demuxer) >> 1);
+ pes_header_bytes += 5;
+ }
+ else if(pts_dts_flags == 3)
+ {
+ *pts = (mpeg3packet_read_char(demuxer) >> 1) & 7; /* Only low 4 bits (7==1111) */
+ *pts <<= 15;
+ *pts |= (mpeg3packet_read_int16(demuxer) >> 1);
+ *pts <<= 15;
+ *pts |= (mpeg3packet_read_int16(demuxer) >> 1);
+ *dts = (mpeg3packet_read_char(demuxer) >> 1) & 7; /* Only low 4 bits (7==1111) */
+ *dts <<= 15;
+ *dts |= (mpeg3packet_read_int16(demuxer) >> 1);
+ *dts <<= 15;
+ *dts |= (mpeg3packet_read_int16(demuxer) >> 1);
+ pes_header_bytes += 10;
+ }
+/* extract other stuff here! */
+
+ mpeg3packet_skip(demuxer, pes_header_data_length - pes_header_bytes);
+ return 0;
+}
+
+int get_unknown_data(mpeg3_demuxer_t *demuxer)
+{
+ mpeg3packet_skip(demuxer, demuxer->raw_size - demuxer->raw_offset);
+ return 0;
+}
+
+int mpeg3_get_pes_packet_data(mpeg3_demuxer_t *demuxer, unsigned int stream_id)
+{
+ unsigned long pts = 0, dts = 0;
+
+ if((stream_id >> 4) == 12 || (stream_id >> 4) == 13)
+ {
+/* Just pick the first available stream if no ID is set */
+ if(demuxer->astream == -1)
+ demuxer->astream = (stream_id & 0x0f);
+
+ if((stream_id & 0x0f) == demuxer->astream && demuxer->do_audio)
+ {
+ mpeg3_get_pes_packet_header(demuxer, &pts, &dts);
+ demuxer->pes_audio_time = pts;
+ demuxer->audio_pid = demuxer->pid;
+ return mpeg3packet_get_data_buffer(demuxer);
+ }
+ }
+ else
+ if((stream_id >> 4)==14)
+ {
+/* Just pick the first available stream if no ID is set */
+ if(demuxer->vstream == -1)
+ demuxer->vstream = (stream_id & 0x0f);
+
+ if((stream_id & 0x0f) == demuxer->vstream && demuxer->do_video)
+ {
+ mpeg3_get_pes_packet_header(demuxer, &pts, &dts);
+ demuxer->pes_video_time = pts;
+ demuxer->video_pid = demuxer->pid;
+ return mpeg3packet_get_data_buffer(demuxer);
+ }
+ }
+ else
+ {
+ return get_unknown_data(demuxer);
+ }
+
+ mpeg3packet_skip(demuxer, demuxer->raw_size - demuxer->raw_offset);
+
+ return 0;
+}
+
+int mpeg3_get_pes_packet(mpeg3_demuxer_t *demuxer)
+{
+ unsigned int stream_id;
+
+ demuxer->pes_packets++;
+ stream_id = mpeg3packet_read_char(demuxer);
+/* Skip startcode */
+ mpeg3packet_read_int24(demuxer);
+/* Skip pes packet length */
+ mpeg3packet_read_int16(demuxer);
+
+ if(stream_id != MPEG3_PRIVATE_STREAM_2 && stream_id != MPEG3_PADDING_STREAM)
+ {
+ return mpeg3_get_pes_packet_data(demuxer, stream_id);
+ }
+ else
+ if(stream_id == MPEG3_PRIVATE_STREAM_2)
+ {
+/* Dump private data! */
+ fprintf(stderr, "stream_id == MPEG3_PRIVATE_STREAM_2\n");
+ mpeg3packet_skip(demuxer, demuxer->raw_size - demuxer->raw_offset);
+ return 0;
+ }
+ else
+ if(stream_id == MPEG3_PADDING_STREAM)
+ {
+ mpeg3packet_skip(demuxer, demuxer->raw_size - demuxer->raw_offset);
+ return 0;
+ }
+ else
+ {
+ fprintf(stderr, "unknown stream_id in pes packet");
+ return 1;
+ }
+ return 0;
+}
+
+int mpeg3_get_payload(mpeg3_demuxer_t *demuxer)
+{
+ if(demuxer->payload_unit_start_indicator)
+ {
+ if(demuxer->pid==0) mpeg3_get_program_association_table(demuxer);
+ else
+ if(mpeg3packet_next_int24(demuxer) == MPEG3_PACKET_START_CODE_PREFIX) mpeg3_get_pes_packet(demuxer);
+ else
+ mpeg3packet_skip(demuxer, demuxer->raw_size - demuxer->raw_offset);
+ }
+ else
+ {
+ if(demuxer->pid == demuxer->audio_pid && demuxer->do_audio)
+ {
+ mpeg3packet_get_data_buffer(demuxer);
+ }
+ else
+ if(demuxer->pid == demuxer->video_pid && demuxer->do_video)
+ {
+ mpeg3packet_get_data_buffer(demuxer);
+ }
+ else
+ mpeg3packet_skip(demuxer, demuxer->raw_size - demuxer->raw_offset);
+ }
+ return 0;
+}
+
+/* Read a transport packet */
+int mpeg3_read_transport(mpeg3_demuxer_t *demuxer)
+{
+ mpeg3_title_t *title = demuxer->titles[demuxer->current_title];
+ int result = mpeg3io_read_data(demuxer->raw_data, demuxer->packet_size, title->fs);
+ unsigned int bits;
+ int table_entry;
+
+ demuxer->raw_size = demuxer->packet_size;
+ demuxer->raw_offset = 0;
+ if(result)
+ {
+ perror("mpeg3_read_transport");
+ return 1;
+ }
+
+/* Sync byte */
+ if(mpeg3packet_read_char(demuxer) != MPEG3_SYNC_BYTE)
+ {
+ fprintf(stderr, "mpeg3packet_read_char(demuxer) != MPEG3_SYNC_BYTE\n");
+ return 1;
+ }
+
+/* bits = mpeg3packet_read_int24(demuxer) & 0x0000ffff; */
+/* demuxer->transport_error_indicator = bits >> 15; */
+/* demuxer->payload_unit_start_indicator = (bits >> 14) & 1; */
+/* demuxer->pid = bits & 0x00001fff; */
+/* demuxer->transport_scrambling_control = (mpeg3packet_next_char(demuxer) >> 6) & 0x3; */
+/* demuxer->adaptation_field_control = (mpeg3packet_next_char(demuxer) >> 4) & 0x3; */
+/* demuxer->continuity_counter = (mpeg3packet_read_char(demuxer) & 0xf); */
+
+ bits = mpeg3packet_read_int24(demuxer) & 0x00ffffff;
+ demuxer->transport_error_indicator = (bits >> 23) & 0x1;
+ demuxer->payload_unit_start_indicator = (bits >> 22) & 0x1;
+ demuxer->pid = (bits >> 8) & 0x00001fff;
+ demuxer->transport_scrambling_control = (bits >> 6) & 0x3;
+ demuxer->adaptation_field_control = (bits >> 4) & 0x3;
+ demuxer->continuity_counter = bits & 0xf;
+
+ if(demuxer->transport_error_indicator)
+ {
+ fprintf(stderr, "demuxer->transport_error_indicator\n");
+ return 1;
+ }
+
+ if (demuxer->pid == 0x1fff)
+ {
+ demuxer->is_padding = 1; /* padding; just go to next */
+ return 0;
+ }
+ else
+ {
+ demuxer->is_padding = 0;
+ }
+
+/* Get pid */
+ for(table_entry = 0, result = 0; table_entry < demuxer->total_pids; table_entry++)
+ {
+ if(demuxer->pid == demuxer->pid_table[table_entry])
+ {
+ result = 1;
+ break;
+ }
+ }
+
+/* Not in pid table */
+ if(!result)
+ {
+ demuxer->pid_table[table_entry] = demuxer->pid;
+ demuxer->continuity_counters[table_entry] = demuxer->continuity_counter; /* init */
+ demuxer->total_pids++;
+ }
+ result = 0;
+
+/* Check counters */
+ if(demuxer->pid != MPEG3_PROGRAM_ASSOCIATION_TABLE &&
+ demuxer->pid != MPEG3_CONDITIONAL_ACCESS_TABLE &&
+ (demuxer->adaptation_field_control == 1 || demuxer->adaptation_field_control == 3))
+ {
+ if(demuxer->continuity_counters[table_entry] != demuxer->continuity_counter)
+ {
+ fprintf(stderr, "demuxer->continuity_counters[table_entry] != demuxer->continuity_counter\n");
+/* Reset it */
+ demuxer->continuity_counters[table_entry] = demuxer->continuity_counter;
+ }
+ if(++(demuxer->continuity_counters[table_entry]) > 15) demuxer->continuity_counters[table_entry] = 0;
+ }
+
+ if(demuxer->adaptation_field_control == 2 || demuxer->adaptation_field_control == 3)
+ result = mpeg3_get_adaptation_field(demuxer);
+
+ if(demuxer->adaptation_field_control == 1 || demuxer->adaptation_field_control == 3)
+ result = mpeg3_get_payload(demuxer);
+
+ return result;
+}
+
+int mpeg3_get_system_header(mpeg3_demuxer_t *demuxer)
+{
+ int length = mpeg3packet_read_int16(demuxer);
+ mpeg3packet_skip(demuxer, length);
+ return 0;
+}
+
+unsigned long mpeg3_get_timestamp(mpeg3_demuxer_t *demuxer)
+{
+ unsigned long timestamp;
+/* Only low 4 bits (7==1111) */
+ timestamp = (mpeg3packet_read_char(demuxer) >> 1) & 7;
+ timestamp <<= 15;
+ timestamp |= (mpeg3packet_read_int16(demuxer) >> 1);
+ timestamp <<= 15;
+ timestamp |= (mpeg3packet_read_int16(demuxer) >> 1);
+ return timestamp;
+}
+
+int mpeg3_get_pack_header(mpeg3_demuxer_t *demuxer, unsigned int *header)
+{
+ unsigned long i, j;
+ unsigned long clock_ref, clock_ref_ext;
+
+/* Get the time code */
+ if((mpeg3packet_next_char(demuxer) >> 4) == 2)
+ {
+/* MPEG-1 */
+ demuxer->time = (double)mpeg3_get_timestamp(demuxer) / 90000;
+/* Skip 3 bytes */
+ mpeg3packet_read_int24(demuxer);
+ }
+ else
+ if(mpeg3packet_next_char(demuxer) & 0x40)
+ {
+ i = mpeg3packet_read_int32(demuxer);
+ j = mpeg3packet_read_int16(demuxer);
+ if(i & 0x40000000 || (i >> 28) == 2)
+ {
+ clock_ref = ((i & 0x31000000) << 3);
+ clock_ref |= ((i & 0x03fff800) << 4);
+ clock_ref |= ((i & 0x000003ff) << 5);
+ clock_ref |= ((j & 0xf800) >> 11);
+ clock_ref_ext = (j >> 1) & 0x1ff;
+
+ demuxer->time = (double)(clock_ref + clock_ref_ext / 300) / 90000;
+/* Skip 3 bytes */
+ mpeg3packet_read_int24(demuxer);
+ i = mpeg3packet_read_char(demuxer) & 0x7;
+
+/* stuffing */
+ mpeg3packet_skip(demuxer, i);
+ }
+ }
+ else
+ {
+ mpeg3packet_skip(demuxer, 2);
+ }
+
+ *header = mpeg3packet_read_int32(demuxer);
+ if(*header == MPEG3_SYSTEM_START_CODE)
+ {
+ mpeg3_get_system_header(demuxer);
+ *header = mpeg3packet_read_int32(demuxer);
+ }
+ return 0;
+}
+
+/* Program packet reading core */
+int mpeg3_get_ps_pes_packet(mpeg3_demuxer_t *demuxer, unsigned int *header)
+{
+ unsigned long pts = 0, dts = 0;
+ int stream_id;
+ int pes_packet_length;
+ int pes_packet_start;
+ int i;
+ mpeg3_t *file = demuxer->file;
+
+ stream_id = *header & 0xff;
+ pes_packet_length = mpeg3packet_read_int16(demuxer);
+ pes_packet_start = demuxer->raw_offset;
+
+ if(stream_id != MPEG3_PRIVATE_STREAM_2 &&
+ stream_id != MPEG3_PADDING_STREAM)
+ {
+ if((mpeg3packet_next_char(demuxer) >> 6) == 0x02)
+ {
+/* Get MPEG-2 packet */
+ int pes_header_bytes = 0;
+ int scrambling = (mpeg3packet_read_char(demuxer) >> 4) & 0x3;
+ int pts_dts_flags = (mpeg3packet_read_char(demuxer) >> 6) & 0x3;
+ int pes_header_data_length = mpeg3packet_read_char(demuxer);
+
+ if(scrambling && (demuxer->do_audio || demuxer->do_video))
+ {
+/* Decrypt it */
+ if(mpeg3_decrypt_packet(demuxer->titles[demuxer->current_title]->fs->css,
+ demuxer->raw_data))
+ {
+ fprintf(stderr, "mpeg3_get_ps_pes_packet: Decryption not available\n");
+ return 1;
+ }
+ }
+
+/* Get Presentation and Decoding Time Stamps */
+ if(pts_dts_flags == 2)
+ {
+ pts = (mpeg3packet_read_char(demuxer) >> 1) & 7; /* Only low 4 bits (7==1111) */
+ pts <<= 15;
+ pts |= (mpeg3packet_read_int16(demuxer) >> 1);
+ pts <<= 15;
+ pts |= (mpeg3packet_read_int16(demuxer) >> 1);
+ pes_header_bytes += 5;
+ }
+ else
+ if(pts_dts_flags == 3)
+ {
+ pts = (mpeg3packet_read_char(demuxer) >> 1) & 7; /* Only low 4 bits (7==1111) */
+ pts <<= 15;
+ pts |= (mpeg3packet_read_int16(demuxer) >> 1);
+ pts <<= 15;
+ pts |= (mpeg3packet_read_int16(demuxer) >> 1);
+ dts = (mpeg3packet_read_char(demuxer) >> 1) & 7; /* Only low 4 bits (7==1111) */
+ dts <<= 15;
+ dts |= (mpeg3packet_read_int16(demuxer) >> 1);
+ dts <<= 15;
+ dts |= (mpeg3packet_read_int16(demuxer) >> 1);
+ pes_header_bytes += 10;
+ }
+
+/* Skip unknown */
+ mpeg3packet_skip(demuxer, pes_header_data_length - pes_header_bytes);
+ }
+ else
+ {
+ int pts_dts_flags;
+/* Get MPEG-1 packet */
+ while(mpeg3packet_next_char(demuxer) == 0xff)
+ {
+ mpeg3packet_read_char(demuxer);
+ }
+
+/* Skip STD buffer scale */
+ if((mpeg3packet_next_char(demuxer) & 0x40) == 0x40)
+ {
+ mpeg3packet_skip(demuxer, 2);
+ }
+
+/* Decide which timestamps are available */
+ pts_dts_flags = mpeg3packet_next_char(demuxer);
+
+ if(pts_dts_flags >= 0x30)
+ {
+/* Get the presentation and decoding time stamp */
+ pts = mpeg3_get_timestamp(demuxer);
+ dts = mpeg3_get_timestamp(demuxer);
+ }
+ else
+ if(pts_dts_flags >= 0x20)
+ {
+/* Get just the presentation time stamp */
+ pts = mpeg3_get_timestamp(demuxer);
+ }
+ else
+ if(pts_dts_flags == 0x0f)
+ {
+/* End of timestamps */
+ mpeg3packet_read_char(demuxer);
+ }
+ else
+ {
+ return 1; /* Error */
+ }
+ }
+
+/* Now extract the payload. */
+ if((stream_id >> 4) == 0xc || (stream_id >> 4) == 0xd)
+ {
+/* Audio data */
+/* Take first stream ID if -1 */
+ pes_packet_length -= demuxer->raw_offset - pes_packet_start;
+ if(!demuxer->do_audio && !demuxer->do_video)
+ demuxer->astream_table[stream_id & 0x0f] = AUDIO_MPEG;
+ else
+ if(demuxer->astream == -1)
+ demuxer->astream = stream_id & 0x0f;
+
+ if((stream_id & 0x0f) == demuxer->astream && demuxer->do_audio)
+ {
+ if(pts) demuxer->pes_audio_time = pts;
+
+ memcpy(&demuxer->data_buffer[demuxer->data_size],
+ &demuxer->raw_data[demuxer->raw_offset],
+ pes_packet_length);
+ demuxer->data_size += pes_packet_length;
+ demuxer->raw_offset += pes_packet_length;
+ }
+ else
+ {
+ mpeg3packet_skip(demuxer, pes_packet_length);
+ }
+ }
+ else
+ if((stream_id >> 4) == 0xe)
+ {
+/* Video data */
+/* Take first stream ID if -1 */
+ if(!demuxer->do_audio && !demuxer->do_video)
+ demuxer->vstream_table[stream_id & 0x0f] = 1;
+ else
+ if(demuxer->vstream == -1)
+ demuxer->vstream = stream_id & 0x0f;
+
+ pes_packet_length -= demuxer->raw_offset - pes_packet_start;
+ if((stream_id & 0x0f) == demuxer->vstream && demuxer->do_video)
+ {
+ if(pts) demuxer->pes_video_time = pts;
+
+ memcpy(&demuxer->data_buffer[demuxer->data_size],
+ &demuxer->raw_data[demuxer->raw_offset],
+ pes_packet_length);
+ demuxer->data_size += pes_packet_length;
+ demuxer->raw_offset += pes_packet_length;
+ }
+ else
+ {
+ mpeg3packet_skip(demuxer, pes_packet_length);
+ }
+ }
+ else
+ if(stream_id == 0xbd && demuxer->raw_data[demuxer->raw_offset] != 0xff)
+ {
+/* DVD audio data */
+/* Get the audio format */
+ int format;
+ if((demuxer->raw_data[demuxer->raw_offset] & 0xf0) == 0xa0)
+ format = AUDIO_PCM;
+ else
+ format = AUDIO_AC3;
+
+ stream_id = demuxer->raw_data[demuxer->raw_offset] - 0x80;
+
+/* Take first stream ID if not building TOC. */
+ if(!demuxer->do_audio && !demuxer->do_video)
+ demuxer->astream_table[stream_id] = format;
+ else
+ if(demuxer->astream == -1)
+ demuxer->astream = stream_id;
+
+ if(stream_id == demuxer->astream && demuxer->do_audio)
+ {
+ demuxer->aformat = format;
+ if(pts) demuxer->pes_audio_time = pts;
+ mpeg3packet_read_int32(demuxer);
+ pes_packet_length -= demuxer->raw_offset - pes_packet_start;
+
+ memcpy(&demuxer->data_buffer[demuxer->data_size],
+ &demuxer->raw_data[demuxer->raw_offset],
+ pes_packet_length);
+ demuxer->data_size += pes_packet_length;
+ demuxer->raw_offset += pes_packet_length;
+ }
+ else
+ {
+ pes_packet_length -= demuxer->raw_offset - pes_packet_start;
+ mpeg3packet_skip(demuxer, pes_packet_length);
+ }
+ }
+ else
+ if(stream_id == 0xbc || 1)
+ {
+ pes_packet_length -= demuxer->raw_offset - pes_packet_start;
+ mpeg3packet_skip(demuxer, pes_packet_length);
+ }
+ }
+ else
+ if(stream_id == MPEG3_PRIVATE_STREAM_2 || stream_id == MPEG3_PADDING_STREAM)
+ {
+ pes_packet_length -= demuxer->raw_offset - pes_packet_start;
+ mpeg3packet_skip(demuxer, pes_packet_length);
+ }
+
+ while(demuxer->raw_offset + 4 < demuxer->raw_size)
+ {
+ *header = mpeg3packet_read_int32(demuxer);
+ if((*header >> 8) != MPEG3_PACKET_START_CODE_PREFIX)
+ demuxer->raw_offset -= 3;
+ else
+ break;
+ }
+
+ return 0;
+}
+
+int mpeg3_read_program(mpeg3_demuxer_t *demuxer)
+{
+ int result = 0, count = 0;
+ mpeg3_t *file = demuxer->file;
+ mpeg3_title_t *title = demuxer->titles[demuxer->current_title];
+ unsigned int header;
+ demuxer->raw_size = demuxer->packet_size;
+ demuxer->raw_offset = 0;
+ demuxer->data_size = 0;
+
+/* Search backward for it. */
+ header = mpeg3io_read_int32(title->fs);
+ result = mpeg3io_eof(title->fs);
+
+ if(!result) result = mpeg3io_seek_relative(title->fs, -4);
+
+// Search backwards for header
+ while(header != MPEG3_PACK_START_CODE && !result && count < demuxer->packet_size)
+ {
+ result = mpeg3io_seek_relative(title->fs, -1);
+ if(!result)
+ {
+ header >>= 8;
+ header |= mpeg3io_read_char(title->fs) << 24;
+ result = mpeg3io_seek_relative(title->fs, -1);
+ }
+ count++;
+ }
+
+ if(result)
+ {
+// couldn't find MPEG3_PACK_START_CODE
+ return 1;
+ }
+
+ result = mpeg3io_read_data(demuxer->raw_data, demuxer->packet_size, title->fs);
+
+ if(result)
+ {
+ perror("mpeg3_read_program");
+ return 1;
+ }
+
+ header = mpeg3packet_read_int32(demuxer);
+ while(demuxer->raw_offset + 4 < demuxer->raw_size && !result)
+ {
+ if(header == MPEG3_PACK_START_CODE)
+ {
+ result = mpeg3_get_pack_header(demuxer, &header);
+ }
+ else
+ if((header >> 8) == MPEG3_PACKET_START_CODE_PREFIX)
+ {
+ result = mpeg3_get_ps_pes_packet(demuxer, &header);
+ }
+ }
+ return result;
+}
+
+double mpeg3_lookup_time_offset(mpeg3_demuxer_t *demuxer, long byte)
+{
+ int i;
+ mpeg3_title_t *title = demuxer->titles[demuxer->current_title];
+
+ if(!title->timecode_table_size) return 0;
+
+ for(i = title->timecode_table_size - 1;
+ i >= 0 && title->timecode_table[i].start_byte > byte;
+ i--)
+ ;
+ if(i < 0) i = 0;
+ return title->timecode_table[i].absolute_start_time - title->timecode_table[i].start_time;
+}
+
+int mpeg3_advance_timecode(mpeg3_demuxer_t *demuxer, int reverse)
+{
+ mpeg3_title_t *title = demuxer->titles[demuxer->current_title];
+ int result = 0;
+ int do_seek = 0;
+
+/* Skip timecode advancing when constructing timecode table */
+ if(!title->timecode_table ||
+ !title->timecode_table_size ||
+ demuxer->generating_timecode) return 0;
+
+ if(!reverse)
+ {
+/* Get inside the current timecode */
+ if(mpeg3io_tell(title->fs) < title->timecode_table[demuxer->current_timecode].start_byte)
+ {
+ mpeg3io_seek(title->fs, title->timecode_table[demuxer->current_timecode].start_byte);
+ }
+
+/* Get the next timecode */
+ while(!result &&
+ (mpeg3io_tell(title->fs) >= title->timecode_table[demuxer->current_timecode].end_byte ||
+ demuxer->current_program != title->timecode_table[demuxer->current_timecode].program))
+ {
+
+/*
+ * printf("mpeg3_advance_timecode %d %d %d %d\n", mpeg3io_tell(title->fs), title->timecode_table[demuxer->current_timecode].end_byte,
+ * demuxer->current_program, title->timecode_table[demuxer->current_timecode].program);
+ */
+
+ demuxer->current_timecode++;
+ if(demuxer->current_timecode >= title->timecode_table_size)
+ {
+ demuxer->current_timecode = 0;
+ if(demuxer->current_title + 1 < demuxer->total_titles)
+ {
+ mpeg3demux_open_title(demuxer, demuxer->current_title + 1);
+ do_seek = 1;
+ }
+ else
+ {
+ mpeg3io_seek(title->fs, mpeg3io_total_bytes(title->fs));
+ result = 1;
+ }
+ }
+ title = demuxer->titles[demuxer->current_title];
+ }
+
+ if(!result && do_seek)
+ {
+ mpeg3io_seek(title->fs, title->timecode_table[demuxer->current_timecode].start_byte);
+ }
+ }
+ else
+ {
+/* Get the previous timecode */
+ while(!result &&
+ (mpeg3io_tell(title->fs) < title->timecode_table[demuxer->current_timecode].start_byte ||
+ demuxer->current_program != title->timecode_table[demuxer->current_timecode].program))
+ {
+/*
+ * if(demuxer->do_audio) printf("mpeg3_reverse_timecode %d %d %d %d\n", mpeg3io_tell(title->fs), title->timecode_table[demuxer->current_timecode].end_byte,
+ * demuxer->current_program, title->timecode_table[demuxer->current_timecode].program);
+ */
+ demuxer->current_timecode--;
+ if(demuxer->current_timecode < 0)
+ {
+ if(demuxer->current_title > 0)
+ {
+ mpeg3demux_open_title(demuxer, demuxer->current_title - 1);
+ title = demuxer->titles[demuxer->current_title];
+ demuxer->current_timecode = title->timecode_table_size - 1;
+ do_seek = 1;
+ }
+ else
+ {
+ mpeg3io_seek(title->fs, 0);
+ demuxer->current_timecode = 0;
+ result = 1;
+ }
+ }
+ }
+
+ if(!result && do_seek)
+ mpeg3io_seek(title->fs, title->timecode_table[demuxer->current_timecode].start_byte);
+ }
+
+ return result;
+}
+
+/* Read packet in the forward direction */
+int mpeg3_read_next_packet(mpeg3_demuxer_t *demuxer)
+{
+ int result = 0;
+ long current_position;
+ mpeg3_t *file = demuxer->file;
+ mpeg3_title_t *title = demuxer->titles[demuxer->current_title];
+ demuxer->data_size = 0;
+ demuxer->data_position = 0;
+
+/* Flip the file descriptor back to the end of the packet for forward */
+/* reading. */
+ if(demuxer->reverse)
+ {
+ result = mpeg3io_seek_relative(title->fs, demuxer->packet_size);
+ demuxer->reverse = 0;
+ }
+
+/* Read packets until the output buffer is full */
+ if(!result)
+ {
+ do
+ {
+ result = mpeg3_advance_timecode(demuxer, 0);
+
+ if(!result)
+ {
+ demuxer->time_offset = mpeg3_lookup_time_offset(demuxer, mpeg3io_tell(title->fs));
+
+ if(file->is_transport_stream)
+ {
+ result = mpeg3_read_transport(demuxer);
+ }
+ else
+ if(file->is_program_stream)
+ {
+ result = mpeg3_read_program(demuxer);
+ }
+ else
+ {
+/* Read elementary stream. */
+ result = mpeg3io_read_data(demuxer->data_buffer, demuxer->packet_size, title->fs);
+ if(!result) demuxer->data_size = demuxer->packet_size;
+ }
+ }
+ }while(!result && demuxer->data_size == 0 && (demuxer->do_audio || demuxer->do_video));
+ }
+
+ return result;
+}
+
+/* Read the packet right before the packet we're currently on. */
+int mpeg3_read_prev_packet(mpeg3_demuxer_t *demuxer)
+{
+ int result = 0;
+ mpeg3_t *file = demuxer->file;
+ long current_position;
+ mpeg3_title_t *title = demuxer->titles[demuxer->current_title];
+
+ demuxer->data_size = 0;
+ demuxer->data_position = 0;
+
+ do
+ {
+/* Rewind to the start of the packet to be read. */
+ result = mpeg3io_seek_relative(title->fs, -demuxer->packet_size);
+
+ if(!result) result = mpeg3_advance_timecode(demuxer, 1);
+ if(!result) demuxer->time_offset = mpeg3_lookup_time_offset(demuxer, mpeg3io_tell(title->fs));
+
+ if(file->is_transport_stream && !result)
+ {
+ result = mpeg3_read_transport(demuxer);
+ if(!mpeg3io_bof(title->fs))
+ /* if(!result) */result = mpeg3io_seek_relative(title->fs, -demuxer->packet_size);
+ }
+ else
+ if(file->is_program_stream && !result)
+ {
+
+ result = mpeg3_read_program(demuxer);
+ if(!mpeg3io_bof(title->fs))
+ /* if(!result) */result = mpeg3io_seek_relative(title->fs, -demuxer->packet_size);
+ }
+ else
+ if(!result)
+ {
+/* Elementary stream */
+/* Read the packet forwards and seek back to the start */
+ result = mpeg3io_read_data(demuxer->data_buffer, demuxer->packet_size, title->fs);
+ if(!result)
+ {
+ demuxer->data_size = demuxer->packet_size;
+ result = mpeg3io_seek_relative(title->fs, -demuxer->packet_size);
+ }
+ }
+ }while(!result && demuxer->data_size == 0 && (demuxer->do_audio || demuxer->do_video));
+
+/* Remember that the file descriptor is at the beginning of the packet just read. */
+ demuxer->reverse = 1;
+ demuxer->error_flag = result;
+ return result;
+}
+
+
+/* Used for audio */
+int mpeg3demux_read_data(mpeg3_demuxer_t *demuxer,
+ unsigned char *output,
+ long size)
+{
+ long i;
+ int result = 0;
+ mpeg3_t *file = demuxer->file;
+ demuxer->error_flag = 0;
+
+ if(demuxer->data_position >= 0)
+ {
+/* Read forwards */
+ for(i = 0; i < size && !result; )
+ {
+ int fragment_size = size - i;
+ if(fragment_size > demuxer->data_size - demuxer->data_position)
+ fragment_size = demuxer->data_size - demuxer->data_position;
+ memcpy(output + i, demuxer->data_buffer + demuxer->data_position, fragment_size);
+ demuxer->data_position += fragment_size;
+ i += fragment_size;
+
+ if(i < size)
+ {
+ result = mpeg3_read_next_packet(demuxer);
+ }
+ }
+ }
+ else
+ {
+/* Read backwards a full packet. */
+/* Only good for reading less than the size of a full packet, but */
+/* this routine should only be used for searching for previous markers. */
+ long current_position = demuxer->data_position;
+ result = mpeg3_read_prev_packet(demuxer);
+ if(!result) demuxer->data_position = demuxer->data_size + current_position;
+ memcpy(output, demuxer->data_buffer + demuxer->data_position, size);
+ demuxer->data_position += size;
+ }
+
+ demuxer->error_flag = result;
+ return result;
+}
+
+unsigned int mpeg3demux_read_char_packet(mpeg3_demuxer_t *demuxer)
+{
+ demuxer->error_flag = 0;
+ if(demuxer->data_position >= demuxer->data_size)
+ demuxer->error_flag = mpeg3_read_next_packet(demuxer);
+ demuxer->next_char = demuxer->data_buffer[demuxer->data_position++];
+ return demuxer->next_char;
+}
+
+unsigned int mpeg3demux_read_prev_char_packet(mpeg3_demuxer_t *demuxer)
+{
+ demuxer->error_flag = 0;
+ demuxer->data_position--;
+ if(demuxer->data_position < 0)
+ {
+ demuxer->error_flag = mpeg3_read_prev_packet(demuxer);
+ if(!demuxer->error_flag) demuxer->data_position = demuxer->data_size - 1;
+ }
+ demuxer->next_char = demuxer->data_buffer[demuxer->data_position];
+ return demuxer->next_char;
+}
+
+mpeg3demux_timecode_t* mpeg3_append_timecode(mpeg3_demuxer_t *demuxer,
+ mpeg3_title_t *title,
+ long prev_byte,
+ double prev_time,
+ long next_byte,
+ double next_time,
+ int dont_store)
+{
+ mpeg3demux_timecode_t *new_table;
+ mpeg3demux_timecode_t *new_timecode, *old_timecode;
+ long i;
+
+ if(!title->timecode_table ||
+ title->timecode_table_allocation <= title->timecode_table_size)
+ {
+ if(title->timecode_table_allocation == 0)
+ title->timecode_table_allocation = 1;
+ else
+ title->timecode_table_allocation *= 2;
+
+ new_table = (mpeg3demux_timecode_t*)calloc(1, sizeof(mpeg3demux_timecode_t) * title->timecode_table_allocation);
+ if(title->timecode_table)
+ {
+ for(i = 0; i < title->timecode_table_size; i++)
+ {
+ new_table[i] = title->timecode_table[i];
+ }
+
+ free(title->timecode_table);
+ }
+ title->timecode_table = new_table;
+ }
+
+ if(!dont_store)
+ {
+ new_timecode = &title->timecode_table[title->timecode_table_size];
+ new_timecode->start_byte = next_byte;
+ new_timecode->start_time = next_time;
+ new_timecode->absolute_start_time = 0;
+
+ if(title->timecode_table_size > 0)
+ {
+ old_timecode = &title->timecode_table[title->timecode_table_size - 1];
+ old_timecode->end_byte = prev_byte;
+ old_timecode->end_time = prev_time;
+ new_timecode->absolute_start_time =
+ prev_time -
+ old_timecode->start_time +
+ old_timecode->absolute_start_time;
+ new_timecode->absolute_end_time = next_time;
+ }
+ }
+
+ title->timecode_table_size++;
+ return new_timecode;
+}
+
+mpeg3demux_timecode_t* mpeg3demux_next_timecode(mpeg3_demuxer_t *demuxer,
+ int *current_title,
+ int *current_timecode,
+ int current_program)
+{
+ int done = 0;
+ while(!done)
+ {
+/* Increase timecode number */
+ if(*current_timecode < demuxer->titles[*current_title]->timecode_table_size - 1)
+ {
+ (*current_timecode)++;
+ if(demuxer->titles[*current_title]->timecode_table[*current_timecode].program == current_program)
+ return &(demuxer->titles[*current_title]->timecode_table[*current_timecode]);
+ }
+ else
+/* Increase title number */
+ if(*current_title < demuxer->total_titles - 1)
+ {
+ (*current_title)++;
+ (*current_timecode) = 0;
+ if(demuxer->titles[*current_title]->timecode_table[*current_timecode].program == current_program)
+ return &(demuxer->titles[*current_title]->timecode_table[*current_timecode]);
+ }
+ else
+/* End of disk */
+ done = 1;
+ }
+ return 0;
+}
+
+mpeg3demux_timecode_t* mpeg3demux_prev_timecode(mpeg3_demuxer_t *demuxer,
+ int *current_title,
+ int *current_timecode,
+ int current_program)
+{
+ int done = 0;
+ while(!done)
+ {
+/* Increase timecode number */
+ if(*current_timecode > 0)
+ {
+ (*current_timecode)--;
+ if(demuxer->titles[*current_title]->timecode_table[*current_timecode].program == current_program)
+ return &(demuxer->titles[*current_title]->timecode_table[*current_timecode]);
+ }
+ else
+/* Increase title number */
+ if(*current_title > 0)
+ {
+ (*current_title)--;
+ (*current_timecode) = demuxer->titles[*current_title]->timecode_table_size - 1;
+ if(demuxer->titles[*current_title]->timecode_table[*current_timecode].program == current_program)
+ return &(demuxer->titles[*current_title]->timecode_table[*current_timecode]);
+ }
+ else
+/* End of disk */
+ done = 1;
+
+ }
+ return 0;
+}
+
+int mpeg3demux_open_title(mpeg3_demuxer_t *demuxer, int title_number)
+{
+ mpeg3_title_t *title;
+
+ if(title_number < demuxer->total_titles)
+ {
+ if(demuxer->current_title >= 0)
+ {
+ mpeg3io_close_file(demuxer->titles[demuxer->current_title]->fs);
+ demuxer->current_title = -1;
+ }
+
+ title = demuxer->titles[title_number];
+ if(mpeg3io_open_file(title->fs))
+ {
+ demuxer->error_flag = 1;
+ perror("mpeg3demux_open_title");
+ }
+ else
+ {
+ demuxer->current_title = title_number;
+ }
+ }
+
+ demuxer->current_timecode = 0;
+
+ return demuxer->error_flag;
+}
+
+/* Assign program numbers to interleaved programs */
+int mpeg3demux_assign_programs(mpeg3_demuxer_t *demuxer)
+{
+ int current_program = 0;
+ int current_title = 0, previous_title;
+ int current_timecode = 0, previous_timecode;
+ double current_time, current_length;
+ int done = 0;
+ int interleaved = 0;
+ mpeg3demux_timecode_t *timecode1, *timecode2;
+ double program_times[MPEG3_MAX_STREAMS];
+ int total_programs = 1;
+ int i, j;
+ int program_exists, last_program_assigned = 0;
+ int total_timecodes;
+ mpeg3_title_t **titles = demuxer->titles;
+
+ for(i = 0, total_timecodes = 0; i < demuxer->total_titles; i++)
+ total_timecodes += demuxer->titles[i]->timecode_table_size;
+
+// if(total_timecodes < 3) return 0;
+
+/*
+ * // Assign programs based on length of contiguous timecode
+ * timecode1 = demuxer->titles[current_title]->timecode_table;
+ * while(!done)
+ * {
+ * if(!timecode1) done = 1;
+ * else
+ * if(timecode1->end_time - timecode1->start_time < MPEG3_PROGRAM_THRESHOLD)
+ * {
+ * // Got interleaved section
+ * interleaved = 1;
+ * program_times[0] = timecode1->end_time;
+ *
+ * while(interleaved && !done)
+ * {
+ * timecode2 = mpeg3demux_next_timecode(demuxer,
+ * &current_title,
+ * &current_timecode,
+ * 0);
+ *
+ * if(!timecode2) done = 1;
+ * else
+ * {
+ * // Another segment of interleaved data
+ * if(timecode2->end_time - timecode2->start_time < MPEG3_PROGRAM_THRESHOLD)
+ * {
+ * // Search program times for where the previous instance of the program left off
+ * for(program_exists = 0, i = 0;
+ * i < total_programs && !program_exists;
+ * i++)
+ * {
+ * // Got a previous instance of the program
+ * if(program_times[i] + 0.5 > timecode2->start_time &&
+ * program_times[i] - 0.5 < timecode2->start_time)
+ * {
+ * program_times[i] = timecode2->end_time;
+ * timecode2->program = i;
+ * program_exists = 1;
+ *
+ * // Programs must always start at 0 for an interleaved section
+ * if(i < last_program_assigned && i != 0)
+ * {
+ * // Shift programs in the interleaved section down until they start at 0
+ * for(j = 0; j < total_programs - 1; j++)
+ * program_times[j] = program_times[j + 1];
+ *
+ * for(previous_title = current_title, previous_timecode = current_timecode;
+ * titles[previous_title]->timecode_table[previous_timecode].program > 0 &&
+ * (previous_title >= 0 || previous_timecode >= 0); )
+ * {
+ * titles[previous_title]->timecode_table[previous_timecode].program--;
+ * previous_timecode--;
+ * if(previous_timecode < 0 && previous_title > 0)
+ * {
+ * previous_title--;
+ * previous_timecode = titles[previous_title]->timecode_table_size - 1;
+ * }
+ * }
+ * }
+ * }
+ * }
+ *
+ * // Didn't get one
+ * if(!program_exists)
+ * {
+ * program_times[total_programs] = timecode2->end_time;
+ * timecode2->program = total_programs++;
+ * }
+ * last_program_assigned = timecode2->program;
+ * }
+ * // No more interleaved section
+ * else
+ * {
+ * interleaved = 0;
+ * // Restart program table from the beginning
+ * total_programs = 1;
+ * last_program_assigned = 0;
+ * timecode1 = mpeg3demux_next_timecode(demuxer,
+ * &current_title,
+ * &current_timecode,
+ * 0);
+ * }
+ * }
+ * }
+ * }
+ * else
+ * // Get next timecode
+ * timecode1 = mpeg3demux_next_timecode(demuxer,
+ * &current_title,
+ * &current_timecode,
+ * 0);
+ * }
+ *
+ * demuxer->total_programs = total_programs;
+ */
+
+/* Assign absolute timecodes in each program. */
+ for(current_program = 0;
+ current_program < total_programs;
+ current_program++)
+ {
+ current_time = 0;
+ current_title = 0;
+ current_timecode = -1;
+ while(timecode1 = mpeg3demux_next_timecode(demuxer,
+ &current_title,
+ &current_timecode,
+ current_program))
+ {
+ timecode1->absolute_start_time = current_time;
+ current_time += timecode1->end_time - timecode1->start_time;
+ timecode1->absolute_end_time = current_time;
+ }
+ }
+//for(i = 0; i < demuxer->total_titles; i++) mpeg3_dump_title(demuxer->titles[i]);
+ demuxer->current_program = 0;
+ return 0;
+}
+
+/* ==================================================================== */
+/* Entry points */
+/* ==================================================================== */
+
+mpeg3_demuxer_t* mpeg3_new_demuxer(mpeg3_t *file, int do_audio, int do_video, int stream_id)
+{
+ mpeg3_demuxer_t *demuxer = (mpeg3_demuxer_t*)calloc(1, sizeof(mpeg3_demuxer_t));
+ int i;
+
+/* The demuxer will change the default packet size for its own use. */
+ demuxer->file = file;
+ demuxer->packet_size = file->packet_size;
+ demuxer->do_audio = do_audio;
+ demuxer->do_video = do_video;
+
+/* Allocate buffer + padding */
+ demuxer->raw_data = (unsigned char*)calloc(1, MPEG3_MAX_PACKSIZE);
+ demuxer->data_buffer = (unsigned char*)calloc(1, MPEG3_MAX_PACKSIZE);
+ demuxer->data_allocated = MPEG3_MAX_PACKSIZE;
+/* System specific variables */
+ demuxer->audio_pid = stream_id;
+ demuxer->video_pid = stream_id;
+ demuxer->astream = stream_id;
+ demuxer->vstream = stream_id;
+ demuxer->current_title = -1;
+ return demuxer;
+}
+
+int mpeg3_delete_demuxer(mpeg3_demuxer_t *demuxer)
+{
+ int i;
+
+ if(demuxer->current_title >= 0)
+ {
+ mpeg3io_close_file(demuxer->titles[demuxer->current_title]->fs);
+ }
+
+ for(i = 0; i < demuxer->total_titles; i++)
+ {
+ mpeg3_delete_title(demuxer->titles[i]);
+ }
+
+ if(demuxer->data_buffer) free(demuxer->data_buffer);
+ free(demuxer->raw_data);
+ free(demuxer);
+}
+
+/* Create a title. */
+/* Build a table of timecodes contained in the program stream. */
+/* If toc is 0 just read the first and last timecode. */
+int mpeg3demux_create_title(mpeg3_demuxer_t *demuxer, int timecode_search, FILE *toc)
+{
+ int result = 0, done = 0, counter_start, counter;
+ mpeg3_t *file = demuxer->file;
+ long next_byte, prev_byte;
+ double next_time, prev_time, absolute_time;
+ long i;
+ mpeg3_title_t *title;
+ unsigned long test_header = 0;
+ mpeg3demux_timecode_t *timecode = 0;
+
+ demuxer->error_flag = 0;
+ demuxer->generating_timecode = 1;
+
+/* Create a single title */
+ if(!demuxer->total_titles)
+ {
+ demuxer->titles[0] = mpeg3_new_title(file, file->fs->path);
+ demuxer->total_titles = 1;
+ mpeg3demux_open_title(demuxer, 0);
+ }
+ title = demuxer->titles[0];
+ title->total_bytes = mpeg3io_total_bytes(title->fs);
+
+
+/* Get the packet size from the file */
+ if(file->is_program_stream)
+ {
+ mpeg3io_seek(title->fs, 4);
+ for(i = 0; i < MPEG3_MAX_PACKSIZE &&
+ test_header != MPEG3_PACK_START_CODE; i++)
+ {
+ test_header <<= 8;
+ test_header |= mpeg3io_read_char(title->fs);
+ }
+ if(i < MPEG3_MAX_PACKSIZE) demuxer->packet_size = i;
+ mpeg3io_seek(title->fs, 0);
+ }
+ else
+ demuxer->packet_size = file->packet_size;
+
+/* Get timecodes for the title */
+ if(file->is_transport_stream || file->is_program_stream)
+ {
+ mpeg3io_seek(title->fs, 0);
+ while(!done && !result && !mpeg3io_eof(title->fs))
+ {
+ next_byte = mpeg3io_tell(title->fs);
+ result = mpeg3_read_next_packet(demuxer);
+
+ if(!result)
+ {
+ next_time = demuxer->time;
+//printf("%f %f\n", next_time, prev_time);
+ if(next_time < prev_time ||
+ next_time - prev_time > MPEG3_CONTIGUOUS_THRESHOLD ||
+ !title->timecode_table_size)
+ {
+/* Discontinuous */
+ timecode = mpeg3_append_timecode(demuxer,
+ title,
+ prev_byte,
+ prev_time,
+ next_byte,
+ next_time,
+ 0);
+/*
+ * printf("timecode: %ld %ld %f %f\n",
+ * timecode->start_byte,
+ * timecode->end_byte,
+ * timecode->start_time,
+ * timecode->end_time);
+ */
+
+ counter_start = (int)next_time;
+ }
+ prev_time = next_time;
+ prev_byte = next_byte;
+ counter = (int)next_time;
+ }
+
+/* Just get the first bytes if not building a toc to get the stream ID's. */
+ if(next_byte > 0x100000 &&
+ (!timecode_search || !toc)) done = 1;
+ }
+
+/* Get the last timecode */
+ if(!toc || !timecode_search)
+ {
+ result = mpeg3io_seek(title->fs, title->total_bytes);
+ if(!result) result = mpeg3_read_prev_packet(demuxer);
+ }
+
+ if(title->timecode_table && timecode)
+ {
+ timecode->end_byte = title->total_bytes;
+// timecode->end_byte = mpeg3io_tell(title->fs)/* + demuxer->packet_size */;
+ timecode->end_time = demuxer->time;
+ timecode->absolute_end_time = timecode->end_time - timecode->start_time;
+ }
+ }
+
+ mpeg3io_seek(title->fs, 0);
+ demuxer->generating_timecode = 0;
+ return 0;
+}
+
+int mpeg3demux_print_timecodes(mpeg3_title_t *title, FILE *output)
+{
+ mpeg3demux_timecode_t *timecode;
+ int i;
+
+ if(title->timecode_table)
+ {
+ for(i = 0; i < title->timecode_table_size; i++)
+ {
+ timecode = &title->timecode_table[i];
+
+ fprintf(output, "REGION: %ld %ld %f %f\n",
+ timecode->start_byte,
+ timecode->end_byte,
+ timecode->start_time,
+ timecode->end_time);
+ }
+ }
+ return 0;
+}
+
+/* Read the title information from a toc */
+int mpeg3demux_read_titles(mpeg3_demuxer_t *demuxer)
+{
+ char string1[MPEG3_STRLEN], string2[MPEG3_STRLEN];
+ long start_byte, end_byte;
+ float start_time, end_time;
+ mpeg3_title_t *title = 0;
+ mpeg3_t *file = demuxer->file;
+
+// Eventually use IFO file to generate titles
+ while(!feof(file->fs->fd))
+ {
+ fscanf(file->fs->fd, "%s %s %ld %f %f %f",
+ string1,
+ string2,
+ &end_byte,
+ &start_time,
+ &end_time);
+
+ if(!strncasecmp(string1, "PATH:", 5))
+ {
+ title = demuxer->titles[demuxer->total_titles++] = mpeg3_new_title(file, string2);
+
+ if(demuxer->current_title < 0)
+ mpeg3demux_open_title(demuxer, 0);
+ }
+ else
+ if(title)
+ {
+ start_byte = atol(string2);
+ if(!strcasecmp(string1, "REGION:"))
+ {
+ mpeg3_append_timecode(demuxer,
+ title,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1);
+ title->timecode_table[title->timecode_table_size - 1].start_byte = start_byte;
+ title->timecode_table[title->timecode_table_size - 1].end_byte = end_byte;
+ title->timecode_table[title->timecode_table_size - 1].start_time = start_time;
+ title->timecode_table[title->timecode_table_size - 1].end_time = end_time;
+ }
+ else
+ if(!strcasecmp(string1, "ASTREAM:"))
+ demuxer->astream_table[start_byte] = end_byte;
+ else
+ if(!strcasecmp(string1, "VSTREAM:"))
+ demuxer->vstream_table[start_byte] = end_byte;
+ else
+ if(!strcasecmp(string1, "SIZE:"))
+ title->total_bytes = start_byte;
+ else
+ if(!strcasecmp(string1, "PACKETSIZE:"))
+ demuxer->packet_size = start_byte;
+ }
+ }
+
+ mpeg3demux_assign_programs(demuxer);
+ return 0;
+}
+
+int mpeg3demux_copy_titles(mpeg3_demuxer_t *dst, mpeg3_demuxer_t *src)
+{
+ long i;
+ mpeg3_t *file = dst->file;
+ mpeg3_title_t *dst_title, *src_title;
+
+ dst->packet_size = src->packet_size;
+ dst->total_titles = src->total_titles;
+ dst->total_programs = src->total_programs;
+ for(i = 0; i < MPEG3_MAX_STREAMS; i++)
+ {
+ dst->astream_table[i] = src->astream_table[i];
+ dst->vstream_table[i] = src->vstream_table[i];
+ }
+ for(i = 0; i < src->total_titles; i++)
+ {
+ src_title = src->titles[i];
+ dst_title = dst->titles[i] = mpeg3_new_title(file, src->titles[i]->fs->path);
+ mpeg3_copy_title(dst_title, src_title);
+ }
+
+ mpeg3demux_open_title(dst, src->current_title);
+ return 0;
+}
+
+int mpeg3demux_print_streams(mpeg3_demuxer_t *demuxer, FILE *toc)
+{
+ int i;
+/* Print the stream information */
+ for(i = 0; i < MPEG3_MAX_STREAMS; i++)
+ {
+ if(demuxer->astream_table[i])
+ fprintf(toc, "ASTREAM: %d %d\n", i, demuxer->astream_table[i]);
+
+ if(demuxer->vstream_table[i])
+ fprintf(toc, "VSTREAM: %d %d\n", i, demuxer->vstream_table[i]);
+ }
+ return 0;
+}
+
+/* Need a timecode table to do this */
+double mpeg3demux_length(mpeg3_demuxer_t *demuxer)
+{
+ mpeg3_title_t *title;
+ int i, j;
+ double length;
+
+ for(i = demuxer->total_titles - 1; i >= 0; i--)
+ {
+ title = demuxer->titles[i];
+ for(j = title->timecode_table_size - 1; j >= 0; j--)
+ {
+ if(title->timecode_table[j].program == demuxer->current_program)
+ {
+ return title->timecode_table[j].end_time -
+ title->timecode_table[j].start_time +
+ title->timecode_table[j].absolute_start_time;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int mpeg3demux_eof(mpeg3_demuxer_t *demuxer)
+{
+ if(demuxer->current_title >= 0)
+ {
+ if(mpeg3io_eof(demuxer->titles[demuxer->current_title]->fs) &&
+ demuxer->current_title >= demuxer->total_titles - 1)
+ return 1;
+ }
+
+ return 0;
+}
+
+int mpeg3demux_bof(mpeg3_demuxer_t *demuxer)
+{
+ if(demuxer->current_title >= 0)
+ {
+ if(mpeg3io_bof(demuxer->titles[demuxer->current_title]->fs) &&
+ demuxer->current_title <= 0)
+ return 1;
+ }
+ return 0;
+}
+
+
+/* For elemental streams seek to a byte */
+int mpeg3demux_seek_byte(mpeg3_demuxer_t *demuxer, long byte)
+{
+ long current_position;
+ mpeg3_t *file = demuxer->file;
+ mpeg3_title_t *title = demuxer->titles[demuxer->current_title];
+
+ demuxer->data_position = 0;
+ demuxer->data_size = 0;
+
+ demuxer->error_flag = mpeg3io_seek(title->fs, byte);
+
+ if(!demuxer->error_flag && (file->is_transport_stream || file->is_program_stream))
+ {
+/* Get on a packet boundary only for system streams. */
+ current_position = mpeg3io_tell(title->fs);
+ if(byte % demuxer->packet_size)
+ {
+ demuxer->error_flag |= mpeg3io_seek(title->fs, current_position - (current_position % demuxer->packet_size));
+ }
+ }
+ return demuxer->error_flag;
+}
+
+/* For programs streams and toc seek to a time */
+int mpeg3demux_seek_time(mpeg3_demuxer_t *demuxer, double new_time)
+{
+ int i, j, done = 0, result = 0;
+ double byte_offset, new_byte_offset;
+ double guess = 0, minimum = 65535;
+ mpeg3_title_t *title;
+ mpeg3demux_timecode_t *timecode;
+
+ demuxer->error_flag = 0;
+
+ i = 0;
+ j = 0;
+ title = demuxer->titles[i];
+ timecode = &title->timecode_table[j];
+
+/* Get the title and timecode of the new position */
+ while(!demuxer->error_flag &&
+ !(timecode->absolute_start_time <= new_time &&
+ timecode->absolute_end_time > new_time &&
+ timecode->program == demuxer->current_program))
+ {
+/* Next timecode */
+ j++;
+ if(j >= title->timecode_table_size)
+ {
+ i++;
+ j = 0;
+ if(i >= demuxer->total_titles)
+ {
+ demuxer->error_flag = 1;
+ return 1;
+ }
+ else
+ {
+ mpeg3demux_open_title(demuxer, i);
+ }
+ }
+
+ title = demuxer->titles[i];
+ timecode = &title->timecode_table[j];
+ }
+
+/* Guess the new byte position */
+ demuxer->current_timecode = j;
+
+ byte_offset = ((new_time - timecode->absolute_start_time) /
+ (timecode->absolute_end_time - timecode->absolute_start_time) *
+ (timecode->end_byte - timecode->start_byte) +
+ timecode->start_byte);
+//printf("mpeg3demux_seek_time %f %f\n", new_time, byte_offset);
+
+ while(!done && !result && byte_offset >= 0)
+ {
+ result = mpeg3demux_seek_byte(demuxer, (long)byte_offset);
+//printf("seek_time 0 byte %.0f want %f result %d\n", byte_offset, new_time, result);
+
+ if(!result)
+ {
+ result = mpeg3_read_next_packet(demuxer);
+// printf("seek_time 1 guess %f want %f\n", guess, new_time);
+ guess = demuxer->time + demuxer->time_offset;
+
+ if(fabs(new_time - guess) >= fabs(minimum)) done = 1;
+ else
+ {
+ minimum = guess - new_time;
+ new_byte_offset = byte_offset + ((new_time - guess) /
+ (timecode->end_time - timecode->start_time) *
+ (timecode->end_byte - timecode->start_byte));
+ if(labs((long)new_byte_offset - (long)byte_offset) < demuxer->packet_size) done = 1;
+ byte_offset = new_byte_offset;
+ }
+ }
+ }
+
+/* Get one packet before the packet just read */
+ if(!result && byte_offset > demuxer->packet_size && minimum > 0)
+ {
+ mpeg3_read_prev_packet(demuxer);
+ mpeg3_read_prev_packet(demuxer);
+ }
+//printf("seek_time %d %d %d\n", demuxer->current_title, demuxer->current_timecode, mpeg3demux_tell(demuxer));
+ demuxer->error_flag = result;
+ return result;
+}
+
+int mpeg3demux_seek_percentage(mpeg3_demuxer_t *demuxer, double percentage)
+{
+ double total_bytes = 0;
+ double absolute_position;
+ long relative_position;
+ int i, new_title;
+ mpeg3_title_t *title;
+
+ demuxer->error_flag = 0;
+
+/* Get the absolute byte position; */
+ for(i = 0; i < demuxer->total_titles; i++)
+ total_bytes += demuxer->titles[i]->total_bytes;
+
+ absolute_position = percentage * total_bytes;
+
+/* Get the title the byte is inside */
+ for(new_title = 0, total_bytes = 0; new_title < demuxer->total_titles; new_title++)
+ {
+ total_bytes += demuxer->titles[new_title]->total_bytes;
+ if(absolute_position < total_bytes) break;
+ }
+
+ if(new_title >= demuxer->total_titles)
+ {
+ new_title = demuxer->total_titles - 1;
+ }
+
+/* Got a title */
+ title = demuxer->titles[new_title];
+ total_bytes -= title->total_bytes;
+ relative_position = (long)(absolute_position - total_bytes);
+
+/* Get the timecode the byte is inside */
+ for(demuxer->current_timecode = 0;
+ demuxer->current_timecode < title->timecode_table_size;
+ demuxer->current_timecode++)
+ {
+ if(title->timecode_table[demuxer->current_timecode].start_byte <= relative_position &&
+ title->timecode_table[demuxer->current_timecode].end_byte > relative_position)
+ {
+ break;
+ }
+ }
+
+ if(demuxer->current_timecode >= title->timecode_table_size)
+ demuxer->current_timecode = title->timecode_table_size - 1;
+
+/* Get the nearest timecode in the same program */
+ while(demuxer->current_timecode < title->timecode_table_size - 1 &&
+ title->timecode_table[demuxer->current_timecode].program != demuxer->current_program)
+ {
+ demuxer->current_timecode++;
+ }
+
+/* Open the new title and seek to the correct byte */
+ if(new_title != demuxer->current_title)
+ {
+ demuxer->error_flag = mpeg3demux_open_title(demuxer, new_title);
+ }
+
+ if(!demuxer->error_flag)
+ demuxer->error_flag = mpeg3io_seek(title->fs, relative_position);
+
+ return demuxer->error_flag;
+}
+
+double mpeg3demux_tell_percentage(mpeg3_demuxer_t *demuxer)
+{
+ double total_bytes = 0;
+ double position = 0;
+ int i;
+
+ demuxer->error_flag = 0;
+ position = mpeg3io_tell(demuxer->titles[demuxer->current_title]->fs);
+ for(i = 0; i < demuxer->total_titles; i++)
+ {
+ if(i == demuxer->current_title)
+ {
+ position += total_bytes;
+ }
+ total_bytes += demuxer->titles[i]->total_bytes;
+ }
+ return position / total_bytes;
+}
+
+double mpeg3demux_get_time(mpeg3_demuxer_t *demuxer)
+{
+ return demuxer->time;
+}
+
+long mpeg3demux_tell(mpeg3_demuxer_t *demuxer)
+{
+ return mpeg3io_tell(demuxer->titles[demuxer->current_title]->fs);
+}
+
+long mpeg3demuxer_total_bytes(mpeg3_demuxer_t *demuxer)
+{
+ mpeg3_title_t *title = demuxer->titles[demuxer->current_title];
+ return title->total_bytes;
+}
+
+mpeg3_demuxer_t* mpeg3_get_demuxer(mpeg3_t *file)
+{
+ if(file->is_program_stream || file->is_transport_stream)
+ {
+ if(file->has_audio) return file->atrack[0]->demuxer;
+ else
+ if(file->has_video) return file->vtrack[0]->demuxer;
+ }
+ return 0;
+}