--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mmserv/thumbnailengine/TneProcessorSrc/mp4demux.cpp Tue Feb 02 01:08:46 2010 +0200
@@ -0,0 +1,722 @@
+/*
+* Copyright (c) 2001 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+
+
+// INCLUDE FILES
+#include "videoprocessorimpl.h"
+#include "statusmonitor.h"
+#include "activequeue.h"
+#include "dataprocessor.h"
+#include "mp4demux.h"
+#include "mp4parser.h"
+
+// ASSERTIONS
+#define DASSERT(x) __ASSERT_DEBUG(x, User::Panic(_L("CVideoPlayer"), EInternalAssertionFailure))
+
+// LOCAL HELPER MACROS
+// Debug printing, define DEBUGPRINT to get output
+//#define DEBUGPRINT
+#ifdef _DEBUG
+#include <e32svr.h>
+#define PRINT(x) RDebug::Print x;
+#else
+#define PRINT(x)
+#endif
+
+// LOCAL CONSTANTS
+const TUint KAudioReadAheadTimeMs = 100;
+//const TUint KMaxMsInQueue = 600;
+const TUint KMaxMsInQueue = 300;
+const TUint KMaxBytesPerRun = 4096;
+const TUint KMaxBlocksInQueue = 16;
+
+
+// MEMBER FUNCTIONS
+
+
+//=============================================================================
+
+// MODULE DATA STRUCTURES
+//enum ?declaration
+//typedef ?declaration
+
+// LOCAL FUNCTION PROTOTYPES
+// ?type ?function_name( ?arg_type, ?arg_type );
+
+// ==================== LOCAL FUNCTIONS ====================
+
+// ================= MEMBER FUNCTIONS =======================
+
+
+// ---------------------------------------------------------
+// CMP4Demux::NewL
+// Symbian two-phased constructor.
+// ---------------------------------------------------------
+//
+CMP4Demux* CMP4Demux::NewL(CActiveQueue *anInputQueue,
+ TUint aNumChannels, TOutputChannel *aOutputChannels,
+ TStreamParameters *aParameters,
+ CStatusMonitor *aStatusMonitor,
+ CMP4Parser *aParser,
+ TInt aPriority)
+{
+
+ CMP4Demux *self = new (ELeave) CMP4Demux(anInputQueue,
+ aNumChannels,
+ aOutputChannels,
+ aParameters,
+ aStatusMonitor,
+ aParser,
+ aPriority);
+
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+
+ return self;
+
+
+}
+
+// ---------------------------------------------------------
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// ---------------------------------------------------------
+//
+CMP4Demux::CMP4Demux(CActiveQueue *anInputQueue,
+ TUint aNumChannels, TOutputChannel *aOutputChannels,
+ TStreamParameters *aParameters,
+ CStatusMonitor *aStatusMonitor,
+ CMP4Parser *aParser,
+ TInt aPriority)
+ : CDemultiplexer(aPriority)
+ {
+ // Remember the objects
+ iInputQueue = anInputQueue;
+ iMonitor = aStatusMonitor;
+ iParser = aParser;
+
+ iPicturePeriodMs = aParameters->iPicturePeriodMs;
+ iAudioFramesInSample = aParameters->iAudioFramesInSample;
+
+ // Remember the channels and mux table
+ iNumOutputChannels = aNumChannels;
+ iOutputChannels = aOutputChannels;
+ }
+
+// EPOC default constructor can leave.
+void CMP4Demux::ConstructL()
+ {
+ TUint i;
+
+ // Set as a reader to the input queue
+ if ( iInputQueue )
+ {
+ iInputQueue->SetReader(this, NULL);
+ iReaderSet = ETrue;
+ }
+
+ // Set as writer to the output queues
+ for ( i = 0; i < iNumOutputChannels; i++ )
+ iOutputChannels[i].iTargetQueue->SetWriter(this, NULL);
+ iWriterSet = ETrue;
+
+ // Add us to active scheduler
+ CActiveScheduler::Add(this);
+
+ iBytesDemuxed = 0;
+ iAudioEnd = iVideoEnd = 0;
+
+ // Open all channels
+ iAudioChannel = 0;
+ iVideoChannel = 0;
+ for ( i = 0; i < iNumOutputChannels; i++ )
+ {
+ TOutputChannel *chan = &iOutputChannels[i];
+
+ // Check the channel type
+ switch ( chan->iDataType )
+ {
+ case EDataAudio:
+
+ if ( !iAudioChannel )
+ iAudioChannel = chan;
+ break;
+
+ case EDataVideo:
+
+ if ( !iVideoChannel )
+ iVideoChannel = chan;
+ break;
+
+ case EDataNone:
+ default:
+ User::Leave(CVideoProcessorImpl::EUnsupportedFormat);
+ }
+ }
+
+ // Make us active
+ SetActive();
+ iStatus = KRequestPending;
+ }
+
+// Destructor
+CMP4Demux::~CMP4Demux()
+ {
+ // If we are demultiplexing, stop
+ if ( iDemultiplexing )
+ Stop();
+
+ // return input block
+ if ( iInputBlock )
+ {
+ iInputQueue->ReturnBlock(iInputBlock);
+ iInputBlock = 0;
+ }
+
+ // Remove from being a reader or a writer
+ if ( iReaderSet )
+ iInputQueue->RemoveReader();
+ if ( iWriterSet )
+ {
+ for ( TUint i = 0; i < iNumOutputChannels; i++ )
+ iOutputChannels[i].iTargetQueue->RemoveWriter();
+ }
+
+ iMonitor = 0;
+ iInputQueue = 0;
+ iParser = 0;
+ iOutputChannels = 0;
+ iVideoChannel = 0;
+ iAudioChannel = 0;
+
+ Cancel();
+
+ }
+
+// ---------------------------------------------------------
+// CMP4Demux::Start
+// Starts demuxing
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+void CMP4Demux::Start()
+ {
+ if ( iDemultiplexing )
+ return;
+
+ // Activate the object if we have data
+ if ( (iStatus == KRequestPending) && (!iInputQueue || iInputQueue->NumDataBlocks()) )
+ {
+ TRequestStatus *status = &iStatus;
+ User::RequestComplete(status, KErrNone);
+ }
+
+ iDemultiplexing = ETrue;
+ iAudioEnd = iVideoEnd = iStreamEnd = 0;
+ iStreamEndDemuxed = 0;
+ }
+
+// ---------------------------------------------------------
+// CMP4Demux::Stop
+// Stops demuxing
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+void CMP4Demux::Stop()
+ {
+ iDemultiplexing = EFalse;
+ }
+
+// ---------------------------------------------------------
+// CMP4Demux::RunL
+// Standard active object running method, called when new input data
+// or free output space has been signaled to be available
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+void CMP4Demux::RunL()
+ {
+ PRINT((_L("MP4Demux::RunL() in") ));
+
+ // If we have demuxed everything up to stream end, theres is nothing for
+ // us to do
+ if ( iStreamEndDemuxed )
+ return;
+
+ // Don't do anything if we are not demuxing
+ if ( !iDemultiplexing )
+ {
+ SetActive();
+ iStatus = KRequestPending;
+ return;
+ }
+
+ // If we don't have a primary channel, we have no open channels and may as
+ // well quit
+ if ( !iAudioChannel && !iVideoChannel )
+ {
+ iMonitor->StreamEndReached();
+ return;
+ }
+
+ // streaming case:
+ // Try to demux as long as we have a free block in the primary output queue
+ // and we can find more frames
+ // If we have both video and audio, we'll check the available space only
+ // in the primary audio queue, and the video queue will allocate more
+ // blocks as needed. This way the audio decoder will get more data as
+ // needed, no matter what the video bitrate is.
+
+ // in file-reading case, GetFrameInfo() checks if there's available space
+ // in queues, and this info is contained in variable iGotFrame
+
+ // send frame(s) if:
+ // a frame is available AND
+ // there are free blocks in output queue AND
+ // we have not demuxed too much during this run so other objects get CPU AND
+ // the stream end has not been demuxed
+
+ iBytesDemuxed = 0;
+
+ // NOTE: only video queue fullness checked for now
+ CActiveQueue *queue = iVideoChannel->iTargetQueue;
+
+ GetFrameInfo();
+
+ while ( iGotFrame && ( (iInputQueue && NumFreeBlocks() > 0) ||
+ ( (queue->NumDataBlocks() < KMaxBlocksInQueue) && (iBytesDemuxed < KMaxBytesPerRun) ) ) &&
+ (!iStreamEndDemuxed) )
+ {
+ // Read & send frame(s)
+ TInt error = ReadAndSendFrames();
+
+ if ( error != KErrNone )
+ {
+ iMonitor->Error(error);
+ return;
+ }
+
+ // And to try get info for new frame
+ GetFrameInfo();
+ }
+
+ // If we have demultiplexed everything up to stream end, signal the queues
+ // and don't demux any more. If we have no output channels, notify the
+ // status monitor.
+ if ( iStreamEnd && (!iGotFrame) )
+ {
+ // report stream end in streaming case
+ // in file-reading case, its reported in GetFrameInfo
+ if ( iNumOutputChannels )
+ {
+ if ( iInputQueue )
+ {
+ TUint i;
+ for ( i = 0; i < iNumOutputChannels; i++ )
+ iOutputChannels[i].iTargetQueue->WriteStreamEnd();
+ }
+ }
+ else
+ {
+ iMonitor->StreamEndReached();
+ }
+ iStreamEndDemuxed = ETrue;
+ return;
+ }
+
+ // Re-activate to get signals about new blocks
+ SetActive();
+ iStatus = KRequestPending;
+
+ PRINT((_L("MP4Demux::RunL() out") ));
+ }
+
+
+// ---------------------------------------------------------
+// CMP4Demux::StreamEndReached
+// Informs the object that stream end has been reached
+// (when we have an input queue, not used in file-reading case)
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+void CMP4Demux::StreamEndReached(TAny* /*aUserPointer*/)
+ {
+ iStreamEnd = ETrue;
+
+ // Signal ourselves if we are demultiplexing
+ if ( iDemultiplexing && (iStatus == KRequestPending) )
+ {
+ TRequestStatus *status = &iStatus;
+ User::RequestComplete(status, KErrNone);
+ }
+ }
+
+// ---------------------------------------------------------
+// CMP4Demux::DoCancel
+// Standard active object cancellation method
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+void CMP4Demux::DoCancel()
+ {
+ // Cancel our internal request
+ if ( iStatus == KRequestPending )
+ {
+ TRequestStatus *status = &iStatus;
+ User::RequestComplete(status, KErrCancel);
+ }
+ }
+
+// ---------------------------------------------------------
+// CMP4Demux::GetFrameInfo
+// Gets information regarding the next frame. In file-reading
+// case, also sets the type of next frame to be read.
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+void CMP4Demux::GetFrameInfo()
+ {
+
+ if ( iGotFrame )
+ return;
+
+ if ( !iInputQueue )
+ {
+ // file-reading case: set frame type according to
+ // queue fullness
+
+ SetFrameType();
+ if ( iFrameType == EDataNone )
+ return;
+
+ }
+
+ TBool frameAvailable = EFalse;
+ // check if parser has info & data for next frame available
+ TInt error = iParser->GetNextFrameInformation((CMP4Parser::TFrameType&)iFrameType,
+ iFrameLen, frameAvailable);
+
+ if ( error != KErrNone )
+ {
+ if ( error != CParser::EParserEndOfStream )
+ {
+ iMonitor->Error(error);
+ }
+#ifdef _DEBUG
+ else
+ DASSERT( iStreamEnd );
+#endif
+ return;
+ }
+
+ if ( iInputQueue )
+ {
+
+ // Read data from input queue until we know the frame type and length
+ // and have data for it available
+ while ( !frameAvailable )
+ {
+ // Get a new input block with data
+ while ( !iInputBlock )
+ {
+ if ( (iInputBlock = iInputQueue->ReadBlock()) == NULL )
+ return;
+
+ // Return empty blocks immediately
+ if ( iInputBlock->Length() == 0 )
+ {
+ iInputQueue->ReturnBlock(iInputBlock);
+ iInputBlock = 0;
+ }
+ }
+
+ // give input block to parser
+ error = iParser->WriteDataBlock(*iInputBlock);
+ if ( error != KErrNone )
+ {
+ iMonitor->Error(error);
+ return;
+ }
+
+ // Return our current input block
+ iInputQueue->ReturnBlock(iInputBlock);
+ iInputBlock = 0;
+
+ // check if parser has info & data for next frame available
+ error = iParser->GetNextFrameInformation((CMP4Parser::TFrameType&)iFrameType,
+ iFrameLen, frameAvailable);
+
+ if ( error != KErrNone )
+ {
+ iMonitor->Error(error);
+ return;
+ }
+ }
+ }
+ else {
+ while ( !frameAvailable )
+ {
+ if ( iFrameType == EDataAudio )
+ {
+ iAudioEnd = ETrue;
+ iAudioChannel->iTargetQueue->WriteStreamEnd();
+ PRINT((_L("MP4Demux, audio ended\n") ));
+ }
+ else
+ {
+ iVideoEnd = ETrue;
+ iVideoChannel->iTargetQueue->WriteStreamEnd();
+ PRINT((_L("MP4Demux, video ended\n") ));
+ }
+ if ( iVideoEnd && (iAudioChannel == 0 || iAudioEnd) )
+ {
+ iStreamEnd = ETrue;
+ return;
+ }
+ iFrameType = EDataNone;
+ SetFrameType();
+ if ( iFrameType == EDataNone )
+ return;
+ error = iParser->GetNextFrameInformation((CMP4Parser::TFrameType&)iFrameType,
+ iFrameLen, frameAvailable);
+ if ( error != KErrNone )
+ {
+ iMonitor->Error(error);
+ return;
+ }
+ }
+ }
+
+ // at least one frame available
+ iGotFrame = ETrue;
+ }
+
+// ---------------------------------------------------------
+// CMP4Demux::NumFreeBlocks
+// Gets the number of free blocks in target queue
+// Relevant in streaming -case
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+TUint CMP4Demux::NumFreeBlocks()
+ {
+ // check if there's space available for next frame
+
+ CActiveQueue *queue = 0;
+
+ // streaming case: use audio queue value for both so
+ // that enough audio is always available regardless of
+ // video bitrate.
+
+ if ( iAudioChannel )
+ queue = iAudioChannel->iTargetQueue;
+ else
+ queue = iVideoChannel->iTargetQueue;
+
+ DASSERT(queue);
+
+ return queue->NumFreeBlocks();
+
+ }
+
+// ---------------------------------------------------------
+// CMP4Demux::SetFrameType
+// Sets the type of next frame to be read
+// Relevant in file-reading case
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CMP4Demux::SetFrameType()
+ {
+
+ TUint audioDataBlocks = 0;
+ TUint audioInQueue = 0;
+ TUint videoDataBlocks = iVideoChannel->iTargetQueue->NumDataBlocks();
+ TUint videoInQueue = videoDataBlocks * iPicturePeriodMs;
+
+ DASSERT( iFrameType == EDataNone );
+
+ if ( iAudioChannel )
+ {
+ audioDataBlocks = iAudioChannel->iTargetQueue->NumDataBlocks();
+ audioInQueue = audioDataBlocks * 20 * iAudioFramesInSample;
+ }
+
+ if ( iAudioChannel == 0 || iAudioEnd )
+ {
+ iFrameType = EDataVideo;
+ }
+
+ else if ( iVideoEnd )
+ {
+ iFrameType = EDataAudio;
+ }
+
+ else
+ {
+ if ( audioInQueue > videoInQueue + KAudioReadAheadTimeMs )
+ iFrameType = EDataVideo;
+ else
+ iFrameType = EDataAudio;
+ }
+
+ //if ( ( iFrameType == EDataVideo && videoInQueue >= KMaxMsInQueue ) ||
+ // ( iFrameType == EDataAudio && audioInQueue >= KMaxMsInQueue + KAudioReadAheadTimeMs ) )
+ // iFrameType = EDataNone;
+
+ }
+
+// ---------------------------------------------------------
+// CMP4Demux::ReadVideoFrames
+// Read video frames to video queue
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+TInt CMP4Demux::ReadVideoFrames(TInt aCount)
+{
+
+ while (aCount--)
+ {
+ iFrameType = EDataVideo;
+
+ TBool frameAvailable = 0;
+ TInt error = iParser->GetNextFrameInformation((CMP4Parser::TFrameType&)iFrameType,
+ iFrameLen, frameAvailable);
+
+ if (error !=KErrNone)
+ return error;
+
+ DASSERT(frameAvailable);
+
+ iGotFrame = ETrue;
+ error = ReadAndSendFrames();
+ if (error !=KErrNone)
+ return error;
+ }
+ return KErrNone;
+
+}
+
+// ---------------------------------------------------------
+// CMP4Demux::StreamEndReached
+// Reads the next frame(s) from parser and writes
+// them to the target queue
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+TInt CMP4Demux::ReadAndSendFrames()
+ {
+
+ DASSERT( iGotFrame );
+
+ // Find the correct channel. If there is no channel open for this
+ // type of frames, we'll simply ignore it
+ TOutputChannel *chan = 0;
+ TUint i;
+ for ( i = 0; i < iNumOutputChannels; i++ )
+ {
+ if ( iOutputChannels[i].iDataType == iFrameType )
+ chan = &iOutputChannels[i];
+ }
+
+ if ( chan )
+ {
+ // OK, we have a target channel. Get a block from its queue
+
+ TPtr8 *block = 0;
+
+ // NOTE: if output block does not need to be saved in any case, make it a local variable
+
+ //PRINT((_L("framelen = %d, bytesdemuxed = %d\n"), iFrameLen, iBytesDemuxed));
+
+ TUint blockLen = iFrameLen;
+ TPtr8 readDes(0,0);
+ TInt error;
+
+ if ( iFrameType == EDataVideo )
+ {
+ // make room for timestamp
+ blockLen += 4;
+ }
+
+
+
+ TRAP( error, (block = chan->iTargetQueue->GetFreeBlockL(blockLen)) );
+ if ( error != KErrNone )
+ return error;
+
+ if ( iFrameType == EDataVideo )
+ {
+ TUint8 *p = (TUint8 *)(block->Ptr()) + 4;
+ readDes.Set( p, 0, TInt(iFrameLen) );
+ }
+ else
+ {
+ readDes.Set( *block );
+ }
+
+ TUint32 numReadFrames = 0;
+ TUint32 timeStamp;
+
+ // read frame(s) from parser
+ error = iParser->ReadFrames(readDes, CMP4Parser::TFrameType(iFrameType),
+ numReadFrames, timeStamp);
+
+ if ( error != KErrNone )
+ return error;
+
+ DASSERT( numReadFrames > 0 );
+
+ if ( iFrameType == EDataAudio )
+ {
+ block->SetLength(readDes.Length());
+ }
+ else
+ {
+ block->SetLength(readDes.Length() + 4);
+
+ // put timestamp in the output block before the actual frame data
+ TUint* d = (TUint *)(block->Ptr());
+ Mem::Copy(d, &timeStamp, 4);
+ }
+
+ iBytesDemuxed += TUint( readDes.Length() );
+
+ // Send the block
+ chan->iTargetQueue->WriteBlock(block);
+ iFrameLen = 0;
+ iFrameType = EDataNone;
+ iGotFrame = EFalse;
+ }
+ else
+ {
+ PRINT((_L("Unknown channel\n")));
+ }
+
+ return KErrNone;
+ }
+