#include "gstreamerplayer.h"

using namespace TT::Modules::Utilities::Video;
using namespace TT::Exceptions;

//library objects
#include "gst\gst.h"
#include "gst\app\gstappsink.h"

GStreamerPlayer::GStreamerPlayer()
{
	Initialziation();
}

GStreamerPlayer::~GStreamerPlayer()
{
	CloseVideoFile();
}

void GStreamerPlayer::Initialziation()
{
	//init gstreamer library
	gst_init(nullptr, nullptr);

	_loop = g_main_loop_new(NULL, FALSE);
}

#pragma region read video
void GStreamerPlayer::OpenVideoFile(QString name)
{
	//reset video parameters
	_fps = 0;
	_width = 0;
	_height = 0;
	_path = name;

	_videoFound = false;
	_subtitlesFound = false;
	_timingFound = false;
	_contentFound = false;

	//make pipeline
	_pipeline = MakePlaybackPipeline(name.toStdString().data());

	//start prerolling
	GstStateChangeReturn status;
	status = gst_element_set_state(_pipeline, GST_STATE_PAUSED);
	if (status == GST_STATE_CHANGE_FAILURE)
		throw ErrorHandler(this, ErrorCodes::CannotPausePipeline);

	//wait for preroll
	StartWaitgLoop();

	//calculate file parameters
	_frameDuration = 1000 / _fps * 1000000;
	_timingDuration = _frameDuration / 1000000;
	_timingAccuracy = _timingDuration * 0.05;
	_timingStep = _timingDuration + _timingAccuracy;
}

VideoFileProperty GStreamerPlayer::GetVideoParameters()
{
	//get file first frame time
	_startTime = GetTimingData(0);

	//get frames count
	gst_element_query_duration(_pipeline, GST_FORMAT_TIME, (gint64*)&_fileDuration); //GST_FORMAT_DEFAULT
	_frames = _fileDuration / _frameDuration;

	//get file last frame time
	_endTime = GetTimingData(_fileDuration - _frameDuration);

	//read file content
	FrameContent segment = GetContentData();

	int segments = segment.index;

	if (_content != nullptr) delete _content;
	_content = new FrameContent[segments];
	
	_content[0] = segment;

	for (int i = 1; i != segments; i++)
	{
		//seek to previous segment
		SeekFrame((segment.frame - 1) * _frameDuration);

		segment = GetContentData();

		_content[i] = segment;
	}

	//fill parameters structure
	VideoFileProperty parameters;

	parameters.Width = _width;
	parameters.Height = _height;
	parameters.FPS = _fps;
	parameters.Frames = _frames;
	parameters.StartTime = _startTime;
	parameters.EndTime = _endTime;
	parameters.Path = _path;

	return parameters;
}

DeviceFrame GStreamerPlayer::SeekVideoFrameByTime(unsigned long long time)
{
	//check time range of the file
	if (time > _endTime)
		throw ErrorHandler(this, ErrorCodes::CannotFindFrame, "outside of file time range");
	if (time < _startTime)
		throw ErrorHandler(this, ErrorCodes::CannotFindFrame, "outside of file time range");

	long long frameTime;

	//search for requested time
	try {
		frameTime = SeekTime(time);
	}
	catch (ErrorHandler error)
	{
		throw ErrorHandler(this, ErrorCodes::CannotFindFrame, "requested time can not be found");
	}

	//get video data
	VideoFrame frame = GetVideoData();
	
	//store data
	DeviceFrame result;

	result.Type = DeviceFrameType::Video;
	result.Time = frameTime;
	result.Index = _currentOffset / _frameDuration;
	result.Count = 1;
	result.Size = frame.Size;
	result.Frames = frame.Data;	//take over data from video frame

	return result;
}

DeviceFrame GStreamerPlayer::SeekVideoFrameByIndex(unsigned long long index)
{
	//check time range of the file
	if (index >= _frames)
		throw ErrorHandler(this, ErrorCodes::CannotFindFrame, "outside of file frames range");

	unsigned long long frameTime;

	//search for requested time
	try {
		SeekFrame(index * _frameDuration);

		frameTime = GetTimingData();
	}
	catch (ErrorHandler error)
	{
		throw ErrorHandler(this, ErrorCodes::CannotFindFrame, "requested index can not be found");
	}

	//get video data
	VideoFrame frame = GetVideoData();

	//store data
	DeviceFrame result;

	result.Type = DeviceFrameType::Video;
	result.Time = frameTime;
	result.Index = _currentOffset / _frameDuration;
	result.Count = 1;
	result.Size = frame.Size;
	result.Frames = frame.Data;	//take over data from video frame

	return result;
}

void GStreamerPlayer::CloseVideoFile()
{
	if (_pipeline == nullptr)
		return;

	//send soft stop signal
//	gst_element_send_event(_pipeline, gst_event_new_eos());

	//activate main loop in blocking way
//	StartWaitgLoop();

	//stop recording
	gst_element_set_state(_pipeline, GST_STATE_NULL);

	_pipeline = nullptr;
}
#pragma endregion

#pragma region data processing functions
unsigned long long GStreamerPlayer::SeekTime(unsigned long long frameTime)
{
	//get current time
	long long currentTime = GetTimingData();
	long long newTime;

	//check for exact match
	if (currentTime == frameTime)
		throw ErrorHandler(this, ErrorCodes::CannotFindFrame, "requested is current frame");

	//search frame
	newTime = SearchContent(frameTime);

	//adjust frames
	//it can be a holes in recording because of capturing errors

	return newTime;
}

unsigned long long GStreamerPlayer::SearchForward(unsigned long long currentTime, unsigned long long frameTime)
{
	while (currentTime + _timingStep < frameTime)
	{
		//seek frame
		SeekFrame(_currentOffset + _frameDuration);

		//get new frame time
		currentTime = GetTimingData();
	}

	return currentTime;
}
unsigned long long GStreamerPlayer::SearchBackward(unsigned long long currentTime, unsigned long long frameTime)
{
	while (currentTime > frameTime)
	{
		SeekFrame(_currentOffset - _frameDuration);

		currentTime = GetTimingData();
	}

	return currentTime;
}

unsigned long long GStreamerPlayer::SearchForwardFast(unsigned long long currentTime, unsigned long long frameTime)
{
	unsigned long long maximumFrameOffset = (_endTime - frameTime) / _timingDuration;
	unsigned long long step;

	while (currentTime + _timingStep < frameTime)
	{
		//reset step counter
		step = 1;

		//calculate positions
		unsigned long long currentFrameOffset = (_fileDuration - _currentOffset) / _frameDuration;

		//calculate step
		if (currentFrameOffset > maximumFrameOffset)
		{
			step = currentFrameOffset - maximumFrameOffset;
		}
		else
		{
			unsigned long long currentTimeLeft = (_endTime - currentTime) / _timingDuration;

			step = currentFrameOffset* maximumFrameOffset / currentTimeLeft;
		}

		//seek frame
		SeekFrame(_currentOffset + _frameDuration * step);

		//get new frame time
		currentTime = GetTimingData();
	}

	//no need to adjust. we was looking step by step
	if (step == 1)
		return currentTime;

	//chech if frame is close enouth
	if (currentTime < frameTime)
		return currentTime;

	//get time of frame before
	SeekFrame(_currentOffset - 1);
	unsigned long long previousTime = GetTimingData();

	//if it is still after, start seeking backward: iteration counter++
	if (previousTime >= frameTime)
		currentTime = SearchBackward(currentTime, frameTime);
	//else move forward one frame
	SeekFrame(_currentOffset + 1);

	return currentTime;
}
unsigned long long GStreamerPlayer::SearchBackwardFast(unsigned long long currentTime, unsigned long long frameTime)
{
	unsigned long long maximumFramesOffset = (frameTime - _startTime) / _timingDuration;
	unsigned long long step;

	while (currentTime > frameTime)
	{
		//reset step counter
		step = 1;

		//calculate positions
		unsigned long long currentFramesLeft = _currentOffset / _frameDuration;

		//calculate step
		if (currentFramesLeft > maximumFramesOffset)
		{
			step = currentFramesLeft - maximumFramesOffset;
		}
		else
		{
			unsigned long long currentTimeLeft = (currentTime - _startTime) / _timingDuration;

			step = currentFramesLeft* maximumFramesOffset / currentTimeLeft;
		}

		//seek frame
		SeekFrame(_currentOffset - _frameDuration * step);

		//get new frame time
		currentTime = GetTimingData();
	}

	//repeat workflow of SeekForward

	return currentTime;
}

unsigned long long GStreamerPlayer::SearchContent(unsigned long long frameTime)
{
	int index = 0;

	//seek section
	while (_content[index].time > frameTime)
		index++;

	//calculate offset
	unsigned long long offsetTime = frameTime - _content[index].time;
	unsigned long long frame = offsetTime / _timingDuration;

	//move to frame
	SeekFrame((_content[index].frame + frame) * _frameDuration);

	return GetTimingData();
}

unsigned long long GStreamerPlayer::GetTimingData(unsigned long long frameOffset)
{
	SeekFrame(frameOffset);

	return GetTimingData();
}
unsigned long long GStreamerPlayer::GetTimingData()
{
	//get data
	GstSample *sample = gst_app_sink_pull_preroll((GstAppSink*)_timingStream);
	GstBuffer *buffer = gst_sample_get_buffer(sample);
	int size = gst_buffer_get_size(buffer);
	gst_buffer_extract(buffer, 0, _timingChars, _timingSize);

	unsigned long long result = 0;

	//convert time
	for (int i = 0; i != _timingSize; i++)
	{
		char byte = _timingChars[i] - 48;

		if (byte == 0 && result == 0) continue;

		if (byte > 10) byte -= 7;

		result = result << 4;

		result += byte;
	}

	//check for corupted time
	if (result < 0)
		throw ErrorHandler(this, ErrorCodes::BadFrameTime, "wrong time of the frame");

	if (result > QDateTime::currentMSecsSinceEpoch())
		throw ErrorHandler(this, ErrorCodes::BadFrameTime, "wrong time of the frame");

	return result;
}

FrameContent GStreamerPlayer::GetContentData(unsigned long long frameOffset)
{
	SeekFrame(frameOffset);

	return GetContentData();
}
FrameContent GStreamerPlayer::GetContentData()
{
	//get data
	GstSample *sample = gst_app_sink_pull_preroll((GstAppSink*)_contentStream);
	GstBuffer *buffer = gst_sample_get_buffer(sample);
	int size = gst_buffer_get_size(buffer);
	_contentChars = new char[size];
	gst_buffer_extract(buffer, 0, _contentChars, size);

	int index = 0;
	FrameContent result;

	//extract segment id
	result.index = ExtractData(_contentChars, &index);

	//extract frames
	result.frame = ExtractData(_contentChars, &index);

	//extraxt time
	result.time = ExtractData(_contentChars, &index);

	return result;
}
unsigned long long GStreamerPlayer::ExtractData(char* data, int* index)
{
	unsigned long long result = 0;

	while (data[*index] != ';')
	{
		result *= 10;

		result += (unsigned long long)_contentChars[*index] - 48;

		(*index)++;
	}

	(*index)++;

	return result;
}

VideoFrame GStreamerPlayer::GetVideoData()
{
	VideoFrame result;

	//get data
	GstSample *sample = gst_app_sink_pull_preroll((GstAppSink*)_videoStream);
	GstBuffer *buffer = gst_sample_get_buffer(sample);
	result.Size = gst_buffer_get_size(buffer);
	result.Data = new char[result.Size];

	gst_buffer_extract(buffer, 0, result.Data, result.Size);

	gst_buffer_unref(buffer);

	return result;
}

void GStreamerPlayer::SeekFrame(unsigned long long frameOffset)
{
	//get current position
	gst_element_query_position(_pipeline, GST_FORMAT_TIME, (gint64*)&_currentOffset);
	
	//seek to frame
	if (_currentOffset != frameOffset)
	{
		if ((bool)gst_element_seek(
									_pipeline,
									1,
									GST_FORMAT_TIME,
									(GstSeekFlags) (GST_SEEK_FLAG_FLUSH), //GST_SEEK_FLAG_FLUSH  GST_SEEK_FLAG_ACCURATE
									GST_SEEK_TYPE_SET,
									frameOffset,
									GST_SEEK_TYPE_NONE,
									GST_CLOCK_TIME_NONE)
			!= true)
				throw ErrorHandler(this, ErrorCodes::CannotSeekFrame, "seek error");

		//wait for the seeking
		StartWaitgLoop();

		//confirm position
		gst_element_query_position(_pipeline, GST_FORMAT_TIME, (gint64*)&_currentOffset);

		if (_currentOffset != frameOffset)
			throw ErrorHandler(this, ErrorCodes::CannotSeekFrame, "seek position error");
	}
}
#pragma endregion

#pragma region pipeline construction functions
GstElement* GStreamerPlayer::MakePlaybackPipeline(const char* path)
{
	GstElement* result = MakePipeline("VideoPlayback");

#pragma region pipeline elements
	GstElement* reader;
//	GstElement* buffer;
	//GstElement* demuxer;

	GstElement* videoQueue;
	GstElement* videoDecoder;
	GstElement* videoFormaterImage;
	GstElement* videoConverter;
	GstElement* videoFormaterRaw;

	GstElement*	titleQueue;

	GstElement*	timeQueue;

	GstElement*	timingQueue;

	GstElement*	contentQueue;
#pragma endregion

#pragma region common elements
	//init file reding
	reader = MakeElement("filesrc", "Reader"); // videotestsrc filesrc

	//set file name
	g_object_set(reader, "location", path, NULL);

	//init file buffering
//	buffer = MakeElement("downloadbuffer", "Buffer"); //downloadbuffer queue2

	//set buffer parameters
//	g_object_set(buffer, "max-size-buffers", 10000000, NULL);
//	g_object_set(buffer, "max-size-time", 200000000000, NULL);
//	g_object_set(buffer, "max-size-bytes", 102428800, NULL);

	//init video demuxing
	_demuxer = MakeElement("matroskademux", "Demuxer"); //autovideosink matroskademux

	//listern for new pads
	g_signal_connect(_demuxer, "pad-added", G_CALLBACK(NewPadAdded), this);
	g_signal_connect(_demuxer, "no-more-pads", G_CALLBACK(NoMorePads), this);
#pragma endregion

#pragma region video elements
	//init video filter
	_videoFilter = MakeElement("capsfilter", "VideoFilter", MakeVideoCap());

	//init video queue
	videoQueue = MakeElement("queue", "VideoQueue");

	//init video videoDecoder
	videoDecoder = MakeElement("jpegdec", "VideoDecoder"); // vorbisdec jpegdec

	//define image format
	videoFormaterImage = MakeElement("capsfilter", "VideoFormatImage", MakeVideoCapImage());
	
	//make video converter
	videoConverter = MakeElement("videoconvert", "VideoConvertor");

	//define raw format
	videoFormaterRaw = MakeElement("capsfilter", "VideoFormatRaw", MakeVideoCapRaw());

	//init video source
	_videoStream = MakeElement("appsink", "VideoFrame");
	//video test elements
//	GstElement* videoConverter = MakeElement("videoconvert", "VideoConverter");
//	GstElement* videoPlayer = MakeElement("autovideosink", "VideoPlayer");
#pragma endregion

#pragma region titles
	//init sublitles filter
	_titleFilter = MakeElement("capsfilter", "TitlesFilter", MakeSubtitleCap());

	//init timing queue
	titleQueue = MakeElement("queue", "TitleQueue");

	//init subtitles source
	_titleStream = MakeElement("appsink", "RaceTitles");
#pragma endregion

#pragma region time
	//init timing filter
	_timeFilter = MakeElement("capsfilter", "TimeFilter", MakeSubtitleCap());

	//init timing queue
	timeQueue = MakeElement("queue", "TimeQueue");

	//init subtitles source
	_timeStream = MakeElement("appsink", "TimeSubtitles");
#pragma endregion

#pragma region timing
	//init timing filter
	_timingFilter = MakeElement("capsfilter", "TimingFilter", MakeSubtitleCap());

	//init timing queue
	timingQueue = MakeElement("queue", "TimingQueue");

	//init subtitles source
	_timingStream = MakeElement("appsink", "TimingSubtitles");
#pragma endregion

#pragma region content
	//init content filter
	_contentFilter = MakeElement("capsfilter", "ContentFilter", MakeSubtitleCap());

	//init timing queue
	contentQueue = MakeElement("queue", "ContentQueue");

	//init subtitles source
	_contentStream = MakeElement("appsink", "ContentSubtitles");
#pragma endregion

#pragma region pipeline construction
	//build the pipeline
	gst_bin_add_many(GST_BIN(result),
						//reader, buffer, _demuxer,
						reader, _demuxer,
						_videoFilter, videoQueue, videoDecoder, videoFormaterImage, videoConverter, videoFormaterRaw, _videoStream,
						//_videoFilter, videoQueue, videoDecoder, videoConverter, videoPlayer,
						_titleFilter, titleQueue, _titleStream,
						_timeFilter, timeQueue, _timeStream,
						_timingFilter, timingQueue, _timingStream,
						_contentFilter, contentQueue, _contentStream,
						NULL
					);

	//link common elemets
	//if ((bool)gst_element_link_many(reader, buffer, _demuxer, NULL) != true)
	if ((bool)gst_element_link(reader, _demuxer) != true)
		throw ErrorHandler(this, ErrorCodes::CannotLinkPipelineElements, "common");

	//link video elemets
	if ((bool)gst_element_link_many(_videoFilter, videoQueue, videoDecoder, videoFormaterImage, videoConverter, videoFormaterRaw, _videoStream, NULL) != true)
		throw ErrorHandler(this, ErrorCodes::CannotLinkPipelineElements, "video track"); 
	//if ((bool)gst_element_link_many(_videoFilter, videoQueue, videoDecoder, videoConverter, videoPlayer, NULL) != true) { throw 5; }

	//link subtitles elemets
	if ((bool)gst_element_link_many(_titleFilter, titleQueue, _titleStream, NULL) != true)
		throw ErrorHandler(this, ErrorCodes::CannotLinkPipelineElements, "subtitles track"); 

	//link time subtitles elemets
	if ((bool)gst_element_link_many(_timeFilter, timeQueue, _timeStream, NULL) != true)
		throw ErrorHandler(this, ErrorCodes::CannotLinkPipelineElements, "time track");

	//link timing subtitles elemets
	if ((bool)gst_element_link_many(_timingFilter, timingQueue, _timingStream, NULL) != true)
		throw ErrorHandler(this, ErrorCodes::CannotLinkPipelineElements, "timing track"); 

	//link time subtitles elemets
	if ((bool)gst_element_link_many(_contentFilter, contentQueue, _contentStream, NULL) != true)
		throw ErrorHandler(this, ErrorCodes::CannotLinkPipelineElements, "content track");

#pragma endregion

	return result;
}
GstElement* GStreamerPlayer::MakePipeline(const char* name)
{
	GstElement* result = gst_pipeline_new(name);

	if (!result)
		throw ErrorHandler(this, ErrorCodes::CannotMakePipeline, "Pipeline");

	return result;
}
GstElement* GStreamerPlayer::MakeElement(const char* name, const char* caption)
{
	GstElement* result = MakeElement(name, caption, nullptr);

	g_object_set(G_OBJECT(result), "stream-type", 0, "format", GST_FORMAT_TIME, NULL);

	return result;
}
GstElement* GStreamerPlayer::MakeElement(const char* name, const char* caption, GstCaps* cap)
{
	GstElement* result = gst_element_factory_make(name, caption);

	if (!result)
		throw ErrorHandler(this, ErrorCodes::CannotMakePipelineElement, QString(caption));

	if (cap != nullptr)
		g_object_set(G_OBJECT(result), "caps", cap, NULL);

	return result;
}
GstBuffer* GStreamerPlayer::MakeBuffer(int size, long offset, long duration)
{
	GstBuffer* result = gst_buffer_new_allocate(NULL, size, NULL);
	GST_BUFFER_PTS(result) = offset;
	GST_BUFFER_DURATION(result) = duration;

	return result;
}
GstCaps* GStreamerPlayer::MakeVideoCap()
{
	return gst_caps_new_simple("image/jpeg",
		NULL);
}
GstCaps* GStreamerPlayer::MakeVideoCapImage()
{
	return gst_caps_new_simple("video/x-raw",
		"format", G_TYPE_STRING, "I420",
//		"width", G_TYPE_INT, _width,
//		"height", G_TYPE_INT, _height,
//		"framerate", GST_TYPE_FRACTION, _fps, 1,
		NULL);
}
GstCaps* GStreamerPlayer::MakeVideoCapRaw()
{
	return gst_caps_new_simple("video/x-raw",
		"format", G_TYPE_STRING, "RGB",
		//		"width", G_TYPE_INT, _width,
		//		"height", G_TYPE_INT, _height,
		//		"framerate", GST_TYPE_FRACTION, _fps, 1,
		NULL);
}
GstCaps* GStreamerPlayer::MakeSubtitleCap()
{
	return gst_caps_new_simple("text/x-raw",
		"format", G_TYPE_STRING, "pango-markup",
		NULL);
}
#pragma endregion

#pragma region pipeline control functions
void GStreamerPlayer::StartWaitgLoop()
{
	//make time out for waiting on the loop

	//register call back function for bus monitoring
	bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline));
	gst_bus_add_watch(bus, BusCallback, this);

	//try to finf a better way to trabsfer exceptin
	if(_loopError != nullptr)
		delete _loopError;

	_loopError = nullptr;

// timeout try
	//GstClock* clock = gst_pipeline_get_pipeline_clock((GstPipeline*)_pipeline);
	//GstClockTime time = 100000000;
	//GstClockID timeoutClock = gst_clock_new_single_shot_id(clock, time);

	//GstClockReturn t = gst_clock_id_wait_async(timeoutClock, LoopTimeout, this, nullptr);
//

	g_main_loop_run(_loop);

// timeout try
//	gst_clock_id_unschedule(timeoutClock);
//

	if(_loopError != nullptr)
		throw *_loopError; //throw first from list exception
}
void GStreamerPlayer::LoopError(ErrorHandler* error)
{
	_loopError = error;

	g_main_loop_quit(_loop);
}
int GStreamerPlayer::LoopTimeout(GstClock *clock, unsigned long long time, void* clockId, void* data)
{
	GStreamerPlayer* player = (GStreamerPlayer*)data;

	g_main_loop_quit(player->_loop);

	player->_loopError = new ErrorHandler(player, ErrorCodes::FileOpenTimeout, "opening video file taking too long");

	return true;
}

int GStreamerPlayer::BusCallback(GstBus* bus, GstMessage* message, void* data)
{
	GStreamerPlayer* player = (GStreamerPlayer*)data;

	switch (GST_MESSAGE_TYPE(message))
	{
	case GST_MESSAGE_WARNING:
	case GST_MESSAGE_ERROR:
		//can not throw from atother thread
		//throw ErrorHandler(nullptr, ErrorCodes::WrongFileFormat, "can not open this file");
		player->LoopError(new ErrorHandler(player, ErrorCodes::WrongFileFormat, "can not open this file"));

		break;

	case GST_MESSAGE_ASYNC_DONE:
	case GST_MESSAGE_EOS:

		g_main_loop_quit(player->_loop);

		return false;

		break;

	default:
		break;
	}

	//return true to be notified next time
	return true;
}

void GStreamerPlayer::NewPadAdded(GstElement* element,GstPad* decoderPad, void* data)
{
	GStreamerPlayer* player = (GStreamerPlayer*)data;
	gchar *padName = gst_pad_get_name(decoderPad);

	GstElement* filter;

	//link video_0
	if (padName[0] == 'v')
	{
		filter = player->_videoFilter;
		player->_videoFound = true;

		//get video parameters
		GstStructure* videoStructure = gst_caps_get_structure(gst_pad_get_current_caps(decoderPad), 0);

		gint frameRateNumerator;
		gint frameRateDenominator;
		if ((bool)gst_structure_get_fraction(videoStructure, "framerate", &frameRateNumerator, &frameRateDenominator) != true)
			//can not throw from atother thread
			//throw ErrorHandler(nullptr, ErrorCodes::WrongFileFormat, "can not open this file");
			player->LoopError(new ErrorHandler(player, ErrorCodes::CannotGetParameter, "framerate"));

		int width;
		if ((bool)gst_structure_get_int(videoStructure, "width", &width) != true)
			//can not throw from atother thread
			//throw ErrorHandler(nullptr, ErrorCodes::WrongFileFormat, "can not open this file");
			player->LoopError(new ErrorHandler(player, ErrorCodes::CannotGetParameter, "width"));

		int height;
		if ((bool)gst_structure_get_int(videoStructure, "height", &height) != true)
			//can not throw from atother thread
			//throw ErrorHandler(nullptr, ErrorCodes::WrongFileFormat, "can not open this file");
			player->LoopError(new ErrorHandler(player, ErrorCodes::CannotGetParameter, "height"));

		//fill parameters
		player->_fps = frameRateNumerator;
		player->_width = width;
		player->_height = height;
	}

	//link subtitle_0
	if (padName[0] == 's' && padName[9] == '0')
	{
		filter = player->_titleFilter;
		player->_subtitlesFound = true;
	}

	//link subtitle_1
	if (padName[0] == 's' && padName[9] == '1')
	{
		filter = player->_timeFilter;
		player->_timeFound = true;
	}

	//link subtitle_2
	if (padName[0] == 's' && padName[9] == '2')
	{
		filter = player->_timingFilter;
		player->_timingFound = true;
	}

	//link subtitle_2
	if (padName[0] == 's' && padName[9] == '3')
	{
		filter = player->_contentFilter;
		player->_contentFound = true;
	}

	GstPad* filterPad = gst_element_get_static_pad(filter, "sink");

	GstPadLinkReturn result = gst_pad_link(decoderPad, filterPad);

	if (result != GST_PAD_LINK_OK)
		//can not throw from atother thread
		//throw ErrorHandler((QObject*)data, ErrorCodes::CannotLinkPipelineElements, QString(GST_ELEMENT_NAME(filter)));
		player->LoopError(new ErrorHandler(player, ErrorCodes::CannotLinkPipelineElements, QString(GST_ELEMENT_NAME(filter))));
}

void GStreamerPlayer::NoMorePads(GstElement* element, void* data)
{
	GStreamerPlayer* player = (GStreamerPlayer*)data;

	if(player->_videoFound == false)
		//can not throw from atother thread
		//throw ErrorHandler((QObject*)data, ErrorCodes::CannotLinkPipelineElements, QString(GST_ELEMENT_NAME(filter)));
		player->LoopError(new ErrorHandler(player, ErrorCodes::WrongFileFormat, "No video track"));

	if (player->_subtitlesFound == false)
		//can not throw from atother thread
		//throw ErrorHandler((QObject*)data, ErrorCodes::CannotLinkPipelineElements, QString(GST_ELEMENT_NAME(filter)));
		player->LoopError(new ErrorHandler(player, ErrorCodes::WrongFileFormat, "No subtitles track"));

	if (player->_timeFound == false)
		//can not throw from atother thread
		//throw ErrorHandler((QObject*)data, ErrorCodes::CannotLinkPipelineElements, QString(GST_ELEMENT_NAME(filter)));
		player->LoopError(new ErrorHandler(player, ErrorCodes::WrongFileFormat, "No time track"));

	if (player->_timingFound == false)
		//can not throw from atother thread
		//throw ErrorHandler((QObject*)data, ErrorCodes::CannotLinkPipelineElements, QString(GST_ELEMENT_NAME(filter)));
		player->LoopError(new ErrorHandler(player, ErrorCodes::WrongFileFormat, "No timing track"));

	if (player->_contentFound == false)
		//can not throw from atother thread
		//throw ErrorHandler((QObject*)data, ErrorCodes::CannotLinkPipelineElements, QString(GST_ELEMENT_NAME(filter)));
		player->LoopError(new ErrorHandler(player, ErrorCodes::WrongFileFormat, "No content track"));
}
#pragma endregion