Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dosbox-staging/dosbox-staging.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/hardware/reelmagic/tools/superanalyze_mpeg_ps.c')
-rw-r--r--src/hardware/reelmagic/tools/superanalyze_mpeg_ps.c763
1 files changed, 763 insertions, 0 deletions
diff --git a/src/hardware/reelmagic/tools/superanalyze_mpeg_ps.c b/src/hardware/reelmagic/tools/superanalyze_mpeg_ps.c
new file mode 100644
index 000000000..c558a2af2
--- /dev/null
+++ b/src/hardware/reelmagic/tools/superanalyze_mpeg_ps.c
@@ -0,0 +1,763 @@
+/*
+ * Copyright (C) 2022 Jon Dennis
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+/*
+
+
+http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html
+
+http://www.mpucoder.com/DVD/mpeg-1_pes-hdr.html
+
+
+*/
+
+
+
+#define _GNU_SOURCE 1
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+
+
+
+
+
+
+
+
+struct pes_substream_buffer;
+struct mpeg1_pes_header;
+struct gop_time_code;
+struct substream;
+struct run_state;
+
+
+typedef int (*obj_reader_t)(struct run_state * const /* rs */, const unsigned char /* stream_id */);
+typedef int (*psubstr_ingester_t)(struct substream * const /* substream, */, const unsigned char /* stream_id */);
+
+struct pes_substream_buffer {
+ unsigned char buf[64 * 1024];
+ unsigned char *ptr;
+ unsigned len;
+};
+
+struct mpeg1_pes_header {
+ unsigned have_pstdbuf, have_pts, have_dts;
+ unsigned pstdBufferScale;
+ unsigned pstdBufferSize;
+ uint64_t pts;
+ uint64_t dts;
+};
+
+struct gop_time_code {
+ unsigned char hour;
+ unsigned char minute;
+ unsigned char second;
+ unsigned char frame;
+};
+
+
+struct substream {
+ unsigned char stream_id; /* 0 for unknown */
+ struct mpeg1_pes_header pes_header;
+ struct pes_substream_buffer data;
+ psubstr_ingester_t ingesters[256];
+
+ union {
+ struct {
+ struct gop_time_code last_gop_time_code;
+ unsigned gop_count;
+ unsigned total_picture_count;
+ unsigned picture_count_since_last_pictype_i;
+ unsigned pictype_i_count;
+ unsigned pictype_p_count;
+ unsigned pictype_b_count;
+ unsigned pictype_d_count;
+
+ uint64_t last_picture_i_pts;
+ uint64_t last_picture_p_pts;
+ uint64_t last_picture_b_pts;
+ uint64_t last_picture_d_pts;
+
+ } video_stats;
+ struct {
+
+ } audio_stats;
+ };
+};
+
+
+struct run_state {
+ FILE *fp;
+ const char *filename;
+
+ obj_reader_t top_level_obj_readers[256];
+ unsigned top_level_stream_id_counters[256];
+
+
+ /* video-specific stuff */
+ struct substream video_stream;
+
+ /* audio-specific stuff */
+ struct substream audio_stream;
+};
+
+static int
+mpgstream_readall(const struct run_state * rs, void * const buf, const unsigned size) {
+ return fread(buf, size, 1, rs->fp);
+}
+static int
+mpgstream_discard(const struct run_state * rs, const unsigned size) {
+ return fseek(rs->fp, (long)size, SEEK_CUR);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#define RET_FAIL(MSG) {fprintf(stderr, "%s\n", MSG); return -1;}
+#define RET_FAILF(FMT, ...) {fprintf(stderr, FMT "\n", __VA_ARGS__); return -1;}
+
+
+static inline void
+hexdump(const void * const vptr, unsigned size) {
+ const unsigned char *ptr;
+
+ fprintf(stderr, "hexdump:");
+ for (ptr = vptr; size--; ++ptr) {
+ fprintf(stderr, " %02hhX", *ptr);
+ }
+ fprintf(stderr, "\n");
+}
+
+static inline void
+slicedump(const void * const vptr, unsigned size) {
+ const unsigned char *ptr;
+
+ printf(" - Raw: ");
+ for (ptr = vptr; size--; ++ptr) {
+ printf("%02hhX", *ptr);
+ }
+ printf("\n");
+}
+
+static inline void
+sysheaddump(const void * const vptr, unsigned size) {
+ const unsigned char *ptr;
+
+ printf("System Header: ");
+ for (ptr = vptr; size--; ++ptr) {
+ printf("%02hhX", *ptr);
+ }
+ printf("\n");
+}
+
+static inline void
+picheaddump(const void * const vptr, unsigned size) {
+ const unsigned char *ptr;
+
+ printf("Picture Header: ");
+ for (ptr = vptr; size--; ++ptr) {
+ printf("%02hhX", *ptr);
+ }
+ printf("\n");
+}
+
+
+
+/*
+ general PES stuff
+*/
+
+static inline void
+increment_pes_substream_buffer(struct pes_substream_buffer * const subbuf, const unsigned length) {
+ subbuf->ptr += length;
+ subbuf->len -= length;
+}
+static void
+shift_pes_substream_buffer_remainder(struct pes_substream_buffer * const subbuf) {
+ if (subbuf->ptr == subbuf->buf) return;
+ if (subbuf->len == 0) {
+ subbuf->ptr = subbuf->buf;
+ return;
+ }
+ memmove(subbuf->buf, subbuf->ptr, subbuf->len);
+ subbuf->ptr = subbuf->buf;
+}
+
+static inline int
+parse_mpeg1_pts_dts_field(uint64_t * const output, const unsigned char * const buf) {
+ /* validate */
+ if ((buf[4] & 0x01) != 0x01) return 0; /* failed */
+ if ((buf[2] & 0x01) != 0x01) return 0; /* failed */
+ if ((buf[0] & 0x01) != 0x01) return 0; /* failed */
+
+ /* extract the 33-bit integer from this ridiculously insane encoding... */
+ (*output) = (buf[0] & 0x0E) >> 1; /* 3 bits */
+ (*output) <<= 8; (*output) |= buf[1]; /* 8 bits */
+ (*output) <<= 7; (*output) |= buf[2] >> 1; /* 7 bits */
+ (*output) <<= 8; (*output) |= buf[3]; /* 8 bits */
+ (*output) <<= 7; (*output) |= buf[4] >> 1; /* 7 bits */
+
+ return 1; /* success */
+}
+
+static int
+read_mpeg1_pes_header(const struct run_state * const rs, struct mpeg1_pes_header * const output, const unsigned max_length, const unsigned char stream_id) {
+ unsigned bytes_read;
+ unsigned pad_ok;
+ unsigned char buf[10];
+
+ bytes_read = 0;
+ pad_ok = 1;
+
+ /* clear out only the "have" fields so that the values can be used historically */
+ output->have_pstdbuf = output->have_pts = output->have_dts = 0;
+
+ while ((bytes_read < max_length) && (!(output->have_pstdbuf && output->have_pts))) {
+ /* read in a byte */
+ if (mpgstream_readall(rs, buf, 1) != 1) RET_FAILF("Failed to read in MPEG-1 PES header byte for stream id 0x%02hhX", stream_id);
+ ++bytes_read;
+ if (pad_ok && (buf[0] == 0xFF)) continue; /* ignore if padding */
+ pad_ok = 0; /* padding not OK after the beginning */
+
+ /* figure out what it means... */
+ if ((buf[0] & 0xC0) == 0x40) {
+ /* first two bits = '01' which means we have a P-STD buffer size field present */
+ if (output->have_pstdbuf) RET_FAILF("PES header contains multiple P-STD buffer size fields for stream ID type 0x%02hhX", stream_id);
+ if (((max_length - bytes_read) < 1) || (mpgstream_readall(rs, &buf[1], 1) != 1)) RET_FAILF("Failed to read in MPEG-1 PES header byte for STD buffer extension for stream id 0x%02hhX", stream_id);
+ output->pstdBufferScale = (buf[0] & 0x20) ? 1024 : 128;
+ output->pstdBufferSize = buf[0] & 0x1F;
+ output->pstdBufferSize <<= 8;
+ output->pstdBufferSize |= buf[1];
+ output->have_pstdbuf = 1;
+ bytes_read += 1;
+ }
+ else if ((buf[0] & 0xF0) == 0x20) {
+ /* only PTS field present */
+ if (output->have_pts || output->have_dts) RET_FAILF("PES header contains multiple PTS/DTS fields for stream ID type 0x%02hhX", stream_id);
+ if (((max_length - bytes_read) < 4) || (mpgstream_readall(rs, &buf[1], 4) != 1)) RET_FAILF("Failed to read in MPEG-1 PES header bytes for PTS-only field for stream id 0x%02hhX", stream_id);
+ if (!parse_mpeg1_pts_dts_field(&output->pts, buf))
+ RET_FAILF("PES header PTS-only decode failed for stream ID type 0x%02hhX", stream_id);
+ output->have_pts = 1;
+ bytes_read += 4;
+ }
+ else if ((buf[0] & 0xF0) == 0x30) {
+ /* PTS and DTS field present */
+ if (output->have_pts || output->have_dts) RET_FAILF("PES header contains multiple PTS/DTS fields for stream ID type 0x%02hhX", stream_id);
+ if (((max_length - bytes_read) < 9) || (mpgstream_readall(rs, &buf[1], 9) != 1)) RET_FAILF("Failed to read in MPEG-1 PES header bytes for PTS+DTS field for stream id 0x%02hhX", stream_id);
+ if (!parse_mpeg1_pts_dts_field(&output->pts, buf))
+ RET_FAILF("PES header PTS decode failed for stream ID type 0x%02hhX", stream_id);
+ if (!parse_mpeg1_pts_dts_field(&output->dts, &buf[5]))
+ RET_FAILF("PES header DTS decode failed for stream ID type 0x%02hhX", stream_id);
+ output->have_pts = output->have_dts = 1;
+ bytes_read += 9;
+ }
+ else if (buf[0] == 0x0f) {
+ /* done with header */
+ break;
+ }
+ else {
+ RET_FAILF("Invalid MPEG-1 PES header: Unknown byte 0x%02hhX for stream ID type 0x%02hhX", buf[0], stream_id);
+ }
+ }
+
+ return bytes_read;
+}
+
+static int
+read_pes_append_substream(const struct run_state * const rs, struct substream * const substream) {
+ unsigned char buf[2];
+ unsigned pes_len;
+ int header_length;
+
+ /* init substream data buffer ptr to beginning of buffer if not previously done */
+ if (substream->data.ptr == NULL) substream->data.ptr = substream->data.buf;
+
+ /* read in the length of this sequence */
+ if (mpgstream_readall(rs, buf, sizeof(buf)) != 1)
+ RET_FAILF("Failed to read substream pes len for 0x%02hhX", substream->stream_id);
+ pes_len = buf[0];
+ pes_len <<= 8;
+ pes_len |= buf[1];
+
+ /* read in the header */
+ header_length = read_mpeg1_pes_header(rs, &substream->pes_header, pes_len, substream->stream_id);
+ if (header_length == -1) return -1;
+ pes_len -= header_length;
+
+ /* read in / append any remaining bytes for this sequence */
+ if (pes_len > 0) {
+ if ((pes_len + substream->data.len + (substream->data.ptr - substream->data.buf)) > sizeof(substream->data.buf))
+ RET_FAILF("Not enough space to buffer continuing ES data for stream id type 0x%02hhx", substream->stream_id);
+ if (mpgstream_readall(rs, &substream->data.ptr[substream->data.len], pes_len) != 1)
+ RET_FAILF("Failed to read substream ES data for 0x%02hhX", substream->stream_id);
+ substream->data.len += pes_len;
+ }
+
+ return 1; /* success */
+}
+
+static int
+dispatch_pes_substream_objects(struct substream * const substream) {
+ psubstr_ingester_t ingester_func;
+ int ingester_func_result;
+ unsigned char substream_id;
+
+#if 0
+ hexdump(substream->data.ptr, substream->data.len);
+ static int do_once = 5;
+ if (do_once) {
+ do_once--;
+ return 1;
+ }
+ return -1;
+#endif
+
+
+ while (substream->data.len >= 4) {
+ if ((substream->data.ptr[0] != 0x00) || (substream->data.ptr[1] != 0x00) || (substream->data.ptr[2] != 0x01))
+ RET_FAILF("Bad substream object start code prefix for stream id: 0x%02hhX\n", substream->stream_id);
+ substream_id = substream->data.ptr[3];
+ if ((ingester_func = substream->ingesters[substream_id]) == NULL)
+ RET_FAILF("No ingester function for stream id: 0x%02hhX:0x%02hhX\n", substream->stream_id, substream_id);
+ increment_pes_substream_buffer(&substream->data, 4);
+
+ ingester_func_result = (*ingester_func)(substream, substream_id);
+ if (ingester_func_result == -2) {
+ /* not enough data for ingester function */
+ /* rewind back to start code and try again later */
+ substream->data.ptr -= 4;
+ substream->data.len += 4;
+ break;
+ }
+ else if (ingester_func_result < 0) {
+ return -1; /* something failed... bailout... */
+ }
+ increment_pes_substream_buffer(&substream->data, ingester_func_result);
+ }
+
+ shift_pes_substream_buffer_remainder(&substream->data);
+ return 1;
+}
+
+
+
+
+
+
+/*
+ * "video PES" processing functions...
+*/
+static int
+ingest_pes_sequence_header(struct substream * const substream, const unsigned char stream_id) {
+ unsigned bitrate_field;
+ const unsigned expected_content_size = 8;
+
+ if (substream->data.len < expected_content_size) return -2; /* need more bytes */
+ bitrate_field = (substream->data.ptr[4]<<10) | (substream->data.ptr[5]<<2) | (substream->data.ptr[6]>>6);
+
+ printf("Sequence:\n");
+ printf(" - Picture Size: %ux%u\n",
+ (substream->data.ptr[0] << 4) | (substream->data.ptr[1] >> 4),
+ (substream->data.ptr[1] << 8) | substream->data.ptr[2]);
+ printf(" - Aspect Radio Code: 0x%02X\n", substream->data.ptr[3] >> 4);
+ printf(" - Frame Rate Code: 0x%02X\n", substream->data.ptr[3] & 0xF);
+ printf(" - Constrained Parameters: %s\n", (substream->data.ptr[7] & 0x4) ? "Yes" : "No");
+ if (bitrate_field == 0x3FFFF)
+ printf(" - Bitrate: %u (VBR)\n", bitrate_field);
+ else
+ printf(" - Bitrate: %u (%u kbps)\n", bitrate_field, (bitrate_field * 400) / 1000);
+ printf(" - VBV Buffer Size: %u\n", ((substream->data.ptr[6]&0x1f)<<5) | (substream->data.ptr[7]>>3));
+ return (int)expected_content_size;
+}
+static int
+ingest_gop(struct substream * const substream, const unsigned char stream_id) {
+ const unsigned expected_content_size = 4;
+
+ if (substream->data.len < expected_content_size) return -2; /* need more bytes */
+ printf("GOP #%u\n", substream->video_stats.gop_count);
+ ++substream->video_stats.gop_count;
+
+ substream->video_stats.last_gop_time_code.hour = (substream->data.ptr[0] >> 2) & 0x1f;
+ substream->video_stats.last_gop_time_code.minute = substream->data.ptr[0] & 0x03;
+ substream->video_stats.last_gop_time_code.minute <<= 4;
+ substream->video_stats.last_gop_time_code.minute |= (substream->data.ptr[1] >> 4) & 0x0f;
+ substream->video_stats.last_gop_time_code.second = substream->data.ptr[1] & 0x07;
+ substream->video_stats.last_gop_time_code.second <<= 3;
+ substream->video_stats.last_gop_time_code.second |= (substream->data.ptr[2] >> 5) & 0x07;
+ substream->video_stats.last_gop_time_code.frame = substream->data.ptr[2] & 0x1f;
+ substream->video_stats.last_gop_time_code.frame <<= 1;
+ substream->video_stats.last_gop_time_code.frame |= (substream->data.ptr[3] >> 7) & 0x01;
+//fprintf(stderr, "GOP Time Code: %02hhu:%02hhu:%02hhu.%02hhu\n", substream->video_stats.last_gop_time_code.hour, substream->video_stats.last_gop_time_code.minute, substream->video_stats.last_gop_time_code.second, substream->video_stats.last_gop_time_code.frame);
+ return (int)expected_content_size;
+}
+static int
+ingest_user(struct substream * const substream, const unsigned char stream_id) {
+ unsigned char *next_substream_object;
+
+ const unsigned char startcode[] = {0x00, 0x00, 0x01};
+
+ next_substream_object = memmem(substream->data.ptr, substream->data.len, startcode, sizeof(startcode));
+ if (next_substream_object == NULL) return -2;
+ printf("User data (%u bytes)\n", (unsigned)(next_substream_object - substream->data.ptr));
+ return next_substream_object - substream->data.ptr;
+}
+static int
+ingest_picture(struct substream * const substream, const unsigned char stream_id) {
+ unsigned expected_content_size;
+ unsigned char picture_type;
+ unsigned char *next_substream_object;
+
+ const unsigned char startcode[] = {0x00, 0x00, 0x01};
+
+ expected_content_size = 4;
+ if (substream->data.len < expected_content_size) return -2; /* need more bytes */
+
+ picture_type = (substream->data.ptr[1] >> 3) & 0x07;
+ if ((picture_type == 2) || (picture_type == 3)) {
+ if (substream->data.len < ++expected_content_size) return -2; /* need more bytes */
+ }
+
+ next_substream_object = memmem(substream->data.ptr, substream->data.len, startcode, sizeof(startcode));
+ if (next_substream_object == NULL) return -2; /* need more bytes */
+ if (expected_content_size > (next_substream_object - substream->data.ptr)) {
+ fprintf(stderr, "Picture header parse error\n");
+ return -1; /* fatal error */
+ }
+ expected_content_size = next_substream_object - substream->data.ptr;
+
+ switch(picture_type) {
+ case 1: /* I picture */
+ printf("I Picture #%u at PTS %lu (%u since last, pts_delta %lu %0.5f sec)\n",
+ substream->video_stats.total_picture_count,
+ (unsigned long)substream->pes_header.pts,
+ substream->video_stats.picture_count_since_last_pictype_i,
+ (unsigned long)(substream->pes_header.pts - substream->video_stats.last_picture_i_pts),
+ ((double)(substream->pes_header.pts - substream->video_stats.last_picture_i_pts)) * (1.0 / 90000.0));
+
+ substream->video_stats.picture_count_since_last_pictype_i = 0;
+ substream->video_stats.last_picture_i_pts = substream->pes_header.pts;
+ ++substream->video_stats.pictype_i_count;
+ break;
+ case 2: /* P picture */
+ printf("P Picture #%u at PTS %lu\n", substream->video_stats.total_picture_count, (unsigned long)substream->pes_header.pts);
+ printf(" - Full PEL Forward Vector: %u\n", (substream->data.ptr[3] >> 2) & 0x01);
+ printf(" - Forward F Code: %u\n", ((substream->data.ptr[3] & 3) << 1 ) | (substream->data.ptr[4] >> 7));
+
+ ++substream->video_stats.picture_count_since_last_pictype_i;
+ ++substream->video_stats.pictype_p_count;
+ substream->video_stats.last_picture_p_pts = substream->pes_header.pts;
+ break;
+ case 3: /* B picture */
+ printf("B Picture #%u at PTS %lu\n", substream->video_stats.total_picture_count, (unsigned long)substream->pes_header.pts);
+ printf(" - Full PEL Forward Vector: %u\n", (substream->data.ptr[3] >> 2) & 0x01);
+ printf(" - Forward F Code: %u\n", ((substream->data.ptr[3] & 3) << 1 ) | (substream->data.ptr[4] >> 7));
+ printf(" - Full PEL Backward Vector: %u\n", (substream->data.ptr[4] >> 6) & 0x01);
+ printf(" - Backward F Code: %u\n", (substream->data.ptr[4] >> 3) & 0x07);
+
+ ++substream->video_stats.picture_count_since_last_pictype_i;
+ ++substream->video_stats.pictype_b_count;
+ substream->video_stats.last_picture_b_pts = substream->pes_header.pts;
+ break;
+ case 4: /* D picture */
+ printf("D Picture #%d at %lu\n", substream->video_stats.total_picture_count, (unsigned long)substream->pes_header.pts);
+ ++substream->video_stats.picture_count_since_last_pictype_i;
+ ++substream->video_stats.pictype_d_count;
+ substream->video_stats.last_picture_d_pts = substream->pes_header.pts;
+ break;
+ default:
+ RET_FAILF("Unknown picture type #%u\n", substream->video_stats.total_picture_count);
+ }
+ printf(" - Temporal Sequence Number: %u\n", (substream->data.ptr[0] << 2) | ((substream->data.ptr[1] >> 6)&0x3));
+ printf(" - VBV Delay: %u\n", ((substream->data.ptr[1]&7)<<13) | (substream->data.ptr[2]<<5) | ((substream->data.ptr[3]>>3)&0x1f));
+ printf(" - "); picheaddump(substream->data.ptr, expected_content_size);
+
+ ++substream->video_stats.total_picture_count;
+
+ return (int)expected_content_size;
+}
+static int
+ingest_slice(struct substream * const substream, const unsigned char stream_id) {
+ unsigned char *next_substream_object;
+
+ const unsigned char startcode[] = {0x00, 0x00, 0x01};
+
+ next_substream_object = memmem(substream->data.ptr, substream->data.len, startcode, sizeof(startcode));
+ if (next_substream_object == NULL) return -2;
+ printf(" - Slice #%hhu (Stream ID: 0x%02hhX)\n", stream_id - 1, stream_id);
+ slicedump(substream->data.ptr, next_substream_object - substream->data.ptr);
+ return next_substream_object - substream->data.ptr;
+}
+static int
+ingest_end(struct substream * const substream, const unsigned char stream_id) {
+ return 0;
+}
+/*
+ * end of "video PES" processing functions...
+*/
+
+
+
+
+
+
+
+
+
+/*
+ * "top level" processing functions...
+*/
+
+static int
+read_program_end_object(struct run_state * const rs, const unsigned char stream_id) {
+ return 0; /* success; this is the end of the stream to return 0 indicating so */
+}
+
+static int
+read_pack_header_object(struct run_state * const rs, const unsigned char stream_id) {
+ unsigned char header_content[8];
+
+ if (mpgstream_readall(rs, header_content, sizeof(header_content)) != 1)
+ RET_FAIL("Failed to read pack header content");
+
+ static int do_once = 1;
+ if (do_once) {
+ fprintf(stderr, "incomplete parsing of PACK header(s)!\n");
+ do_once = 0;
+ }
+ printf("Pack Header:\n");
+ printf(" - Program Mux Rate: %u\n", (unsigned)(((header_content[5] & 0x7F) << 15) | (header_content[6] << 7) | (header_content[7] >> 1)));
+ return 1; /* success */
+}
+
+static int
+read_system_header_object(struct run_state * const rs, const unsigned char stream_id) {
+ unsigned char header_content[14];
+
+ if (mpgstream_readall(rs, header_content, sizeof(header_content)) != 1)
+ RET_FAIL("Failed to read system header content");
+
+ static int do_once = 1;
+ if (do_once) {
+ fprintf(stderr, "incomplete parsing of SYSTEM header(s)!\n");
+ do_once = 0;
+ }
+ sysheaddump(header_content, sizeof(header_content));
+ return 1; /* success */
+}
+
+static int
+discard_pes_object_content(struct run_state * const rs, const unsigned char stream_id) {
+ unsigned char buf[2];
+ unsigned pes_len;
+
+ if (mpgstream_readall(rs, buf, sizeof(buf)) != 1)
+ RET_FAILF("Failed to read padding stream pes len for 0x%02hhX", stream_id);
+ pes_len = buf[0];
+ pes_len <<= 8;
+ pes_len |= buf[1];
+
+ if (mpgstream_discard(rs, pes_len) == -1)
+ RET_FAILF("failed to discard stream object content of %u bytes for stream ID type 0x%02hhX", pes_len, stream_id);
+
+ return 1; /* success */
+}
+
+static int
+read_video_stream_object(struct run_state * const rs, const unsigned char stream_id) {
+ /* currently only one video ES supported */
+ if (rs->video_stream.stream_id != stream_id) {
+ if (rs->video_stream.stream_id != 0) {
+ /* ignore other video streams */
+ return discard_pes_object_content(rs, stream_id);
+ }
+ /* first video ES found will be the "primary" */
+ rs->video_stream.stream_id = stream_id;
+ fprintf(stderr, "Discovered Video ES @ 0x%02hhX\n", rs->video_stream.stream_id);
+ }
+
+ /* read the PES header and append some PES data */
+ if (read_pes_append_substream(rs, &rs->video_stream) == -1) return -1;
+#if 0
+fprintf(stderr, "Have PSTDBUF: %u\n", rs->video_stream.pes_header.have_pstdbuf);
+fprintf(stderr, "Have PTS: %u\n", rs->video_stream.pes_header.have_pts);
+fprintf(stderr, "Have DTS: %u\n", rs->video_stream.pes_header.have_dts);
+fprintf(stderr, "pstdBufferScale: %u\n", rs->video_stream.pes_header.pstdBufferScale);
+fprintf(stderr, "pstdBufferSize: %u\n", rs->video_stream.pes_header.pstdBufferSize);
+fprintf(stderr, "pts: %lu\n", (unsigned long)rs->video_stream.pes_header.pts);
+fprintf(stderr, "dts: %lu\n", (unsigned long)rs->video_stream.pes_header.dts);
+#endif
+
+ /* process any complete sub-items within this elementary stream */
+ return dispatch_pes_substream_objects(&rs->video_stream);
+}
+
+static int
+read_audio_stream_object(struct run_state * const rs, const unsigned char stream_id) {
+ /* currently only one audio ES supported */
+ if (rs->audio_stream.stream_id != stream_id) {
+ if (rs->audio_stream.stream_id != 0) {
+ /* ignore other audio streams */
+ return discard_pes_object_content(rs, stream_id);
+ }
+ /* first audio ES found will be the "primary" */
+ rs->audio_stream.stream_id = stream_id;
+ fprintf(stderr, "Discovered Audio ES @ 0x%02hhX\n", rs->audio_stream.stream_id);
+ }
+
+ /* XXX do nothing for now */
+ return discard_pes_object_content(rs, stream_id);
+
+ /* read the PES header and append some PES data */
+ if (read_pes_append_substream(rs, &rs->audio_stream) == -1) return -1;
+
+ return 1; /* success */
+}
+
+static int
+read_top_level_object(struct run_state * const rs) {
+ unsigned char header[4];
+ obj_reader_t reader_func;
+
+ switch(mpgstream_readall(rs, header, sizeof(header))) {
+ case 0: return 0; /* end of stream */
+ case -1: return -1; /* failure */
+ }
+
+ if ((header[0] != 0x00) || (header[1] != 0x00) || (header[2] != 0x01))
+ RET_FAIL("Bad object start code prefix");
+
+ if ((reader_func = rs->top_level_obj_readers[header[3]]) == NULL)
+ RET_FAILF("No reader function for stream ID type 0x%02hhX", header[3]);
+
+ ++rs->top_level_stream_id_counters[header[3]];
+ return (*reader_func)(rs, header[3]);
+}
+/*
+ * end of "top level" processing functions...
+*/
+
+
+
+
+
+
+
+static void
+populate_stream_id_handlers(struct run_state * const rs) {
+ unsigned i;
+
+ /* "top level" stuff */
+ rs->top_level_obj_readers[0xB9] = &read_program_end_object;
+ rs->top_level_obj_readers[0xBA] = &read_pack_header_object;
+ rs->top_level_obj_readers[0xBB] = &read_system_header_object;
+ rs->top_level_obj_readers[0xBE] = &discard_pes_object_content;
+ for (i = 0xc0; i <= 0xdf; ++i)
+ rs->top_level_obj_readers[i] = &read_audio_stream_object;
+ for (i = 0xe0; i <= 0xef; ++i)
+ rs->top_level_obj_readers[i] = &read_video_stream_object;
+
+
+
+ /* video stuff */
+ rs->video_stream.ingesters[0x00] = &ingest_picture;
+ rs->video_stream.ingesters[0xB2] = &ingest_user;
+ rs->video_stream.ingesters[0xB3] = &ingest_pes_sequence_header;
+ rs->video_stream.ingesters[0xB7] = &ingest_end;
+ rs->video_stream.ingesters[0xB8] = &ingest_gop;
+ for (i = 0x01; i <= 0xaf; ++i)
+ rs->video_stream.ingesters[i] = &ingest_slice;
+}
+
+int
+main( int argc, char *argv[] ) {
+ struct run_state rs;
+ unsigned i;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s INPUT_FILE\n", argv[0]);
+ return 1;
+ }
+
+ bzero(&rs, sizeof(rs));
+ populate_stream_id_handlers(&rs);
+
+ rs.filename = argv[1];
+
+ rs.fp = fopen(rs.filename, "rb");
+ if (rs.fp == NULL) {
+ fprintf(stderr, "Failed to open '%s'", rs.filename);
+ perror("");
+ return 1;
+ };
+
+ while ( 1 ) switch(read_top_level_object(&rs)) {
+ case -1: /* failure occured */
+ fclose(rs.fp);
+ return 1;
+ case 0: /* analyze success */
+ goto done_success;
+ }
+
+done_success:
+ fclose(rs.fp);
+ fprintf(stderr, "Top-Level Stream ID Counters:\n");
+ for (i = 0; i <= 0xFF; ++i) {
+ if (rs.top_level_stream_id_counters[i] == 0) continue;
+ fprintf(stderr, " . 0x%02X: %u\n", i, rs.top_level_stream_id_counters[i]);
+ }
+ if (!rs.top_level_stream_id_counters[0xB9]) {
+ fprintf(stderr, "WARNING: Stream terminated prematuraly; no 'program end' found!\n");
+ }
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Video Elementary Stream Statistics:\n");
+ fprintf(stderr, " . Video Stream ID: 0x%02hhX\n", rs.video_stream.stream_id);
+// fprintf(stderr, " . Last GOP Time: %02hhu:%02hhu:%02hhu.%02hhu\n", rs.video_stream.video_stats.last_gop_time_code.hour, rs.video_stream.video_stats.last_gop_time_code.minute, rs.video_stream.video_stats.last_gop_time_code.second, rs.video_stream.video_stats.last_gop_time_code.frame);
+ fprintf(stderr, " . GOP Count: %u\n", rs.video_stream.video_stats.gop_count);
+ fprintf(stderr, " . Picture Count: %u\n", rs.video_stream.video_stats.total_picture_count);
+ fprintf(stderr, " . I-Picture Count: %u\n", rs.video_stream.video_stats.pictype_i_count);
+ fprintf(stderr, " . P-Picture Count: %u\n", rs.video_stream.video_stats.pictype_p_count);
+ fprintf(stderr, " . B-Picture Count: %u\n", rs.video_stream.video_stats.pictype_b_count);
+ fprintf(stderr, " . D-Picture Count: %u\n", rs.video_stream.video_stats.pictype_d_count);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Audio Elementary Stream Statistics:\n");
+ fprintf(stderr, " . Audio Stream ID: 0x%02hhX\n", rs.audio_stream.stream_id);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Successfully completed analysis!\n");
+ return 0;
+}