/* # # MPEG2Parser class testing tool # # Copyright (C) 2001-2003 Kees Cook # kees@outflux.net, http://outflux.net/ # # Sliced VBI decoding and conversion of scr, pts and dts to seconds is ... # Copyright (C) 2009 Andy Walls # # 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://www.gnu.org/copyleft/gpl.html # */ /* * buffer management inspired by mplayer */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mpeg2structs.h" typedef unsigned long long u64; typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; static u32 g_vid_count; static u32 g_aud_count; static u32 g_vbi_count; static u64 g_first_vid_pts; static u64 g_last_vid_pts; static int g_verbose; static void statistics() { printf("\nStatistics:\n\n"); printf("video frames: %u\n", g_vid_count); printf("audio frames: %u\n", g_aud_count); printf("private packets: %u\n", g_vbi_count); printf("total time: %.2f seconds\n", (g_last_vid_pts - g_first_vid_pts) / 90000.0); } static void psread(int fh, void *buf, int cnt, bool valid_eof = false) { int res = read(fh, buf, cnt); if (res == cnt) return; if (res == 0 && valid_eof) fprintf(stderr, "end of file\n"); else fprintf(stderr, "broken stream\n"); statistics(); exit(1); } static unsigned psread_u32(int fh, bool valid_eof = false) { unsigned char buf[4]; psread(fh, buf, 4, valid_eof); return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; } static unsigned short psread_u16(int fh, bool valid_eof = false) { unsigned char buf[2]; psread(fh, buf, 2, valid_eof); return (buf[0] << 8) | buf[1]; } static const char *vbi_line_id(u8 id) { const char *ret; switch (id) { case V4L2_MPEG_VBI_IVTV_TELETEXT_B: ret = "Teletext B"; break; case V4L2_MPEG_VBI_IVTV_CAPTION_525: ret = "Closed Caption (525 line system)"; break; case V4L2_MPEG_VBI_IVTV_WSS_625: ret = "Wide Screen Signalling (625 line system)"; break; case V4L2_MPEG_VBI_IVTV_VPS: ret = "Video Programming System"; break; default: ret = "unknown"; break; } return ret; } static void vbi_decode(u8 *buf, int length) { struct v4l2_mpeg_vbi_fmt_ivtv *vbi; u32 linemask[2]; struct v4l2_mpeg_vbi_itv0_line *line; int i, f, l; vbi = (struct v4l2_mpeg_vbi_fmt_ivtv *) buf; if (!memcmp(vbi->magic, V4L2_MPEG_VBI_IVTV_MAGIC1, sizeof(vbi->magic))) { printf("36 lines:\n"); line = vbi->ITV0.line; /* This only works for little endian machines */ linemask[0] = 0xffffffff; linemask[1] = 0x0000000f; } else if (!memcmp(vbi->magic, V4L2_MPEG_VBI_IVTV_MAGIC0, sizeof(vbi->magic))) { printf("<36 lines:\n"); line = vbi->itv0.line; /* This only works for little endian machines */ linemask[0] = vbi->itv0.linemask[0]; linemask[1] = vbi->itv0.linemask[1]; } else { printf("unknown "); return; } i = 0; for (f = 1; f <= 2; f++) { for (l = 6; l <= 23; l++) { if (linemask[0] & 1) { printf("\t\tfield %d, line %2d: %s\n", f, l, vbi_line_id(line[i].id)); i++; } linemask[0] >>= 1; } linemask[0] |= (linemask[1] << (32 - (23-6+1))); linemask[1] = 0; } printf("\t\t"); return; } static u64 scr2ns(u64 scr, unsigned scr_ext) { return (300 * scr + scr_ext) * 1000 / 27; } static u64 xts2us(u64 xts) { return (100 * xts) / 9; } static void pack_header(int fh, u64 pos) { unsigned char hdr[10]; unsigned marker; u64 scr; unsigned scr_ext; unsigned mux_rate; psread(fh, hdr, 10); lseek64(fh, hdr[9] & 3, SEEK_CUR); // Skip stuffing bytes scr = (u64)(hdr[0] & 0x38) << 27; scr |= (u64)(hdr[0] & 3) << 28; scr |= (u64)hdr[1] << 20; scr |= (u64)(hdr[2] & 0xf8) << 12; scr |= (u64)(hdr[2] & 3) << 13; scr |= (u64)hdr[3] << 5; scr |= (u64)(hdr[4] & 0xf8) >> 3; scr_ext = (unsigned)(hdr[4] & 0x3) << 7; scr_ext |= (hdr[5] & 0xfe) >> 1; mux_rate = (unsigned)(hdr[6]) << 14; mux_rate |= (unsigned)(hdr[7]) << 6; mux_rate |= (unsigned)(hdr[8] & 0xfc) >> 2; if (g_verbose) printf("%lld: pack scr=%lld scr_ext=%3u scr=%lld ns mux_rate=%u\n", pos, scr, scr_ext, scr2ns(scr, scr_ext), mux_rate); marker = psread_u32(fh); if (marker != 0x000001bb) { lseek64(fh, -4, SEEK_CUR); return; } // System header unsigned short length = psread_u16(fh); if (g_verbose) printf("\tSystem header\n"); lseek64(fh, length, SEEK_CUR); } static void pes_packet(int fh, int code, u16 length, u64 pos) { if (code == 0xbe) { if (g_verbose) printf("\t%lld: Padding length=%d\n", pos, length); return; } if (code == 0xbf) { printf("\t%lld: Navigation packet length=%d\n", pos, length); return; } if (code != 0xbd && code != 0xc0 && code != 0xe0) { printf("\t%lld: Unknown packet %02x\n", pos, code); return; } unsigned char hdr[13]; u64 pts = 0; u64 dts = 0; psread(fh, hdr, 3); /* flags, flags, PES header data length */ psread(fh, &(hdr[3]), hdr[2]); if (hdr[1] & 0x80) { pts = (u64)(hdr[3] & 0xe) << 29; pts |= (u64)(hdr[4]) << 22; pts |= (u64)(hdr[5] & 0xfe) << 14; pts |= (u64)(hdr[6]) << 7; pts |= (u64)(hdr[7]) >> 1; } if (hdr[1] & 0x40) { dts = (u64)(hdr[8] & 0xe) << 29; dts |= (u64)(hdr[9]) << 22; dts |= (u64)(hdr[10] & 0xfe) << 14; dts |= (u64)(hdr[11]) << 7; dts |= (u64)(hdr[12]) >> 1; } if (pts == 0) return; if (code == 0xc0) { printf("\t%lld: Audio ", pos); g_aud_count++; } else if (code == 0xbd) { u8 buf[2048]; u16 remaining = length - (3 + hdr[2]); printf("\t%lld: Private Packet ", pos); if (remaining <= sizeof(buf)) { psread(fh, buf, remaining); vbi_decode(buf, remaining); } g_vbi_count++; } else { printf("\t%lld: Video ", pos); if (g_vid_count++ == 0) g_first_vid_pts = pts; g_last_vid_pts = pts; } if (dts) printf("pts=%llu (%llu us) dts=%llu (%llu us) length=%d\n", pts, xts2us(pts), dts, xts2us(dts), length); else printf("pts=%llu (%lld us) length=%d\n", pts, xts2us(pts), length); } int main(int argc, char *argv[]) { int fh; int opt; u64 pos = 0; u64 len; for (;;) { opt = getopt(argc, argv, "vh"); if (opt == -1) break; switch (opt) { case 'v': g_verbose = 1; break; case 'h': printf("usage: %s [-v] [-h] mpeg-file\n", argv[0]); printf("-v: verbose output\n"); printf("-h: this help message\n"); exit(0); } } if (optind != argc - 1) { printf("usage: %s [-v] [-h] mpeg-file\n", argv[0]); printf("-v: verbose output\n"); printf("-h: this help message\n"); return -1; } fh = open(argv[optind], O_RDONLY); if (fh < 0) { fprintf(stderr, "%s: cannot open mpeg file %s\n", argv[0], argv[optind]); return -2; } len = lseek64(fh, 0, SEEK_END); while (1) { lseek64(fh, pos, SEEK_SET); u32 marker = psread_u32(fh); if (marker != 0x000001ba) { if (marker == 0x000001b9) { // PS end marker printf("\t%lld: End marker\n", pos); break; } fprintf(stderr, "missing pack marker (got %08x @ %lld)\n", marker, pos); break; } pack_header(fh, pos); while (1) { pos = lseek64(fh, 0, SEEK_CUR); marker = psread_u32(fh, pos == len); if (marker == 0x000001ba || marker == 0x000001b9) break; u16 length = psread_u16(fh); pes_packet(fh, marker & 0xff, length, pos); pos += length + 6; lseek64(fh, pos, SEEK_SET); } } close(fh); statistics(); return 0; }