/*****************************************************************************
Copyright(c) 2005 Analog Devices, Inc.  All Rights Reserved. This software is 
proprietary and confidential to Analog Devices, Inc. and its licensors.
******************************************************************************

$RCSfile: mjpeg_decode.c,v $
$Revision: 1.1 $
$Date: 2007/05/22 18:37:53 $

Project:	BlackfinSDK (JPEG-MJPEG)
Title:		MJPEG decode
Author(s):	ku, bmk
Revised by: 

Description:
			High level MJPEG decoding functions

References:
			None

******************************************************************************
Tab Setting:			4

Target Processor:		ADSP-BF5xx
Target Tools Revision:	ADSP VisualDSP++ v4.5
******************************************************************************

Modification History:
====================
$Log: mjpeg_decode.c,v $
Revision 1.1  2007/05/22 18:37:53  gstephan
Creating VDSP 5.00 release folder.  Created from the head copy of
VDSP 4.50 release folder.

Revision 1.6  2006/11/10 07:18:58  bmk
merged BF533 & BF561 apps
Fixed BF561 caches issue

Revision 1.5  2006/11/03 07:12:07  bmk
SDK 2.0  files - Initial Entry

*****************************************************************************/

#include <jpeg_mjpeg_system.h>		// JPEG-MJPEG System includes

#include <adi_usbio_blackfin.h>		// USB I/O functions for Blackfin
#include <string.h>					// string header
#include <math.h>
#include <cycle_count.h> 			// for basic cycle counting

// codec APIs
#include <JPEG_api_decoder.h>
#include <JPEG_BitsBuffer.h>
#include <IMG_common.h>
#include <JPEG_memalloc.h>
#include <MJPEG_AVI_FileReader.h>

#include <jpeg_mjpeg_err.h>

// MJPEG file parameter info prototype
extern int32 MJPEG_AVI_GetImageInfo(int8 *lInputFileName,tJpegParam *limageParam,uint32* lBufLength,FILE *TestReportFile);
extern void *mcu_ex;

// local prototype
int MJPEG_decode(char* input_file);

/*********************************************************************

    Function:       do_MJPEG_Decode

    Description:    File Handling for MJPEG decoding

*********************************************************************/
section("sdram0_bank1_nocache")
int do_MJPEG_Decode(void)
{
	char  current_input_file_name[MAX_FILE_NAME_LEN];
	//char  file_name[MAX_FILE_NAME_LEN];
	static char current_path[MAX_PATH_NAME_LEN];
	static char temp_path[MAX_PATH_NAME_LEN];

	FILE *fp_mjpg_dir;			// file pointer to list file

	int next_file = 1;	// let next file value not be a null
	char key;
	bool replay = FALSE;	// clear replay flag initially

	int return_code = CODEC_SUCCESS;

	printf("Blackfin(r) Multimedia Starter Kit - MJPEG Decoder\n");
	printf("(c) 2006 Analog Devices Inc.\n\n");
	
	// Reset Video codecs
	ResetVideoCodecs();
		
	// Display output video mode
	if (OutputVideoMode == ITU656_NTSC)
		// NTSC video out
		printf ("ITU656 NTSC Interlaced Video Out\n\n");
	else if (OutputVideoMode == ITU656_PAL)
		// PAL video out
		printf ("ITU656 PAL Interlaced Video Out\n\n");
				
	// prompt for file name & location?
	if (File_Settings == PROMPT_FOR_FILE_SETTINGS) 
	{
	    printf("Enter directory path holding MJPEG file(s): ");
	    fflush(stdout);
	    scanf ("%s", current_path);	// get file path
	    strcat(current_path,"\\");
	    printf("Enter MJPEG (.avi) file name to decode: ");
	    fflush(stdout);
	    scanf ("%s", current_input_file_name);	// get file name
	    key = getchar();	// clear the final character (\n) in the input buffer
	}   
	else if (File_Settings == GET_FILE_SETTINGS_FROM_TXT_FILE) 
	{
  		// Open MJPEG list file
  		strcpy(current_path, SPEC_DIR); 
  		fp_mjpg_dir = fopen( strcat(current_path, MJPEG_LIST_FILE), "r" );  	
 		if (fp_mjpg_dir == NULL)
		{
			fprintf( fperr, "Error: could not read directory file\n" );
			return CODEC_FILE_ERROR;
		}
		
		printf("Reading list of MJPEG files from %s\n", MJPEG_LIST_FILE);
		// Get first File name from directory file
		next_file = fscanf(fp_mjpg_dir, "%s", current_input_file_name);
	}
	
	// Install Video Encoder (ADV717x)
	InstallVideoEncoder();

	// Format a JPEG image buffers as ITU656 frame (2 frames for MJPEG)
	if (VideoMode == ITU656_PAL)
	{
		// Format as PAL frame
		adi_video_FrameFormat ((char *)pITU656Frames[0],PAL_IL);
		// Format as PAL frame
		adi_video_FrameFormat ((char *)pITU656Frames[1],PAL_IL);
	}
	else // VideoMode must be ITU656_NTSC
	{
		// Format as NTSC frame
		adi_video_FrameFormat ((char *)pITU656Frames[0],NTSC_IL);
		// Format as NTSC frame
		adi_video_FrameFormat ((char *)pITU656Frames[1],NTSC_IL);
	}

	// Submit Video Output buffers for MJPEG
	SubmitVideoOutBufs();	
  	// Setup MDMA for YUV to ITU656 conversion
 	SetupMDMA_YUV_ITU656();
 			 		
	while((next_file != EOF) && (next_file != (int)NULL))
	{
		// clear out video frames (black screen)
		if (VideoMode == ITU656_PAL)
		{
			// Fill the Video out frame with background color
			adi_video_FrameFill ((char *)pITU656Frames[0],PAL_IL,JPEGImageBackground);
			// Fill the Video out frame with background color
			adi_video_FrameFill ((char *)pITU656Frames[1],PAL_IL,JPEGImageBackground);
		}
		else // VideoMode must be ITU656_NTSC
		{
			// Fill the Video out frame with background color
			adi_video_FrameFill ((char *)pITU656Frames[0],NTSC_IL,JPEGImageBackground);
			// Fill the Video out frame with background color
			adi_video_FrameFill ((char *)pITU656Frames[1],NTSC_IL,JPEGImageBackground);
		}

		// generate next file path only if replay is not selected		
		if (!replay)
		{
			if (File_Settings == PROMPT_FOR_FILE_SETTINGS) 
			{
	    		// check for file extension. add file extension if user didn't provide it	    	
	    		if ((strstr(current_input_file_name, ".avi") == NULL) &&
	    			(strstr(current_input_file_name, ".AVI") == NULL))
	    			strcat(current_input_file_name,".avi");	// add file extension	    				    
				// build the next input file name in tempstring
	    		strcpy (temp_path,current_path);
				strcat(temp_path, current_input_file_name);
			}
			else if (File_Settings == GET_FILE_SETTINGS_FROM_TXT_FILE) 
			{
				printf("\nInput file: %s\n", current_input_file_name);

				// build the next input file name in tempstring
				strcpy(temp_path, ROOT_DIR);
				strcat(strcat(temp_path, IMAGE_DIR_MJPEG), current_input_file_name);
			}
		}
		
		// decode the MJPEG file
		if ((return_code = MJPEG_decode(temp_path)) != CODEC_SUCCESS)
		{
			// if it was a file error, try next file in the list
			if (return_code != CODEC_FILE_ERROR)
				break;			// error! Abort loop
		}
		
		replay = FALSE;		// clear replay flag
								
		//  Fixes frame jitter at end of file by copying last frame to other output buffer
		if(pDestDescStartAddr == pITU656Frames[0]) 
    	    memcpy(pITU656Frames[1], pITU656Frames[0], (DataPerLine * FrameLines));
    	else 
    	    memcpy(pITU656Frames[0], pITU656Frames[1], (DataPerLine * FrameLines));
	
		// Wait for Key stroke
		printf("\nHit 'Return' to go to next file, 'r' to replay this file or 'q' to quit: ");
		fflush(stdout);
	
		while (key = getchar())
		{
			if ((key == 'q') || (key == 'Q'))
				next_file = EOF;
			else if ((key == 'r') || (key == 'R'))
				replay = TRUE;		// set replay flag
			else if (key == '\n')
				break;
		}

		// read next file only when replay or quit not selected by the user
		if ((next_file != EOF) && (!replay))
		{
			if (File_Settings == PROMPT_FOR_FILE_SETTINGS) 
			{
				printf("\nEnter next MJPEG (.avi) file name to decode: ");
	    		fflush(stdout);
	    		scanf ("%s", current_input_file_name);
	    		key = getchar();	// clear the final character (\n) in the input buffer
			}
			else if (File_Settings == GET_FILE_SETTINGS_FROM_TXT_FILE)
	        	// Get Next File name from directory file
				next_file = fscanf(fp_mjpg_dir, "%s", current_input_file_name);	
		}
	}

	if(return_code)
		fprintf(fperr, "\n!!! MJPEG Decoding Failed !!!\n");

	if (File_Settings == GET_FILE_SETTINGS_FROM_TXT_FILE)
		// close the list file
		fclose( fp_mjpg_dir );

	// free up Video and DMA resources	
	// Free MDMA stream used for YUV to ITU656 conversion
	FreeMDMA_YUV_ITU656 ();		
	// Close Video Encoder (ADV717x)
	ezErrorCheck(adi_dev_Close(ADV717xDeviceHandle));
		
	return CODEC_SUCCESS;
}

/*********************************************************************

    Function:       MJPEG_decode

    Description:    Decodes a selected MJPEG file
    				(by interacting with MJPEG Decode library)

*********************************************************************/	
section("App_Code_L1")
int MJPEG_decode(char* input_file_name)
{
	tJpegParam   				lImageParam;
	uint32						lMinStreamBufLength = 0;
	uint32 						lStreamBufferLength = 0;
	uint32 						lStreamFileInHandle = 0;
    uint32 						lStreamHandle = 0;
    MemObjHandle                *StreamBuffer_Obj;
    uint8						*lStreamBuffer = NULL;
	tIMG_BufferInfo				lStreamBufferInfo;
	tJpegDecoder				*JpegDecHandle = 0;
	uint8	        			*lOutputBuffer;

	unsigned int				lNumFrames = 0;
    int32 				    	lResult = 0;

	// Parameters returned from CONFIG
    unsigned int    			CodingType 	= 0;
    unsigned int    			JPEGWidth 	= 0;
    unsigned int    			JPEGHeight	= 0;

#ifdef ADI_MMSK_EXTRA_INFO		// macro to enable extra debug information    	
	// cycle counting
	static cycle_t 				start_count;
	static cycle_t 				final_count;

	static cycle_t 				min_frame_cycles;	// decode time
	static cycle_t 				max_frame_cycles;

	static cycle_t 				min_usb_cycles;	// usb read time / frame
	static cycle_t 				max_usb_cycles;
	static unsigned int 		min_usb_bytes;	// bytes / frame read
	static unsigned int 		max_usb_bytes;
	float  						cycle_sum;
#endif
	
	// Setup MJPEG Decoder
	// Read the JPEG information from the first frame of the input file
	lResult = JPEG_Param_INIT(&lImageParam);
	if(lResult != E_SUCCESS)	
	{
		JPEG_ProcessErrorCode(lResult, "JPEG_Param_INIT");
		return CODEC_FILE_ERROR;
	}
	
    // Open the AVI file
    lResult = MJPEG_AVI_OpenFileRead(&lStreamFileInHandle, (int8 *)input_file_name);
    if(lResult != MJPEG_AVI_RETURN_OK)
    {
        MJPEG_ProcessErrorCode(lResult, "MJPEG_AVI_OpenFileRead");
        fprintf(fperr, "Error while opening the Input file: %s \n", input_file_name);
        return CODEC_FILE_ERROR;
    }
    
    // Get the handle for AVI stream
    lResult = MJPEG_AVI_OpenStreamRead(lStreamFileInHandle, &lStreamHandle, MJPEG_AVI_StreamTypeVIDEO, 0);
    if(lResult != MJPEG_AVI_RETURN_OK)
    {
        MJPEG_ProcessErrorCode(lResult, "MJPEG_AVI_OpenStreamRead");
        return CODEC_FILE_ERROR;
    }
	
    // go to start of the frame
    lResult = MJPEG_AVI_RewindToFirstFrame(lStreamHandle, &lStreamBufferLength);
    
	if(lResult == MJPEG_AVI_RETURN_NOMORESAMPLES)
    	fprintf(fperr, "Not enough samples to read the first JPEG image from stream\n");    	
    else if(lResult != MJPEG_AVI_RETURN_OK)
    {
        MJPEG_ProcessErrorCode(lResult, "MJPEG_AVI_RewindToFirstFrame");
        return CODEC_FILE_ERROR;
    }
    
    // Dynamically allocate memory to read a MJPEG frame
    StreamBuffer_Obj = JPEG_MemAlloc_NEW(lStreamBufferLength, 1, MEM_TYPE_DATA);

    if(StreamBuffer_Obj == NULL)
    {
    	fprintf(fperr,"Memory allocation for StreamBuffer failed.\n");
		fprintf(fperr, "Could not allocate %d bytes for input buffer\n", lStreamBufferLength); return CODEC_FILE_ERROR;
	}

	// get the address of memory location where the MJPEG frame will be read to
	lStreamBuffer = (uint8*)JPEG_MemAlloc_ADDRESS(StreamBuffer_Obj);

	// check if there is any MJPEG frame available for decoding
	lResult = MJPEG_AVI_ReadNextFrame(lStreamHandle, lStreamBuffer, &lMinStreamBufLength);
	if(lResult != MJPEG_AVI_RETURN_OK)
    {
        MJPEG_ProcessErrorCode(lResult, "MJPEG_AVI_ReadNextFrame");
		JPEG_MemAlloc_DELETE(StreamBuffer_Obj);
        return CODEC_FILE_ERROR;
    }
	
    // setup JPEG Decoder
	lStreamBufferInfo.Length   = lStreamBufferLength;	// frame size
	lStreamBufferInfo.Pointer  = lStreamBuffer;			// pointer to MJPEG frame in Blackfin memory
	
	// set the input buffer address to JPEG library
	lResult = JPEG_Param_CONFIG(&lImageParam, JPEG_POINTER_INPUT, (int)&lStreamBufferInfo);
	if(lResult != E_SUCCESS)    
	{
        JPEG_ProcessErrorCode(lResult, "JPEG_Param_CONFIG");
		JPEG_MemAlloc_DELETE(StreamBuffer_Obj);
        return CODEC_ALGORITHM_ERROR;
    }
    
    // YUV output buffer address
	lOutputBuffer = (uint8*)pYUVBufs[0];
	
	// set the output buffer address to JPEG library
	lResult = JPEG_Param_CONFIG(&lImageParam, JPEG_POINTER_OUTPUT, (int)lOutputBuffer);
	if(lResult != E_SUCCESS)    
	{
        JPEG_ProcessErrorCode(lResult, "JPEG_Param_CONFIG");
        JPEG_MemAlloc_DELETE(StreamBuffer_Obj);
        return CODEC_ALGORITHM_ERROR;
    }
    
	// create a JPEG Decoder instance
	JpegDecHandle= JPEG_Decoder_NEW(&lImageParam);

	if(JpegDecHandle == NULL)
	{
		fprintf(fperr, "Could not create JPEG decoder instance\n");
		JPEG_MemAlloc_DELETE(StreamBuffer_Obj);
		return CODEC_FILE_ERROR;
	}

	// Process JPEG file header info
	lResult = JPEG_ProcessHeader(JpegDecHandle, &lImageParam);
	if(lResult != E_SUCCESS)
    {
        JPEG_ProcessErrorCode(lResult, "JPEG_ProcessHeader");
        JPEG_MemAlloc_DELETE(StreamBuffer_Obj);
        JPEG_Decoder_DELETE(JpegDecHandle);
        return CODEC_FILE_ERROR;
    }
	
    // Get frame width	
	lResult = JPEG_Param_STATUS(&lImageParam, JPEG_FRAME_WIDTH, &JPEGWidth);
	if(lResult != E_SUCCESS)	JPEG_ProcessErrorCode(lResult, "JPEG_Param_STATUS");
	// Get frame height
	lResult = JPEG_Param_STATUS(&lImageParam, JPEG_FRAME_HEIGHT, &JPEGHeight);
	if(lResult != E_SUCCESS)	JPEG_ProcessErrorCode(lResult, "JPEG_Param_STATUS");
	// Get frame format
	lResult = JPEG_Param_STATUS(&lImageParam, JPEG_IMAGEFORMAT, &JPEGFrameFormat);
	if(lResult != E_SUCCESS)	JPEG_ProcessErrorCode(lResult, "JPEG_Param_STATUS");
	// Get coding type
	lResult = JPEG_Param_STATUS(&lImageParam, JPEG_ENCODINGMODE, &CodingType);
	if(lResult != E_SUCCESS)	JPEG_ProcessErrorCode(lResult, "JPEG_Param_STATUS");
	
	if( (JPEGWidth * JPEGHeight) > (MAX_JPEG_IMAGE_WIDTH * MAX_JPEG_IMAGE_HEIGHT) )
	{
	    // This image can't be decoded as its bigger than available buffer size
		fprintf(fperr, "Image size larger than YUV buffer size\n");
		fprintf(fperr, "Image size - %d x %d pixels (includes padding), YUV size - %d x %d pixels\n",\
						JPEGWidth,JPEGHeight,MAX_JPEG_IMAGE_WIDTH,MAX_JPEG_IMAGE_HEIGHT);
		fprintf(fperr, "This file will be skipped!");
		printf("\n");
		JPEG_MemAlloc_DELETE(StreamBuffer_Obj);
		JPEG_Decoder_DELETE(JpegDecHandle);
		return CODEC_FILE_ERROR;
	}

    // Check the decoded MJPEG image Frame Format
	if (JPEGFrameFormat != 1)
	{
	    // image format not supported by this app
		fprintf(fperr,"Input image format not supported, \n");
		fprintf(fperr,"this version of developer kit only support YUV420 format files\n");
		JPEG_MemAlloc_DELETE(StreamBuffer_Obj);
		JPEG_Decoder_DELETE(JpegDecHandle);
		return CODEC_FILE_ERROR;
	}

    // go to start of the frame
	lResult = MJPEG_AVI_RewindToFirstFrame(lStreamHandle, &lStreamBufferLength);
	if(lResult != MJPEG_AVI_RETURN_OK)	MJPEG_ProcessErrorCode(lResult, "MJPEG_AVI_RewindToFirstFrame");

 	// initialise YUV to video (ITU656) frame conversion parameters
 	JPEGImageWidth 	= JPEGWidth;
 	JPEGImageHeight	= JPEGHeight;
 	// Source Descriptor start address - YUV buffer(s)
 	pSrcDescStartAddr	= pYUVBufs[0];
 	// Destination Descriptor start buffer - Video (ITU656) buffer(s)
 	pDestDescStartAddr	= pITU656Frames[0];
 			
 	// switch to JPEG image Frame format
 	switch (JPEGFrameFormat)
 	{
 	    // case (JPEG image is in YUV420 format)
 		case 1:
			// setup YUV420 to ITU656 frame conversion
			SetupMDMA_YUV420toITU656 ();

		// this would never occur as we've already validated JPEG image format
		default:	
			break;
 	}

#ifdef ADI_MMSK_EXTRA_INFO		// macro to enable extra debug information    	
	// check if the image frame is bigger than output display resolution size
	if ((JPEGWidth * JPEGHeight) > (ITU_PIXEL_PER_LINE * ActiveFrameLines))
	    // This image will be clipped
		printf("Frame size larger than Display resolution. This file will be clipped!\n\n");

	printf("Actual  Frame Size = %d x %d pixels\n", JPEGWidth, JPEGHeight);
	printf("Display Frame Size = %d x %d pixels\n", ITU_PIXEL_PER_LINE, ActiveFrameLines);
#endif

	printf("\nPlaying MJPEG file.Do not press any keys until prompted...\n");

/****************************
Start Decoding MJPEG file
****************************/

	// Initialise counters/flags
	BufferLevel 	= 0;
  	StartMDMA 		= FALSE;
    FrameReady 		= TRUE;
	MJPEG_YUV_BufID = 0;
	YUV_MDMA_BufID	= 0;	
	lNumFrames 		= 0;	// # of frames played
	isMJDInputDataAvailable = E_TRUE;
	FrameDroppedCounter = 0;					// # of frames dropped

#ifdef ADI_MMSK_EXTRA_INFO		// macro to enable extra debug information    	
	// initialise statistics
	max_frame_cycles = 0;
	min_frame_cycles = (cycle_t)10*600*1000000;		// set to time approx equivalent to 10Seconds at 600MHz - no frame should take longer than that to decode :)

	max_usb_cycles = 0;
	min_usb_cycles = (cycle_t)10*600*1000000;		// set to time approx equivalent to 10Seconds at 600MHz - no frame should take longer than that to be read :)
	max_usb_bytes = 0;
	min_usb_bytes = (unsigned int)0x0fffffff;		// big value for min calculation
	cycle_sum = 0;
#endif

	// Continue decoding till reaching end of file
	while(E_TRUE == isMJDInputDataAvailable)
	{
		// Enable MDMA only when half of YUV buffers is filled with MJPEG frame (YUV) data
		if( (!StartMDMA) && (BufferLevel >= NUM_YUV_BUFS) )
			StartMDMA = TRUE;

#ifdef ADI_MMSK_EXTRA_INFO		// Extra debug information
	START_CYCLE_COUNT(start_count)	// begin counting time
#endif

		// Read next JPEG frame
		lResult = MJPEG_AVI_ReadNextFrame(lStreamHandle, lStreamBuffer, &lMinStreamBufLength);
		
		MJPEG_ProcessErrorCode(lResult, "MJPEG_AVI_ReadNextFrame");
		
        switch (lResult)
		{
			case MJPEG_AVI_RETURN_OK:
				break;

			case MJPEG_AVI_RETURN_NOMORESAMPLES:
#ifdef ADI_MMSK_EXTRA_INFO		// Extra debug information	
				printf("Reached end of MJPEG stream\n");
#endif				
				isMJDInputDataAvailable = E_FALSE;
				continue;

			case MJPEG_AVI_RETURN_EVALUATIONLIMITREACHED:
				fprintf(fperr, "*** Reached limit of evaluation library ***\n");
				isMJDInputDataAvailable = E_FALSE;
				continue;

			default:
				printf("Cannot read to next frame of the MJPEG stream.  Error code = %d\n", lResult);
				return CODEC_FILE_ERROR;
		}

#ifdef ADI_MMSK_EXTRA_INFO		// Extra debug information
		STOP_CYCLE_COUNT(final_count,start_count)	// stop counting
		// update cycle measurements
		min_usb_cycles = min(min_usb_cycles, final_count);
		max_usb_cycles = max(max_usb_cycles, final_count);
		min_usb_bytes = min(min_usb_bytes, lMinStreamBufLength);
		max_usb_bytes = max(max_usb_bytes, lMinStreamBufLength);	
#endif

		// Next YUV buffer to hold decoded MJPEG frame data
		lOutputBuffer = (uint8*)(pYUVBufs[MJPEG_YUV_BufID]);
		// update the output buffer address to store the decoded image
		lResult = JPEG_Param_CONFIG(&lImageParam, JPEG_POINTER_OUTPUT, (int)lOutputBuffer);
		JPEG_McuBuffer_CONFIG(mcu_ex, MCU_POINTER_C1, (unsigned int)lOutputBuffer);		
    	if(lResult != E_SUCCESS)	JPEG_ProcessErrorCode(lResult, "JPEG_Param_CONFIG");        

		// Wait for free buffers for decoding
		while ( (BufferLevel >= NUM_YUV_BUFS) );

#ifdef ADI_MMSK_EXTRA_INFO		// Extra debug information
		START_CYCLE_COUNT(start_count)	// begin counting time
#endif
		
		// Decode this frame
		lResult = JPEG_DecodeSequentialImage(JpegDecHandle, &lImageParam);

#ifdef ADI_MMSK_EXTRA_INFO		// Extra debug information
		STOP_CYCLE_COUNT(final_count,start_count)	// stop counting
		cycle_sum += (float)final_count;
		// update cycle measurements
		min_frame_cycles = min(min_frame_cycles, final_count);
		max_frame_cycles = max(max_frame_cycles, final_count);
#endif
		
		if (lResult != E_SUCCESS)
		{
	    	MJPEG_ProcessErrorCode(lResult, "JPEG_DecodeSequentialImage");			
	    	fprintf(fperr, "Failed to Decode this file!\n");
			JPEG_Decoder_DELETE(JpegDecHandle);
	    	JPEG_MemAlloc_DELETE(StreamBuffer_Obj);
			return CODEC_ALGORITHM_ERROR;
		}

		// Update the count (next YUV buffer to be used by MJPEG decoder)
		++MJPEG_YUV_BufID;
		if (MJPEG_YUV_BufID >= NUM_YUV_BUFS)
			MJPEG_YUV_BufID = 0;
		
		// increment buffer level
		++BufferLevel;
		// increment frame counter
		lNumFrames++;
	}		// while (E_TRUE == isMJDInputDataAvailable)

	// Wait for all remaining frames to be played
	while (BufferLevel > 0);
	
	// Disable MDMA
	StartMDMA = false;

	// some statistics
	printf("\nPlayed %d Frames", lNumFrames);

#ifdef ADI_MMSK_EXTRA_INFO		// Extra debug information	
	printf(", Dropped %d Frame(s)\n\n", FrameDroppedCounter);
	printf("Slowest Frame decoded in %llu cycles, equivalent to %lu cyles/pixel\n", max_frame_cycles,max_frame_cycles/(JPEGWidth * JPEGHeight));
	printf("Fastest Frame decoded in %llu cycles, equivalent to %lu cyles/pixel\n", min_frame_cycles,min_frame_cycles/(JPEGWidth * JPEGHeight));	
	printf("Average decoding in %.0f cycles, equivalent to %.2f cycles/pixel\n",cycle_sum/lNumFrames,cycle_sum/lNumFrames/(JPEGWidth*JPEGHeight));
	printf("Slowest Frame read %d bytes over USB in %llu cycles\n", max_usb_bytes, max_usb_cycles);
	printf("Fastest Frame read %d bytes over USB in %llu cycles\n", min_usb_bytes, min_usb_cycles);
#endif 

	// clean up and close streams
	if (MJPEG_AVI_CloseStreamRead(lStreamHandle) == MJPEG_AVI_RETURN_ERROR)
	{
		fprintf(fperr, "Cannot close MJPEG Decoder AVI stream\n");
		JPEG_Decoder_DELETE(JpegDecHandle);
	    JPEG_MemAlloc_DELETE(StreamBuffer_Obj);
		return CODEC_FILE_ERROR;
	}

	if (MJPEG_AVI_CloseFileRead(lStreamFileInHandle) == MJPEG_AVI_RETURN_ERROR)
	{
		fprintf(fperr, "Cannot close MJPEG Decoder input file\n");
		JPEG_Decoder_DELETE(JpegDecHandle);
	    JPEG_MemAlloc_DELETE(StreamBuffer_Obj);
		return CODEC_FILE_ERROR;
	}

	// Free and destroy used resources
	JPEG_Decoder_DELETE(JpegDecHandle);
    JPEG_MemAlloc_DELETE(StreamBuffer_Obj);

	printf("\n***  MJPEG Playback successful ***\n");
  	return CODEC_SUCCESS;
}

/*****/
