mmserv/thumbnailengine/TneProcessorSrc/mp4demux.cpp
changeset 0 71ca22bcf22a
--- /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;
+    }
+