#if 0 /* TRG=`basename $0 .c`; rm -f "$TRG" #"${TRG}_defs.hg" #trap 'rm -f "${TRG}_defs.hg"' 0; perl -x "$0" "$0" > "${TRG}_defs.hg" WARN="-Wall -Wstrict-prototypes -pedantic -Wno-long-long" WARN="$WARN -Wcast-align -Wpointer-arith " # -Wfloat-equal #-Werror WARN="$WARN -W -Wwrite-strings -Wcast-qual -Wshadow" # -Wconversion FLAGS='-O2 -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64' # -pg lastrun() { echo "$@"; "$@"; exit $?; } lastrun ${CC:-gcc} $WARN "$@" -o "$TRG" "$0" $FLAGS -DCDATE="\"`date`\"" #*/ #endif /* * $Id: mpeg2info.c 560 2005-09-05 20:21:31Z too $ * * Author: Tomi Ollila too ät iki fi * * Copyright (c) 2004 Tomi Ollila * All rights reserved * * Created: Thu Sep 23 19:28:18 EEST 2004 too * Last modified: Mon Sep 05 23:20:48 EEST 2005 too * * This program is licensed under the GPL v2. See file COPYING for details. */ #define VER "1.0" #define VERDATE "2005/03/16" /* * To compile this, enter `sh mpeg2info.c'. */ /* * See http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html * * Thanks also to mpeg2enc, bbinfo, mpegcat, python_mpeg and so on... */ #include /* for NULL */ #include /* for memmove() */ #include /* for read() */ #include #include #include #include #include #include #define ud(x) typedef unsigned char u ## x ud(1); ud(2); ud(3); ud(4); ud(5); ud(6); ud(7); ud(8); #undef ud #if 0 void __cyg_profile_func_enter(void *this_fn, void *call_site) __attribute ((no_instrument_function)); void __cyg_profile_func_enter(void *this_fn, void *call_site) { write(1, "enter\n", 6); } void __cyg_profile_func_exit(void *this_fn, void *call_site) __attribute ((no_instrument_function)); void __cyg_profile_func_exit(void *this_fn, void *call_site) { write(1, "exit\n", 5); } #endif #define DBGS 0 #define D(x, y) typedef enum { false = 0, true = 1 } bool; #if (__GNUC__ >= 3) #define GCCATTR_PRINTF(m, n) __attribute__ ((format (printf, m, n))) #define GCCATTR_UNUSED __attribute ((unused)) #define GCCATTR_NORETURN __attribute ((noreturn)) #define GCCATTR_CONST __attribute ((const)) #else #define GCCATTR_PRINTF(m, n) #define GCCATTR_UNUSED #define GCCATTR_NORETURN #define GCCATTR_CONST #endif typedef struct _ZeroZeroOneBuf ZeroZeroOneBuf; struct _ZeroZeroOneBuf { #define uchar unsigned char /* public output variables -- read only (writing these will break things) */ uchar * data; int len; off_t pos; /* private */ struct { /* c_* variables constant after initialized */ int c_fd; uchar * c_buf; /* pointer to start of whole buffer area */ int c_buflen; /* bytes of whole buffer area */ int len; /* bytes of active data in buffer */ } p; #undef uchar }; #if DBGS static char * printzzob(ZeroZeroOneBuf * zb) /* ; to avoid prototype generation */ { static char buf[128]; sprintf(buf, "{%x, %d -> %d}", mb->data - mb->p.c_buf, mb->p.len, mb->len); return buf; } #define PMD printzzob(zzob) #endif static bool s__fillbuf(ZeroZeroOneBuf * zzob); static unsigned char * s__memmem(const unsigned char * s, size_t l, const unsigned char * b, size_t bl); static bool s__fillbuf(ZeroZeroOneBuf * zzob) { int l; unsigned char * bufend = zzob->p.c_buf + zzob->p.c_buflen; unsigned char * rpos = zzob->data + zzob->p.len; D(1, ("s__fillbuf(%s)\n", PMD)); if (rpos - bufend >= 0) { if (zzob->p.len == zzob->p.c_buflen) return false; /* raise BufferFullException */ if (zzob->p.len) memmove(zzob->p.c_buf, zzob->data, zzob->p.len); zzob->data = zzob->p.c_buf; rpos = zzob->data + zzob->p.len; } l = read(zzob->p.c_fd, rpos, bufend - rpos /*> 3? 3: bufend - rpos*/); if (l <= 0) return false; /* raise EOFException */ zzob->p.len += l; return true; } /* Used when \0\0\1 not found in first full buf... */ static bool zzob_init__searchmore(ZeroZeroOneBuf * zzob) { if (zzob->p.len == zzob->p.c_buflen) { int i = zzob->p.len - 2; memcpy(zzob->p.c_buf, zzob->p.c_buf + i, 2); zzob->data = zzob->p.c_buf; zzob->pos += i; zzob->p.len = 2; return true; } else return false; } bool zzob_init(ZeroZeroOneBuf * zzob, int fd, unsigned char * buf, int buflen) { unsigned char * data; zzob->p.c_fd = fd; zzob->p.c_buf = buf; zzob->p.c_buflen = buflen; zzob->p.len = 0; zzob->pos = 0; zzob->data = buf; zzob->len = 0; D(1, ("zzob_init(fd=%d, %s)\n", fd, PMD)); while (true) { if (s__fillbuf(zzob)) { data = s__memmem(zzob->data, zzob->p.len, (const unsigned char *)"\000\000\001", 3); if (data != NULL) { zzob->pos += data - zzob->data; zzob->p.len -= data - zzob->data; zzob->data = data; return true; } } else if (!zzob_init__searchmore(zzob)) return false; } } bool zzob_data(ZeroZeroOneBuf * zzob, bool locked) { unsigned char * p; int o; D(1, ("zzob_data(%s)\n", PMD)); if (locked == false) { zzob->p.len -= zzob->len; zzob->data += zzob->len; zzob->pos += zzob->len; zzob->len = 0; } /* if (locked) outf("locked!\n"); */ o = zzob->len + 3; while (true) { if (zzob->p.len - o > 0 && (p = s__memmem(zzob->data + o, zzob->p.len - o, (const unsigned char *)"\000\000\001", 3)) != NULL) break; if (! s__fillbuf(zzob)) { D(1, ("s__fillbuf(zzob, zzob->p.len) returned false (%s).\n", PMD)); if (locked || zzob->p.len < 4) return false; p = zzob->data + zzob->p.len; break; } } zzob->len = p - zzob->data; D(1, ("zzob_data end: %s\n", PMD)); return true; } /* --- util funcs --- */ static unsigned char * s__memmem(const unsigned char * s, size_t l, const unsigned char * b, size_t bl) { const unsigned char * ss = s; D(1, ("s__memmem(l = %d)\n", l)); while ((s = memchr(ss, b[0], l)) != NULL) { l -= (s - ss); D(0, ("s - ss = %d, l = %d\n", s - ss, l)); if (l < bl) return NULL; if (memcmp(s, b, bl) == 0) { D(1, ("s\n")); return (unsigned char *)s; /* unavoidable compiler warnings !!! */ } ss = s + 1; l--; } return NULL; } struct { char * prgname; int gops; int pictures; int seconds; bool flush; u8 print_now; struct { bool print_all; bool print_details; bool print_matrixes; bool filter_picture; bool filter_slices; bool filter_b_hdrs; bool filter_audio_pes; bool filter_video_pes; } o; } G; static void init_G(char ** argv) { memset(&G, 0, sizeof G); G.prgname = argv[0]; } static void set_filter(char * str, char chr, bool * vp) { if (strchr(str, chr) != NULL) *vp = true; } static void set_filters(char * str) { set_filter(str, 'p', &G.o.filter_picture); set_filter(str, 's', &G.o.filter_slices); set_filter(str, 'b', &G.o.filter_b_hdrs); set_filter(str, 'a', &G.o.filter_audio_pes); set_filter(str, 'v', &G.o.filter_video_pes); } static bool display(bool hdr, int hdrtype) { if (G.print_now) { G.print_now--; return true; } if (hdrtype == 0) /* */ { if (G.o.filter_picture) return false; } else if (hdrtype < 0xb0) /* */ { if (G.o.filter_slices) return false; } else if (hdrtype >= 0xb0 && hdrtype < 0xc0) /* */ { if (G.o.filter_b_hdrs) return false; } else if (hdrtype >= 0xc0 && hdrtype < 0xe0) /* */ { if (G.o.filter_audio_pes) return false; } else if (hdrtype >= 0xe0 && hdrtype < 0xf0) /* */ { if (G.o.filter_video_pes) return false; } if (hdr && (G.o.print_all || G.o.print_details)) return true; if (G.o.print_details) return true; return false; } #define chklen(l) if (len < l) return #define pos zzob->pos #define len zzob->len #define data zzob->data #define hdrname(args) \ do { printf("%9lld %4d %02x: ", pos, len, (unsigned char)data[3]); \ printf args; puts("."); G.flush = true; } while (0) #define ifchanged_simple(var, txt) \ if (prev.var != v.var) { G.print_now = 2;\ printf("\t*** Changed: " txt " (prev %d). (gop %d, pic %d).\n", \ prev.var, G.gops, G.pictures); } #define ifchanged_simple2(var, txt) \ if (prevp->var != v.var) { G.print_now = 2;\ printf("\t*** Changed: " txt " (prev %d). (gop %d, pic %d).\n", \ prevp->var, G.gops, G.pictures); } #define changed(args) \ do { printf("\t*** Changed: "); printf args; \ printf(". (gop %d, pic %d).\n", G.gops, G.pictures); G.print_now = 2; \ } while (0) #define set_if_first(n) if (first) \ { first = false; memset(&prev, 0, sizeof prev); G.print_now = n; } static void mpgcode_b2(ZeroZeroOneBuf * zzob) { static bool first = true; if (first) { first = false; G.print_now = 1; } if (display(true, 0xb2)) hdrname(("user data")); } struct matrix_info { const char * name; const char matrix[64]; /* in zigzag form */ }; const struct { struct matrix_info intra[4]; struct matrix_info inter[3]; } known_matrixes = { /* Contents from tables.c of mjpegtools/mpeg2enc */ {{ "kvcd notch", { 8, 9, 9, 12, 10, 12, 22, 14, 14, 22, 26, 26, 18, 26, 26, 27, 27, 27, 27, 27, 27, 29, 29, 29, 31, 29, 29, 29, 34, 34, 34, 36, 36, 34, 34, 34, 37, 37, 37, 39, 37, 37, 37, 38, 38, 38, 38, 38, 38, 40, 40, 40, 40, 40, 48, 48, 48, 48, 58, 58, 58, 69, 69, 79 }}, { "default", { 8, 16, 16, 19, 16, 19, 22, 22, 22, 22, 22, 22, 26, 24, 26, 27, 27, 27, 26, 26, 26, 26, 27, 27, 27, 29, 29, 29, 34, 34, 34, 29, 29, 29, 27, 27, 29, 29, 32, 32, 34, 34, 37, 38, 37, 35, 35, 34, 35, 38, 38, 40, 40, 40, 48, 48, 46, 46, 56, 56, 58, 69, 69, 83 }}, { "hires", { 8, 16, 16, 18, 16, 18, 20, 20, 20, 20, 21, 21, 22, 23, 24, 25, 25, 24, 23, 23, 23, 24, 24, 24, 24, 26, 26, 26, 30, 30, 28, 26, 25, 25, 25, 25, 26, 26, 28, 28, 28, 29, 30, 31, 31, 30, 30, 29, 28, 29, 29, 30, 30, 31, 33, 33, 31, 31, 34, 34, 36, 38, 38, 42 }}, { "tmpgenc", { 8, 16, 16, 19, 16, 19, 22, 22, 22, 22, 22, 22, 26, 24, 26, 27, 27, 27, 26, 26, 26, 26, 27, 27, 27, 29, 29, 29, 34, 34, 34, 29, 29, 29, 27, 27, 29, 29, 32, 32, 34, 34, 37, 38, 37, 35, 35, 34, 35, 38, 38, 40, 40, 40, 48, 40, 46, 46, 56, 56, 58, 69, 69, 83 }} }, {{ "kvcd notch", { 16, 18, 18, 20, 20, 20, 22, 22, 22, 22, 24, 24, 24, 24, 24, 26, 26, 26, 26, 26, 26, 28, 28, 28, 30, 28, 28, 28, 30, 30, 30, 32, 32, 30, 30, 30, 32, 32, 32, 34, 32, 32, 32, 34, 34, 34, 34, 34, 34, 36, 36, 36, 36, 36, 38, 38, 38, 38, 40, 42, 40, 42, 42, 44 }}, { "default", { 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 }}, { "tmpgenc", { 16, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 25, 24, 24, 24, 25, 26, 26, 26, 26, 25, 27, 27, 27, 27, 27, 28, 28, 28, 28, 30, 30, 30, 31, 31, 33 }} } }; static const char * known_matrix(const unsigned char * matrix, const struct matrix_info * matrix_info, int matrix_infos) { int i; while (matrix_infos-- > 0) { for (i = 63; i >= 0; i--) if (matrix[i] != matrix_info[matrix_infos].matrix[i]) break; if (i < 0) return matrix_info[matrix_infos].name; } return " + "; } static void print_matrix(const unsigned char * matrix) { int i; int j; for (i = 0; i < 4; i++) { printf("\t\t"); for (j = 0; j < 16; j++) printf(" %02d", matrix[i * 16 + j]); printf("\n"); } } static void mpgcode_b3(ZeroZeroOneBuf * zzob) { static bool first = true; struct vst { int horizontal_size; int vertical_size; u4 aspect_ratio; u4 frame_rate; int bitrate; int vbv_buffer_size; u1 constrained_parameters_flag; u1 load_intra_quantiser_matrix; u1 load_non_intra_quantiser_matrix; }; static struct vst prev; struct vst v; unsigned char * p; /* matrixes, in zig-zag form. */ unsigned char intra_mzz[64], inter_mzz[64]; static unsigned char prev_intra_mzz[64], prev_inter_mzz[64]; const char * istr = " - ", *nistr = istr; int i, hdfm; v.horizontal_size = (data[4] << 4) + (data[5] >> 4); v.vertical_size = ((data[5] & 0x0f) << 8) + data[6]; v.aspect_ratio = data[7] >> 4; v.frame_rate = data[7] & 0x0f; v.bitrate = (data[8] << 10) + (data[9] << 2) + (data[10] >> 6); v.vbv_buffer_size = (data[10] << 5) + (data[11] >> 3); v.constrained_parameters_flag = !! (data[11] & 0x04); p = data + 11; v.load_intra_quantiser_matrix = !! (*p & 0x02); if (v.load_intra_quantiser_matrix) { for (i = 0; i < 64; i++, p++) intra_mzz[i] = ((*p & 0x01) << 7) | (p[1] >> 1); istr = known_matrix(intra_mzz, known_matrixes.intra, sizeof known_matrixes.intra / sizeof known_matrixes.intra[0]); } v.load_non_intra_quantiser_matrix = !! (*p++ & 0x01); if (v.load_non_intra_quantiser_matrix) { for (i = 0; i < 64; i++) inter_mzz[i] = *p++; nistr = known_matrix(inter_mzz, known_matrixes.inter, sizeof known_matrixes.inter / sizeof known_matrixes.inter[0]); } set_if_first(2) else { ifchanged_simple(horizontal_size, "horizontal size"); ifchanged_simple(vertical_size, "vertical size"); ifchanged_simple(aspect_ratio, "aspect ratio"); ifchanged_simple(frame_rate, "frame rate"); ifchanged_simple(load_intra_quantiser_matrix, "load intra quantiser matrix"); if (v.load_intra_quantiser_matrix && memcmp(prev_intra_mzz, intra_mzz, 64) != 0) changed(("intra matrix")); if (v.load_non_intra_quantiser_matrix && memcmp(prev_inter_mzz, inter_mzz, 64) != 0) changed(("non-intra matrix")); ifchanged_simple(load_non_intra_quantiser_matrix, "load non-intra quantiser matrix"); #if 0 if (prev.horizontal_size != v.horizontal_size) changed(("horizontal size (prev: %d)", prev.horizontal_size)); #endif } if ((hdfm = display(true, 0xb3))) hdrname(("sequence header")); if (display(false, 0xb3)) { printf("\t horizontal size: %d\n", v.horizontal_size); printf("\t vertical size: %d\n", v.vertical_size); printf("\t aspect ratio: %d\n", v.aspect_ratio); printf("\t frame rate: %d\n", v.frame_rate); printf("\t bit rate: %d\n", v.bitrate); printf("\t VBV buffer size: %d\n", v.vbv_buffer_size); printf("\t constrained parameters flag: %d\n", v.constrained_parameters_flag); printf("\t load intra quantiser matrix: %d (%s)\n", v.load_intra_quantiser_matrix, istr); hdfm &= G.o.print_matrixes; if (hdfm && v.load_intra_quantiser_matrix) print_matrix(intra_mzz); printf("\t load non-intra quantiser matrix: %d (%s)\n", v.load_non_intra_quantiser_matrix, nistr); if (hdfm && v.load_non_intra_quantiser_matrix) print_matrix(inter_mzz); putchar('\n'); G.flush = true; } if (v.load_intra_quantiser_matrix) memcpy(prev_intra_mzz,intra_mzz,64); if (v.load_non_intra_quantiser_matrix) memcpy(prev_inter_mzz,inter_mzz,64); prev = v; } static void mpgcode_b4(ZeroZeroOneBuf * zzob) { hdrname(("sequence error")); } static void mpgcode_b5(ZeroZeroOneBuf * zzob) { unsigned char * p; switch (data[4] & 0xf0) { case 0x10: { static bool first = true; struct vst { u8 profile_and_level; u1 progressive_sequence; u2 chroma_format; u2 horizontal_size_extension; u2 vertical_size_extension; int bit_rate_extension; u8 vbv_buffer_size_extension; u1 low_delay; u2 frame_rate_extension_n; u5 frame_rate_extension_d; }; static struct vst prev; struct vst v; v.profile_and_level = ((data[4] & 0x0f) << 4) + (data[5] >> 4); v.progressive_sequence = !! (data[5] & 0x08); v.chroma_format = (data[5] & 0x06) >> 1; v.horizontal_size_extension = ((data[5] & 0x01) << 1) + (data[6] >> 7); v.vertical_size_extension = (data[6] & 0x60) >> 5; v.bit_rate_extension = ((data[6] & 0x1f) << 7) + (data[7] >> 1); v.vbv_buffer_size_extension = data[8]; v.low_delay = !! (data[9] & 0x80); v.frame_rate_extension_n = (data[9] & 0x60) >> 5; v.frame_rate_extension_d = data[9] & 0x1f; set_if_first(2) else { ifchanged_simple(profile_and_level, "profile and level"); ifchanged_simple(progressive_sequence, "progressive sequence"); ifchanged_simple(chroma_format, "chroma format"); ifchanged_simple(horizontal_size_extension, "horizontal size extension"); ifchanged_simple(vertical_size_extension, "vertical_size_extension"); ifchanged_simple(bit_rate_extension, "bit_rate_extension"); /*ifchanged_simple(); */ /*ifchanged_simple(); */ /*ifchanged_simple(); */ } if (display(true, 0xb5)) hdrname(("1: sequence extension")); if (display(false, 0xb5)) { printf("\t profile and level: %d\n", v.profile_and_level); printf("\t progressive sequence: %d\n", v.progressive_sequence); printf("\t chroma format: %d\n", v.chroma_format); printf("\t horizontal size extension: %d\n", v.horizontal_size_extension); printf("\t vertical size extension: %d\n", v.vertical_size_extension); printf("\t bit rate extension: %d\n", v.bit_rate_extension); printf("\t vbv buffer size extension: %d\n", v.vbv_buffer_size_extension); printf("\t low delay: %d\n", v.low_delay); printf("\t frame rate extension n: %d\n", v.frame_rate_extension_n); printf("\t frame rate extension d: %d\n", v.frame_rate_extension_d); putchar('\n'); G.flush = true; } prev = v; } break; case 0x20: { static bool first = true; struct vst { u3 video_format; u1 color_description_flag; u8 color_primaries; u8 transfer_characteristics; u8 matrix_coefficients; int display_horizontal_size; int display_vertical_size; }; static struct vst prev; struct vst v; v.video_format = (data[4] & 0x0e) >> 1; v.color_description_flag = !! (data[4] & 0x01); if (v.color_description_flag) { v.color_primaries = data[5]; v.transfer_characteristics = data[6]; v.matrix_coefficients = data[7]; p = data + 8; } else p = data + 5; v.display_horizontal_size = (p[0] << 6) + (p[1] >> 2); v.display_vertical_size = ((p[1] & 0x01) << 13) + (p[2] << 5) + (p[3] >> 3); set_if_first(2) else { ifchanged_simple(video_format, "video format"); ifchanged_simple(display_horizontal_size, "display_horizontal_size"); ifchanged_simple(display_vertical_size, "display_vertical_size"); } if (display(true, 0xb5)) hdrname(("2: sequence display extension")); if (display(false, 0xb5)) { printf("\t video format: %d\n", v.video_format); printf("\t color description flag: %d\n", v.color_description_flag); if (v.color_description_flag) { printf("\t - color primaries: %d\n", v.color_primaries); printf("\t - transfer characteristics %d\n", v.transfer_characteristics); printf("\t - matrix coefficients: %d\n", v.matrix_coefficients); } printf("\t display horizontal size: %d\n", v.display_horizontal_size); printf("\t display vertical size: %d\n", v.display_vertical_size); putchar('\n'); G.flush = true; } prev = v; } break; case 0x80: { static bool first = true; struct vst { u4 f_code_fh; u4 f_code_fv; u4 f_code_bh; u4 f_code_bv; u2 intra_dc_precision; u2 picture_structure; u1 top_field_first; u1 frame_pred_frame_dct; u1 concealment_motion_vectors; u1 q_scale_type; u1 intra_vlc_format; u1 alternate_scan; u1 repeat_first_field; u1 chroma_420_type; u1 progressive_frame; u1 composite_display; /* more to follow, but ignored */ }; static struct vst prev; struct vst v; v.f_code_fh = data[4] & 0x0f; v.f_code_fv = data[5] >> 4; v.f_code_bh = data[5] & 0x0f; v.f_code_bv = data[6] >> 4; v.intra_dc_precision = (data[6] >> 2) & 0x03; v.picture_structure = data[6] & 0x03; v.top_field_first = !!(data[7] & 0x80); v.frame_pred_frame_dct = !!(data[7] & 0x40); v.concealment_motion_vectors = !!(data[7] & 0x20); v.q_scale_type = !!(data[7] & 0x10); v.intra_vlc_format = !!(data[7] & 0x08); v.alternate_scan = !!(data[7] & 0x04); v.repeat_first_field = !!(data[7] & 0x02); v.chroma_420_type = data[7] & 0x01; v.progressive_frame = !!(data[8] & 0x80); v.composite_display = !!(data[8] & 0x40); /* if (composite_display) ... */ set_if_first(2) else { /*ifchanged_simple(f_code_fh, "f_code[0][0]"); */ /* ... */ ifchanged_simple(intra_dc_precision, "intra dc precision"); ifchanged_simple(picture_structure, "picture structure"); ifchanged_simple(top_field_first, "top field first"); /*ifchanged_simple(frame_pred_frame_dct, "frame pred frame dct"); */ ifchanged_simple(concealment_motion_vectors, "concealment motion vectors"); ifchanged_simple(intra_vlc_format, "intra vlc format"); ifchanged_simple(alternate_scan, "alternate scan"); ifchanged_simple(repeat_first_field, "repeat first field"); ifchanged_simple(chroma_420_type, "chroma 420 type"); ifchanged_simple(progressive_frame, "progressive frame"); ifchanged_simple(composite_display, "composite display"); } if (display(true, 0xb5)) hdrname(("8: picture coding extension")); if (display(false, 0xb5)) { printf("\t f_code[0][0]: %d, f_code[0][1]: %d" " f_code[1][0]: %d, f_code[1][1]: %d\n", v.f_code_fh, v.f_code_fv, v.f_code_bh, v.f_code_bv); printf("\t intra dc precision: %d\n", v.intra_dc_precision); printf("\t picture structure: %d\n", v.picture_structure); printf("\t top field first: %d\n", v.top_field_first); printf("\t frame pred frame dct: %d\n", v.frame_pred_frame_dct); printf("\t concealment motion vectors: %d\n", v.concealment_motion_vectors); printf("\t intra vlc format: %d\n", v.intra_vlc_format); printf("\t alternate scan: %d\n", v.alternate_scan); printf("\t repeat first field: %d\n", v.repeat_first_field); printf("\t chroma 420 type: %d\n", v.chroma_420_type); printf("\t progressive frame: %d\n", v.progressive_frame); printf("\t composite display: %d\n", v.composite_display); putchar('\n'); G.flush = true; } prev = v; } break; } } static void mpgcode_b7(ZeroZeroOneBuf * zzob) { static bool first = true; if (first) { first = false; G.print_now = 1; } if (display(true, 0xb7)) hdrname(("sequence end")); } static void mpgcode_b8(ZeroZeroOneBuf * zzob) { static bool first = true; struct vst { int drop_frame_flag; int hour, minute, second, frame; int closed_gop, broken_gop; unsigned long xtime; }; static struct vst prev; struct vst v; chklen(7); v.drop_frame_flag = !! (data[4] & 0x80); v.hour = (data[4] & 0x7c) >> 2; v.minute = ((data[4] & 0x03) << 4) + (data[5] >> 4); v.second = ((data[5] & 0x07) << 3) + (data[6] >> 5); v.frame = ((data[6] & 0x1f) << 1) + (data[7] >> 7); v.xtime = (v.hour * 60 + v.minute) * 60 + v.second; v.closed_gop = !! (data[7] & 0x40); v.broken_gop = !! (data[7] & 0x20); set_if_first(2) else { ifchanged_simple(broken_gop, "broken gop"); if (v.xtime != prev.xtime) { G.seconds++; if (v.xtime - prev.xtime > 2) { changed(("unexpected time jump; prev: %02d:%02d:%02d.%02d", prev.hour, prev.minute, prev.second, prev.frame)); } } } G.gops++; if (display(true, 0xb8)) hdrname(("GOP header (#%d)", G.gops)); if (display(false, 0xb8)) { printf("\t drop: %d, hour: %2d, min: %2d, sec: %2d, frame: %2d, " "closed: %d, broken: %d\n", v.drop_frame_flag, v.hour, v.minute, v.second, v.frame, v.closed_gop, v.broken_gop); putchar('\n'); G.flush = true; } prev = v; } static void mpgcode_b9(ZeroZeroOneBuf * zzob) { static bool first = true; if (first) { first = false; G.print_now = 1; } if (display(true, 0xb9)) hdrname(("program end")); } /* Pack Header: 00 00 01 ba * * byte 4 byte 5 byte 6 byte 7 byte 8 byte 9 * 76543210 76543210 76543210 76543210 76543210 76543210 * 01[SCR]1 [ SCR 29..15 ]1[ SCR 14..00 ]1[ SCR_ext]1 * * byte10 byte11 byte12 byte13 * 76543210 76543210 76543210 76543210 * [ Program_Mux_Rate ]11 [res][psl] */ static void mpgcode_ba(ZeroZeroOneBuf * zzob) { static bool first = true; struct vst { long long scr; int scr_ext; int program_mux_rate; /* int stuffing_length; */ }; static struct vst prev; struct vst v; double scrms; chklen(13); v.scr = ((long long)(data[4] & 0x38) << 27) + ((data[4] & 0x03) << 28) + (data[5] << 20) + ((data[6] & 0xf8) << 12) + ((data[6] & 0x03) << 13) + (data[7] << 5) + ((data[8] & 0xf8) >> 3); v.scr_ext = ((data[8] & 0x03) << 7) + (data[9] >> 1); scrms = (v.scr * 300 + v.scr_ext) / 27000.0; v.program_mux_rate = (data[10] << 14) + (data[11] << 6) + (data[12] >> 2); set_if_first(2) else { ifchanged_simple(program_mux_rate, "program mux rate"); } if (display(true, 0xba)) hdrname(("pack header")); if (display(false, 0xba)) { printf("\t SCR: %lld SCR_ext: %d (= %f ms) program mux rate: %d\n", v.scr, v.scr_ext, scrms, v.program_mux_rate); putchar('\n'); G.flush = true; } prev = v; } /* 2.5.3.5 in ... system header start code 32 bits (0x000001bb) header length 16 bits marker bit 1 rate bound 22 bits marker bit 1 audio bound 6 fixed flag 1 csps flag 1 system audio lock flag 1 system video lock flag 1 marker bit 1 video bound 5 packet rate restriction flag 1 bit reserved byte 7 while nextbit == 1 stream id 8 '1 1' 2 P-STD buffer bound scale 1 P-STD buffer size bound 13 So interest for this does not break through my inertia threshold just now */ static void mpgcode_bb(ZeroZeroOneBuf * zzob) { static bool first = true; if (first) { first = false; G.print_now = 1; } if (display(true, 0xbb)) hdrname(("system header")); } /* 2.5.4.1 in above doc. Same comment... */ static void mpgcode_bc(ZeroZeroOneBuf * zzob) { static bool first = true; if (first) { first = false; G.print_now = 1; } if (display(true, 0xbc)) hdrname(("program stream map")); } static void mpgcode_bd(ZeroZeroOneBuf * zzob) { static bool first = true; if (first) { first = false; G.print_now = 1; } if (display(true, 0xbd)) hdrname(("private stream 1 (non MPEG audio, subpictures)")); } static void mpgcode_be(ZeroZeroOneBuf * zzob) { static bool first = true; if (first) { first = false; G.print_now = 1; } if (display(true, 0xbe)) hdrname(("padding stream")); } static void mpgcode_bf(ZeroZeroOneBuf * zzob) { static bool first = true; if (first) { first = false; G.print_now = 1; } if (display(true, 0xbf)) hdrname(("private stream 2 (navigation data)")); } static void pes_header(ZeroZeroOneBuf * zzob, const char * hdrnam) { unsigned char * dp; static bool first_a = true; static bool first_v = true; struct vst { int pes_packet_length; u2 pes_scrambling_control; u1 pes_priority; u1 data_alignment_indicator; u1 copyright; u1 original_or_copy; u2 pts_dts_flags; u1 escr_flag; u1 es_rate_flag; u1 dsm_trick_mode_flag; u1 additional_copy_info_flag; u1 pes_crc_flag; u1 pes_extension_flag; u8 pes_header_data_length; long long pts; long long dts; long long escr_base; int escr_ext; int es_rate; }; /* this might need more later, if used */ static struct vst prev_a; static struct vst prev_v; struct vst * prevp = (data[3] >= 0xe0)? &prev_v: &prev_a; bool * firstp = (data[3] >= 0xe0)? &first_v: &first_a; struct vst v; v.pes_packet_length = data[4] * 256 + data[5]; v.pes_scrambling_control = (data[6] & 0x30) >> 4; v.pes_priority = !!(data[6] & 0x08); v.data_alignment_indicator = !!(data[6] & 0x04); v.copyright = !!(data[6] & 0x02); v.original_or_copy = data[6] & 0x01; v.pts_dts_flags = (data[7] & 0xc0) >> 6; v.escr_flag = !!(data[7] & 0x20); v.es_rate_flag = !!(data[7] & 0x10); v.dsm_trick_mode_flag = !!(data[7] & 0x08); v.additional_copy_info_flag = !!(data[7] & 0x04); v.pes_crc_flag = !!(data[7] & 0x02); v.pes_extension_flag = data[7] & 0x01; v.pes_header_data_length = data[8]; dp = data + 9; if (v.pts_dts_flags & 0x02) { v.pts = ((long long)(data[9] & 0x0e) << 29) + (data[10] << 22) + ((data[11] & 0xfe) << 14) + (data[12] << 7) + (data[13] >> 1); dp += 5; } if (v.pts_dts_flags == 0x03) { v.dts = ((long long)(data[14] & 0x0e) << 29) + (data[15] << 22) + ((data[16] & 0xfe) << 14) + (data[17] << 7) + (data[18] >> 1); dp += 5; } #if 0 /* the following data is not verified (ands and shifts) to work */ if (escr_flag) { escr = ((long long)(dp[0] & 0x38) << 27) + ((dp[1] & 0x03) << 28) + (dp[1] << 20) + ((dp[2] & 0xf8) << 12) + ((dp[3] & 0x03) << 12) + (dp[3] << 5) + (dp[4] >> 3); escr_ext = ((dp[4] & 0x03) << 6) + (dp[5] >> 1); dp += 6; } if (es_rate_flag) { es_rate = ((dp[0] & 0x7f) << 15) + (dp[1] << 7) + (dp[2] >> 1); dp += 3; } if (dsm_trick_flag) { ; } if (additional_copy_info_flag) { add_copy_info = dp[0] & 0x7f; dp += 1; } /* eek, too much to tune just now -- anyone cares ??? */ if (f_pes_crc) { ; } #endif if (*firstp) { *firstp = false; memset(prevp, 0, sizeof *prevp); G.print_now = 2; } else { /*ifchanged_simple2(pts, "pts (testing...)") */ ifchanged_simple2(pes_scrambling_control, "pes scrambling control"); ifchanged_simple2(pes_priority, "pes priority"); ifchanged_simple2(data_alignment_indicator, "data alignment indicator"); ifchanged_simple2(copyright, "copyright"); ifchanged_simple2(original_or_copy, "original or copy"); ifchanged_simple2(escr_flag, "escr flag"); ifchanged_simple2(es_rate_flag, "es rate flag"); ifchanged_simple2(dsm_trick_mode_flag, "dsm trick mode flag"); ifchanged_simple2(additional_copy_info_flag, "additional copy info flag"); ifchanged_simple2(pes_crc_flag, "pes crc flag"); #if 0 /* hits so often... */ ifchanged_simple2(pes_extension_flag, "pes extension flag"); ifchanged_simple2(pes_header_data_length, "pes header data length"); #endif } if (display(true, data[3])) hdrname((hdrnam)); if (display(false, data[3])) { printf("\t pes packet length: %d (> %lld). pts_dts: %d\n", v.pes_packet_length, pos + v.pes_packet_length + 6, v.pts_dts_flags); if (v.pts_dts_flags == 0x02) printf("\t pts: %lld (%f ms)\n", v.pts, v.pts / 90.0); else if (v.pts_dts_flags == 0x03) printf("\t pts: %lld (%f ms), dts: %lld (%f ms)\n", v.pts, v.pts / 90.0, v.dts, v.dts / 90.0); printf("\t pes scrabling control: %d\n", v.pes_scrambling_control); printf("\t pes priority: %d\n", v.pes_priority); printf("\t data alignment indicator: %d\n", v.data_alignment_indicator); printf("\t copyright: %d\n", v.copyright); printf("\t original or copy: %d\n", v.original_or_copy); printf("\t escr flag: %d\n", v.escr_flag); printf("\t es rate flag: %d\n", v.es_rate_flag); printf("\t dsm trick mode flag: %d\n", v.dsm_trick_mode_flag); printf("\t additional copy info flag: %d\n", v.additional_copy_info_flag); printf("\t pes crc flag: %d\n", v.pes_crc_flag); printf("\t pes extension flag: %d\n", v.pes_extension_flag); printf("\t pes header data length: %d\n", v.pes_header_data_length); putchar('\n'); G.flush = true; } *prevp = v; } static void audio_stream(ZeroZeroOneBuf * zzob) { while (len < 8) if (! zzob_data(zzob, true)) return; pes_header(zzob, "audio stream"); } static void video_stream(ZeroZeroOneBuf * zzob) { while (len < 8) if (! zzob_data(zzob, true)) return; pes_header(zzob, "video_stream"); } /* --*-- */ static void picture(ZeroZeroOneBuf * zzob) { static char x[] = "xIPBDxxx"; static bool first = true; struct vst { int temperal_sequence_number; int frame_type; int vbv_delay; }; static struct vst prev; struct vst v; chklen(7); v.temperal_sequence_number = (data[4] << 2) + ((data[5] & 0xc0) >> 6); v.frame_type = ((data[5] & 0x38) >> 3); v.vbv_delay = ((data[5] & 0x02) << 13) + (data[6] << 5) + ((data[7] & 0xf8) >> 3); /* XXX vectors omitted (mpeg-1 stuff anyway) */ set_if_first(2) else { ifchanged_simple(vbv_delay, "vbv delay"); } G.pictures++; if (display(true, 00)) hdrname(("picture #%d", G.pictures)); if (display(false, 00)) { printf("\t temperal sequence number: %2d, " "frame type: %d(%c), vbv delay: %d\n", v.temperal_sequence_number, v.frame_type, x[v.frame_type], v.vbv_delay); putchar('\n'); G.flush = true; } prev = v; } static void slice(ZeroZeroOneBuf * zzob) { static bool first = true; if (first) { first = false; G.print_now = 1; } if (display(true, 01)) hdrname(("slice")); } #undef pos #undef len #undef data /* --*-- */ static void usage(const char * format, ...) GCCATTR_PRINTF(1, 2) GCCATTR_NORETURN; static void usage(const char * format, ...) { if (format) { va_list ap; fprintf(stderr, "\n%s: ", G.prgname); va_start(ap, format); vfprintf(stderr, format, ap); fputs("\n", stderr); } fprintf(stderr,"\n*** mpeg2info " VER " (" VERDATE ").\n" "\nUsage: %s [-adm] [-f filteropts] filename.\n\n" "\tOptions:\n" "\t\t-a: show all headers.\n" "\t\t-d: show details of headers.\n" "\t\t-m: show quantiser matrixes (in zigzag form).\n" "\t\t-f filteropts: filter some headers out:\n" "\t\t\t chars `s', `p', `b', `a' and `v' are effective here.\n" "\n\tfilename: mpeg file name or `-' for stdin.\n\n", G.prgname); exit(1); } static char * get_next_arg(int * c, char *** v) { if ((*v)[1] == NULL) usage(NULL); (*v)++; (*c)--; return **v; } int main(int argc, char ** argv) { ZeroZeroOneBuf zzob; unsigned char buf[65536]; char * arg; /* put stdout in fully buffered mode, we fflush() when needed */ setvbuf(stdout, NULL, _IOFBF, 0); init_G(argv); while ((arg = get_next_arg(&argc, &argv))[0] == '-' && arg[1] != '\0') { char * p = arg + 1; for (p = arg + 1; *p; p++) { switch (*p) { case 'a': G.o.print_all = 1; break; case 'd': G.o.print_details = 1; break; case 'm': G.o.print_matrixes = 1; break; case 'f': set_filters(get_next_arg(&argc, &argv)); break; default: usage("%c: unknown arg.", *p); } } } if (argc != 1) usage(NULL); if (arg[0] == '-' && arg[1] == '\0') { if (! zzob_init(&zzob, 0, buf, sizeof buf)) return 1; } else { int fd; if ((fd = open(arg, O_RDONLY)) < 0) { fprintf(stderr, "%s: Can not open file %s: %s.\n", G.prgname, arg, strerror(errno)); exit(1); } if (! zzob_init(&zzob, fd, buf, sizeof buf)) return 1; } for (; zzob_data(&zzob, false); ) { #if 0 write(1, zzob.data, zzob.len); continue; #endif #if 0 if (1 && zzob.data[3] < 128) continue; if (0 && zzob.data[3] != 0xb3) continue; printf("pos: %9lld, len: %5d. %02x %02x %02x %02x\n", zzob.pos, zzob.len, zzob.data[0], zzob.data[1], zzob.data[2], zzob.data[3]); fflush(stdout); #endif if (zzob.data[3] == 0x00) { picture(&zzob); } else if (zzob.data[3] < 0xb0) { slice(&zzob); } else if (zzob.data[3] >= 0xc0 && zzob.data[3] < 0xe0) { audio_stream(&zzob); } else if (zzob.data[3] >= 0xe0 && zzob.data[3] < 0xf0) { video_stream(&zzob); } else switch (zzob.data[3]) { #define callmpgcode(hex) \ case 0x ## hex: mpgcode_ ## hex (&zzob); break callmpgcode(b2); callmpgcode(b3); callmpgcode(b4); callmpgcode(b5); callmpgcode(b7); callmpgcode(b8); callmpgcode(b9); callmpgcode(ba); callmpgcode(bb); callmpgcode(bc); callmpgcode(bd); callmpgcode(be); callmpgcode(bf); default: #define pos zzob.pos #define len zzob.len #define data zzob.data hdrname(("unhandled start code %x " "(might not be start code at all...)", data[3])); } if (G.flush) { fflush(stdout); G.flush = false; } } printf("Summary:\n GOPs %d, pictures %d,", G.gops, G.pictures); printf(" seconds ~%d, length %lld (~%d kbs).\n", G.seconds, pos, (int)(pos / G.seconds * 8 / 1000)); return 0; } /* * Local variables: * mode: c * c-file-style: "gnu" * compile-command: "sh mpeg2info.c" * tab-width: 8 * End: */