/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

// Authors: Konstantin Dols <dols@ient.rwth-aachen.de>
//          Mark Asbach <asbach@ient.rwth-aachen.de>
//
//          Institute of Communications Engineering
//          RWTH Aachen University


#include "_highgui.h"

// required to enable some functions used here...
#define XINE_ENABLE_EXPERIMENTAL_FEATURES

#include <cv.h>
#include <cassert>

extern "C"
{
#include <xine.h>
	//#include <xine/xineutils.h>

	// forward declaration from <xine/xineutils.h>
	const char *xine_get_homedir( void );
}

typedef struct CvCaptureAVI_XINE
{
	/// method call table
	CvCaptureVTable * vtable;

	xine_t * xine;
	xine_stream_t * stream;
	xine_video_port_t * vo_port;

	/// frame returned by xine_get_next_video_frame()
	xine_video_frame_t xine_frame;

	IplImage	* yuv_frame;
	IplImage	* bgr_frame;

	/// image dimansions of the input stream.
	CvSize	size;

	/// framenumber of the last frame received from xine_get_next_video_frame().
	/// note: always keep this value updated !!!!
	int	frame_number;

	/// framerate of the opened stream
	double	frame_rate;

	/// duration of a frame in stream
	double	frame_duration;

	/// indicated if input is seekable
	bool	seekable;

}
CvCaptureAVI_XINE;


// 4:2:2 interleaved -> BGR
static void icvYUY2toBGR( CvCaptureAVI_XINE * capture )
{
	uint8_t * v	= capture->xine_frame.data;
	int offset;
	for ( int y = 0; y < capture->yuv_frame->height; y++ )
	{
		offset	= y * capture->yuv_frame->widthStep;

		for ( int x = 0; x < capture->yuv_frame->width; x++, offset += 3 )
		{
			capture->yuv_frame->imageData[ offset + 1 ] = v[ 3 ];
			capture->yuv_frame->imageData[ offset + 2 ] = v[ 1 ];
			if ( x & 1 )
			{
				capture->yuv_frame->imageData[ offset ] = v[ 2 ];
				v += 4;
			}
			else
			{
				capture->yuv_frame->imageData[ offset ] = v[ 0 ];
			}
		}
	}

	// convert to BGR
	cvCvtColor( capture->yuv_frame, capture->bgr_frame, CV_YCrCb2BGR );
}


// 4:2:0 planary -> BGR
static void icvYV12toBGR( CvCaptureAVI_XINE * capture )
{
	IplImage * yuv	= capture->yuv_frame;
	int	w_Y	= capture->size.width;
	int	h_Y	= capture->size.height;

	int	w_UV	= w_Y >> 1;

	int	size_Y	= w_Y * h_Y;
	int	size_UV	= size_Y / 4;

	int	line	= yuv->widthStep;

	uint8_t * addr_Y = capture->xine_frame.data;
	uint8_t * addr_U = addr_Y + size_Y;
	uint8_t * addr_V = addr_U + size_UV;

	// YYYY..UU.VV. -> BGRBGRBGR...
	for ( int y = 0; y < h_Y; y++ )
	{
		int offset = y * line;
		for ( int x = 0; x < w_Y; x++, offset += 3 )
		{
			/*
			if ( x&1 )
			{
				addr_U++; addr_V++;
			}
			*/
			int one_zero = x & 1;
			addr_U += one_zero;
			addr_V += one_zero;

			yuv->imageData[ offset ] = *( addr_Y++ );
			yuv->imageData[ offset + 1 ] = *addr_U;
			yuv->imageData[ offset + 2 ] = *addr_V;
		}

		if ( y & 1 )
		{
			addr_U -= w_UV;
			addr_V -= w_UV;
		}
	}

	/* convert to BGR */
	cvCvtColor( capture->yuv_frame, capture->bgr_frame, CV_YCrCb2BGR );
}

static void icvCloseAVI_XINE( CvCaptureAVI_XINE* capture )
{
	xine_free_video_frame( capture->vo_port, &capture->xine_frame );

	if ( capture->yuv_frame ) cvReleaseImage( &capture->yuv_frame );
	if ( capture->bgr_frame ) cvReleaseImage( &capture->bgr_frame );

	xine_close( capture->stream );
	//	xine_dispose( capture->stream );

	if ( capture->vo_port ) xine_close_video_driver( capture->xine, capture->vo_port );

	xine_exit( capture->xine );
}


/**
 * CHECKS IF THE STREAM IN * capture IS SEEKABLE.
**/
static void icvCheckSeekAVI_XINE( CvCaptureAVI_XINE * capture )
{
	OPENCV_ASSERT ( capture,                        "icvCheckSeekAVI_XINE( CvCaptureAVI_XINE* )", "illegal capture");
	OPENCV_ASSERT ( capture->stream,
                        "icvCheckSeekAVI_XINE( CvCaptureAVI_XINE* )", "illegal capture->stream");
	OPENCV_ASSERT ( capture->vo_port,
                        "icvCheckSeekAVI_XINE( CvCaptureAVI_XINE* )", "illegal capture->vo_port");

#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvCheckSeekAVI_XINE ... start\n" );
#endif

	// temp. frame for testing.
	xine_video_frame_t tmp;
	// try to seek to a future frame...
	xine_play( capture->stream, 0, 300 ); /* 300msec */
	// try to receive the frame...
	xine_get_next_video_frame( capture->vo_port, &tmp );
	// if the framenumber is still 0, we can't use the xine seek functionality
	capture->seekable = ( tmp.frame_number != 0 );
	// reset stream
	xine_play( capture->stream, 0, 0 );
	// release xine_frame
	xine_free_video_frame( capture->vo_port, &tmp );

#ifndef NDEBUG
	if ( capture->seekable )
		fprintf( stderr, "(DEBUG) icvCheckSeekAVI_XINE: Input is seekable, using XINE seek implementation.\n" );
	else
		fprintf( stderr, "(DEBUG) icvCheckSeekAVI_XINE: Input is NOT seekable, using fallback function.\n" );
	
	fprintf( stderr, "(DEBUG) icvCheckSeekAVI_XINE ... end\n" );
#endif
}


static int icvOpenAVI_XINE( CvCaptureAVI_XINE* capture, const char* filename )
{
#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvOpenAVI_XINE ... start\n" );
#endif

	char configfile[ 2048 ];

	capture->xine = xine_new();
	sprintf( configfile, "%s%s", xine_get_homedir(), "/.xine/config" );

	xine_config_load( capture->xine, configfile );
	xine_init( capture->xine );

	xine_engine_set_param( capture->xine, 0, 0 );
	capture->vo_port = xine_new_framegrab_video_port( capture->xine );
	if ( capture->vo_port == NULL )
	{
		printf( "(ERROR)icvOpenAVI_XINE(): Unable to initialize video driver.\n" );
		return 0;
	}

	capture->stream = xine_stream_new( capture->xine, NULL, capture->vo_port );

	if ( !xine_open( capture->stream, filename ) )
	{
		printf( "(ERROR)icvOpenAVI_XINE(): Unable to open source '%s'\n", filename );
		return 0;
	}
	// reset stream...
	xine_play( capture->stream, 0, 0 );
	

	// initialize some internals...
	capture->frame_number = 0;

	if ( !xine_get_next_video_frame( capture->vo_port, &capture->xine_frame ) )
	{
#ifndef NDEBUG
		fprintf( stderr, "(DEBUG) icvOpenAVI_XINE ... failed!\n" );
#endif
		return 0;
	}

	capture->size = cvSize( capture->xine_frame.width, capture->xine_frame.height );
	capture->yuv_frame = cvCreateImage( capture->size, IPL_DEPTH_8U, 3 );
	capture->bgr_frame = cvCreateImage( capture->size, IPL_DEPTH_8U, 3 );

	xine_free_video_frame( capture->vo_port, &capture->xine_frame );
	capture->xine_frame.data[ 0 ] = 0;

	icvCheckSeekAVI_XINE( capture );

	capture->frame_duration = xine_get_stream_info( capture->stream, XINE_STREAM_INFO_FRAME_DURATION ) / 90.;
	capture->frame_rate = 1000 / capture->frame_duration;

#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) frame_duration = %f, framerate = %f\n", capture->frame_duration, capture->frame_rate );
#endif
	
	OPENCV_ASSERT ( capture->yuv_frame,
                        "icvOpenAVI_XINE( CvCaptureAVI_XINE *, const char *)", "couldn't create yuv frame");
			
	OPENCV_ASSERT ( capture->bgr_frame,
                        "icvOpenAVI_XINE( CvCaptureAVI_XINE *, const char *)", "couldn't create bgr frame");
			
#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvOpenAVI_XINE ... end\n" );
#endif
	return 1;
}


static int icvGrabFrameAVI_XINE( CvCaptureAVI_XINE* capture )
{
#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvGrabFrameAVI_XINE ... start\n" );
#endif

	OPENCV_ASSERT ( capture,
                        "icvGrabFrameAVI_XINE( CvCaptureAVI_XINE * )", "illegal capture");
	OPENCV_ASSERT ( capture->vo_port,
                        "icvGrabFrameAVI_XINE( CvCaptureAVI_XINE * )", "illegal capture->vo_port");

	int res = xine_get_next_video_frame( capture->vo_port, &capture->xine_frame );

	/* always keep internal framenumber updated !!! */
	if ( res ) capture->frame_number++;

#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvGrabFrameAVI_XINE ... end\n" );
#endif
	return res;
}


static const IplImage* icvRetrieveFrameAVI_XINE( CvCaptureAVI_XINE* capture )
{
#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvRetrieveFrameAVI_XINE ... start\n" );
#endif

	OPENCV_ASSERT ( capture,
                        "icvRetrieveFrameAVI_XINE( CvCaptureAVI_XINE * )", "illegal capture");
	OPENCV_ASSERT ( capture->stream,
                        "icvRetrieveFrameAVI_XINE( CvCaptureAVI_XINE * )", "illegal capture->stream");
	OPENCV_ASSERT ( capture->vo_port,
                        "icvRetrieveFrameAVI_XINE( CvCaptureAVI_XINE * )", "illegal capture->vo_port");

	/* no frame grabbed yet? so let's do it now! */
	int res = 0;
	if ( capture->xine_frame.data == 0 )
	{
		res = icvGrabFrameAVI_XINE( capture );
	}
	else
	{
		res = 1;
	}

	if ( res )
	{
		switch ( capture->xine_frame.colorspace )
		{
				case XINE_IMGFMT_YV12: icvYV12toBGR( capture );
#ifndef NDEBUG
				printf( "(DEBUG)icvRetrieveFrameAVI_XINE: converted YV12 to BGR.\n" );
#endif
				break;

				case XINE_IMGFMT_YUY2: icvYUY2toBGR( capture );
#ifndef NDEBUG
				printf( "(DEBUG)icvRetrieveFrameAVI_XINE: converted YUY2 to BGR.\n" );
#endif
				break;
				case XINE_IMGFMT_XVMC: printf( "(ERROR)icvRetrieveFrameAVI_XINE: XVMC format not supported!\n" );
				break;

				case XINE_IMGFMT_XXMC: printf( "(ERROR)icvRetrieveFrameAVI_XINE: XXMC format not supported!\n" );
				break;

				default: printf( "(ERROR)icvRetrieveFrameAVI_XINE: unknown color/pixel format!\n" );
		}

		/* always release last xine_frame, not needed anymore, but store its frame_number in *capture ! */
		xine_free_video_frame( capture->vo_port, &capture->xine_frame );
		capture->xine_frame.data = 0;

#ifndef NDEBUG
		fprintf( stderr, "(DEBUG) icvRetrieveFrameAVI_XINE ... end\n" );
#endif
		return capture->bgr_frame;
	}

#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvRetrieveFrameAVI_XINE ... failed!\n" );
#endif
	return 0;
}


/**
 * THIS FUNCTION IS A FALLBACK FUNCTION FOR THE CASE THAT THE XINE SEEK IMPLEMENTATION
 * DOESN'T WORK WITH THE ACTUAL INPUT. THIS FUNCTION IS ONLY USED IN THE CASE OF AN EMERGENCY,
 * BECAUSE IT IS VERY SLOW !
**/
static int icvOldSeekFrameAVI_XINE( CvCaptureAVI_XINE* capture, int f )
{
#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvOldSeekFrameAVI_XINE ... start\n" );
#endif

	OPENCV_ASSERT ( capture,
                        "icvRetricvOldSeekFrameAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture");
	OPENCV_ASSERT ( capture->stream,
                        "icvOldSeekFrameAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture->stream");
	OPENCV_ASSERT ( capture->vo_port,
                        "icvOldSeekFrameAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture->vo_port");

// not needed tnx to asserts...				
	// we need a valid capture context and it's stream to seek through
//	if ( !capture || !capture->stream ) return 0;

	// no need to seek if we are already there...
	if ( f == capture->frame_number )
	{
#ifndef NDEBUG
		fprintf( stderr, "(DEBUG) icvOldSeekFrameAVI_XINE ... end\n" );
#endif
		return 1;
	}
	// if the requested position is behind out actual position,
	// we just need to read the remaining amount of frames until we are there.
	else if ( f > capture->frame_number )
	{
		for ( ;capture->frame_number < f;capture->frame_number++ )
			/// un-increment framenumber grabbing failed
			if ( !xine_get_next_video_frame( capture->vo_port, &capture->xine_frame ) )
			{
				capture->frame_number--;
				break;
			}
			else
			{
				xine_free_video_frame( capture->vo_port, &capture->xine_frame );
			}
	}
	// otherwise we need to reset the stream and
	// start reading frames from the beginning.
	else // f < capture->frame_number
	{
		/// reset stream, should also work with non-seekable input
		xine_play( capture->stream, 0, 0 );
		/// read frames until we are at the requested frame
		for ( capture->frame_number = 0; capture->frame_number < f; capture->frame_number++ )
			/// un-increment last framenumber if grabbing failed
			if ( !xine_get_next_video_frame( capture->vo_port, &capture->xine_frame ) )
			{
				capture->frame_number--;
				break;
			}
			else
			{
				xine_free_video_frame( capture->vo_port, &capture->xine_frame );
			}
	}


#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvOldSeekFrameAVI_XINE ... end\n" );
#endif
	return ( f == capture->frame_number ) ? 1 : 0;
}


static int icvSeekFrameAVI_XINE( CvCaptureAVI_XINE* capture, int f )
{
#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvSeekFrameAVI_XINE ... start\n" );
#endif

	OPENCV_ASSERT ( capture,
                        "icvSeekFrameAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture");
	OPENCV_ASSERT ( capture->stream,
                        "icvSeekFrameAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture->stream");
	OPENCV_ASSERT ( capture->vo_port,
                        "icvSeekFrameAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture->vo_port");

// not needed tnx to asserts...				
	// we need a valid capture context and it's stream to seek through
//	if ( !capture || !capture->stream ) return 0;

	if ( capture->seekable )
	{

		/// use xinelib's seek functionality
		int new_time = ( int ) ( ( f + 1 ) * ( float ) capture->frame_duration );

#ifndef NDEBUG
		fprintf( stderr, "(DEBUG) calling xine_play()" );
#endif
		if ( xine_play( capture->stream, 0, new_time ) )
		{
#ifndef NDEBUG
			fprintf( stderr, "ok\n" );
			fprintf( stderr, "(DEBUG) icvSeekFrameAVI_XINE ... end\n" );
#endif
			capture->frame_number = f;
			return 1;
		}
		else
		{
#ifndef NDEBUG
			fprintf( stderr, "failed\n" );
			fprintf( stderr, "(DEBUG) icvSeekFrameAVI_XINE ... failed\n" );
#endif
			return 0;
		}
	}
	else
	{
#ifndef NDEBUG
		fprintf( stderr, "(DEBUG) icvSeekFrameAVI_XINE ... end\n" );
#endif
		return icvOldSeekFrameAVI_XINE( capture, f );
	}
}


static int icvSeekTimeAVI_XINE( CvCaptureAVI_XINE* capture, int t )
{
#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvSeekTimeAVI_XINE ... start\n" );
#endif

	OPENCV_ASSERT ( capture,
                        "icvSeekTimeAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture");
	OPENCV_ASSERT ( capture->stream,
                        "icvSeekTimeAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture->stream");
	OPENCV_ASSERT ( capture->vo_port,
                        "icvSeekTimeAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture->vo_port");
			
#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvSeekTimeAVI_XINE ... start\n" );
#endif	

// not needed tnx to asserts...				
	// we need a valid capture context and it's stream to seek through
//	if ( !capture || !capture->stream ) return 0;

	if ( capture->seekable )
	{
		/// use xinelib's seek functionality
		if ( xine_play( capture->stream, 0, t ) )
		{
			capture->frame_number = ( int ) ( ( float ) t * capture->frame_rate / 1000 );
#ifndef NDEBUG			
			fprintf( stderr, "(DEBUG) icvSeekFrameAVI_XINE ... end\n" );
#endif
			return 1;
		}
		else
		{
#ifndef NDEBUG		
			fprintf( stderr, "(DEBUG) icvSeekFrameAVI_XINE ... failed!\n" );
#endif
			return 0;
		}
	}
	else
	{
		int new_frame = ( int ) ( ( float ) t * capture->frame_rate / 1000 );
#ifndef NDEBUG				
		fprintf( stderr, "(DEBUG) icvSeekFrameAVI_XINE ....end\n" );
#endif
		return icvOldSeekFrameAVI_XINE( capture, new_frame );
	}
}


static int icvSeekRatioAVI_XINE( CvCaptureAVI_XINE* capture, double ratio )
{
#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvSeekRatioAVI_XINE ... start\n" );
#endif

	OPENCV_ASSERT ( capture,
                        "icvSeekRatioAVI_XINE( CvCaptureAVI_XINE *, double )", "illegal capture");
	OPENCV_ASSERT ( capture->stream,
                        "icvSeekRatioAVI_XINE( CvCaptureAVI_XINE *, double )", "illegal capture->stream");
	OPENCV_ASSERT ( capture->vo_port,
                        "icvSeekRatioAVI_XINE( CvCaptureAVI_XINE *, double )", "illegal capture->vo_port");

// not needed tnx to asserts...				
	// we need a valid capture context and it's stream to seek through
//	if ( !capture || !capture->stream ) return 0;

	/// ratio must be [0..1]
	if ( ratio > 1 || ratio < 0 ) return 0;

	if ( capture->seekable )
	{
	// TODO: FIX IT, DOESN'T WORK PROPERLY, YET...!
		int pos_t, pos_l, length;
		xine_get_pos_length( capture->stream, &pos_l, &pos_t, &length );
		fprintf( stderr, "ratio on GetProperty(): %n\n", pos_l );

		/// use xinelib's seek functionality
		if ( xine_play( capture->stream, (int)(ratio*(float)length), 0 ) )
		{
			capture->frame_number = ( int ) ( ratio*length / capture->frame_duration );
		}
		else
		{
#ifndef NDEBUG
			fprintf( stderr, "(DEBUG) icvSeekRatioAVI_XINE ... failed!\n" );
#endif
			return 0;
		}
	}
	else
	{
		/// TODO: fill it !
		fprintf( stderr, "icvSeekRatioAVI_XINE(): Seek not supported by stream !\n" );
		fprintf( stderr, "icvSeekRatioAVI_XINE(): (seek in stream with NO seek support NOT implemented...yet!)\n" );
#ifndef NDEBUG
		fprintf( stderr, "(DEBUG) icvSeekRatioAVI_XINE ... failed!\n" );
#endif
		return 0;
	}

#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvSeekRatioAVI_XINE ... end!\n" );
#endif
	return 1;
}


static double icvGetPropertyAVI_XINE( CvCaptureAVI_XINE* capture, int property_id )
{
#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvGetPropertyAVI_XINE ... start\n" );
#endif

	OPENCV_ASSERT ( capture,
                        "icvGetPropertyAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture");
	OPENCV_ASSERT ( capture->stream,
                        "icvGetPropertyAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture->stream");
	OPENCV_ASSERT ( capture->vo_port,
                        "icvGetPropertyAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture->vo_port");
	OPENCV_ASSERT ( capture->xine,
                        "icvGetPropertyAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture->xine");
	OPENCV_ASSERT ( capture->bgr_frame,
                        "icvGetPropertyAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture->bgr_frame");

// not needed tnx to asserts...				
	// we need a valid capture context and it's stream to seek through
//	if ( !capture || !capture->stream || !capture->bgr_frame || !capture->xine || !capture->vo_port ) return 0

	int pos_t, pos_l, length;
	xine_get_pos_length( capture->stream, &pos_l, &pos_t, &length );
	fprintf( stderr, "ratio on GetProperty(): %i\n", pos_l );

	switch ( property_id )
	{
			/// return actual position in msec
			case CV_CAP_PROP_POS_MSEC:
			if ( !capture->seekable )
			{
				fprintf( stderr, "(ERROR) GetPropertyAVI_XINE(CV_CAP_PROP_POS_MSEC:\n" );
				fprintf( stderr, "	Stream is NOT seekable, so position info may NOT be valid !!\n" );
			}
			return pos_t;

			/// return actual frame number
			case CV_CAP_PROP_POS_FRAMES:
			/// we insist the capture->frame_number to be remain updated !!!!
			return capture->frame_number;

			/// return actual position ratio in the range [0..1] depending on
			/// the total length of the stream and the actual position
			case CV_CAP_PROP_POS_AVI_RATIO:
			if ( !capture->seekable )
			{
				fprintf( stderr, "(ERROR) GetPropertyAVI_XINE(CV_CAP_PROP_POS_AVI_RATIO:\n" );
				fprintf( stderr, "	Stream is NOT seekable, so ratio info may NOT be valid !!\n" );
			}
			if ( length == 0 ) break;
			else return pos_l / 65535;


			/// return width of image source
			case CV_CAP_PROP_FRAME_WIDTH:
			return capture->size.width;

			/// return height of image source
			case CV_CAP_PROP_FRAME_HEIGHT:
			return capture->size.height;

			/// return framerate of stream
			case CV_CAP_PROP_FPS:
			if ( !capture->seekable )
			{
				fprintf( stderr, "(ERROR) GetPropertyAVI_XINE(CV_CAP_PROP_FPS:\n" );
				fprintf( stderr, "	Stream is NOT seekable, so FPS info may NOT be valid !!\n" );
			}
			return capture->frame_rate;

			/// return four-character-code (FOURCC) of source's codec
			case CV_CAP_PROP_FOURCC:
			return ( double ) xine_get_stream_info( capture->stream, XINE_STREAM_INFO_VIDEO_FOURCC );
	}

#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvGetPropertyAVI_XINE ... failed!\n" );
#endif

	return 0;
}


static int icvSetPropertyAVI_XINE( CvCaptureAVI_XINE* capture,
                                   int property_id, double value )
{
#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvSetPropertyAVI_XINE ... start\n" );
#endif

	OPENCV_ASSERT ( capture,
                        "icvSetPropertyAVI_XINE( CvCaptureAVI_XINE *, int, double )", "illegal capture");
	OPENCV_ASSERT ( capture->stream,
                        "icvGetPropericvSetPropertyAVI_XINE( CvCaptureAVI_XINE *, int )", "illegal capture->stream");
	OPENCV_ASSERT ( capture->vo_port,
                        "icvSetPropertyAVI_XINE( CvCaptureAVI_XINE *, int, double )", "illegal capture->vo_port");

// not needed tnx to asserts...				
	// we need a valid capture context and it's stream to seek through
//	if ( !capture || !capture->stream || !capture->bgr_frame || !capture->xine || !capture->vo_port ) return 0

#ifndef NDEBUG
	fprintf( stderr, "(DEBUG) icvSetPropertyAVI_XINE: seeking to value %f ... ", value );
#endif

	switch ( property_id )
	{
			/// set (seek to) position in msec
			case CV_CAP_PROP_POS_MSEC:
			return icvSeekTimeAVI_XINE( capture, ( int ) value );

			/// set (seek to) frame number
			case CV_CAP_PROP_POS_FRAMES:
			return icvSeekFrameAVI_XINE( capture, ( int ) value );

			/// set (seek to) position ratio in the range [0..1] depending on
			/// the total length of the stream and the actual position
			case CV_CAP_PROP_POS_AVI_RATIO:
			return icvSeekRatioAVI_XINE( capture, value );

			default:
#ifndef NDEBUG
			fprintf( stderr, "(DEBUG) icvSetPropertyAVI_XINE ... failed!\n" );
#endif
			
			return 0;
	}
}


static CvCaptureVTable captureAVI_XINE_vtable =
{
	6,
	( CvCaptureCloseFunc ) icvCloseAVI_XINE,
	( CvCaptureGrabFrameFunc ) icvGrabFrameAVI_XINE,
	( CvCaptureRetrieveFrameFunc ) icvRetrieveFrameAVI_XINE,
	( CvCaptureGetPropertyFunc ) icvGetPropertyAVI_XINE,
	( CvCaptureSetPropertyFunc ) icvSetPropertyAVI_XINE,
	( CvCaptureGetDescriptionFunc ) 0
};


CvCapture* cvCaptureFromFile_XINE( const char* filename )
{
	// construct capture struct
	CvCaptureAVI_XINE * capture = ( CvCaptureAVI_XINE* ) cvAlloc ( sizeof ( CvCaptureAVI_XINE ) );
	memset( capture, 0, sizeof ( CvCaptureAVI_XINE ) );
	capture->vtable = &captureAVI_XINE_vtable;

	// initialize XINE
	if ( !icvOpenAVI_XINE( capture, filename ) )
		return 0;

	OPENCV_ASSERT ( capture,
                        "cvCaptureFromFile_XINE( const char * )", "couldn't create capture");
		
	return ( CvCapture* ) capture;

}

#undef NDEBUG
