📄 demux_mpg.c
字号:
// MPG/VOB file parser for DEMUXER v2.5 by A'rpi/ESP-team#include <mplaylib.h>#include <mplaylib.h>#include <mplaylib.h>#include <math.h>#include "config.h"#include "mp_msg.h"#include "help_mp.h"#include "stream/stream.h"#include "demuxer.h"#include "parse_es.h"#include "stheader.h"#include "mp3_hdr.h"//#define MAX_PS_PACKETSIZE 2048#define MAX_PS_PACKETSIZE (224*1024)#define UNKNOWN 0#define VIDEO_MPEG1 0x10000001#define VIDEO_MPEG2 0x10000002#define VIDEO_MPEG4 0x10000004#define VIDEO_H264 0x10000005#define AUDIO_MP2 0x50#define AUDIO_A52 0x2000#define AUDIO_LPCM_BE 0x10001#define AUDIO_AAC mmioFOURCC('M', 'P', '4', 'A')typedef struct mpg_demuxer { float last_pts; float first_pts; // first pts found in stream float first_to_final_pts_len; // difference between final pts and first pts int has_valid_timestamps; // !=0 iff time stamps look linear // (not necessarily starting with 0) unsigned int es_map[0x40]; //es map of stream types (associated to the pes id) from 0xb0 to 0xef int num_a_streams; int a_stream_ids[MAX_A_STREAMS];} mpg_demuxer_t;extern char* dvdsub_lang;static int mpeg_pts_error=0;off_t ps_probe = 0;static int parse_psm(demuxer_t *demux, int len) { unsigned char c, id, type; unsigned int plen, prog_len, es_map_len; mpg_demuxer_t *priv = (mpg_demuxer_t *) demux->priv; mp_dbg(MSGT_DEMUX,MSGL_V, "PARSE_PSM, len=%d\n", len); if(! len || len > 1018) return 0; c = stream_read_char(demux->stream); if(! (c & 0x80)) { stream_skip(demux->stream, len - 1); //not yet valid, discard return 0; } stream_skip(demux->stream, 1); prog_len = stream_read_word(demux->stream); //length of program descriptors stream_skip(demux->stream, prog_len); //.. that we ignore es_map_len = stream_read_word(demux->stream); //length of elementary streams map es_map_len = FFMIN(es_map_len, len - prog_len - 8); //sanity check while(es_map_len > 0) { type = stream_read_char(demux->stream); id = stream_read_char(demux->stream); if(id >= 0xB0 && id <= 0xEF && priv) { int idoffset = id - 0xB0; switch(type) { case 0x1: priv->es_map[idoffset] = VIDEO_MPEG1; break; case 0x2: priv->es_map[idoffset] = VIDEO_MPEG2; break; case 0x3: case 0x4: priv->es_map[idoffset] = AUDIO_MP2; break; case 0x0f: case 0x11: priv->es_map[idoffset] = AUDIO_AAC; break; case 0x10: priv->es_map[idoffset] = VIDEO_MPEG4; break; case 0x1b: priv->es_map[idoffset] = VIDEO_H264; break; case 0x81: priv->es_map[idoffset] = AUDIO_A52; break; } mp_dbg(MSGT_DEMUX,MSGL_V, "PSM ES, id=0x%x, type=%x, stype: %x\n", id, type, priv->es_map[idoffset]); } plen = stream_read_word(demux->stream); //length of elementary stream descriptors plen = FFMIN(plen, es_map_len); //sanity check stream_skip(demux->stream, plen); //skip descriptors for now es_map_len -= 4 + plen; } stream_skip(demux->stream, 4); //skip crc32 return 1;}// 500000 is a wild guess#define TIMESTAMP_PROBE_LEN 500000//MAX_PTS_DIFF_FOR_CONSECUTIVE denotes the maximum difference//between two pts to consider them consecutive//1.0 is a wild guess#define MAX_PTS_DIFF_FOR_CONSECUTIVE 1.0//returns the first pts found within TIME_STAMP_PROBE_LEN bytes after stream_pos in demuxer's stream.//if no pts is found or an error occurs, -1.0 is returned.//Packs are freed.static float read_first_mpeg_pts_at_position(demuxer_t* demuxer, off_t stream_pos){ stream_t *s = demuxer->stream; mpg_demuxer_t *mpg_d = demuxer->priv; float pts = -1.0; //the pts to return; float found_pts1; //the most recently found pts float found_pts2; //the pts found before found_pts1 float found_pts3; //the pts found before found_pts2 int found = 0; if(!mpg_d || stream_pos < 0) return pts; found_pts3 = found_pts2 = found_pts1 = mpg_d->last_pts; stream_seek(s, stream_pos); //We look for pts. //However, we do not stop at the first found one, as timestamps may reset //Therefore, we seek until we found three consecutive //pts within MAX_PTS_DIFF_FOR_CONSECUTIVE. while(found<3 && !s->eof && (fabsf(found_pts2-found_pts1) < MAX_PTS_DIFF_FOR_CONSECUTIVE) && (fabsf(found_pts3-found_pts2) < MAX_PTS_DIFF_FOR_CONSECUTIVE) && (stream_tell(s) < stream_pos + TIMESTAMP_PROBE_LEN) && ds_fill_buffer(demuxer->video)) { if(mpg_d->last_pts != found_pts1) { if(!found) found_pts3 = found_pts2 = found_pts1 = mpg_d->last_pts; //the most recently found pts else { found_pts3 = found_pts2; found_pts2 = found_pts1; found_pts1 = mpg_d->last_pts; } found++; } } if(found == 3) pts = found_pts3; //clean up from searching of first pts; ds_free_packs(demuxer->audio); ds_free_packs(demuxer->video); ds_free_packs(demuxer->sub); return pts;}/// Open an mpg physical streamstatic demuxer_t* demux_mpg_open(demuxer_t* demuxer) { stream_t *s = demuxer->stream; mpg_demuxer_t* mpg_d; if (!ds_fill_buffer(demuxer->video)) return 0; mpg_d = calloc(1,sizeof(mpg_demuxer_t)); if(mpg_d) { demuxer->priv = mpg_d; mpg_d->last_pts = -1.0; mpg_d->first_pts = -1.0; //if seeking is allowed set has_valid_timestamps if appropriate if(demuxer->seekable && (demuxer->stream->type == STREAMTYPE_FILE || demuxer->stream->type == STREAMTYPE_VCD) && demuxer->movi_start != demuxer-> movi_end ) { //We seek to the beginning of the stream, to somewhere in the //middle, and to the end of the stream, while remembering the pts //at each of the three positions. With these pts, we check whether //or not the pts are "linear enough" to justify seeking by the pts //of the stream //The position where the stream is now off_t pos = stream_tell(s); float first_pts = read_first_mpeg_pts_at_position(demuxer, demuxer->movi_start); if(first_pts != -1.0) { float middle_pts = read_first_mpeg_pts_at_position(demuxer, (demuxer->movi_end + demuxer->movi_start)/2); if(middle_pts != -1.0) { float final_pts = read_first_mpeg_pts_at_position(demuxer, demuxer->movi_end - TIMESTAMP_PROBE_LEN); if(final_pts != -1.0) { // found proper first, middle, and final pts. float proportion = (middle_pts-first_pts==0) ? -1 : (final_pts-middle_pts)/(middle_pts-first_pts); // if they are linear enough set has_valid_timestamps if((0.5 < proportion) && (proportion < 2)) { mpg_d->first_pts = first_pts; mpg_d->first_to_final_pts_len = final_pts - first_pts; mpg_d->has_valid_timestamps = 1; } } } } //Cleaning up from seeking in stream demuxer->stream->eof=0; demuxer->video->eof=0; demuxer->audio->eof=0; stream_seek(s,pos); ds_fill_buffer(demuxer->video); } // if ( demuxer->seekable ) } // if ( mpg_d ) return demuxer;}static void demux_close_mpg(demuxer_t* demuxer) { mpg_demuxer_t* mpg_d = demuxer->priv; if (mpg_d) free(mpg_d);}static unsigned long long read_mpeg_timestamp(stream_t *s,int c){ unsigned int d,e; unsigned long long pts; d=stream_read_word(s); e=stream_read_word(s); if( ((c&1)!=1) || ((d&1)!=1) || ((e&1)!=1) ){ ++mpeg_pts_error; return 0; // invalid pts } pts=(((uint64_t)((c>>1)&7))<<30)|((d>>1)<<15)|(e>>1); mp_dbg(MSGT_DEMUX,MSGL_DBG3," pts {%"PRIu64"}",pts); return pts;}static void new_audio_stream(demuxer_t *demux, int aid){ if(!demux->a_streams[aid]){ mpg_demuxer_t *mpg_d=(mpg_demuxer_t*)demux->priv; sh_audio_t* sh_a; new_sh_audio(demux,aid); sh_a = (sh_audio_t*)demux->a_streams[aid]; switch(aid & 0xE0){ // 1110 0000 b (high 3 bit: type low 5: id) case 0x00: sh_a->format=0x50;break; // mpeg case 0xA0: sh_a->format=0x10001;break; // dvd pcm case 0x80: if((aid & 0xF8) == 0x88) sh_a->format=0x2001;//dts else sh_a->format=0x2000;break; // ac3 } //evo files if((aid & 0xC0) == 0xC0) sh_a->format=0x2000; else if(aid >= 0x98 && aid <= 0x9f) sh_a->format=0x2001; if (mpg_d) mpg_d->a_stream_ids[mpg_d->num_a_streams++] = aid; } if(demux->audio->id==-1) demux->audio->id=aid;}static int demux_mpg_read_packet(demuxer_t *demux,int id){ int d; int len; int set_pts=0; // !=0 iff pts has been set to a proper value unsigned char c=0; unsigned long long pts=0; unsigned long long dts=0; int l; int pes_ext2_subid=-1; double stream_pts = MP_NOPTS_VALUE; demux_stream_t *ds=NULL; demux_packet_t* dp; mpg_demuxer_t *priv = (mpg_demuxer_t *) demux->priv; mp_dbg(MSGT_DEMUX,MSGL_DBG3,"demux_read_packet: %X\n",id);// if(id==0x1F0){// demux->synced=0; // force resync after 0x1F0// return -1;//}// if(id==0x1BA) packet_start_pos=stream_tell(demux->stream); if((id<0x1BC || id>=0x1F0) && id != 0x1FD) return -1; if(id==0x1BE) return -1; // padding stream if(id==0x1BF) return -1; // private2 len=stream_read_word(demux->stream); mp_dbg(MSGT_DEMUX,MSGL_DBG3,"PACKET len=%d",len);// if(len==62480){ demux->synced=0;return -1;} /* :) */ if(len==0 || len>MAX_PS_PACKETSIZE){ mp_dbg(MSGT_DEMUX,MSGL_DBG2,"Invalid PS packet len: %d\n",len); return -2; // invalid packet !!!!!! } mpeg_pts_error=0; if(id==0x1BC) { parse_psm(demux, len); return 0; } while(len>0){ // Skip stuFFing bytes c=stream_read_char(demux->stream); --len; if(c!=0xFF)break; } if((c>>6)==1){ // Read (skip) STD scale & size value// printf(" STD_scale=%d",(c>>5)&1); d=((c&0x1F)<<8)|stream_read_char(demux->stream); len-=2;// printf(" STD_size=%d",d); c=stream_read_char(demux->stream); } // Read System-1 stream timestamps: if((c>>4)==2){ pts=read_mpeg_timestamp(demux->stream,c); set_pts=1; len-=4; } else if((c>>4)==3){ pts=read_mpeg_timestamp(demux->stream,c); c=stream_read_char(demux->stream); if((c>>4)!=1) pts=0; //printf("{ERROR4}"); else set_pts = 1; dts=read_mpeg_timestamp(demux->stream,c); len-=4+1+4; } else if((c>>6)==2){ int pts_flags; int hdrlen; int parse_ext2; // System-2 (.VOB) stream: c=stream_read_char(demux->stream); pts_flags=c>>6; parse_ext2 = (id == 0x1FD) && ((c & 0x3F) == 1); c=stream_read_char(demux->stream); hdrlen=c; len-=2; mp_dbg(MSGT_DEMUX,MSGL_DBG3," hdrlen=%d (len=%d)",hdrlen,len); if(hdrlen>len){ mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: invalid header length \n"); return -1;} if(pts_flags==2 && hdrlen>=5){ c=stream_read_char(demux->stream); pts=read_mpeg_timestamp(demux->stream,c); set_pts=1; len-=5;hdrlen-=5; } else if(pts_flags==3 && hdrlen>=10){ c=stream_read_char(demux->stream); pts=read_mpeg_timestamp(demux->stream,c); set_pts=1; c=stream_read_char(demux->stream); dts=read_mpeg_timestamp(demux->stream,c); len-=10;hdrlen-=10; } len-=hdrlen; if(parse_ext2 && hdrlen>=3) { c=stream_read_char(demux->stream); hdrlen--; if((c & 0x0F) != 0x0F) { mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: pes_extension_flag2 not set, discarding pes packet\n"); return -1; } if(c & 0x80) { //pes_private_data_flag if(hdrlen<16) { mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: not enough pes_private_data bytes: %d < 16, discarding pes packet\n", hdrlen); return -1; } stream_skip(demux->stream, 16); hdrlen-=16; } if(c & 0x40) { //pack_header_field_flag int l = stream_read_char(demux->stream); if(l < 0) //couldn't read from the stream? return -1; hdrlen--; if(l < 0 || hdrlen < l) { mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: not enough pack_header bytes: hdrlen: %d < skip: %d, discarding pes packet\n", hdrlen, l); return -1; } stream_skip(demux->stream, l); hdrlen-=l; } if(c & 0x20) { //program_packet_sequence_counter_flag if(hdrlen < 2) { mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: not enough program_packet bytes: hdrlen: %d, discarding pes packet\n", hdrlen); return -1; } stream_skip(demux->stream, 2); hdrlen-=2; } if(c & 0x10) { //STD stream_skip(demux->stream, 2); hdrlen-=2; } c=stream_read_char(demux->stream); //pes_extension2 flag hdrlen--; if(c!=0x81) { mp_msg(MSGT_DEMUX,MSGL_V,"demux_mpg: unknown pes_extension2 format, len is > 1 \n"); return -1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -