videoeditorengine/vedengine/videoprocessor/src/mp4demux.cpp
changeset 0 951a5db380a0
equal deleted inserted replaced
-1:000000000000 0:951a5db380a0
       
     1 /*
       
     2 * Copyright (c) 2010 Ixonos Plc.
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of the "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - Initial contribution
       
    11 *
       
    12 * Contributors:
       
    13 * Ixonos Plc
       
    14 *
       
    15 * Description:  
       
    16 * Implementation of mp4 demux class.
       
    17 *
       
    18 */
       
    19 
       
    20 
       
    21 // INCLUDE FILES
       
    22 #include "movieprocessorimpl.h"
       
    23 #include "statusmonitor.h"
       
    24 #include "activequeue.h"
       
    25 #include "dataprocessor.h"
       
    26 #include "mp4demux.h"
       
    27 #include "mp4parser.h"
       
    28 
       
    29 // ASSERTIONS
       
    30 #define DASSERT(x) __ASSERT_DEBUG(x, User::Panic(_L("CVideoPlayer"), EInternalAssertionFailure))
       
    31 
       
    32 //  LOCAL HELPER MACROS
       
    33 // Debug printing, define DEBUGPRINT to get output
       
    34 //#define DEBUGPRINT
       
    35 #ifdef _DEBUG
       
    36 #include <e32svr.h>
       
    37 #define PRINT(x) RDebug::Print x;
       
    38 #else
       
    39 #define PRINT(x)
       
    40 #endif
       
    41 
       
    42 //  LOCAL CONSTANTS
       
    43 const TUint KAudioReadAheadTimeMs = 100;
       
    44 //const TUint KMaxMsInQueue = 600;
       
    45 //const TUint KMaxMsInQueue = 300;
       
    46 const TUint KMaxBytesPerRun = 4096;
       
    47 const TUint KMaxBlocksInQueue = 16;
       
    48 
       
    49 
       
    50 //  MEMBER FUNCTIONS
       
    51 
       
    52 
       
    53 //=============================================================================
       
    54 
       
    55 // MODULE DATA STRUCTURES
       
    56 //enum ?declaration
       
    57 //typedef ?declaration
       
    58 
       
    59 // LOCAL FUNCTION PROTOTYPES
       
    60 // ?type ?function_name( ?arg_type, ?arg_type );
       
    61 
       
    62 // ==================== LOCAL FUNCTIONS ====================
       
    63 
       
    64 // ================= MEMBER FUNCTIONS =======================
       
    65 
       
    66 
       
    67 // ---------------------------------------------------------
       
    68 // CMP4Demux::NewL
       
    69 // Symbian two-phased constructor.
       
    70 // ---------------------------------------------------------
       
    71 //
       
    72 CMP4Demux* CMP4Demux::NewL(CActiveQueue *anInputQueue,              
       
    73                            TUint aNumChannels, TOutputChannel *aOutputChannels,
       
    74                            TStreamParameters *aParameters,
       
    75                            CStatusMonitor *aStatusMonitor,
       
    76                            CMP4Parser *aParser,                  
       
    77                            TInt aPriority)
       
    78 {
       
    79 
       
    80     CMP4Demux *self = new (ELeave) CMP4Demux(anInputQueue,              
       
    81                                              aNumChannels, 
       
    82                                              aOutputChannels,
       
    83                                              aParameters,
       
    84                                              aStatusMonitor,
       
    85                                              aParser,
       
    86                                              aPriority);
       
    87 
       
    88     CleanupStack::PushL(self);
       
    89     self->ConstructL();
       
    90     CleanupStack::Pop();
       
    91 
       
    92     return self;
       
    93 
       
    94 
       
    95 }
       
    96 
       
    97 // ---------------------------------------------------------
       
    98 // C++ default constructor can NOT contain any code, that
       
    99 // might leave.
       
   100 // ---------------------------------------------------------
       
   101 //
       
   102 CMP4Demux::CMP4Demux(CActiveQueue *anInputQueue,
       
   103                      TUint aNumChannels, TOutputChannel *aOutputChannels,
       
   104                      TStreamParameters *aParameters,
       
   105                      CStatusMonitor *aStatusMonitor,
       
   106                      CMP4Parser *aParser,                     
       
   107                      TInt aPriority)
       
   108                      : CDemultiplexer(aPriority)
       
   109     {
       
   110     // Remember the objects
       
   111     iInputQueue = anInputQueue;
       
   112     iMonitor = aStatusMonitor;
       
   113     iParser = aParser;    
       
   114 
       
   115     iPicturePeriodMs = aParameters->iPicturePeriodMs;
       
   116     iAudioFramesInSample = aParameters->iAudioFramesInSample;    
       
   117 
       
   118     // Remember the channels and mux table
       
   119     iNumOutputChannels = aNumChannels;
       
   120     iOutputChannels = aOutputChannels;
       
   121     }
       
   122 
       
   123 // EPOC default constructor can leave.
       
   124 void CMP4Demux::ConstructL()
       
   125     {
       
   126     TUint i;
       
   127     
       
   128     // Set as a reader to the input queue
       
   129     if ( iInputQueue )
       
   130         {
       
   131         iInputQueue->SetReader(this, NULL);
       
   132         iReaderSet = ETrue;
       
   133         }
       
   134     
       
   135     // Set as writer to the output queues
       
   136     for ( i = 0; i < iNumOutputChannels; i++ )
       
   137         iOutputChannels[i].iTargetQueue->SetWriter(this, NULL);
       
   138     iWriterSet = ETrue;
       
   139     
       
   140     // Add us to active scheduler
       
   141     CActiveScheduler::Add(this);
       
   142 
       
   143     iBytesDemuxed = 0;
       
   144     iAudioEnd = iVideoEnd = 0;
       
   145     
       
   146     // Open all channels
       
   147     iAudioChannel = 0;
       
   148     iVideoChannel = 0;
       
   149     for ( i = 0; i < iNumOutputChannels; i++ )
       
   150         {
       
   151         TOutputChannel *chan = &iOutputChannels[i];
       
   152         
       
   153         // Check the channel type
       
   154         switch ( chan->iDataType )
       
   155             {
       
   156             case EDataAudio:                
       
   157                 
       
   158                 if ( !iAudioChannel )
       
   159                     iAudioChannel = chan;
       
   160                 break;
       
   161                 
       
   162             case EDataVideo:
       
   163                 
       
   164                 if ( !iVideoChannel )
       
   165                     iVideoChannel = chan;
       
   166                 break;
       
   167 
       
   168             case EDataNone:
       
   169             default:
       
   170                 User::Leave(CMovieProcessorImpl::EUnsupportedFormat);
       
   171             }
       
   172         }     
       
   173 
       
   174     // Make us active
       
   175     SetActive();
       
   176     iStatus = KRequestPending;
       
   177     }
       
   178 
       
   179 // Destructor
       
   180 CMP4Demux::~CMP4Demux()
       
   181     {
       
   182 
       
   183     // If we are demultiplexing, stop
       
   184     if ( iDemultiplexing )
       
   185         Stop();
       
   186 
       
   187     // return input block
       
   188     if ( iInputBlock )
       
   189         {
       
   190         if (iInputQueue)
       
   191             iInputQueue->ReturnBlock(iInputBlock);
       
   192         iInputBlock = 0;
       
   193         }
       
   194      
       
   195     // Remove from being a reader or a writer
       
   196     if ( iReaderSet )
       
   197         {
       
   198         if (iInputQueue)
       
   199             iInputQueue->RemoveReader();
       
   200         }
       
   201 
       
   202     if ( iWriterSet )
       
   203         {
       
   204         for ( TUint i = 0; i < iNumOutputChannels; i++ )
       
   205             {
       
   206             if (iOutputChannels[i].iTargetQueue)
       
   207                 iOutputChannels[i].iTargetQueue->RemoveWriter();
       
   208             }
       
   209         }
       
   210 
       
   211     iMonitor = 0;
       
   212     iInputQueue = 0;
       
   213     iParser = 0;
       
   214     iOutputChannels = 0;
       
   215     iVideoChannel = 0;
       
   216     iAudioChannel = 0;
       
   217         
       
   218     Cancel();
       
   219     
       
   220     }
       
   221 
       
   222 // ---------------------------------------------------------
       
   223 // CMP4Demux::Start
       
   224 // Starts demuxing
       
   225 // (other items were commented in a header).
       
   226 // ---------------------------------------------------------
       
   227 //
       
   228 
       
   229 void CMP4Demux::Start()
       
   230     {
       
   231     if ( iDemultiplexing )
       
   232         return;
       
   233     
       
   234     if (!IsActive())
       
   235     {
       
   236         // Make us active
       
   237         SetActive();
       
   238         iStatus = KRequestPending;
       
   239     }
       
   240     
       
   241     // Activate the object if we have data
       
   242     if ( (iStatus == KRequestPending) && (!iInputQueue || iInputQueue->NumDataBlocks()) )
       
   243         {
       
   244         TRequestStatus *status = &iStatus;
       
   245         User::RequestComplete(status, KErrNone);
       
   246         }
       
   247     
       
   248     iDemultiplexing = ETrue;
       
   249     iAudioEnd = iVideoEnd = iStreamEnd = 0;
       
   250     iStreamEndDemuxed = 0;
       
   251     }
       
   252 
       
   253 // ---------------------------------------------------------
       
   254 // CMP4Demux::Stop
       
   255 // Stops demuxing
       
   256 // (other items were commented in a header).
       
   257 // ---------------------------------------------------------
       
   258 //
       
   259 
       
   260 void CMP4Demux::Stop()
       
   261     {
       
   262     iDemultiplexing = EFalse;
       
   263     iGotFrame = EFalse;
       
   264     iFrameType = EDataNone;
       
   265     }
       
   266 
       
   267 // ---------------------------------------------------------
       
   268 // CMP4Demux::RunL
       
   269 // Standard active object running method, called when new input data 
       
   270 // or free output space has been signaled to be available 
       
   271 // (other items were commented in a header).
       
   272 // ---------------------------------------------------------
       
   273 //
       
   274 
       
   275 void CMP4Demux::RunL()
       
   276     {
       
   277     PRINT((_L("MP4Demux::RunL() in") ));
       
   278 
       
   279     // If we have demuxed everything up to stream end, theres is nothing for
       
   280     // us to do
       
   281     if ( iStreamEndDemuxed )
       
   282         return;
       
   283     
       
   284     // Don't do anything if we are not demuxing
       
   285     if ( !iDemultiplexing )
       
   286         {
       
   287         SetActive();
       
   288         iStatus = KRequestPending;
       
   289         return;
       
   290         }
       
   291     
       
   292     // If we don't have a primary channel, we have no open channels and may as
       
   293     // well quit
       
   294     if ( !iAudioChannel && !iVideoChannel )
       
   295         {
       
   296         iMonitor->StreamEndReached();
       
   297         return;
       
   298         }    
       
   299 
       
   300     // streaming case:
       
   301     // Try to demux as long as we have a free block in the primary output queue
       
   302     // and we can find more frames 
       
   303     // If we have both video and audio, we'll check the available space only
       
   304     // in the primary audio queue, and the video queue will allocate more
       
   305     // blocks as needed. This way the audio decoder will get more data as
       
   306     // needed, no matter what the video bitrate is.            
       
   307 
       
   308     // in file-reading case, GetFrameInfo() checks if there's available space
       
   309     // in queues, and this info is contained in variable iGotFrame       
       
   310 
       
   311     // send frame(s) if:
       
   312     // a frame is available AND
       
   313     // there are free blocks in output queue AND
       
   314     // we have not demuxed too much during this run so other objects get CPU AND
       
   315     // the stream end has not been demuxed    
       
   316 
       
   317     iBytesDemuxed = 0;
       
   318 
       
   319     // NOTE: only video queue fullness checked for now
       
   320     CActiveQueue *queue = iVideoChannel->iTargetQueue;
       
   321 
       
   322     TInt error = GetFrameInfo();
       
   323     if (error != KErrNone)
       
   324         return;
       
   325 
       
   326     while ( iGotFrame && ( (iInputQueue && NumFreeBlocks() > 0) || 
       
   327           ( (queue->NumDataBlocks() < KMaxBlocksInQueue) && (iBytesDemuxed < KMaxBytesPerRun) ) ) && 
       
   328           (!iStreamEndDemuxed) )
       
   329         {
       
   330         // Read & send frame(s)        
       
   331         TInt error = ReadAndSendFrames();
       
   332         
       
   333         if ( error != KErrNone )
       
   334             {
       
   335             iMonitor->Error(error);
       
   336             return;        
       
   337             }
       
   338         
       
   339         // And to try get info for new frame
       
   340         error = GetFrameInfo();
       
   341         if (error != KErrNone)
       
   342             return;
       
   343         }
       
   344     
       
   345     // If we have demultiplexed everything up to stream end, signal the queues
       
   346     // and don't demux any more. If we have no output channels, notify the
       
   347     // status monitor.
       
   348     if ( iStreamEnd && (!iGotFrame) )
       
   349         {
       
   350         // report stream end in streaming case
       
   351         // in file-reading case, its reported in GetFrameInfo
       
   352         if ( iNumOutputChannels )
       
   353             {
       
   354             if ( iInputQueue )
       
   355                 {
       
   356                 TUint i;
       
   357                 for ( i = 0; i < iNumOutputChannels; i++ )
       
   358                     iOutputChannels[i].iTargetQueue->WriteStreamEnd();
       
   359                 }
       
   360             }
       
   361         else
       
   362             {
       
   363             iMonitor->StreamEndReached();
       
   364             }
       
   365         iStreamEndDemuxed = ETrue;
       
   366         return;
       
   367         }
       
   368     
       
   369     // Re-activate to get signals about new blocks
       
   370     SetActive();
       
   371     iStatus = KRequestPending;
       
   372 
       
   373     PRINT((_L("MP4Demux::RunL() out") ));
       
   374     }
       
   375 
       
   376 
       
   377 // ---------------------------------------------------------
       
   378 // CMP4Demux::StreamEndReached
       
   379 // Informs the object that stream end has been reached
       
   380 // (when we have an input queue, not used in file-reading case)
       
   381 // (other items were commented in a header).
       
   382 // ---------------------------------------------------------
       
   383 //
       
   384 
       
   385 void CMP4Demux::StreamEndReached(TAny* /*aUserPointer*/)
       
   386     {
       
   387     iStreamEnd = ETrue;
       
   388     
       
   389     // Signal ourselves if we are demultiplexing
       
   390     if ( iDemultiplexing && (iStatus == KRequestPending) )
       
   391         {
       
   392         TRequestStatus *status = &iStatus;
       
   393         User::RequestComplete(status, KErrNone);
       
   394         }
       
   395     }
       
   396 
       
   397 // ---------------------------------------------------------
       
   398 // CMP4Demux::DoCancel
       
   399 // Standard active object cancellation method
       
   400 // (other items were commented in a header).
       
   401 // ---------------------------------------------------------
       
   402 //
       
   403 
       
   404 void CMP4Demux::DoCancel()
       
   405     {
       
   406     // Cancel our internal request
       
   407     if ( iStatus == KRequestPending )
       
   408         {
       
   409         TRequestStatus *status = &iStatus;
       
   410         User::RequestComplete(status, KErrCancel);
       
   411         }
       
   412     }
       
   413 
       
   414 // ---------------------------------------------------------
       
   415 // CMP4Demux::GetFrameInfo
       
   416 // Gets information regarding the next frame. In file-reading
       
   417 // case, also sets the type of next frame to be read. 
       
   418 // (other items were commented in a header).
       
   419 // ---------------------------------------------------------
       
   420 //
       
   421 
       
   422 TInt CMP4Demux::GetFrameInfo()
       
   423     {     
       
   424     
       
   425     if ( iGotFrame )
       
   426         return KErrNone;
       
   427     
       
   428     if ( !iInputQueue )
       
   429         {
       
   430         // file-reading case: set frame type according to 
       
   431         // queue fullness 
       
   432         
       
   433         SetFrameType();
       
   434         if ( iFrameType == EDataNone )            
       
   435             return KErrNone;
       
   436 
       
   437         }
       
   438     
       
   439     TBool frameAvailable = EFalse;
       
   440     // check if parser has info & data for next frame available
       
   441     TInt error = iParser->GetNextFrameInformation((CMP4Parser::TFrameType&)iFrameType, 
       
   442         iFrameLen, frameAvailable);
       
   443     
       
   444     if ( error != KErrNone )
       
   445         {
       
   446         if ( error != CParser::EParserEndOfStream )
       
   447             {            
       
   448             iMonitor->Error(error);
       
   449             return error;
       
   450             }
       
   451         else
       
   452             {            
       
   453             DASSERT( iStreamEnd );
       
   454             return KErrNone;
       
   455             }
       
   456         
       
   457         }
       
   458     
       
   459     if ( iInputQueue ) 
       
   460         {                             
       
   461         
       
   462         // Read data from input queue until we know the frame type and length
       
   463         // and have data for it available        
       
   464         while ( !frameAvailable )
       
   465             {
       
   466             // Get a new input block with data
       
   467             while ( !iInputBlock )
       
   468                 {
       
   469                 if ( (iInputBlock = iInputQueue->ReadBlock()) == NULL )
       
   470                     return KErrNone;                    
       
   471                 
       
   472                 // Return empty blocks immediately
       
   473                 if ( iInputBlock->Length() == 0 ) 
       
   474                     {
       
   475                     iInputQueue->ReturnBlock(iInputBlock);
       
   476                     iInputBlock = 0;
       
   477                     }
       
   478                 }      
       
   479             
       
   480             // give input block to parser
       
   481             error = iParser->WriteDataBlock(*iInputBlock);
       
   482             if ( error != KErrNone )
       
   483                 {
       
   484                 iMonitor->Error(error);
       
   485                 return error;
       
   486                 }
       
   487             
       
   488             // Return our current input block 
       
   489             iInputQueue->ReturnBlock(iInputBlock);
       
   490             iInputBlock = 0;                                             
       
   491             
       
   492             // check if parser has info & data for next frame available
       
   493             error = iParser->GetNextFrameInformation((CMP4Parser::TFrameType&)iFrameType, 
       
   494                 iFrameLen, frameAvailable);
       
   495             
       
   496             if ( error != KErrNone ) 
       
   497                 {
       
   498                 iMonitor->Error(error);                
       
   499                 return error;
       
   500                 }
       
   501             }
       
   502         }
       
   503     else {
       
   504         while ( !frameAvailable )
       
   505         {
       
   506             if ( iFrameType == EDataAudio )
       
   507             {
       
   508                 iAudioEnd = ETrue;
       
   509                 iAudioChannel->iTargetQueue->WriteStreamEnd();
       
   510                 PRINT((_L("MP4Demux, audio ended\n") ));
       
   511             }
       
   512             else
       
   513             {
       
   514                 iVideoEnd = ETrue;
       
   515                 iVideoChannel->iTargetQueue->WriteStreamEnd();
       
   516                 PRINT((_L("MP4Demux, video ended\n") ));
       
   517             }
       
   518             if ( iVideoEnd && (iAudioChannel == 0 || iAudioEnd) )
       
   519             {
       
   520                 iStreamEnd = ETrue;
       
   521                 return KErrNone;
       
   522             }
       
   523             iFrameType = EDataNone;
       
   524             SetFrameType();
       
   525             if ( iFrameType == EDataNone )
       
   526                 return KErrNone;
       
   527             error = iParser->GetNextFrameInformation((CMP4Parser::TFrameType&)iFrameType, 
       
   528                 iFrameLen, frameAvailable);
       
   529             if ( error != KErrNone ) 
       
   530             {
       
   531                 iMonitor->Error(error);                
       
   532                 return error;
       
   533             }
       
   534         }
       
   535     }
       
   536            
       
   537     // at least one frame available
       
   538     iGotFrame = ETrue;    
       
   539     return KErrNone;
       
   540     }
       
   541 
       
   542 // ---------------------------------------------------------
       
   543 // CMP4Demux::NumFreeBlocks
       
   544 // Gets the number of free blocks in target queue
       
   545 // Relevant in streaming -case
       
   546 // (other items were commented in a header).
       
   547 // ---------------------------------------------------------
       
   548 //
       
   549 
       
   550 TUint CMP4Demux::NumFreeBlocks()
       
   551     {
       
   552     // check if there's space available for next frame    
       
   553     
       
   554     CActiveQueue *queue = 0;
       
   555     
       
   556     // streaming case: use audio queue value for both so
       
   557     // that enough audio is always available regardless of 
       
   558     // video bitrate.
       
   559 
       
   560     if ( iAudioChannel )    
       
   561         queue = iAudioChannel->iTargetQueue;    
       
   562     else     
       
   563         queue = iVideoChannel->iTargetQueue;
       
   564     
       
   565     DASSERT(queue);
       
   566 
       
   567     return queue->NumFreeBlocks();
       
   568     
       
   569     }
       
   570 
       
   571 // ---------------------------------------------------------
       
   572 // CMP4Demux::SetFrameType
       
   573 // Sets the type of next frame to be read
       
   574 // Relevant in file-reading case
       
   575 // (other items were commented in a header).
       
   576 // ---------------------------------------------------------
       
   577 //
       
   578 void CMP4Demux::SetFrameType()
       
   579     {   
       
   580     
       
   581     TUint audioDataBlocks = 0;
       
   582     TUint audioInQueue = 0;
       
   583     TUint videoDataBlocks = iVideoChannel->iTargetQueue->NumDataBlocks();    
       
   584     TUint videoInQueue = videoDataBlocks * iPicturePeriodMs;    
       
   585 
       
   586     DASSERT( iFrameType == EDataNone );
       
   587 
       
   588     if ( iAudioChannel )
       
   589     {
       
   590         audioDataBlocks = iAudioChannel->iTargetQueue->NumDataBlocks();
       
   591         audioInQueue = audioDataBlocks * 20 * iAudioFramesInSample;
       
   592     }
       
   593 
       
   594     if ( iAudioChannel == 0 || iAudioEnd )
       
   595     {
       
   596         iFrameType = EDataVideo;
       
   597     }        
       
   598 
       
   599     else if ( iVideoEnd )
       
   600     {    
       
   601         iFrameType = EDataAudio;        
       
   602     }
       
   603 
       
   604     else 
       
   605     {
       
   606         if ( audioInQueue > videoInQueue + KAudioReadAheadTimeMs )
       
   607             iFrameType = EDataVideo;
       
   608         else 
       
   609             iFrameType = EDataAudio;            
       
   610     }
       
   611     
       
   612     //if ( ( iFrameType == EDataVideo && videoInQueue >= KMaxMsInQueue ) || 
       
   613     //     ( iFrameType == EDataAudio && audioInQueue >= KMaxMsInQueue + KAudioReadAheadTimeMs ) )
       
   614     //    iFrameType = EDataNone;          
       
   615     
       
   616     }
       
   617 
       
   618 // ---------------------------------------------------------
       
   619 // CMP4Demux::ReadVideoFrames
       
   620 // Read video frames to video queue
       
   621 // (other items were commented in a header).
       
   622 // ---------------------------------------------------------
       
   623 //
       
   624 TInt CMP4Demux::ReadVideoFrames(TInt aCount)
       
   625 {
       
   626     
       
   627     while (aCount--)
       
   628     {
       
   629         iFrameType = EDataVideo;
       
   630 
       
   631         TBool frameAvailable = 0;
       
   632         TInt error = iParser->GetNextFrameInformation((CMP4Parser::TFrameType&)iFrameType, 
       
   633             iFrameLen, frameAvailable);
       
   634 
       
   635         if (error !=KErrNone)
       
   636             return error;
       
   637 
       
   638         DASSERT(frameAvailable);
       
   639 
       
   640         iGotFrame = ETrue;
       
   641         error = ReadAndSendFrames();
       
   642         if (error !=KErrNone)
       
   643             return error;
       
   644     }
       
   645     return KErrNone;
       
   646 
       
   647 }
       
   648 
       
   649 // ---------------------------------------------------------
       
   650 // CMP4Demux::StreamEndReached
       
   651 // Reads the next frame(s) from parser and writes
       
   652 // them to the target queue
       
   653 // (other items were commented in a header).
       
   654 // ---------------------------------------------------------
       
   655 //
       
   656 
       
   657 TInt CMP4Demux::ReadAndSendFrames()
       
   658     {    
       
   659 
       
   660     DASSERT( iGotFrame );
       
   661 
       
   662     // Find the correct channel. If there is no channel open for this
       
   663     // type of frames, we'll simply ignore it
       
   664     TOutputChannel *chan = 0;
       
   665     TUint i;
       
   666     for ( i = 0; i < iNumOutputChannels; i++ )
       
   667         {
       
   668         if ( iOutputChannels[i].iDataType == iFrameType )
       
   669             chan = &iOutputChannels[i];
       
   670         }    
       
   671     
       
   672     if ( chan )
       
   673         {
       
   674         // OK, we have a target channel. Get a block from its queue
       
   675         
       
   676         TPtr8 *block = 0;                       
       
   677         
       
   678         // NOTE: if output block does not need to be saved in any case, make it a local variable
       
   679 
       
   680         //PRINT((_L("framelen = %d, bytesdemuxed = %d\n"), iFrameLen, iBytesDemuxed));
       
   681 
       
   682         TUint blockLen = iFrameLen;
       
   683         TPtr8 readDes(0,0);        
       
   684         TInt error;
       
   685 
       
   686         if ( iFrameType == EDataVideo ) 
       
   687         {
       
   688             // make room for timestamp
       
   689             blockLen += 4;
       
   690         }              
       
   691         
       
   692 
       
   693 
       
   694         TRAP( error, (block = chan->iTargetQueue->GetFreeBlockL(blockLen)) );
       
   695         if ( error != KErrNone )
       
   696             return error;        
       
   697 
       
   698         if ( iFrameType == EDataVideo ) 
       
   699         {
       
   700             TUint8 *p = (TUint8 *)(block->Ptr()) + 4;
       
   701             readDes.Set( p, 0, TInt(iFrameLen) );
       
   702         }
       
   703         else
       
   704         {
       
   705             readDes.Set( *block );
       
   706         }
       
   707                 
       
   708         TUint32 numReadFrames = 0;
       
   709         TUint32 timeStamp;
       
   710 
       
   711         // read frame(s) from parser
       
   712         error = iParser->ReadFrames(readDes, CMP4Parser::TFrameType(iFrameType), 
       
   713             numReadFrames, timeStamp);
       
   714    
       
   715         if ( error != KErrNone )
       
   716             return error;
       
   717 
       
   718         DASSERT( numReadFrames > 0 );   
       
   719         
       
   720         if ( iFrameType == EDataAudio )
       
   721         {
       
   722             block->SetLength(readDes.Length());            
       
   723         }
       
   724         else
       
   725         {            
       
   726             block->SetLength(readDes.Length() + 4);
       
   727 
       
   728             // put timestamp in the output block before the actual frame data                       
       
   729             TUint* d = (TUint *)(block->Ptr());            
       
   730             Mem::Copy(d, &timeStamp, 4);
       
   731         }
       
   732 
       
   733         iBytesDemuxed += TUint( readDes.Length() );
       
   734         
       
   735         // Send the block
       
   736         chan->iTargetQueue->WriteBlock(block);        
       
   737         iFrameLen = 0;
       
   738         iFrameType = EDataNone;
       
   739         iGotFrame = EFalse;
       
   740         }
       
   741     else
       
   742         {     
       
   743         PRINT((_L("Unknown channel\n")));
       
   744         }    
       
   745     
       
   746     return KErrNone;
       
   747     }
       
   748