📄 mpgparse.cpp
字号:
#include "mpgparse.h"#include <memory.h>#include <stdio.h>#if 0#ifdef _WIN32#include <windows.h>static void debug_outs (char *szMsg, ...){ char szBuf[256]; wvsprintf (szBuf, szMsg, (LPSTR)(&szMsg + 1)); OutputDebugString (szBuf);}#else#include <stdio.h>#define debug_outs printf#endifstatic void debug_break (void){}#define ASSERT(exp) ((void)((exp)?1:(debug_outs ("ASSERT failed: line %d, file %s\n", __LINE__,__FILE__), debug_break(), 0)))#define DEBUGMSG(cond,printf_exp) ((void)((cond)?(debug_outs printf_exp),1:0))#else#define ASSERT(exp)#define DEBUGMSG(cond,printf_exp)#endif#define MPG_MIN(X,Y) (((X)<=(Y))?(X):(Y)) // to find the min between two number.static void MPEG1DemuxCallback (RMuint8 *pData, RMuint32 Length, RMuint8 PacketId, RMint64 Scr, RMint64 Pts, RMint64 Dts, RMuint8 Flags, RMint64 offset, void *Context){ MPEGDemux *pDemux = (MPEGDemux *)Context; ASSERT (pDemux); pDemux->MPEG1DemuxCallback (pData, Length, PacketId, Scr, Pts, Dts, Flags, offset);}static void PESDemuxCallback (RMuint8 *pData, RMuint32 Length, RMuint8 StreamId, RMuint8 SubStreamId, RMint64 Scr, RMint64 Pts, RMint64 Dts, RMuint8 Flags, RMint64 offset, void *Context){ MPEGDemux *pDemux = (MPEGDemux *)Context; ASSERT (pDemux); pDemux->PESDemuxCallback (pData, Length, StreamId, SubStreamId, Scr, Pts, Dts, Flags, offset);}static void AC3DTSInfoCallback (RMuint8 numberOfFrameHeaders, RMuint16 firstAccessUnitPointer, void *context){ MPEGDemux *pDemux = (MPEGDemux *)context; ASSERT (pDemux); pDemux->AC3DTSInfoCallback (numberOfFrameHeaders, firstAccessUnitPointer);}static void LPCMInfoCallback (RMuint8 numberOfFrameHeaders, RMuint16 firstAccessUnitPointer, RMuint32 frequency, RMuint8 numberOfChannels, RMuint8 quantizationWordLength, void *context){ MPEGDemux *pDemux = (MPEGDemux *)context; ASSERT (pDemux); pDemux->LPCMInfoCallback (numberOfFrameHeaders, firstAccessUnitPointer, frequency, numberOfChannels, quantizationWordLength);}MPEGDemux::MPEGDemux (){ m_file = 0;}MPEGDemux::~MPEGDemux (){ if (m_file) m_CallbackTable.fclose (m_file, m_CallbackTable.context);}// call this after creationMPG_DEMUX_ERROR MPEGDemux::Init (){ // initialize internal mpeg demux callbacks // initialize any internal variables m_MPEG1Demux.m_Callback = ::MPEG1DemuxCallback; m_MPEG1Demux.m_Context = this; m_ProgramDemux.m_Callback = ::PESDemuxCallback; m_ProgramDemux.m_ac3dtsCallback = ::AC3DTSInfoCallback; m_ProgramDemux.m_lpcmCallback = ::LPCMInfoCallback; m_ProgramDemux.m_Context = this; m_ProgramDemux.m_IsDVDStream = 0; m_MPEG1Demux.Reset (); m_MPEG1Demux.ResetState (); m_ProgramDemux.Reset (); m_ProgramDemux.ResetState (); m_DemuxType = 0; if (m_file) m_CallbackTable.fclose (m_file, m_CallbackTable.context); m_file = 0; m_CurrentPosition = 0; m_IFrameState = 0; m_BytesToSkip = 0; m_IFrameSubState = 0; m_StartPosition = 0; m_ReverseErrorLoop = 0; return MPG_DEMUX_ERROR_NOT_IMPLEMENTED;}// call this to initalize the required callbacksMPG_DEMUX_ERROR MPEGDemux::InitCallbackTable (MPG_CALLBACK_TABLE *pCallbackTable){ memcpy (&m_CallbackTable, pCallbackTable, sizeof (MPG_CALLBACK_TABLE)); return MPG_DEMUX_ERROR_NO_ERROR;}// call this to identify the file typeMPG_DEMUX_ERROR MPEGDemux::DecodeFile (RMint8 *filename){ // try to detect if the file is a valid mpeg file if (IsTransportStream (filename)) { // no support for transport streams DEBUGMSG (1, ("error: no support for transport streams\n")); return MPG_DEMUX_ERROR_NOT_AN_MPEG_FILE; } if (IsDVDStream (filename)) { DEBUGMSG (1, ("DVD stream\n")); m_DemuxType = DVD_DEMUX; m_ProgramDemux.m_IsDVDStream = 1; } else if (IsProgramStream (filename)) { DEBUGMSG (1, ("MPEG 2 program stream\n")); m_DemuxType = PROGRAM_DEMUX; } else if (IsMPEG1SystemStream (filename)) { DEBUGMSG (1, ("MPEG 1 system stream\n")); m_DemuxType = SYSTEM_DEMUX; if (IsVCDStream (filename)) { DEBUGMSG (1, ("DAT file\n")); m_DemuxType = DAT_DEMUX; } } else if (IsMPEG124ElementaryStream (filename, &m_mpeg12)) { m_DemuxType = NO_DEMUX; } else { DEBUGMSG (1, ("error: MPG_DEMUX_ERROR_NOT_AN_MPEG_FILE\n")); return MPG_DEMUX_ERROR_NOT_AN_MPEG_FILE; } ASSERT (m_file == 0); m_file = m_CallbackTable.fopen (filename, m_CallbackTable.context); ASSERT (m_file); return MPG_DEMUX_ERROR_NO_ERROR;}RMuint32 MPEGDemux::GetDemuxType (void){ return m_DemuxType;}RMuint32 MPEGDemux::AssumeDVDPacketStructure (RMint32 followdvd){ if ((m_DemuxType == PROGRAM_DEMUX) || (m_DemuxType == DVD_DEMUX)) { if (followdvd) m_DemuxType = DVD_DEMUX; else m_DemuxType = PROGRAM_DEMUX; m_ProgramDemux.m_IsDVDStream = followdvd; } return 0;}// call this repeatly to do workMPG_DEMUX_ERROR MPEGDemux::Schedule (){ RMuint8 *p, *ptemp; RMint32 l, temp, readlength; // get a buffer an demux it if (m_CallbackTable.getMPG (&p, (RMuint32 *)&l, m_CallbackTable.context) == MPG_DEMUX_ERROR_NO_ERROR) { ASSERT (m_file); if (m_DemuxType == DAT_DEMUX) l -= (l % 2352); ASSERT (l); l = m_CallbackTable.fread (m_file, p, l, m_CallbackTable.context); readlength = l; if (l) { // demux it m_CallbackTable.addref (p, m_CallbackTable.context); ptemp = p; while (l) { ASSERT (l > 0); temp = MPG_MIN (l, 32768); DEBUGMSG (0, ("demux (%d)\n", temp)); demux (ptemp, temp); ptemp += temp; l -= temp; m_CurrentPosition += temp; if (m_BytesToSkip) { if (m_IFrameSubState == MPG_FOUND_2ND_PICTURE_HEADER) { m_MPEG1Demux.Reset (); m_ProgramDemux.Reset (); if (m_BytesToSkip > 0) { if (l < m_BytesToSkip) { m_BytesToSkip -= l; m_CurrentPosition += m_BytesToSkip; DEBUGMSG (1, ("skipping %d bytes to %d\n", (RMint32)m_BytesToSkip, (RMint32)m_CurrentPosition)); m_CallbackTable.fseek (m_file, (RMint32)m_CurrentPosition, SEEK_SET, m_CallbackTable.context); } else { l -= m_BytesToSkip; ptemp += m_BytesToSkip; } } else { if (m_IFramePosition != -1) { m_CurrentPosition = m_IFramePosition + m_BytesToSkip; if (m_CurrentPosition <= 0) { DEBUGMSG (1, ("(1) play backward hit start of file.\n")); m_CallbackTable.fseek (m_file, 0, SEEK_SET, m_CallbackTable.context); m_CallbackTable.release (p, m_CallbackTable.context); return MPG_DEMUX_ERROR_FILE_DONE; } } else { ASSERT (0); m_StartPosition -= 1024 * 128; if (m_StartPosition < 0) { DEBUGMSG (1, ("(2) play backward hit start of file.\n")); m_CallbackTable.fseek (m_file, 0, SEEK_SET, m_CallbackTable.context); m_CallbackTable.release (p, m_CallbackTable.context); return MPG_DEMUX_ERROR_FILE_DONE; } m_CurrentPosition = m_StartPosition; } } l = 0; m_IFrameState = 0; m_IFrameSubState = MPG_LOOKING_FOR_SEQUENCE_HEADER; DEBUGMSG (1, ("seeking to %d\n", (RMint32)m_CurrentPosition)); m_CallbackTable.fseek (m_file, (RMint32)m_CurrentPosition, SEEK_SET, m_CallbackTable.context); } else if (m_IFrameSubState == MPG_ERROR_SAME_IFRAME_DURING_REVERSE) { ASSERT (m_IFramePosition != -1); m_CurrentPosition = m_IFramePosition + m_BytesToSkip - 1024*32*m_ReverseErrorLoop; if (m_CurrentPosition < 0) { DEBUGMSG (1, ("(3) play backward hit start of file.\n")); m_CallbackTable.fseek (m_file, 0, SEEK_SET, m_CallbackTable.context); m_CallbackTable.release (p, m_CallbackTable.context); return MPG_DEMUX_ERROR_FILE_DONE; } l = 0; m_ReverseErrorLoop++; DEBUGMSG (1, ("m_ReverseErrorLoop = %d\n", (RMint32)m_ReverseErrorLoop)); m_IFrameState = 0; m_IFrameSubState = MPG_LOOKING_FOR_SEQUENCE_HEADER; DEBUGMSG (1, ("seeking to %d\n", (RMint32)m_CurrentPosition)); m_CallbackTable.fseek (m_file, (RMint32)m_CurrentPosition, SEEK_SET, m_CallbackTable.context); } } } m_CallbackTable.release (p, m_CallbackTable.context); } else return MPG_DEMUX_ERROR_FILE_DONE; } return MPG_DEMUX_ERROR_NOT_IMPLEMENTED;}MPG_DEMUX_ERROR MPEGDemux::SendIFramesOnly (RMint32 BytesToSkip){ DEBUGMSG (1, ("MPEGDemux::SendIFramesOnly (%d)\n", BytesToSkip)); if (m_BytesToSkip) { if (m_IFramePosition != -1) { DEBUGMSG (1, ("seeking to %d\n", (RMint32)m_IFramePosition)); m_CallbackTable.fseek (m_file, (RMint32)m_IFramePosition, SEEK_SET, m_CallbackTable.context); } } // reset i frame state m_IFrameState = 0; m_IFrameSubState = MPG_LOOKING_FOR_SEQUENCE_HEADER; m_IFramePosition = -1; m_BytesToSkip = BytesToSkip; m_MPEG1Demux.Reset (); m_ProgramDemux.Reset (); if (m_DemuxType == DVD_DEMUX) m_ProgramDemux.m_IsDVDStream = 1; m_StartPosition = m_CurrentPosition; return MPG_DEMUX_ERROR_NO_ERROR;}//////////////////////////////////////////////////////////////////#define TRANSPORT_BLOCK_LENGTH (1024*16)RMint32 MPEGDemux::IsTransportStream (RMint8 *filename){ DEBUGMSG (1, ("IsTransportStream (%s)\n", filename)); // we will consider this a transport stream if we find at leat 5 sync words (0x47) // in the first TRANSPORT_BLOCK_LENGTH bytes of the file RMint32 IsTransport = 0, n, i; RMuint32 file = m_CallbackTable.fopen (filename, m_CallbackTable.context); if (file == 0) return 0; RMuint8 *pBuffer = 0; RMuint32 length = TRANSPORT_BLOCK_LENGTH; m_CallbackTable.getDetectionBuffer (&pBuffer, &length, m_CallbackTable.context); ASSERT (pBuffer); if (pBuffer) { n = m_CallbackTable.fread (file, pBuffer, TRANSPORT_BLOCK_LENGTH, m_CallbackTable.context); RMuint8 *p = pBuffer; RMint32 sync_counter = 0; RMint32 sync_distance = 0; for (i=0; i<(n - 188); i++) { if (*p == 0x47) { if ((sync_distance == 0) || (sync_distance == 187)) sync_counter++; sync_distance = 0; } else sync_distance++; p++; } if (sync_counter >= 5) { IsTransport = 1; } } m_CallbackTable.fclose (file, m_CallbackTable.context); return IsTransport;}#define PROGRAM_BLOCK_LENGTH (1024*4)RMint32 MPEGDemux::IsProgramStream (RMint8 *filename){ DEBUGMSG (1, ("IsProgramStream (%s)\n", filename)); // we will consider this a program stream if we find 0x000001BA in the first // PROGRAM_BLOCK_LENGTH bytes of the file. // Then we will try to detect the pack as a MPEG-2 Pack RMint32 IsProgram = 0, n, i; RMuint32 file = m_CallbackTable.fopen (filename, m_CallbackTable.context); if (file == 0) return 0; RMuint8 *pBuffer = 0; RMuint32 length = PROGRAM_BLOCK_LENGTH; m_CallbackTable.getDetectionBuffer (&pBuffer, &length, m_CallbackTable.context); ASSERT (pBuffer); if (pBuffer) { RMint32 j; j = 0; while (j++ < 64) { n = m_CallbackTable.fread (file, pBuffer, PROGRAM_BLOCK_LENGTH, m_CallbackTable.context); RMuint8 *p = pBuffer; for (i=0; i<(n - 5); i++) { if ((p[0] == 0x00) && (p[1] == 0x00) && (p[2] == 0x01) && (p[3] == 0xBA)) { // for program streams, after the pack start code, // 01b must be present - this is how we can tell the // difference between mpeg-2 program streams and mpeg-1 system streams if ((p[4] & 0xC0) == 0x40) { IsProgram = 1; j = 64; } break; } p++; } } } m_CallbackTable.fclose (file, m_CallbackTable.context); return IsProgram;}#define MPEG1SYSTEM_BLOCK_LENGTH (1024*4)RMint32 MPEGDemux::IsMPEG1SystemStream (RMint8 *filename){ DEBUGMSG (1, ("IsMPEG1SystemStream (%s)\n", filename)); // we will consider this a program stream if we find 0x000001BA in the first // PROGRAM_BLOCK_LENGTH bytes of the file. // Then we will try to detect the pack as a MPEG-1 Pack RMint32 IsSystem = 0, n, i; RMuint32 file = m_CallbackTable.fopen (filename, m_CallbackTable.context); if (file == 0) return 0; RMuint8 *pBuffer = 0; RMuint32 length = PROGRAM_BLOCK_LENGTH; m_CallbackTable.getDetectionBuffer (&pBuffer, &length, m_CallbackTable.context); ASSERT (pBuffer); if (pBuffer) { n = m_CallbackTable.fread (file, pBuffer, MPEG1SYSTEM_BLOCK_LENGTH, m_CallbackTable.context); RMuint8 *p = pBuffer; for (i=0; i<(n - 5); i++) { if ((p[0] == 0x00) && (p[1] == 0x00) && (p[2] == 0x01) && (p[3] == 0xBA)) { // for system streams, after the pack start code, // 0010b must be present - this is how we can tell the // difference between mpeg-2 program streams and mpeg-1 system streams if ((p[4] & 0xf0) == 0x20) IsSystem = 1; break; } p++; } } m_CallbackTable.fclose (file, m_CallbackTable.context); return IsSystem;}#define DVD_BLOCK_LENGTH (2048*5)RMint32 MPEGDemux::IsDVDStream (RMint8 *filename){ DEBUGMSG (1, ("IsDVDStream (%s)\n", filename)); // we consider this a DVD stream if there are PACK start codes 2048 bytes // apart AND the first pack is a Navigation pack RMint32 IsDVD = 0, n; RMuint32 file = m_CallbackTable.fopen (filename, m_CallbackTable.context); if (file == 0) return 0; RMuint8 *pBuffer = 0; RMuint32 length = DVD_BLOCK_LENGTH; m_CallbackTable.getDetectionBuffer (&pBuffer, &length, m_CallbackTable.context); ASSERT (pBuffer); if (pBuffer) { DEBUGMSG (1, ("IsDVDStream: m_CallbackTable.fread\n")); n = m_CallbackTable.fread (file, pBuffer, DVD_BLOCK_LENGTH, m_CallbackTable.context); RMuint8 *p1 = pBuffer + 0; RMuint8 *p2 = pBuffer + 2048; RMuint8 *p3 = pBuffer + 4096; RMuint8 *p4 = pBuffer + 6144; RMuint8 *p5 = pBuffer + 8192; IsDVD = 1; // must be 5 packs seperated by 2k each if (!( (p1[0] == 0x00) &&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -