home *** CD-ROM | disk | FTP | other *** search
- /*
- * Demux.cpp
- *
- * Copyright (C) Alberto Vigata - January 2000 - ultraflask@yahoo.com
- *
- * This file is part of FlasKMPEG, a free MPEG to MPEG/AVI converter
- *
- * FlasKMPEG 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, or (at your option)
- * any later version.
- *
- * FlasKMPEG 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 GNU Make; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-
- #include "demux.h"
- #include "assert.h"
-
- CDemux::CDemux()
- {
- inp = NULL;
- strHandle = NULL;
- }
-
-
-
- CDemux::~CDemux()
- {
- }
-
- // The Mism is handled externally. Just get it.
- int CDemux::SetInput(LPTWorkingMism pMismInfo)
- {
- inp = pMismInfo;
- strHandle = pMismInfo->handle;
- mismReadStream = pMismInfo->pMismInfo->ReadStream;
- return 1;
- }
-
-
- int inline CDemux::ReadStream(ui8 *buf, unsigned int size)
- {
- return mismReadStream( strHandle, buf, size );
- }
-
- i64 inline CDemux::getSCR()
- {
- if(isMPEG2)
- return ((SCRbase*300)+SCRext);
- else
- return SCR*300;
- }
-
- i64 inline CDemux::getPTS()
- {
- return PTS*300;
- }
-
-
- inline void CDemux::ResetPTS()
- {
- PTS=0;
- }
- inline void CDemux::ResetSCR()
- {
- SCR=0;
- }
-
-
- int CDemux::AlignPack(){
- int val=1;
-
- while( (inbuf[0]!=0x00 || inbuf[1]!=0x00 || inbuf[2]!=0x01 || inbuf[3]!=0xBA) && val){
- inbuf[0]=inbuf[1];
- inbuf[1]=inbuf[2];
- inbuf[2]=inbuf[3];
- val=ReadStream(&inbuf[3],1);
- }
- //Align
- if(val)
- {
- SetStreamPos( GetStreamPos() - 4 );
- }
- return val;
- }
-
-
- bool inline CDemux::GetBackByte( ui8 *byte )
- {
- // If the chunk is finished, grab previous one
- // this will position the pointer at the end
- // of the chunk
- if( m_nPosEngPtr <= 0)
- {
- if(!GrabPrevChunk())
- return false;
- }
- m_nPosEngPtr--;
- *byte = *( m_pPosEngBfr + m_nPosEngPtr );
-
- return true;
- }
-
- bool inline CDemux::GetForWord( ui16 *word )
- {
- ui8 byte;
-
- *word = 0;
-
- bool bSuccess = GetFordByte( &byte );
- *word = byte << 8;
- if( bSuccess )
- bSuccess = GetFordByte( &byte );
- *word |= byte;
-
- return bSuccess;
- }
-
- bool inline CDemux::GetFordDWord( ui32 *dword )
- {
- ui8 byte;
-
- *dword = 0;
-
- bool bSuccess = GetFordByte( &byte );
- *dword = byte << 24;
- if( bSuccess )
- bSuccess = GetFordByte( &byte );
- *dword |= byte << 16;
- if( bSuccess )
- bSuccess = GetFordByte( &byte );
- *dword |= byte << 8;
- if( bSuccess )
- bSuccess = GetFordByte( &byte );
- *dword |= byte;
-
- return bSuccess;
- }
-
- bool inline CDemux::GetFordByte( ui8 *byte )
- {
- // If not enough data in the chunk get next one.
- // This will position the pointer at the beginning
- if( m_nPosEngPtr>=(i32)m_nPosEngSize )
- {
- if(!GrabNextChunk())
- return false;
- }
- // GetFordByte moves the file pointer. Get it back again.
- *byte = *( m_pPosEngBfr + m_nPosEngPtr );
- m_nPosEngPtr++;
-
- return true;
- }
-
- ui64 CDemux::GetSyncPoint(ui64 pos)
- {
- int i;
- ui32 nTargetStartCode = 0x000001BA;
- ui64 nSyncPos;
- ui8 pPackHdr[6],bByte;
- bool isMPEG2;
- bool bFound = false;
- bool bSuccess = true;
-
- SetStreamPos(pos);
-
- StartPositioningEngine();
-
- // Look for previous startcode from this position
- while( !bFound && bSuccess )
- {
- ui32 nStartCode = 0xFFFFFFFF;
- while( nStartCode!=nTargetStartCode && bSuccess )
- {
- bSuccess = GetBackByte(&bByte);
- nStartCode = (nStartCode>>8) | (bByte<<24);
- }
- if( bSuccess )
- {
- // We found a suitable startcode and we're aligned with it.
- // Make sure this is a pack start code
- GetFordDWord(&nStartCode);
- // Get next 6 bytes
- for(i=0; i<6; i++)
- GetFordByte( &pPackHdr[i] );
- // now, do the checking
- isMPEG2 = pPackHdr[0]>>6 == 1;
- if(isMPEG2)
- {
- if(!IS_MPEG2PACKHEADER(pPackHdr))
- continue;
- }
- else
- {
- if(!IS_MPEG1PACKHEADER(pPackHdr))
- continue;
- }
-
- // If we are here, the pack was Ok.
- bSuccess = true;
- nSyncPos = GetPosEngPos() - 10;
- break;
- }
- }
- StopPositioningEngine();
- if(bSuccess)
- return nSyncPos;
- else
- return GetStreamPos();
- }
-
- // This function sets the file pointer
- // to the beginning of the previous PES
- // with the indicated id. It also updates
- // pack_bytes accordingly.
- bool CDemux::RewindPreviousPES( ui8 nStreamID )
- {
- bool bFound = false;
- bool bSuccess = true;
- ui8 bByte;
- ui32 nTargetStartCode = 0x00000100 | nStreamID;
- ui64 nPESBeginPos, nPackBeginPos;
- ui16 nPesLength;
-
- StartPositioningEngine();
-
- // Look for previous startcode from this position
- while( !bFound && bSuccess )
- {
- ui32 nStartCode = 0xFFFFFFFF;
- while( nStartCode!=nTargetStartCode && bSuccess )
- {
- bSuccess = GetBackByte(&bByte);
- nStartCode = (nStartCode>>8) | (bByte<<24);
- }
- if( bSuccess )
- {
- // We found a suitable startcode and we're aligned with it
- // Now look backward in search for the beginning of the pack
- nPESBeginPos = GetPosEngPos();
- // Make a little checking to make
- // sure this is a PES
- GetFordDWord( &nStartCode );
- GetForWord( &nPesLength );
- // Jump to the end of the PES
- SetPosEngPos( nPESBeginPos + nPesLength + 6);
- // Now we must be aligned with a start code prefix
- GetFordDWord( &nStartCode );
- // Return to the original location
- SetPosEngPos( nPESBeginPos );
- if( (nStartCode >> 8) != 1 )
- // No start code. Continue parsing.
- continue;
-
-
- nTargetStartCode = 0x000001BA;
- nStartCode = 0xFFFFFFFF;
- while( nStartCode!=nTargetStartCode && bSuccess )
- {
- bSuccess = GetBackByte(&bByte);
- nStartCode = (nStartCode>>8) | (bByte<<24);
- }
- if( bSuccess )
- {
- // We found the beginning of the Pack.
- nPackBeginPos = GetPosEngPos();
- // Guess if this is an MPEG1 or 2 stream
- // parse start code
- GetFordDWord( &nStartCode );
- GetFordByte( &bByte );
- isMPEG2 = bByte>>6 == 1;
-
- // Pack bytes
- pack_bytes = (ui32)(nPESBeginPos - nPackBeginPos + 4);
- // Restore position of the stream to the beginning
- // of the PES
- SetPosEngPos( nPESBeginPos );
- bFound = true;
- }
- }
- }
-
- StopPositioningEngine();
- return bSuccess;
- }
-
- bool CDemux::ReadPES(unsigned char **buffer, PESinfo *PES)
- {
- ui8 stream_id,substream_id;
- ui16 packet_lenght,header_lenght,bytes_read;
-
-
- //Read prefix
- PTS = 0;
- PES->pack_header_parsed = false;
- start:
- PES->payloadSize = 0xFF;
-
-
- if(!ReadStream(inbuf, 3)){
- PES->payloadSize=0xFF;
- return false;
- }
- // 3 bytes were read succesfully
- pack_bytes += 3;
- stream_id=0;
- substream_id=0;
- if(inbuf[0]!=0 || inbuf[1]!=0 || inbuf[2]!=1){
- AlignPack(); //Stream is not pack aligned
- goto start;
- }
-
- pack_bytes += ReadStream(&stream_id, 1); //Read packet identifier
-
- if(stream_id==(ui8)PACK_ID){ //We've got a PACK
-
- PES->pack_header_parsed = true;
- //Retrieve SCR and muxrate
- //Identify either MPEG1 or MPEG2 stream
-
- pack_bytes += ReadStream(inbuf, 1);
- if( (inbuf[0] & 0xC0)==0x40 ){ //MPEG2 program stream '01'
- isMPEG2=true;
- ReadStream(&inbuf[1], 9); //Read pack header
-
- SCRbase = GET_SCRBASE(inbuf);
- SCRext = GET_SCREXT(inbuf);
- muxRate = GET_MPEG2MUXRATE(inbuf);
- //number of bytes after the byte
- //containing last bit of SCRBASE field
- pack_bytes = 5;
-
- //parse stuffing bytes
- pack_bytes += ReadStream(inbuf, GET_MPEG2STUFFING(inbuf));
- //printf("Pack parsed. \n");
- }
- else if ((inbuf[0] & 0xF0)==0x20 ){
- isMPEG2=false;
-
- ReadStream(&inbuf[1], 7); //Read pack header
-
- SCR = GET_SCR(inbuf);
- muxRate = GET_MPEG1_MUXRATE(inbuf);
- pack_bytes = 3;
- //printf("Pack parsed. \n");
- }
- else{ //arghh, there's something wrong with this stream
- AlignPack();
- goto start;
- }
- goto start; //Parse next PES or whatever
- }
-
- if( stream_id >= 0xC0 && stream_id <= 0xEF || stream_id==PRIVATE_STREAM_1)
- { //MPEG AUDIO or MPEG video or PRIVATE stream
- if(isMPEG2)
- {
- pack_bytes += ReadStream(inbuf, 5);
- packet_lenght = GET_MPEG_PACKET_LENGHT(inbuf);
- header_lenght= inbuf[4];
- if(GET_MPEG2_PTS_FLAGS(inbuf)&0x02)
- { //if a PTS stamp is present in this PES
- pack_bytes += ReadStream(inbuf, 5);
- PTS=GET_MPEG2_PTS(inbuf);
- //Read the whole PES
- pack_bytes += ReadStream(inbuf, packet_lenght-8); //PES_packet_lenght - 'parsed data'
- *buffer= (unsigned char *)&inbuf[header_lenght - 5] ; //Set payload base
- }
- else
- {
- PTS=0;
- pack_bytes += ReadStream(inbuf, packet_lenght-3); //PES_packet_lenght - 'parsed data'
- *buffer= (unsigned char *)&inbuf[header_lenght]; //Set payload base
- }
- PES->payloadSize= packet_lenght - 3 - header_lenght;
-
- if(stream_id==PRIVATE_STREAM_1)
- {
- substream_id=**buffer;
- (*buffer)+=1; //AC3 ident bytes
- PES->payloadSize-=1;
- if(substream_id >= 0x20 && substream_id < 0x40)
- {
- /* subpic; don't do anything special. the subpic code needs
- * everything else */
- } else {
- /* AC3 (or something else which we don't care about): skip an
- * extra 3 bytes */
- (*buffer)+=3; //AC3 ident bytes
- PES->payloadSize-=3;
- }
- }
- else
- substream_id=0;
-
- }
- else{
- //MPEG1
- pack_bytes += ReadStream(inbuf, 2);
- packet_lenght = GET_MPEG_PACKET_LENGHT(inbuf);
- bytes_read=0;
-
- // Rule: a byte has been read when a field begins
- // Read first byte
- pack_bytes += ReadStream(inbuf, 1);
- bytes_read++;
-
- while(inbuf[0]&0x80)
- { //Parse stuffing bytes
- pack_bytes += ReadStream(inbuf, 1);
- bytes_read++;
- }
-
- if( (inbuf[0]&0xC0) ==0x40 ){
- // Finish parsing STD field
- /*pack_bytes += ReadStream(inbuf, 1); //STD_buffer_scale & size
- bytes_read++;
- // Read next byte
- pack_bytes += ReadStream(inbuf, 1); //STD_buffer_scale & size
- bytes_read++;*/
- pack_bytes += ReadStream(inbuf, 2); //STD_buffer_scale & size
- bytes_read+=2;
- inbuf[0] = inbuf[1];
- }
-
- PTS=0;
- switch ((inbuf[0]&0xF0))
- {
- case 0x20:
- pack_bytes += ReadStream(&inbuf[1], 4);
- bytes_read+=4;
- PTS=GET_MPEG1_PTS(inbuf);
- break;
- case 0x30:
- pack_bytes += ReadStream(&inbuf[1], 9);
- bytes_read+=9;
- PTS=GET_MPEG1_PTS(inbuf);
- break;
- default:
- PTS=0;
- }
- /*
- if( (inbuf[0]&0xF0) ==0x20 ){ //PTS stamp present
- pack_bytes += ReadStream(&inbuf[1], 4);
- bytes_read+=4;
- PTS=GET_MPEG1_PTS(inbuf);
- }
- else if( (inbuf[0]&0xF0) ==0x30 ){ //PTS & DTS stamp present
- pack_bytes += ReadStream(&inbuf[1], 9);
- bytes_read+=9;
- PTS=GET_MPEG1_PTS(inbuf);
- }
- // else '00001111' 0x0F
- else{
- PTS=0;
- }*/
- PES->payloadSize= packet_lenght - bytes_read;
-
- pack_bytes += ReadStream(inbuf, PES->payloadSize);
- *buffer= (unsigned char *)&inbuf[0];
- }
- }
- else{
- //Fetch PES length
- pack_bytes += ReadStream(inbuf, 2);
- pack_bytes += ReadStream(inbuf, GET_UINT16(inbuf));
- }
-
- PES->muxrate = muxRate;
- PES->PTS = getPTS();
- PES->SCR = getSCR();
- PES->streamID = stream_id;
- PES->subStreamID = substream_id;
- PES->pack_bytes = pack_bytes;
- return true;
- }
-
-
- i64 CDemux::GetTime()
- {
- return getSCR();
- }
-
- bool CDemux::ReadLPES(unsigned char **buffer, PESinfo *pInfo)
- {
- bool ret;
- i64 nextSCR;
-
- ret=ReadPES(buffer, pInfo);
- if(!ret)
- return ret;
-
- if(firstTime){
- delta = 0 - pInfo->SCR;
- firstTime = false;
- }
- else{
- // Handle clock disruption
- if((lastSCR >= pInfo->SCR) &&
- pInfo->pack_header_parsed )
- {
- // work out the time of the last byte of the previous pack
- nextSCR = lastSCR +
- (int)(( (double)(lastPackBytes + 1)
- / (double)(lastMuxRate *50 ) )
- *(double)MPEG2_CLK_REF);
-
- // update delta
- // delta = OUR_CLK - STREAM_CLK
- // OUR_CLK = STREAM_CLK + delta
- delta = (nextSCR + delta) - pInfo->SCR;
- }
- }
- lastSCR = pInfo->SCR;
- lastPackBytes = pInfo->pack_bytes;
- lastMuxRate = pInfo->muxrate;
-
- pInfo->SCR = pInfo->SCR + delta;
- pInfo->PTS = pInfo->PTS ? (pInfo->PTS + delta) : 0;
-
-
-
- return ret;
- }
-
- bool CDemux::SetStreamPos(ui64 pos)
- {
- return inp->pMismInfo->SetStreamPos(strHandle, pos)==FM_MISM_OK;
- }
-
- ui64 CDemux::GetStreamPos()
- {
- ui64 pos;
- inp->pMismInfo->GetStreamPos(strHandle, &pos);
- return pos;
- }
-
- char *CDemux::GetFileName()
- {
- return inp->pMismInfo->GetFileName(strHandle);
- }
-
- ui64 CDemux::GetStreamSize()
- {
- return inp->pMismInfo->GetStreamSize(strHandle);
- }
-
- void CDemux::StartReadLPES()
- {
- // ReadLPES variables
- delta = 0;
- lastSCR = 0;
- lastPackBytes = 0;
- lastMuxRate = 0;
- firstTime = true;
- return;
- }
-