📄 mpeg.c
字号:
/* $Id: mpeg.c,v 1.2 2004/04/11 12:20:32 miguelfreitas Exp $ Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> 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*/#ifdef HAVE_CONFIG_H# include "config.h"#endif#include <stdio.h>#include <cdio/cdio.h>/* Private headers */#include "bitvec.h"#include "mpeg.h"#include "util.h"static const char _rcsid[] = "$Id: mpeg.c,v 1.2 2004/04/11 12:20:32 miguelfreitas Exp $";#define MPEG_START_CODE_PATTERN ((uint32_t) 0x00000100)#define MPEG_START_CODE_MASK ((uint32_t) 0xffffff00)#define MPEG_PICTURE_CODE ((uint32_t) 0x00000100)/* [...slice codes... 0x1a7] */#define MPEG_USER_CODE ((uint32_t) 0x000001b2)#define MPEG_SEQUENCE_CODE ((uint32_t) 0x000001b3)#define MPEG_EXT_CODE ((uint32_t) 0x000001b5)#define MPEG_SEQ_END_CODE ((uint32_t) 0x000001b7)#define MPEG_GOP_CODE ((uint32_t) 0x000001b8)#define MPEG_PROGRAM_END_CODE ((uint32_t) 0x000001b9)#define MPEG_PACK_HEADER_CODE ((uint32_t) 0x000001ba)#define MPEG_SYSTEM_HEADER_CODE ((uint32_t) 0x000001bb)#define MPEG_PRIVATE_1_CODE ((uint32_t) 0x000001bd)#define MPEG_PAD_CODE ((uint32_t) 0x000001be)#define MPEG_AUDIO_C0_CODE ((uint32_t) 0x000001c0) /* default */#define MPEG_AUDIO_C1_CODE ((uint32_t) 0x000001c1) /* 2nd audio stream id (dual channel) */#define MPEG_AUDIO_C2_CODE ((uint32_t) 0x000001c2) /* 3rd audio stream id (surround sound) */#define MPEG_VIDEO_E0_CODE ((uint32_t) 0x000001e0) /* motion */#define MPEG_VIDEO_E1_CODE ((uint32_t) 0x000001e1) /* lowres still */#define MPEG_VIDEO_E2_CODE ((uint32_t) 0x000001e2) /* hires still */#define PICT_TYPE_I 1#define PICT_TYPE_P 2#define PICT_TYPE_B 3#define PICT_TYPE_D 4static struct { mpeg_norm_t norm; unsigned hsize; unsigned vsize; int frate_idx;} const norm_table[] = { { MPEG_NORM_FILM, 352, 240, 1 }, { MPEG_NORM_PAL, 352, 288, 3 }, { MPEG_NORM_NTSC, 352, 240, 4 }, { MPEG_NORM_PAL_S, 480, 576, 3 }, { MPEG_NORM_NTSC_S, 480, 480, 4 }, { MPEG_NORM_OTHER, }};static const double frame_rates[16] = { 0.0, 24000.0/1001, 24.0, 25.0, 30000.0/1001, 30.0, 50.0, 60000.0/1001, 60.0, 0.0, };#ifdef DEBUG# define MARKER(buf, offset) \ vcd_assert (vcd_bitvec_read_bit (buf, offset) == 1)#else# define MARKER(buf, offset) \ { if (GNUC_UNLIKELY (vcd_bitvec_read_bit (buf, offset) != 1)) vcd_debug ("mpeg: some marker is not set..."); }#endifstatic inline bool_start_code_p (uint32_t code){ return (code & MPEG_START_CODE_MASK) == MPEG_START_CODE_PATTERN;}static inline int _vid_streamid_idx (uint8_t streamid){ switch (streamid | MPEG_START_CODE_PATTERN) { case MPEG_VIDEO_E0_CODE: return 0; break; case MPEG_VIDEO_E1_CODE: return 1; break; case MPEG_VIDEO_E2_CODE: return 2; break; default: vcd_assert_not_reached (); break; } return -1;}static inline int _aud_streamid_idx (uint8_t streamid){ switch (streamid | MPEG_START_CODE_PATTERN) { case MPEG_AUDIO_C0_CODE: return 0; break; case MPEG_AUDIO_C1_CODE: return 1; break; case MPEG_AUDIO_C2_CODE: return 2; break; default: vcd_assert_not_reached (); break; } return -1;}/* used for SCR, PTS and DTS */static inline uint64_t_parse_timecode (const uint8_t *buf, unsigned *offset){ uint64_t _retval; _retval = vcd_bitvec_read_bits (buf, offset, 3); MARKER (buf, offset); _retval <<= 15; _retval |= vcd_bitvec_read_bits (buf, offset, 15); MARKER (buf, offset); _retval <<= 15; _retval |= vcd_bitvec_read_bits (buf, offset, 15); MARKER (buf, offset); return _retval;}static void_parse_sequence_header (uint8_t streamid, const uint8_t *buf, VcdMpegStreamCtx *state){ unsigned offset = 0; unsigned hsize, vsize, aratio, frate, brate, bufsize, constr; const uint8_t *data = buf; const int vid_idx = _vid_streamid_idx (streamid); const double aspect_ratios[16] = { 0.0000, 1.0000, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9375, 0.9815, 1.0255, 1.0695, 1.1250, 1.1575, 1.2015, 0.0000 }; if (state->stream.shdr[vid_idx].seen) /* we have it already */ return; hsize = vcd_bitvec_read_bits (data, &offset, 12); vsize = vcd_bitvec_read_bits (data, &offset, 12); aratio = vcd_bitvec_read_bits (data, &offset, 4); frate = vcd_bitvec_read_bits (data, &offset, 4); brate = vcd_bitvec_read_bits (data, &offset, 18); MARKER (data, &offset); bufsize = vcd_bitvec_read_bits (data, &offset, 10); constr = vcd_bitvec_read_bits (data, &offset, 1); /* skip intra quantizer matrix */ if (vcd_bitvec_read_bits (data, &offset, 1)) offset += 64 << 3; /* skip non-intra quantizer matrix */ if (vcd_bitvec_read_bits (data, &offset, 1)) offset += 64 << 3; state->stream.shdr[vid_idx].hsize = hsize; state->stream.shdr[vid_idx].vsize = vsize; state->stream.shdr[vid_idx].aratio = aspect_ratios[aratio]; state->stream.shdr[vid_idx].frate = frame_rates[frate]; state->stream.shdr[vid_idx].bitrate = 400 * brate; state->stream.shdr[vid_idx].vbvsize = bufsize * 16 * 1024; state->stream.shdr[vid_idx].constrained_flag = (constr != 0); state->stream.shdr[vid_idx].seen = true;}static void_parse_gop_header (uint8_t streamid, const uint8_t *buf, VcdMpegStreamCtx *state){ const uint8_t *data = buf; unsigned offset = 0; bool drop_flag; /* bool close_gop; */ /* bool broken_link; */ unsigned hour, minute, second, frame; drop_flag = vcd_bitvec_read_bits(data, &offset, 1) != 0; hour = vcd_bitvec_read_bits(data, &offset, 5); minute = vcd_bitvec_read_bits(data, &offset, 6); MARKER (data, &offset); second = vcd_bitvec_read_bits(data, &offset, 6); frame = vcd_bitvec_read_bits(data, &offset, 6); /* close_gop = vcd_bitvec_read_bits(data, &offset, 1) != 0; */ /* broken_link = vcd_bitvec_read_bits(data, &offset, 1) != 0; */ state->packet.gop = true; state->packet.gop_timecode.h = hour; state->packet.gop_timecode.m = minute; state->packet.gop_timecode.s = second; state->packet.gop_timecode.f = frame;}static inline void_check_scan_data (const char str[], const msf_t *msf, VcdMpegStreamCtx *state){ char tmp[16]; if (state->stream.scan_data_warnings > VCD_MPEG_SCAN_DATA_WARNS) return; if (state->stream.scan_data_warnings == VCD_MPEG_SCAN_DATA_WARNS) { vcd_warn ("mpeg user scan data: from now on, scan information " "data errors will not be reported anymore---consider" " enabling the 'update scan offsets' option, " "if it is not enabled already!"); state->stream.scan_data_warnings++; return; } if (msf->m == 0xff && msf->s == 0xff && msf->f == 0xff) return; if ((msf->s & 0x80) == 0 || (msf->f & 0x80) == 0) { snprintf (tmp, sizeof (tmp), "%.2x:%.2x.%.2x", msf->m, msf->s, msf->f); vcd_warn ("mpeg user scan data: msb of second or frame field " "not set for '%s': [%s]", str, tmp); state->stream.scan_data_warnings++; return; } if ((msf->m >> 4) > 9 || ((0x80 ^ msf->s) >> 4) > 9 || ((0x80 ^ msf->f) >> 4) > 9 || (msf->m & 0xf) > 9 || (msf->s & 0xf) > 9 || (msf->f & 0xf) > 9) { snprintf (tmp, sizeof (tmp), "%.2x:%.2x.%.2x", msf->m, 0x80 ^ msf->s, 0x80 ^ msf->f); vcd_warn ("mpeg user scan data: one or more BCD fields out of range " "for '%s': [%s]", str, tmp); state->stream.scan_data_warnings++; }}static void_parse_user_data (uint8_t streamid, const void *buf, unsigned len, unsigned offset, VcdMpegStreamCtx *state){ unsigned pos = 0; PRAGMA_BEGIN_PACKED struct { uint8_t tag; uint8_t len; uint8_t data[EMPTY_ARRAY_SIZE]; } GNUC_PACKED const *udg = buf; PRAGMA_END_PACKED if (udg->tag == 0x00) /* if first tag's already 0x00 */ { vcd_debug ("strange (possibly non-compliant) user_data seen..."); } else while (pos + 2 < len) { if (udg->tag == 0x00) break; if (pos + udg->len >= len) break; if (udg->len < 2) break; switch (udg->tag) { case 0x00: vcd_assert_not_reached (); break; case 0x10: /* scan information */ { struct vcd_mpeg_scan_data_t *usdi = (void *) udg; vcd_assert (sizeof (struct vcd_mpeg_scan_data_t) == 14); if (GNUC_UNLIKELY (usdi->len != 14)) { vcd_warn ("invalid user scan data length (%d != 14)", usdi->len); break; } vcd_assert (usdi->len == 14); _check_scan_data ("previous_I_offset", &usdi->prev_ofs, state); _check_scan_data ("next_I_offset ", &usdi->next_ofs, state); _check_scan_data ("backward_I_offset", &usdi->back_ofs, state); _check_scan_data ("forward_I_offset ", &usdi->forw_ofs, state); state->packet.scan_data_ptr = usdi; state->stream.scan_data++; } break; case 0x11: /* closed caption data */ vcd_debug ("closed caption data seen -- not supported yet (len = %d)", udg->len); break; default: vcd_warn ("unknown user data tag id 0x%.2x encountered", udg->tag); return; /* since we cannot rely on udg->len anymore... */ break; } pos += udg->len; vcd_assert (udg->len >= 2); udg = (void *) &udg->data[udg->len - 2]; } vcd_assert (pos <= len);}static int_analyze_pes_header (const uint8_t *buf, int len, VcdMpegStreamCtx *state){ bool _has_pts = false; bool _has_dts = false; int64_t pts = 0; mpeg_vers_t pes_mpeg_ver = MPEG_VERS_INVALID; int pos; if (vcd_bitvec_peek_bits (buf, 0, 2) == 2) /* %10 - ISO13818-1 */ { unsigned pos2 = 0; pes_mpeg_ver = MPEG_VERS_MPEG2; pos2 += 2; pos2 += 2; /* PES_scrambling_control */ pos2++; /* PES_priority */ pos2++; /* data_alignment_indicator */ pos2++; /* copyright */ pos2++; /* original_or_copy */ switch (vcd_bitvec_read_bits (buf, &pos2, 2)) /* PTS_DTS_flags */ { case 2: /* %10 */ _has_pts = true; break; case 3: /* %11 */ _has_dts = _has_pts = true; break; default: /* NOOP */ break; } pos2++; /* ESCR_flag */ pos2++; /* */ pos2++; /* */ pos2++; /* */ pos2++; /* */ pos2++; /* PES_extension_flag */ pos = vcd_bitvec_read_bits (buf, &pos2, 8); /* PES_header_data_length */ pos += pos2 >> 3; if (_has_pts && _has_dts) { vcd_assert (vcd_bitvec_peek_bits (buf, pos2, 4) == 3); /* %0011 */ pos2 += 4; pts = _parse_timecode (buf, &pos2); vcd_assert (vcd_bitvec_peek_bits (buf, pos2, 4) == 1); /* %0001 */ pos2 += 4; /* dts = */ _parse_timecode (buf, &pos2); } else if (_has_pts) { vcd_assert (vcd_bitvec_peek_bits (buf, pos2, 4) == 2); /* %0010 */ pos2 += 4; pts = _parse_timecode (buf, &pos2); } } else /* ISO11172-1 */ { unsigned pos2 = 0; pes_mpeg_ver = MPEG_VERS_MPEG1; /* get rid of stuffing bytes */ while (((pos2 + 8) < (len << 3)) && vcd_bitvec_peek_bits (buf, pos2, 8) == 0xff) pos2 += 8; if (vcd_bitvec_peek_bits (buf, pos2, 2) == 1) /* %01 */ { pos2 += 2; pos2++; /* STD_buffer_scale */ pos2 += 13; /* STD_buffer_size */ } switch (vcd_bitvec_peek_bits (buf, pos2, 4)) { case 0x2: /* %0010 */ pos2 += 4; _has_pts = true; pts = _parse_timecode (buf, &pos2); break; case 0x3: /* %0011 */ pos2 += 4; _has_dts = _has_pts = true; pts = _parse_timecode (buf, &pos2); vcd_assert (vcd_bitvec_peek_bits (buf, pos2, 4) == 1); /* %0001 */ pos2 += 4; /* dts = */ _parse_timecode (buf, &pos2); break; case 0x0: /* %0000 */ vcd_assert (vcd_bitvec_peek_bits (buf, pos2, 8) == 0x0f); pos2 += 8; break; case 0xf: /* %1111 - actually a syntax error! */ vcd_assert (vcd_bitvec_peek_bits (buf, pos2, 8) == 0xff); vcd_warn ("Unexpected stuffing byte noticed in ISO11172 PES header!"); pos2 += 8; break; default: vcd_error ("Error in ISO11172 PES header"); break; } pos = pos2 >> 3; } if (_has_pts) { double pts2; pts2 = (double) pts / 90000.0; if (!state->stream.seen_pts) { state->stream.max_pts = state->stream.min_pts = pts2; state->stream.seen_pts = true; } else { state->stream.max_pts = MAX (state->stream.max_pts, pts2); state->stream.min_pts = MIN (state->stream.min_pts, pts2); } state->packet.has_pts = true; state->packet.pts = pts2; } if (state->stream.version != pes_mpeg_ver) vcd_warn ("pack header mpeg version does not match pes header mpeg version"); return pos;}static void_analyze_audio_pes (uint8_t streamid, const uint8_t *buf, int len, bool only_pts, VcdMpegStreamCtx *state){ const int aud_idx = _aud_streamid_idx (streamid); unsigned bitpos; vcd_assert (aud_idx != -1); bitpos = _analyze_pes_header (buf, len, state); /* if only pts extraction was needed, we are done here... */ if (only_pts) return; if (state->stream.ahdr[aud_idx].seen) return; bitpos <<= 3; while (bitpos <= (len << 3)) { unsigned syncword = vcd_bitvec_peek_bits (buf, bitpos, 12); if (syncword != 0xfff) { bitpos += 8; continue; } bitpos += 12; if (GNUC_UNLIKELY (!vcd_bitvec_read_bits (buf, &bitpos, 1))) { vcd_debug ("non-MPEG1 audio stream header seen"); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -