Esempio n. 1
0
static gboolean tsmf_gstreamer_seek_data(GstAppSrc *src, guint64 offset, gpointer user_data)
{
	TSMFGstreamerDecoder* mdecoder = user_data;
	(void) mdecoder;
	DEBUG_TSMF("%s offset=%llu", get_type(mdecoder), offset);

	if (!mdecoder->paused)
		tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);

	gst_app_src_end_of_stream((GstAppSrc*) mdecoder->src);

	if (!mdecoder->paused)
		tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);

	if (mdecoder->sync_cb)
		mdecoder->sync_cb(mdecoder->stream);

	return TRUE;
}
Esempio n. 2
0
void tsmf_gstreamer_clean_up(TSMFGstreamerDecoder* mdecoder)
{
	if (!mdecoder || !mdecoder->pipe)
		return;

	if (mdecoder->pipe && GST_OBJECT_REFCOUNT_VALUE(mdecoder->pipe) > 0)
	{
		tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
		gst_object_unref(mdecoder->pipe);
	}

	tsmf_window_destroy(mdecoder);
	mdecoder->ready = FALSE;
	mdecoder->pipe = NULL;
	mdecoder->src = NULL;
}
Esempio n. 3
0
static void tsmf_gstreamer_control(ITSMFDecoder* decoder, ITSMFControlMsg control_msg, UINT32 *arg)
{
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;

	if (!mdecoder)
		return;

	if (control_msg == Control_Pause)
	{
		DEBUG_TSMF("Control_Pause %s", get_type(mdecoder));

		if (mdecoder->paused)
		{
			WLog_ERR(TAG, "%s: Ignoring control PAUSE, already received!", get_type(mdecoder));
			return;
		}

		tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
		mdecoder->paused = TRUE;

		if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
			tsmf_window_pause(mdecoder);
	}
	else if (control_msg == Control_Resume)
	{
		DEBUG_TSMF("Control_Resume %s", get_type(mdecoder));

		if (!mdecoder->paused && !mdecoder->shutdown)
		{
			WLog_ERR(TAG, "%s: Ignoring control RESUME, already received!", get_type(mdecoder));
			return;
		}

		mdecoder->paused = FALSE;
		mdecoder->shutdown = FALSE;

		if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
			tsmf_window_resume(mdecoder);

		tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
	}
	else if (control_msg == Control_Stop)
	{
		DEBUG_TSMF("Control_Stop %s", get_type(mdecoder));

		if (mdecoder->shutdown)
		{
			WLog_ERR(TAG, "%s: Ignoring control STOP, already received!", get_type(mdecoder));
			return;
		}

		mdecoder->shutdown = TRUE;
		/* Reset stamps, flush buffers, etc */
		tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);

		if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
			tsmf_window_pause(mdecoder);

		gst_app_src_end_of_stream((GstAppSrc *)mdecoder->src);
	}
	else
		WLog_ERR(TAG, "Unknown control message %08x", control_msg);
}
Esempio n. 4
0
static BOOL tsmf_gstreamer_decodeEx(ITSMFDecoder* decoder, const BYTE *data, UINT32 data_size, UINT32 extensions,
									UINT64 start_time, UINT64 end_time, UINT64 duration)
{
	GstBuffer *gst_buf;
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
	UINT64 sample_time = tsmf_gstreamer_timestamp_ms_to_gst(start_time);
	UINT64 sample_duration = tsmf_gstreamer_timestamp_ms_to_gst(duration);

	if (!mdecoder)
	{
		WLog_ERR(TAG, "Decoder not initialized!");
		return FALSE;
	}

	/*
	 * This function is always called from a stream-specific thread.
	 * It should be alright to block here if necessary.
	 * We don't expect to block here often, since the pipeline should
	 * have more than enough buffering.
	 */
	DEBUG_TSMF("%s. Start:(%llu) End:(%llu) Duration:(%llu) Last End:(%llu)",
			   get_type(mdecoder), start_time, end_time, duration,
			   mdecoder->last_sample_end_time);

	if (mdecoder->gst_caps == NULL)
	{
		WLog_ERR(TAG, "tsmf_gstreamer_set_format not called or invalid format.");
		return FALSE;
	}

	if (!mdecoder->src)
	{
		WLog_ERR(TAG, "failed to construct pipeline correctly. Unable to push buffer to source element.");
		return FALSE;
	}

	gst_buf = tsmf_get_buffer_from_data(data, data_size);

	if (gst_buf == NULL)
	{
		WLog_ERR(TAG, "tsmf_get_buffer_from_data(%p, %d) failed.", data, data_size);
		return FALSE;
	}

	if (mdecoder->pipeline_start_time_valid)
	{
		long long diff = start_time;
		diff -= mdecoder->last_sample_end_time;

		if (diff < 0)
			diff *= -1;

		/* The pipe is initialized, but there is a discontinuity.
		 * Seek to the start position... */
		if (diff > 50)
		{
			DEBUG_TSMF("%s seeking to %lld", get_type(mdecoder), start_time);

			if (!gst_element_seek(mdecoder->pipe, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
								  GST_SEEK_TYPE_SET, sample_time,
								  GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
			{
				WLog_ERR(TAG, "seek failed");
			}

			mdecoder->pipeline_start_time_valid = 0;
		}
	}
	else
	{
		DEBUG_TSMF("%s start time %llu", get_type(mdecoder), sample_time);
		mdecoder->pipeline_start_time_valid = 1;
	}

#if GST_VERSION_MAJOR > 0
	GST_BUFFER_PTS(gst_buf) = sample_time;
#else
	GST_BUFFER_TIMESTAMP(gst_buf) = sample_time;
#endif
	GST_BUFFER_DURATION(gst_buf) = sample_duration;
	gst_app_src_push_buffer(GST_APP_SRC(mdecoder->src), gst_buf);

	if (mdecoder->ack_cb)
		mdecoder->ack_cb(mdecoder->stream, TRUE);

	mdecoder->last_sample_end_time = end_time;

	if (GST_STATE(mdecoder->pipe) != GST_STATE_PLAYING)
	{
		DEBUG_TSMF("%s: state=%s", get_type(mdecoder), gst_element_state_get_name(GST_STATE(mdecoder->pipe)));

		if (!mdecoder->paused && !mdecoder->shutdown && mdecoder->ready)
			tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
	}

	return TRUE;
}
Esempio n. 5
0
BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder)
{
	const char* appsrc = "appsrc name=source ! decodebin name=decoder !";
	const char* video = "autovideoconvert ! videoscale !";
	const char* audio = "audioconvert ! audiorate ! audioresample ! volume name=audiovolume !";
	char pipeline[1024];

	if (!mdecoder)
		return FALSE;

	/* TODO: Construction of the pipeline from a string allows easy overwrite with arguments.
	 *       The only fixed elements necessary are appsrc and the volume element for audio streams.
	 *       The rest could easily be provided in gstreamer pipeline notation from command line. */
	if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
		snprintf(pipeline, sizeof(pipeline), "%s %s %s name=outsink", appsrc, video, tsmf_platform_get_video_sink());
	else
		snprintf(pipeline, sizeof(pipeline), "%s %s %s name=outsink", appsrc, audio, tsmf_platform_get_audio_sink());

	DEBUG_TSMF("pipeline=%s", pipeline);
	mdecoder->pipe = gst_parse_launch(pipeline, NULL);

	if (!mdecoder->pipe)
	{
		WLog_ERR(TAG, "Failed to create new pipe");
		return FALSE;
	}

	mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "source");

	if (!mdecoder->src)
	{
		WLog_ERR(TAG, "Failed to get appsrc");
		return FALSE;
	}

	mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "outsink");

	if (!mdecoder->outsink)
	{
		WLog_ERR(TAG, "Failed to get sink");
		return FALSE;
	}

	if (mdecoder->media_type != TSMF_MAJOR_TYPE_VIDEO)
	{
		mdecoder->volume = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiovolume");

		if (!mdecoder->volume)
		{
			WLog_ERR(TAG, "Failed to get volume");
			return FALSE;
		}
	}

	tsmf_platform_register_handler(mdecoder);
	/* AppSrc settings */
	GstAppSrcCallbacks callbacks =
	{
		tsmf_gstreamer_need_data,
		tsmf_gstreamer_enough_data,
		tsmf_gstreamer_seek_data
	};
	g_object_set(mdecoder->src, "format", GST_FORMAT_TIME, NULL);
	g_object_set(mdecoder->src, "is-live", TRUE, NULL);
	g_object_set(mdecoder->src, "block", TRUE, NULL);
	gst_app_src_set_caps((GstAppSrc *) mdecoder->src, mdecoder->gst_caps);
	gst_app_src_set_callbacks((GstAppSrc *)mdecoder->src, &callbacks, mdecoder, NULL);
	gst_app_src_set_stream_type((GstAppSrc *) mdecoder->src, GST_APP_STREAM_TYPE_SEEKABLE);
	tsmf_window_create(mdecoder);
	tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_READY);
	tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
	mdecoder->pipeline_start_time_valid = 0;
	mdecoder->shutdown = 0;

	GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(mdecoder->pipe), GST_DEBUG_GRAPH_SHOW_ALL, get_type(mdecoder));

	return TRUE;
}
Esempio n. 6
0
static BOOL tsmf_gstreamer_control(ITSMFDecoder* decoder, ITSMFControlMsg control_msg, UINT32 *arg)
{
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;

	if (!mdecoder)
	{
		WLog_ERR(TAG, "Control called with no decoder!");
		return TRUE;
	}

	if (control_msg == Control_Pause)
	{
		DEBUG_TSMF("Control_Pause %s", get_type(mdecoder));

		if (mdecoder->paused)
		{
			WLog_ERR(TAG, "%s: Ignoring Control_Pause, already received!", get_type(mdecoder));
			return TRUE;
		}

		tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
		mdecoder->shutdown = 0;
		mdecoder->paused = TRUE;
	}
	else if (control_msg == Control_Resume)
	{
		DEBUG_TSMF("Control_Resume %s", get_type(mdecoder));

		if (!mdecoder->paused && !mdecoder->shutdown)
		{
			WLog_ERR(TAG, "%s: Ignoring Control_Resume, already received!", get_type(mdecoder));
			return TRUE;
		}

		mdecoder->shutdown = 0;
		mdecoder->paused = FALSE;
	}
	else if (control_msg == Control_Stop)
	{
		DEBUG_TSMF("Control_Stop %s", get_type(mdecoder));

		if (mdecoder->shutdown)
		{
			WLog_ERR(TAG, "%s: Ignoring Control_Stop, already received!", get_type(mdecoder));
			return TRUE;
		}

		/* Reset stamps, flush buffers, etc */
		if (mdecoder->pipe)
		{
			tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
			tsmf_window_destroy(mdecoder);
			tsmf_gstreamer_clean_up(mdecoder);
		}
		mdecoder->seek_offset = 0;
		mdecoder->pipeline_start_time_valid = 0;
		mdecoder->shutdown = 1;
	}
	else if (control_msg == Control_Restart)
	{
		DEBUG_TSMF("Control_Restart %s", get_type(mdecoder));
		mdecoder->shutdown = 0;
		mdecoder->paused = FALSE;

		if (mdecoder->pipeline_start_time_valid)
			tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
	}
	else
		WLog_ERR(TAG, "Unknown control message %08x", control_msg);

	return TRUE;
}
Esempio n. 7
0
static BOOL tsmf_gstreamer_decodeEx(ITSMFDecoder* decoder, const BYTE *data, UINT32 data_size, UINT32 extensions,
									UINT64 start_time, UINT64 end_time, UINT64 duration)
{
	GstBuffer *gst_buf;
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
	UINT64 sample_time = tsmf_gstreamer_timestamp_ms_to_gst(start_time);
	BOOL useTimestamps = TRUE;

	if (!mdecoder)
	{
		WLog_ERR(TAG, "Decoder not initialized!");
		return FALSE;
	}

	/*
	 * This function is always called from a stream-specific thread.
	 * It should be alright to block here if necessary.
	 * We don't expect to block here often, since the pipeline should
	 * have more than enough buffering.
	 */
	DEBUG_TSMF("%s. Start:(%"PRIu64") End:(%"PRIu64") Duration:(%"PRIu64") Last Start:(%"PRIu64")",
			   get_type(mdecoder), start_time, end_time, duration,
			   mdecoder->last_sample_start_time);

	if (mdecoder->shutdown)
	{
		WLog_ERR(TAG, "decodeEx called on shutdown decoder");
		return TRUE;
	}

	if (mdecoder->gst_caps == NULL)
	{
		WLog_ERR(TAG, "tsmf_gstreamer_set_format not called or invalid format.");
		return FALSE;
	}

	if (!mdecoder->pipe)
		tsmf_gstreamer_pipeline_build(mdecoder);

	if (!mdecoder->src)
	{
		WLog_ERR(TAG, "failed to construct pipeline correctly. Unable to push buffer to source element.");
		return FALSE;
	}

	gst_buf = tsmf_get_buffer_from_data(data, data_size);

	if (gst_buf == NULL)
	{
		WLog_ERR(TAG, "tsmf_get_buffer_from_data(%p, %"PRIu32") failed.", (void*) data, data_size);
		return FALSE;
	}

	/* Relative timestamping will sometimes be set to 0
	 * so we ignore these timestamps just to be safe(bit 8)
	 */
	if (extensions & 0x00000080)
	{
		DEBUG_TSMF("Ignoring the timestamps - relative - bit 8");
		useTimestamps = FALSE;
	}

	/* If no timestamps exist then we dont want to look at the timestamp values (bit 7) */
	if (extensions & 0x00000040)
	{
		DEBUG_TSMF("Ignoring the timestamps - none - bit 7");
		useTimestamps = FALSE;
	}

	/* If performing a seek */
	if (mdecoder->seeking)
	{
		mdecoder->seeking = FALSE;
		tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
		mdecoder->pipeline_start_time_valid = 0;	
	}

	if (mdecoder->pipeline_start_time_valid)
	{
		DEBUG_TSMF("%s start time %"PRIu64"", get_type(mdecoder), start_time);

		/* Adjusted the condition for a seek to be based on start time only
		 * WMV1 and WMV2 files in particular have bad end time and duration values
		 * there seems to be no real side effects of just using the start time instead
		 */
		UINT64 minTime = mdecoder->last_sample_start_time - (UINT64) SEEK_TOLERANCE;
		UINT64 maxTime = mdecoder->last_sample_start_time + (UINT64) SEEK_TOLERANCE;

		/* Make sure the minTime stops at 0 , should we be at the beginning of the stream */ 
		if (mdecoder->last_sample_start_time < (UINT64) SEEK_TOLERANCE)
			minTime = 0;    

		/* If the start_time is valid and different from the previous start time by more than the seek tolerance, then we have a seek condition */
		if (((start_time > maxTime) || (start_time < minTime)) && useTimestamps)
		{
			DEBUG_TSMF("tsmf_gstreamer_decodeEx: start_time=[%"PRIu64"] > last_sample_start_time=[%"PRIu64"] OR ", start_time, mdecoder->last_sample_start_time);
			DEBUG_TSMF("tsmf_gstreamer_decodeEx: start_time=[%"PRIu64"] < last_sample_start_time=[%"PRIu64"] with", start_time, mdecoder->last_sample_start_time);
			DEBUG_TSMF("tsmf_gstreamer_decodeEX: a tolerance of more than [%lu] from the last sample", SEEK_TOLERANCE);
			DEBUG_TSMF("tsmf_gstreamer_decodeEX: minTime=[%"PRIu64"] maxTime=[%"PRIu64"]", minTime, maxTime);

			mdecoder->seeking = TRUE;

			/* since we cant make the gstreamer pipeline jump to the new start time after a seek - we just maintain
			 * a offset between realtime and gstreamer time
			 */
			mdecoder->seek_offset = start_time;
		}
	}
	else
	{
		DEBUG_TSMF("%s start time %"PRIu64"", get_type(mdecoder), start_time);
		/* Always set base/start time to 0. Will use seek offset to translate real buffer times
		 * back to 0. This allows the video to be started from anywhere and the ability to handle seeks
		 * without rebuilding the pipeline, etc. since that is costly
		 */
		gst_element_set_base_time(mdecoder->pipe, tsmf_gstreamer_timestamp_ms_to_gst(0));
		gst_element_set_start_time(mdecoder->pipe, tsmf_gstreamer_timestamp_ms_to_gst(0));
		mdecoder->pipeline_start_time_valid = 1;

		/* Set the seek offset if buffer has valid timestamps. */
		if (useTimestamps)
			mdecoder->seek_offset = start_time;

		if (!gst_element_seek(mdecoder->pipe, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
						GST_SEEK_TYPE_SET, 0,
						GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
		{
			WLog_ERR(TAG, "seek failed");
		}
	}

#if GST_VERSION_MAJOR > 0
	if (useTimestamps)
		GST_BUFFER_PTS(gst_buf) = sample_time - tsmf_gstreamer_timestamp_ms_to_gst(mdecoder->seek_offset);
	else
		GST_BUFFER_PTS(gst_buf) = GST_CLOCK_TIME_NONE;
#else
	if (useTimestamps)
		GST_BUFFER_TIMESTAMP(gst_buf) = sample_time - tsmf_gstreamer_timestamp_ms_to_gst(mdecoder->seek_offset);
	else
		GST_BUFFER_TIMESTAMP(gst_buf) = GST_CLOCK_TIME_NONE;
#endif
	GST_BUFFER_DURATION(gst_buf) = GST_CLOCK_TIME_NONE;
	GST_BUFFER_OFFSET(gst_buf) = GST_BUFFER_OFFSET_NONE;
#if GST_VERSION_MAJOR > 0
#else
	gst_buffer_set_caps(gst_buf, mdecoder->gst_caps);
#endif
	gst_app_src_push_buffer(GST_APP_SRC(mdecoder->src), gst_buf);

	/* Should only update the last timestamps if the current ones are valid */
	if (useTimestamps)
	{
		mdecoder->last_sample_start_time = start_time;
		mdecoder->last_sample_end_time = end_time;
	}

	if (mdecoder->pipe && (GST_STATE(mdecoder->pipe) != GST_STATE_PLAYING))
	{
		DEBUG_TSMF("%s: state=%s", get_type(mdecoder), gst_element_state_get_name(GST_STATE(mdecoder->pipe)));	

		DEBUG_TSMF("%s Paused: %"PRIi32"   Shutdown: %i   Ready: %"PRIi32"", get_type(mdecoder), mdecoder->paused, mdecoder->shutdown, mdecoder->ready);
		if (!mdecoder->paused && !mdecoder->shutdown && mdecoder->ready)
			tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
	}

	return TRUE;
}
Esempio n. 8
0
BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder)
{
#if GST_VERSION_MAJOR > 0
	const char* video = "appsrc name=videosource ! queue2 name=videoqueue ! decodebin name=videodecoder !";
        const char* audio = "appsrc name=audiosource ! queue2 name=audioqueue ! decodebin name=audiodecoder ! audioconvert ! audiorate ! audioresample ! volume name=audiovolume !";
#else
	const char* video = "appsrc name=videosource ! queue2 name=videoqueue ! decodebin2 name=videodecoder !";
	const char* audio = "appsrc name=audiosource ! queue2 name=audioqueue ! decodebin2 name=audiodecoder ! audioconvert ! audiorate ! audioresample ! volume name=audiovolume !";
#endif
	char pipeline[1024];

	if (!mdecoder)
		return FALSE;

	/* TODO: Construction of the pipeline from a string allows easy overwrite with arguments.
	 *       The only fixed elements necessary are appsrc and the volume element for audio streams.
	 *       The rest could easily be provided in gstreamer pipeline notation from command line. */
	if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
		sprintf_s(pipeline, sizeof(pipeline), "%s %s name=videosink", video, tsmf_platform_get_video_sink());
	else
		sprintf_s(pipeline, sizeof(pipeline), "%s %s name=audiosink", audio, tsmf_platform_get_audio_sink());

	DEBUG_TSMF("pipeline=%s", pipeline);
	mdecoder->pipe = gst_parse_launch(pipeline, NULL);

	if (!mdecoder->pipe)
	{
		WLog_ERR(TAG, "Failed to create new pipe");
		return FALSE;
	}

	if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
		mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videosource");
	else
		mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiosource");

	if (!mdecoder->src)
	{
		WLog_ERR(TAG, "Failed to get appsrc");
		return FALSE;
	}

	if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
		mdecoder->queue = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videoqueue");
	else
		mdecoder->queue = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audioqueue");

	if (!mdecoder->queue)
	{
		WLog_ERR(TAG, "Failed to get queue");
		return FALSE;
	}

	if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
		mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videosink");
	else
		mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiosink");

	if (!mdecoder->outsink)
	{
		WLog_ERR(TAG, "Failed to get sink");
		return FALSE;
	}

	g_signal_connect(mdecoder->outsink, "child-added", G_CALLBACK(cb_child_added), mdecoder);

	if (mdecoder->media_type == TSMF_MAJOR_TYPE_AUDIO)
	{
		mdecoder->volume = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiovolume");

		if (!mdecoder->volume)
		{
			WLog_ERR(TAG, "Failed to get volume");
			return FALSE;
		}

		tsmf_gstreamer_change_volume((ITSMFDecoder*)mdecoder, mdecoder->gstVolume*((double) 10000), mdecoder->gstMuted);
	}

	tsmf_platform_register_handler(mdecoder);
	/* AppSrc settings */
	GstAppSrcCallbacks callbacks =
	{
		tsmf_gstreamer_need_data,
		tsmf_gstreamer_enough_data,
		tsmf_gstreamer_seek_data
	};
	g_object_set(mdecoder->src, "format", GST_FORMAT_TIME, NULL);
	g_object_set(mdecoder->src, "is-live", FALSE, NULL);
	g_object_set(mdecoder->src, "block", FALSE, NULL);
	g_object_set(mdecoder->src, "blocksize", 1024, NULL);
	gst_app_src_set_caps((GstAppSrc *) mdecoder->src, mdecoder->gst_caps);
	gst_app_src_set_callbacks((GstAppSrc *)mdecoder->src, &callbacks, mdecoder, NULL);
	gst_app_src_set_stream_type((GstAppSrc *) mdecoder->src, GST_APP_STREAM_TYPE_SEEKABLE);
	gst_app_src_set_latency((GstAppSrc *) mdecoder->src, 0, -1);
	gst_app_src_set_max_bytes((GstAppSrc *) mdecoder->src, (guint64) 0);//unlimited
	g_object_set(G_OBJECT(mdecoder->queue), "use-buffering", FALSE, NULL);
	g_object_set(G_OBJECT(mdecoder->queue), "use-rate-estimate", FALSE, NULL);
	g_object_set(G_OBJECT(mdecoder->queue), "max-size-buffers", 0, NULL);
	g_object_set(G_OBJECT(mdecoder->queue), "max-size-bytes", 0, NULL);
	g_object_set(G_OBJECT(mdecoder->queue), "max-size-time", (guint64) 0, NULL);

	/* Only set these properties if not an autosink, otherwise we will set properties when real sinks are added */
	if (!g_strcmp0(G_OBJECT_TYPE_NAME(mdecoder->outsink), "GstAutoVideoSink") && !g_strcmp0(G_OBJECT_TYPE_NAME(mdecoder->outsink), "GstAutoAudioSink"))
	{
		if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
		{
			gst_base_sink_set_max_lateness((GstBaseSink *) mdecoder->outsink, 10000000); /* nanoseconds */
		}
		else
		{
			gst_base_sink_set_max_lateness((GstBaseSink *) mdecoder->outsink, 10000000); /* nanoseconds */
			g_object_set(G_OBJECT(mdecoder->outsink), "buffer-time", (gint64) 20000, NULL); /* microseconds */
			g_object_set(G_OBJECT(mdecoder->outsink), "drift-tolerance", (gint64) 20000, NULL); /* microseconds */
			g_object_set(G_OBJECT(mdecoder->outsink), "latency-time", (gint64) 10000, NULL); /* microseconds */
			g_object_set(G_OBJECT(mdecoder->outsink), "slave-method", 1, NULL);
		}
		g_object_set(G_OBJECT(mdecoder->outsink), "sync", TRUE, NULL); /* synchronize on the clock */
		g_object_set(G_OBJECT(mdecoder->outsink), "async", TRUE, NULL); /* no async state changes */
	}

	tsmf_window_create(mdecoder);
	tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_READY);
	tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
	mdecoder->pipeline_start_time_valid = 0;
	mdecoder->shutdown = 0;
	mdecoder->paused = FALSE;

	GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(mdecoder->pipe), GST_DEBUG_GRAPH_SHOW_ALL, get_type(mdecoder));

	return TRUE;
}