Blame channels/tsmf/client/gstreamer/tsmf_gstreamer.c

Packit 1fb8d4
/*
Packit 1fb8d4
 * FreeRDP: A Remote Desktop Protocol Implementation
Packit 1fb8d4
 * Video Redirection Virtual Channel - GStreamer Decoder
Packit 1fb8d4
 *
Packit 1fb8d4
 * (C) Copyright 2012 HP Development Company, LLC
Packit 1fb8d4
 * (C) Copyright 2014 Thincast Technologies GmbH
Packit 1fb8d4
 * (C) Copyright 2014 Armin Novak <armin.novak@thincast.com>
Packit 1fb8d4
 *
Packit 1fb8d4
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit 1fb8d4
 * you may not use this file except in compliance with the License.
Packit 1fb8d4
 * You may obtain a copy of the License at
Packit 1fb8d4
 *
Packit 1fb8d4
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 1fb8d4
 *
Packit 1fb8d4
 * Unless required by applicable law or agreed to in writing, software
Packit 1fb8d4
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 1fb8d4
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 1fb8d4
 * See the License for the specific language governing permissions and
Packit 1fb8d4
 * limitations under the License.
Packit Service 5a9772
 */
Packit 1fb8d4
Packit 1fb8d4
#ifdef HAVE_CONFIG_H
Packit 1fb8d4
#include "config.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#include <assert.h>
Packit 1fb8d4
Packit 1fb8d4
#include <fcntl.h>
Packit 1fb8d4
#include <stdio.h>
Packit 1fb8d4
#include <stdlib.h>
Packit 1fb8d4
#include <string.h>
Packit 1fb8d4
#include <unistd.h>
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/string.h>
Packit 1fb8d4
Packit 1fb8d4
#if __clang__
Packit 1fb8d4
#pragma clang diagnostic push
Packit 1fb8d4
#pragma clang diagnostic ignored "-Wparentheses-equality"
Packit 1fb8d4
#endif /* __clang__ */
Packit 1fb8d4
#include <gst/gst.h>
Packit 1fb8d4
#if __clang__
Packit 1fb8d4
#pragma clang diagnostic pop
Packit 1fb8d4
#endif /* __clang__ */
Packit 1fb8d4
Packit 1fb8d4
#include <gst/app/gstappsrc.h>
Packit 1fb8d4
#include <gst/app/gstappsink.h>
Packit 1fb8d4
Packit 1fb8d4
#include "tsmf_constants.h"
Packit 1fb8d4
#include "tsmf_decoder.h"
Packit 1fb8d4
#include "tsmf_platform.h"
Packit 1fb8d4
Packit 1fb8d4
#ifdef HAVE_INTTYPES_H
Packit 1fb8d4
#include <inttypes.h>
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
/* 1 second = 10,000,000 100ns units*/
Packit Service 5a9772
#define SEEK_TOLERANCE 10 * 1000 * 1000
Packit 1fb8d4
Packit 1fb8d4
static BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder);
Packit 1fb8d4
static void tsmf_gstreamer_clean_up(TSMFGstreamerDecoder* mdecoder);
Packit 1fb8d4
static int tsmf_gstreamer_pipeline_set_state(TSMFGstreamerDecoder* mdecoder,
Packit Service 5a9772
                                             GstState desired_state);
Packit 1fb8d4
static BOOL tsmf_gstreamer_buffer_level(ITSMFDecoder* decoder);
Packit 1fb8d4
Packit Service 5a9772
static const char* get_type(TSMFGstreamerDecoder* mdecoder)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!mdecoder)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	switch (mdecoder->media_type)
Packit 1fb8d4
	{
Packit 1fb8d4
		case TSMF_MAJOR_TYPE_VIDEO:
Packit 1fb8d4
			return "VIDEO";
Packit 1fb8d4
		case TSMF_MAJOR_TYPE_AUDIO:
Packit 1fb8d4
			return "AUDIO";
Packit 1fb8d4
		default:
Packit 1fb8d4
			return "UNKNOWN";
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static void cb_child_added(GstChildProxy* child_proxy, GObject* object,
Packit Service 5a9772
                           TSMFGstreamerDecoder* mdecoder)
Packit 1fb8d4
{
Packit 1fb8d4
	DEBUG_TSMF("NAME: %s", G_OBJECT_TYPE_NAME(object));
Packit 1fb8d4
Packit Service 5a9772
	if (!g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstXvImageSink") ||
Packit Service 5a9772
	    !g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstXImageSink") ||
Packit Service 5a9772
	    !g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstFluVAAutoSink"))
Packit 1fb8d4
	{
Packit Service 5a9772
		gst_base_sink_set_max_lateness((GstBaseSink*)object, 10000000); /* nanoseconds */
Packit Service 5a9772
		g_object_set(G_OBJECT(object), "sync", TRUE, NULL);  /* synchronize on the clock */
Packit 1fb8d4
		g_object_set(G_OBJECT(object), "async", TRUE, NULL); /* no async state changes */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	else if (!g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstAlsaSink") ||
Packit Service 5a9772
	         !g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstPulseSink"))
Packit 1fb8d4
	{
Packit Service 5a9772
		gst_base_sink_set_max_lateness((GstBaseSink*)object, 10000000); /* nanoseconds */
Packit 1fb8d4
		g_object_set(G_OBJECT(object), "slave-method", 1, NULL);
Packit Service 5a9772
		g_object_set(G_OBJECT(object), "buffer-time", (gint64)20000, NULL);     /* microseconds */
Packit Service 5a9772
		g_object_set(G_OBJECT(object), "drift-tolerance", (gint64)20000, NULL); /* microseconds */
Packit Service 5a9772
		g_object_set(G_OBJECT(object), "latency-time", (gint64)10000, NULL);    /* microseconds */
Packit Service 5a9772
		g_object_set(G_OBJECT(object), "sync", TRUE, NULL);  /* synchronize on the clock */
Packit 1fb8d4
		g_object_set(G_OBJECT(object), "async", TRUE, NULL); /* no async state changes */
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static void tsmf_gstreamer_enough_data(GstAppSrc* src, gpointer user_data)
Packit 1fb8d4
{
Packit 1fb8d4
	TSMFGstreamerDecoder* mdecoder = user_data;
Packit Service 5a9772
	(void)mdecoder;
Packit 1fb8d4
	DEBUG_TSMF("%s", get_type(mdecoder));
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static void tsmf_gstreamer_need_data(GstAppSrc* src, guint length, gpointer user_data)
Packit 1fb8d4
{
Packit 1fb8d4
	TSMFGstreamerDecoder* mdecoder = user_data;
Packit Service 5a9772
	(void)mdecoder;
Packit 1fb8d4
	DEBUG_TSMF("%s length=%u", get_type(mdecoder), length);
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static gboolean tsmf_gstreamer_seek_data(GstAppSrc* src, guint64 offset, gpointer user_data)
Packit 1fb8d4
{
Packit 1fb8d4
	TSMFGstreamerDecoder* mdecoder = user_data;
Packit Service 5a9772
	(void)mdecoder;
Packit Service 5a9772
	DEBUG_TSMF("%s offset=%" PRIu64 "", get_type(mdecoder), offset);
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL tsmf_gstreamer_change_volume(ITSMFDecoder* decoder, UINT32 newVolume, UINT32 muted)
Packit 1fb8d4
{
Packit Service 5a9772
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder || !mdecoder->pipe)
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
Packit 1fb8d4
	if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
Packit Service 5a9772
	mdecoder->gstMuted = (BOOL)muted;
Packit Service 5a9772
	DEBUG_TSMF("mute=[%" PRId32 "]", mdecoder->gstMuted);
Packit Service 5a9772
	mdecoder->gstVolume = (double)newVolume / (double)10000;
Packit 1fb8d4
	DEBUG_TSMF("gst_new_vol=[%f]", mdecoder->gstVolume);
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder->volume)
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
Packit 1fb8d4
	if (!G_IS_OBJECT(mdecoder->volume))
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
Packit 1fb8d4
	g_object_set(mdecoder->volume, "mute", mdecoder->gstMuted, NULL);
Packit 1fb8d4
	g_object_set(mdecoder->volume, "volume", mdecoder->gstVolume, NULL);
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static inline GstClockTime tsmf_gstreamer_timestamp_ms_to_gst(UINT64 ms_timestamp)
Packit 1fb8d4
{
Packit 1fb8d4
	/*
Packit 1fb8d4
	 * Convert Microsoft 100ns timestamps to Gstreamer 1ns units.
Packit 1fb8d4
	 */
Packit 1fb8d4
	return (GstClockTime)(ms_timestamp * 100);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
int tsmf_gstreamer_pipeline_set_state(TSMFGstreamerDecoder* mdecoder, GstState desired_state)
Packit 1fb8d4
{
Packit 1fb8d4
	GstStateChangeReturn state_change;
Packit 1fb8d4
	const char* name;
Packit 1fb8d4
	const char* sname = get_type(mdecoder);
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder)
Packit 1fb8d4
		return 0;
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder->pipe)
Packit Service 5a9772
		return 0; /* Just in case this is called during startup or shutdown when we don't expect it
Packit Service 5a9772
		           */
Packit 1fb8d4
Packit 1fb8d4
	if (desired_state == mdecoder->state)
Packit Service 5a9772
		return 0; /* Redundant request - Nothing to do */
Packit 1fb8d4
Packit 1fb8d4
	name = gst_element_state_get_name(desired_state); /* For debug */
Packit 1fb8d4
	DEBUG_TSMF("%s to %s", sname, name);
Packit 1fb8d4
	state_change = gst_element_set_state(mdecoder->pipe, desired_state);
Packit 1fb8d4
Packit 1fb8d4
	if (state_change == GST_STATE_CHANGE_FAILURE)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "%s: (%s) GST_STATE_CHANGE_FAILURE.", sname, name);
Packit 1fb8d4
	}
Packit 1fb8d4
	else if (state_change == GST_STATE_CHANGE_ASYNC)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "%s: (%s) GST_STATE_CHANGE_ASYNC.", sname, name);
Packit 1fb8d4
		mdecoder->state = desired_state;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		mdecoder->state = desired_state;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return 0;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static GstBuffer* tsmf_get_buffer_from_data(const void* raw_data, gsize size)
Packit 1fb8d4
{
Packit 1fb8d4
	GstBuffer* buffer;
Packit 1fb8d4
	gpointer data;
Packit 1fb8d4
Packit 1fb8d4
	if (!raw_data)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	if (size < 1)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	data = g_malloc(size);
Packit 1fb8d4
Packit 1fb8d4
	if (!data)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "Could not allocate %" G_GSIZE_FORMAT " bytes of data.", size);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	CopyMemory(data, raw_data, size);
Packit 1fb8d4
Packit 1fb8d4
#if GST_VERSION_MAJOR > 0
Packit 1fb8d4
	buffer = gst_buffer_new_wrapped(data, size);
Packit 1fb8d4
#else
Packit 1fb8d4
	buffer = gst_buffer_new();
Packit 1fb8d4
Packit 1fb8d4
	if (!buffer)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Could not create GstBuffer");
Packit 1fb8d4
		free(data);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	GST_BUFFER_MALLOCDATA(buffer) = data;
Packit 1fb8d4
	GST_BUFFER_SIZE(buffer) = size;
Packit 1fb8d4
	GST_BUFFER_DATA(buffer) = GST_BUFFER_MALLOCDATA(buffer);
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
	return buffer;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* media_type)
Packit 1fb8d4
{
Packit Service 5a9772
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	DEBUG_TSMF("");
Packit 1fb8d4
Packit 1fb8d4
	switch (media_type->MajorType)
Packit 1fb8d4
	{
Packit 1fb8d4
		case TSMF_MAJOR_TYPE_VIDEO:
Packit 1fb8d4
			mdecoder->media_type = TSMF_MAJOR_TYPE_VIDEO;
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_MAJOR_TYPE_AUDIO:
Packit 1fb8d4
			mdecoder->media_type = TSMF_MAJOR_TYPE_AUDIO;
Packit 1fb8d4
			break;
Packit 1fb8d4
		default:
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	switch (media_type->SubType)
Packit 1fb8d4
	{
Packit 1fb8d4
		case TSMF_SUB_TYPE_WVC1:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "video/x-wmv", "bitrate", G_TYPE_UINT, media_type->BitRate, "width", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Width, "height", G_TYPE_INT, media_type->Height, "wmvversion",
Packit Service 5a9772
			    G_TYPE_INT, 3,
Packit 1fb8d4
#if GST_VERSION_MAJOR > 0
Packit Service 5a9772
			    "format", G_TYPE_STRING, "WVC1",
Packit 1fb8d4
#else
Packit Service 5a9772
			    "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('W', 'V', 'C', '1'),
Packit 1fb8d4
#endif
Packit Service 5a9772
			    "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Denominator, "pixel-aspect-ratio", GST_TYPE_FRACTION,
Packit Service 5a9772
			    1, 1, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_MP4S:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "video/x-divx", "divxversion", G_TYPE_INT, 5, "bitrate", G_TYPE_UINT,
Packit Service 5a9772
			    media_type->BitRate, "width", G_TYPE_INT, media_type->Width, "height", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Height,
Packit 1fb8d4
#if GST_VERSION_MAJOR > 0
Packit Service 5a9772
			    "format", G_TYPE_STRING, "MP42",
Packit Service 5a9772
#else
Packit Service 5a9772
			    "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('M', 'P', '4', '2'),
Packit 1fb8d4
#endif
Packit Service 5a9772
			    "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Denominator, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_MP42:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "video/x-msmpeg", "msmpegversion", G_TYPE_INT, 42, "bitrate", G_TYPE_UINT,
Packit Service 5a9772
			    media_type->BitRate, "width", G_TYPE_INT, media_type->Width, "height", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Height,
Packit Service 5a9772
#if GST_VERSION_MAJOR > 0
Packit Service 5a9772
			    "format", G_TYPE_STRING, "MP42",
Packit 1fb8d4
#else
Packit Service 5a9772
			    "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('M', 'P', '4', '2'),
Packit 1fb8d4
#endif
Packit Service 5a9772
			    "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Denominator, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_MP43:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "video/x-msmpeg", "msmpegversion", G_TYPE_INT, 43, "bitrate", G_TYPE_UINT,
Packit Service 5a9772
			    media_type->BitRate, "width", G_TYPE_INT, media_type->Width, "height", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Height,
Packit Service 5a9772
#if GST_VERSION_MAJOR > 0
Packit Service 5a9772
			    "format", G_TYPE_STRING, "MP43",
Packit Service 5a9772
#else
Packit Service 5a9772
			    "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('M', 'P', '4', '3'),
Packit 1fb8d4
#endif
Packit Service 5a9772
			    "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Denominator, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_M4S2:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "video/mpeg", "mpegversion", G_TYPE_INT, 4, "width", G_TYPE_INT, media_type->Width,
Packit Service 5a9772
			    "height", G_TYPE_INT, media_type->Height,
Packit Service 5a9772
#if GST_VERSION_MAJOR > 0
Packit Service 5a9772
			    "format", G_TYPE_STRING, "M4S2",
Packit Service 5a9772
#else
Packit Service 5a9772
			    "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('M', '4', 'S', '2'),
Packit 1fb8d4
#endif
Packit Service 5a9772
			    "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Denominator, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_WMA9:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "audio/x-wma", "wmaversion", G_TYPE_INT, 3, "rate", G_TYPE_INT,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Numerator, "channels", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Channels, "bitrate", G_TYPE_INT, media_type->BitRate, "depth",
Packit Service 5a9772
			    G_TYPE_INT, media_type->BitsPerSample, "width", G_TYPE_INT,
Packit Service 5a9772
			    media_type->BitsPerSample, "block_align", G_TYPE_INT, media_type->BlockAlign, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_WMA1:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "audio/x-wma", "wmaversion", G_TYPE_INT, 1, "rate", G_TYPE_INT,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Numerator, "channels", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Channels, "bitrate", G_TYPE_INT, media_type->BitRate, "depth",
Packit Service 5a9772
			    G_TYPE_INT, media_type->BitsPerSample, "width", G_TYPE_INT,
Packit Service 5a9772
			    media_type->BitsPerSample, "block_align", G_TYPE_INT, media_type->BlockAlign, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_WMA2:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "audio/x-wma", "wmaversion", G_TYPE_INT, 2, "rate", G_TYPE_INT,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Numerator, "channels", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Channels, "bitrate", G_TYPE_INT, media_type->BitRate, "depth",
Packit Service 5a9772
			    G_TYPE_INT, media_type->BitsPerSample, "width", G_TYPE_INT,
Packit Service 5a9772
			    media_type->BitsPerSample, "block_align", G_TYPE_INT, media_type->BlockAlign, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_MP3:
Packit Service 5a9772
			mdecoder->gst_caps =
Packit Service 5a9772
			    gst_caps_new_simple("audio/mpeg", "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT,
Packit Service 5a9772
			                        3, "rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
Packit Service 5a9772
			                        "channels", G_TYPE_INT, media_type->Channels, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_WMV1:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "video/x-wmv", "bitrate", G_TYPE_UINT, media_type->BitRate, "width", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Width, "height", G_TYPE_INT, media_type->Height, "wmvversion",
Packit Service 5a9772
			    G_TYPE_INT, 1,
Packit Service 5a9772
#if GST_VERSION_MAJOR > 0
Packit Service 5a9772
			    "format", G_TYPE_STRING, "WMV1",
Packit Service 5a9772
#else
Packit Service 5a9772
			    "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('W', 'M', 'V', '1'),
Packit 1fb8d4
#endif
Packit Service 5a9772
			    "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Denominator, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_WMV2:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "video/x-wmv", "width", G_TYPE_INT, media_type->Width, "height", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Height, "wmvversion", G_TYPE_INT, 2,
Packit Service 5a9772
#if GST_VERSION_MAJOR > 0
Packit Service 5a9772
			    "format", G_TYPE_STRING, "WMV2",
Packit 1fb8d4
#else
Packit Service 5a9772
			    "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('W', 'M', 'V', '2'),
Packit 1fb8d4
#endif
Packit Service 5a9772
			    "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Denominator, "pixel-aspect-ratio", GST_TYPE_FRACTION,
Packit Service 5a9772
			    1, 1, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_WMV3:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "video/x-wmv", "bitrate", G_TYPE_UINT, media_type->BitRate, "width", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Width, "height", G_TYPE_INT, media_type->Height, "wmvversion",
Packit Service 5a9772
			    G_TYPE_INT, 3,
Packit Service 5a9772
#if GST_VERSION_MAJOR > 0
Packit Service 5a9772
			    "format", G_TYPE_STRING, "WMV3",
Packit 1fb8d4
#else
Packit Service 5a9772
			    "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('W', 'M', 'V', '3'),
Packit 1fb8d4
#endif
Packit Service 5a9772
			    "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Denominator, "pixel-aspect-ratio", GST_TYPE_FRACTION,
Packit Service 5a9772
			    1, 1, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_AVC1:
Packit 1fb8d4
		case TSMF_SUB_TYPE_H264:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "video/x-h264", "width", G_TYPE_INT, media_type->Width, "height", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Height, "framerate", GST_TYPE_FRACTION,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
Packit Service 5a9772
			    "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, "stream-format", G_TYPE_STRING,
Packit Service 5a9772
			    "byte-stream", "alignment", G_TYPE_STRING, "nal", NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_AC3:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "audio/x-ac3", "rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
Packit Service 5a9772
			    "channels", G_TYPE_INT, media_type->Channels, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_AAC:
Packit 1fb8d4
Packit 1fb8d4
			/* For AAC the pFormat is a HEAACWAVEINFO struct, and the codec data
Packit 1fb8d4
			   is at the end of it. See
Packit 1fb8d4
			   http://msdn.microsoft.com/en-us/library/dd757806.aspx */
Packit 1fb8d4
			if (media_type->ExtraData)
Packit 1fb8d4
			{
Packit 1fb8d4
				media_type->ExtraData += 12;
Packit 1fb8d4
				media_type->ExtraDataSize -= 12;
Packit 1fb8d4
			}
Packit 1fb8d4
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "audio/mpeg", "rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
Packit Service 5a9772
			    "channels", G_TYPE_INT, media_type->Channels, "mpegversion", G_TYPE_INT, 4,
Packit Service 5a9772
			    "framed", G_TYPE_BOOLEAN, TRUE, "stream-format", G_TYPE_STRING, "raw", NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_MP1A:
Packit Service 5a9772
			mdecoder->gst_caps =
Packit Service 5a9772
			    gst_caps_new_simple("audio/mpeg", "mpegversion", G_TYPE_INT, 1, "channels",
Packit Service 5a9772
			                        G_TYPE_INT, media_type->Channels, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_MP1V:
Packit Service 5a9772
			mdecoder->gst_caps =
Packit Service 5a9772
			    gst_caps_new_simple("video/mpeg", "mpegversion", G_TYPE_INT, 1, "width", G_TYPE_INT,
Packit Service 5a9772
			                        media_type->Width, "height", G_TYPE_INT, media_type->Height,
Packit Service 5a9772
			                        "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_YUY2:
Packit 1fb8d4
#if GST_VERSION_MAJOR > 0
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "video/x-raw", "format", G_TYPE_STRING, "YUY2", "width", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Width, "height", G_TYPE_INT, media_type->Height, NULL);
Packit 1fb8d4
#else
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple(
Packit Service 5a9772
			    "video/x-raw-yuv", "format", G_TYPE_STRING, "YUY2", "width", G_TYPE_INT,
Packit Service 5a9772
			    media_type->Width, "height", G_TYPE_INT, media_type->Height, "framerate",
Packit Service 5a9772
			    GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
Packit Service 5a9772
			    media_type->SamplesPerSecond.Denominator, NULL);
Packit 1fb8d4
#endif
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_MP2V:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple("video/mpeg", "mpegversion", G_TYPE_INT, 2,
Packit Service 5a9772
			                                         "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_MP2A:
Packit Service 5a9772
			mdecoder->gst_caps =
Packit Service 5a9772
			    gst_caps_new_simple("audio/mpeg", "mpegversion", G_TYPE_INT, 1, "rate", G_TYPE_INT,
Packit Service 5a9772
			                        media_type->SamplesPerSecond.Numerator, "channels", G_TYPE_INT,
Packit Service 5a9772
			                        media_type->Channels, NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		case TSMF_SUB_TYPE_FLAC:
Packit Service 5a9772
			mdecoder->gst_caps = gst_caps_new_simple("audio/x-flac", "", NULL);
Packit 1fb8d4
			break;
Packit 1fb8d4
		default:
Packit 1fb8d4
			WLog_ERR(TAG, "unknown format:(%d).", media_type->SubType);
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (media_type->ExtraDataSize > 0)
Packit 1fb8d4
	{
Packit Service 5a9772
		GstBuffer* buffer;
Packit Service 5a9772
		DEBUG_TSMF("Extra data available (%" PRIu32 ")", media_type->ExtraDataSize);
Packit 1fb8d4
		buffer = tsmf_get_buffer_from_data(media_type->ExtraData, media_type->ExtraDataSize);
Packit 1fb8d4
Packit 1fb8d4
		if (!buffer)
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "could not allocate GstBuffer!");
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		gst_caps_set_simple(mdecoder->gst_caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	DEBUG_TSMF("%p format '%s'", (void*)mdecoder, gst_caps_to_string(mdecoder->gst_caps));
Packit 1fb8d4
	tsmf_platform_set_format(mdecoder);
Packit 1fb8d4
Packit 1fb8d4
	/* Create the pipeline... */
Packit 1fb8d4
	if (!tsmf_gstreamer_pipeline_build(mdecoder))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
void tsmf_gstreamer_clean_up(TSMFGstreamerDecoder* mdecoder)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!mdecoder || !mdecoder->pipe)
Packit 1fb8d4
		return;
Packit 1fb8d4
Packit 1fb8d4
	if (mdecoder->pipe && GST_OBJECT_REFCOUNT_VALUE(mdecoder->pipe) > 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
Packit 1fb8d4
		gst_object_unref(mdecoder->pipe);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	mdecoder->ready = FALSE;
Packit 1fb8d4
	mdecoder->paused = FALSE;
Packit 1fb8d4
Packit 1fb8d4
	mdecoder->pipe = NULL;
Packit 1fb8d4
	mdecoder->src = NULL;
Packit 1fb8d4
	mdecoder->queue = NULL;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder)
Packit 1fb8d4
{
Packit 1fb8d4
#if GST_VERSION_MAJOR > 0
Packit Service 5a9772
	const char* video =
Packit Service 5a9772
	    "appsrc name=videosource ! queue2 name=videoqueue ! decodebin name=videodecoder !";
Packit Service 5a9772
	const char* audio =
Packit Service 5a9772
	    "appsrc name=audiosource ! queue2 name=audioqueue ! decodebin name=audiodecoder ! "
Packit Service 5a9772
	    "audioconvert ! audiorate ! audioresample ! volume name=audiovolume !";
Packit 1fb8d4
#else
Packit Service 5a9772
	const char* video =
Packit Service 5a9772
	    "appsrc name=videosource ! queue2 name=videoqueue ! decodebin2 name=videodecoder !";
Packit Service 5a9772
	const char* audio =
Packit Service 5a9772
	    "appsrc name=audiosource ! queue2 name=audioqueue ! decodebin2 name=audiodecoder ! "
Packit Service 5a9772
	    "audioconvert ! audiorate ! audioresample ! volume name=audiovolume !";
Packit 1fb8d4
#endif
Packit 1fb8d4
	char pipeline[1024];
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	/* TODO: Construction of the pipeline from a string allows easy overwrite with arguments.
Packit 1fb8d4
	 *       The only fixed elements necessary are appsrc and the volume element for audio streams.
Packit 1fb8d4
	 *       The rest could easily be provided in gstreamer pipeline notation from command line. */
Packit 1fb8d4
	if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
Packit Service 5a9772
		sprintf_s(pipeline, sizeof(pipeline), "%s %s name=videosink", video,
Packit Service 5a9772
		          tsmf_platform_get_video_sink());
Packit 1fb8d4
	else
Packit Service 5a9772
		sprintf_s(pipeline, sizeof(pipeline), "%s %s name=audiosink", audio,
Packit Service 5a9772
		          tsmf_platform_get_audio_sink());
Packit 1fb8d4
Packit 1fb8d4
	DEBUG_TSMF("pipeline=%s", pipeline);
Packit 1fb8d4
	mdecoder->pipe = gst_parse_launch(pipeline, NULL);
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder->pipe)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to create new pipe");
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
Packit 1fb8d4
		mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videosource");
Packit 1fb8d4
	else
Packit 1fb8d4
		mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiosource");
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder->src)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to get appsrc");
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
Packit 1fb8d4
		mdecoder->queue = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videoqueue");
Packit 1fb8d4
	else
Packit 1fb8d4
		mdecoder->queue = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audioqueue");
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder->queue)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to get queue");
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
Packit 1fb8d4
		mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videosink");
Packit 1fb8d4
	else
Packit 1fb8d4
		mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiosink");
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder->outsink)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to get sink");
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	g_signal_connect(mdecoder->outsink, "child-added", G_CALLBACK(cb_child_added), mdecoder);
Packit 1fb8d4
Packit 1fb8d4
	if (mdecoder->media_type == TSMF_MAJOR_TYPE_AUDIO)
Packit 1fb8d4
	{
Packit 1fb8d4
		mdecoder->volume = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiovolume");
Packit 1fb8d4
Packit 1fb8d4
		if (!mdecoder->volume)
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "Failed to get volume");
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit Service 5a9772
		tsmf_gstreamer_change_volume((ITSMFDecoder*)mdecoder, mdecoder->gstVolume * ((double)10000),
Packit Service 5a9772
		                             mdecoder->gstMuted);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	tsmf_platform_register_handler(mdecoder);
Packit 1fb8d4
	/* AppSrc settings */
Packit Service 5a9772
	GstAppSrcCallbacks callbacks = {
Packit Service 5a9772
		tsmf_gstreamer_need_data, tsmf_gstreamer_enough_data, tsmf_gstreamer_seek_data, { NULL }
Packit 1fb8d4
	};
Packit 1fb8d4
	g_object_set(mdecoder->src, "format", GST_FORMAT_TIME, NULL);
Packit 1fb8d4
	g_object_set(mdecoder->src, "is-live", FALSE, NULL);
Packit 1fb8d4
	g_object_set(mdecoder->src, "block", FALSE, NULL);
Packit 1fb8d4
	g_object_set(mdecoder->src, "blocksize", 1024, NULL);
Packit Service 5a9772
	gst_app_src_set_caps((GstAppSrc*)mdecoder->src, mdecoder->gst_caps);
Packit Service 5a9772
	gst_app_src_set_callbacks((GstAppSrc*)mdecoder->src, &callbacks, mdecoder, NULL);
Packit Service 5a9772
	gst_app_src_set_stream_type((GstAppSrc*)mdecoder->src, GST_APP_STREAM_TYPE_SEEKABLE);
Packit Service 5a9772
	gst_app_src_set_latency((GstAppSrc*)mdecoder->src, 0, -1);
Packit Service 5a9772
	gst_app_src_set_max_bytes((GstAppSrc*)mdecoder->src, (guint64)0); // unlimited
Packit 1fb8d4
	g_object_set(G_OBJECT(mdecoder->queue), "use-buffering", FALSE, NULL);
Packit 1fb8d4
	g_object_set(G_OBJECT(mdecoder->queue), "use-rate-estimate", FALSE, NULL);
Packit 1fb8d4
	g_object_set(G_OBJECT(mdecoder->queue), "max-size-buffers", 0, NULL);
Packit 1fb8d4
	g_object_set(G_OBJECT(mdecoder->queue), "max-size-bytes", 0, NULL);
Packit Service 5a9772
	g_object_set(G_OBJECT(mdecoder->queue), "max-size-time", (guint64)0, NULL);
Packit 1fb8d4
Packit Service 5a9772
	/* Only set these properties if not an autosink, otherwise we will set properties when real
Packit Service 5a9772
	 * sinks are added */
Packit Service 5a9772
	if (!g_strcmp0(G_OBJECT_TYPE_NAME(mdecoder->outsink), "GstAutoVideoSink") &&
Packit Service 5a9772
	    !g_strcmp0(G_OBJECT_TYPE_NAME(mdecoder->outsink), "GstAutoAudioSink"))
Packit 1fb8d4
	{
Packit 1fb8d4
		if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
Packit 1fb8d4
		{
Packit Service 5a9772
			gst_base_sink_set_max_lateness((GstBaseSink*)mdecoder->outsink,
Packit Service 5a9772
			                               10000000); /* nanoseconds */
Packit 1fb8d4
		}
Packit 1fb8d4
		else
Packit 1fb8d4
		{
Packit Service 5a9772
			gst_base_sink_set_max_lateness((GstBaseSink*)mdecoder->outsink,
Packit Service 5a9772
			                               10000000); /* nanoseconds */
Packit Service 5a9772
			g_object_set(G_OBJECT(mdecoder->outsink), "buffer-time", (gint64)20000,
Packit Service 5a9772
			             NULL); /* microseconds */
Packit Service 5a9772
			g_object_set(G_OBJECT(mdecoder->outsink), "drift-tolerance", (gint64)20000,
Packit Service 5a9772
			             NULL); /* microseconds */
Packit Service 5a9772
			g_object_set(G_OBJECT(mdecoder->outsink), "latency-time", (gint64)10000,
Packit Service 5a9772
			             NULL); /* microseconds */
Packit 1fb8d4
			g_object_set(G_OBJECT(mdecoder->outsink), "slave-method", 1, NULL);
Packit 1fb8d4
		}
Packit Service 5a9772
		g_object_set(G_OBJECT(mdecoder->outsink), "sync", TRUE,
Packit Service 5a9772
		             NULL); /* synchronize on the clock */
Packit 1fb8d4
		g_object_set(G_OBJECT(mdecoder->outsink), "async", TRUE, NULL); /* no async state changes */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	tsmf_window_create(mdecoder);
Packit 1fb8d4
	tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_READY);
Packit 1fb8d4
	tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
Packit 1fb8d4
	mdecoder->pipeline_start_time_valid = 0;
Packit 1fb8d4
	mdecoder->shutdown = 0;
Packit 1fb8d4
	mdecoder->paused = FALSE;
Packit 1fb8d4
Packit Service 5a9772
	GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(mdecoder->pipe), GST_DEBUG_GRAPH_SHOW_ALL,
Packit Service 5a9772
	                          get_type(mdecoder));
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL tsmf_gstreamer_decodeEx(ITSMFDecoder* decoder, const BYTE* data, UINT32 data_size,
Packit Service 5a9772
                                    UINT32 extensions, UINT64 start_time, UINT64 end_time,
Packit Service 5a9772
                                    UINT64 duration)
Packit 1fb8d4
{
Packit Service 5a9772
	GstBuffer* gst_buf;
Packit Service 5a9772
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
Packit 1fb8d4
	UINT64 sample_time = tsmf_gstreamer_timestamp_ms_to_gst(start_time);
Packit 1fb8d4
	BOOL useTimestamps = TRUE;
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Decoder not initialized!");
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/*
Packit 1fb8d4
	 * This function is always called from a stream-specific thread.
Packit 1fb8d4
	 * It should be alright to block here if necessary.
Packit 1fb8d4
	 * We don't expect to block here often, since the pipeline should
Packit 1fb8d4
	 * have more than enough buffering.
Packit 1fb8d4
	 */
Packit Service 5a9772
	DEBUG_TSMF(
Packit Service 5a9772
	    "%s. Start:(%" PRIu64 ") End:(%" PRIu64 ") Duration:(%" PRIu64 ") Last Start:(%" PRIu64 ")",
Packit Service 5a9772
	    get_type(mdecoder), start_time, end_time, duration, mdecoder->last_sample_start_time);
Packit 1fb8d4
Packit 1fb8d4
	if (mdecoder->shutdown)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "decodeEx called on shutdown decoder");
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (mdecoder->gst_caps == NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "tsmf_gstreamer_set_format not called or invalid format.");
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder->pipe)
Packit 1fb8d4
		tsmf_gstreamer_pipeline_build(mdecoder);
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder->src)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(
Packit Service 5a9772
		    TAG,
Packit Service 5a9772
		    "failed to construct pipeline correctly. Unable to push buffer to source element.");
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	gst_buf = tsmf_get_buffer_from_data(data, data_size);
Packit 1fb8d4
Packit 1fb8d4
	if (gst_buf == NULL)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "tsmf_get_buffer_from_data(%p, %" PRIu32 ") failed.", (void*)data, data_size);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* Relative timestamping will sometimes be set to 0
Packit 1fb8d4
	 * so we ignore these timestamps just to be safe(bit 8)
Packit 1fb8d4
	 */
Packit 1fb8d4
	if (extensions & 0x00000080)
Packit 1fb8d4
	{
Packit 1fb8d4
		DEBUG_TSMF("Ignoring the timestamps - relative - bit 8");
Packit 1fb8d4
		useTimestamps = FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* If no timestamps exist then we dont want to look at the timestamp values (bit 7) */
Packit 1fb8d4
	if (extensions & 0x00000040)
Packit 1fb8d4
	{
Packit 1fb8d4
		DEBUG_TSMF("Ignoring the timestamps - none - bit 7");
Packit 1fb8d4
		useTimestamps = FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* If performing a seek */
Packit 1fb8d4
	if (mdecoder->seeking)
Packit 1fb8d4
	{
Packit 1fb8d4
		mdecoder->seeking = FALSE;
Packit 1fb8d4
		tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
Packit Service 5a9772
		mdecoder->pipeline_start_time_valid = 0;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (mdecoder->pipeline_start_time_valid)
Packit 1fb8d4
	{
Packit Service 5a9772
		DEBUG_TSMF("%s start time %" PRIu64 "", get_type(mdecoder), start_time);
Packit 1fb8d4
Packit 1fb8d4
		/* Adjusted the condition for a seek to be based on start time only
Packit 1fb8d4
		 * WMV1 and WMV2 files in particular have bad end time and duration values
Packit 1fb8d4
		 * there seems to be no real side effects of just using the start time instead
Packit 1fb8d4
		 */
Packit Service 5a9772
		UINT64 minTime = mdecoder->last_sample_start_time - (UINT64)SEEK_TOLERANCE;
Packit Service 5a9772
		UINT64 maxTime = mdecoder->last_sample_start_time + (UINT64)SEEK_TOLERANCE;
Packit 1fb8d4
Packit Service 5a9772
		/* Make sure the minTime stops at 0 , should we be at the beginning of the stream */
Packit Service 5a9772
		if (mdecoder->last_sample_start_time < (UINT64)SEEK_TOLERANCE)
Packit Service 5a9772
			minTime = 0;
Packit 1fb8d4
Packit Service 5a9772
		/* If the start_time is valid and different from the previous start time by more than the
Packit Service 5a9772
		 * seek tolerance, then we have a seek condition */
Packit 1fb8d4
		if (((start_time > maxTime) || (start_time < minTime)) && useTimestamps)
Packit 1fb8d4
		{
Packit Service 5a9772
			DEBUG_TSMF("tsmf_gstreamer_decodeEx: start_time=[%" PRIu64
Packit Service 5a9772
			           "] > last_sample_start_time=[%" PRIu64 "] OR ",
Packit Service 5a9772
			           start_time, mdecoder->last_sample_start_time);
Packit Service 5a9772
			DEBUG_TSMF("tsmf_gstreamer_decodeEx: start_time=[%" PRIu64
Packit Service 5a9772
			           "] < last_sample_start_time=[%" PRIu64 "] with",
Packit Service 5a9772
			           start_time, mdecoder->last_sample_start_time);
Packit Service 5a9772
			DEBUG_TSMF(
Packit Service 5a9772
			    "tsmf_gstreamer_decodeEX: a tolerance of more than [%lu] from the last sample",
Packit Service 5a9772
			    SEEK_TOLERANCE);
Packit Service 5a9772
			DEBUG_TSMF("tsmf_gstreamer_decodeEX: minTime=[%" PRIu64 "] maxTime=[%" PRIu64 "]",
Packit Service 5a9772
			           minTime, maxTime);
Packit 1fb8d4
Packit 1fb8d4
			mdecoder->seeking = TRUE;
Packit 1fb8d4
Packit Service 5a9772
			/* since we cant make the gstreamer pipeline jump to the new start time after a seek -
Packit Service 5a9772
			 * we just maintain a offset between realtime and gstreamer time
Packit 1fb8d4
			 */
Packit 1fb8d4
			mdecoder->seek_offset = start_time;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit Service 5a9772
		DEBUG_TSMF("%s start time %" PRIu64 "", get_type(mdecoder), start_time);
Packit 1fb8d4
		/* Always set base/start time to 0. Will use seek offset to translate real buffer times
Packit Service 5a9772
		 * back to 0. This allows the video to be started from anywhere and the ability to handle
Packit Service 5a9772
		 * seeks without rebuilding the pipeline, etc. since that is costly
Packit 1fb8d4
		 */
Packit 1fb8d4
		gst_element_set_base_time(mdecoder->pipe, tsmf_gstreamer_timestamp_ms_to_gst(0));
Packit 1fb8d4
		gst_element_set_start_time(mdecoder->pipe, tsmf_gstreamer_timestamp_ms_to_gst(0));
Packit 1fb8d4
		mdecoder->pipeline_start_time_valid = 1;
Packit 1fb8d4
Packit 1fb8d4
		/* Set the seek offset if buffer has valid timestamps. */
Packit 1fb8d4
		if (useTimestamps)
Packit 1fb8d4
			mdecoder->seek_offset = start_time;
Packit 1fb8d4
Packit 1fb8d4
		if (!gst_element_seek(mdecoder->pipe, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
Packit Service 5a9772
		                      GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "seek failed");
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
#if GST_VERSION_MAJOR > 0
Packit 1fb8d4
	if (useTimestamps)
Packit Service 5a9772
		GST_BUFFER_PTS(gst_buf) =
Packit Service 5a9772
		    sample_time - tsmf_gstreamer_timestamp_ms_to_gst(mdecoder->seek_offset);
Packit 1fb8d4
	else
Packit 1fb8d4
		GST_BUFFER_PTS(gst_buf) = GST_CLOCK_TIME_NONE;
Packit 1fb8d4
#else
Packit 1fb8d4
	if (useTimestamps)
Packit Service 5a9772
		GST_BUFFER_TIMESTAMP(gst_buf) =
Packit Service 5a9772
		    sample_time - tsmf_gstreamer_timestamp_ms_to_gst(mdecoder->seek_offset);
Packit 1fb8d4
	else
Packit 1fb8d4
		GST_BUFFER_TIMESTAMP(gst_buf) = GST_CLOCK_TIME_NONE;
Packit 1fb8d4
#endif
Packit 1fb8d4
	GST_BUFFER_DURATION(gst_buf) = GST_CLOCK_TIME_NONE;
Packit 1fb8d4
	GST_BUFFER_OFFSET(gst_buf) = GST_BUFFER_OFFSET_NONE;
Packit 1fb8d4
#if GST_VERSION_MAJOR > 0
Packit 1fb8d4
#else
Packit 1fb8d4
	gst_buffer_set_caps(gst_buf, mdecoder->gst_caps);
Packit 1fb8d4
#endif
Packit 1fb8d4
	gst_app_src_push_buffer(GST_APP_SRC(mdecoder->src), gst_buf);
Packit 1fb8d4
Packit 1fb8d4
	/* Should only update the last timestamps if the current ones are valid */
Packit 1fb8d4
	if (useTimestamps)
Packit 1fb8d4
	{
Packit 1fb8d4
		mdecoder->last_sample_start_time = start_time;
Packit 1fb8d4
		mdecoder->last_sample_end_time = end_time;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (mdecoder->pipe && (GST_STATE(mdecoder->pipe) != GST_STATE_PLAYING))
Packit 1fb8d4
	{
Packit Service 5a9772
		DEBUG_TSMF("%s: state=%s", get_type(mdecoder),
Packit Service 5a9772
		           gst_element_state_get_name(GST_STATE(mdecoder->pipe)));
Packit 1fb8d4
Packit Service 5a9772
		DEBUG_TSMF("%s Paused: %" PRIi32 "   Shutdown: %i   Ready: %" PRIi32 "", get_type(mdecoder),
Packit Service 5a9772
		           mdecoder->paused, mdecoder->shutdown, mdecoder->ready);
Packit 1fb8d4
		if (!mdecoder->paused && !mdecoder->shutdown && mdecoder->ready)
Packit 1fb8d4
			tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL tsmf_gstreamer_control(ITSMFDecoder* decoder, ITSMFControlMsg control_msg, UINT32* arg)
Packit 1fb8d4
{
Packit Service 5a9772
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Control called with no decoder!");
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (control_msg == Control_Pause)
Packit 1fb8d4
	{
Packit 1fb8d4
		DEBUG_TSMF("Control_Pause %s", get_type(mdecoder));
Packit 1fb8d4
Packit 1fb8d4
		if (mdecoder->paused)
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "%s: Ignoring Control_Pause, already received!", get_type(mdecoder));
Packit 1fb8d4
			return TRUE;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
Packit 1fb8d4
		mdecoder->shutdown = 0;
Packit 1fb8d4
		mdecoder->paused = TRUE;
Packit 1fb8d4
	}
Packit 1fb8d4
	else if (control_msg == Control_Resume)
Packit 1fb8d4
	{
Packit 1fb8d4
		DEBUG_TSMF("Control_Resume %s", get_type(mdecoder));
Packit 1fb8d4
Packit 1fb8d4
		if (!mdecoder->paused && !mdecoder->shutdown)
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "%s: Ignoring Control_Resume, already received!", get_type(mdecoder));
Packit 1fb8d4
			return TRUE;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		mdecoder->shutdown = 0;
Packit 1fb8d4
		mdecoder->paused = FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
	else if (control_msg == Control_Stop)
Packit 1fb8d4
	{
Packit 1fb8d4
		DEBUG_TSMF("Control_Stop %s", get_type(mdecoder));
Packit 1fb8d4
Packit 1fb8d4
		if (mdecoder->shutdown)
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "%s: Ignoring Control_Stop, already received!", get_type(mdecoder));
Packit 1fb8d4
			return TRUE;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		/* Reset stamps, flush buffers, etc */
Packit 1fb8d4
		if (mdecoder->pipe)
Packit 1fb8d4
		{
Packit 1fb8d4
			tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
Packit 1fb8d4
			tsmf_window_destroy(mdecoder);
Packit 1fb8d4
			tsmf_gstreamer_clean_up(mdecoder);
Packit 1fb8d4
		}
Packit 1fb8d4
		mdecoder->seek_offset = 0;
Packit 1fb8d4
		mdecoder->pipeline_start_time_valid = 0;
Packit 1fb8d4
		mdecoder->shutdown = 1;
Packit 1fb8d4
	}
Packit 1fb8d4
	else if (control_msg == Control_Restart)
Packit 1fb8d4
	{
Packit 1fb8d4
		DEBUG_TSMF("Control_Restart %s", get_type(mdecoder));
Packit 1fb8d4
		mdecoder->shutdown = 0;
Packit 1fb8d4
		mdecoder->paused = FALSE;
Packit 1fb8d4
Packit 1fb8d4
		if (mdecoder->pipeline_start_time_valid)
Packit 1fb8d4
			tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
		WLog_ERR(TAG, "Unknown control message %08x", control_msg);
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL tsmf_gstreamer_buffer_level(ITSMFDecoder* decoder)
Packit 1fb8d4
{
Packit Service 5a9772
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
Packit 1fb8d4
	DEBUG_TSMF("");
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	guint clbuff = 0;
Packit 1fb8d4
Packit 1fb8d4
	if (G_IS_OBJECT(mdecoder->queue))
Packit 1fb8d4
		g_object_get(mdecoder->queue, "current-level-buffers", &clbuff, NULL);
Packit 1fb8d4
Packit 1fb8d4
	DEBUG_TSMF("%s buffer level %u", get_type(mdecoder), clbuff);
Packit 1fb8d4
	return clbuff;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static void tsmf_gstreamer_free(ITSMFDecoder* decoder)
Packit 1fb8d4
{
Packit Service 5a9772
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
Packit 1fb8d4
	DEBUG_TSMF("%s", get_type(mdecoder));
Packit 1fb8d4
Packit 1fb8d4
	if (mdecoder)
Packit 1fb8d4
	{
Packit 1fb8d4
		tsmf_window_destroy(mdecoder);
Packit 1fb8d4
		tsmf_gstreamer_clean_up(mdecoder);
Packit 1fb8d4
Packit 1fb8d4
		if (mdecoder->gst_caps)
Packit 1fb8d4
			gst_caps_unref(mdecoder->gst_caps);
Packit 1fb8d4
Packit 1fb8d4
		tsmf_platform_free(mdecoder);
Packit 1fb8d4
		ZeroMemory(mdecoder, sizeof(TSMFGstreamerDecoder));
Packit 1fb8d4
		free(mdecoder);
Packit 1fb8d4
		mdecoder = NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static UINT64 tsmf_gstreamer_get_running_time(ITSMFDecoder* decoder)
Packit 1fb8d4
{
Packit Service 5a9772
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder)
Packit 1fb8d4
		return 0;
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder->outsink)
Packit 1fb8d4
		return mdecoder->last_sample_start_time;
Packit 1fb8d4
Packit 1fb8d4
	if (!mdecoder->pipe)
Packit 1fb8d4
		return 0;
Packit 1fb8d4
Packit 1fb8d4
	GstFormat fmt = GST_FORMAT_TIME;
Packit 1fb8d4
	gint64 pos = 0;
Packit 1fb8d4
#if GST_VERSION_MAJOR > 0
Packit 1fb8d4
	gst_element_query_position(mdecoder->pipe, fmt, &pos;;
Packit 1fb8d4
#else
Packit 1fb8d4
	gst_element_query_position(mdecoder->pipe, &fmt, &pos;;
Packit 1fb8d4
#endif
Packit Service 5a9772
	return (UINT64)(pos / 100 + mdecoder->seek_offset);
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL tsmf_gstreamer_update_rendering_area(ITSMFDecoder* decoder, int newX, int newY,
Packit Service 5a9772
                                                 int newWidth, int newHeight, int numRectangles,
Packit Service 5a9772
                                                 RDP_RECT* rectangles)
Packit 1fb8d4
{
Packit Service 5a9772
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
Packit Service 5a9772
	DEBUG_TSMF("x=%d, y=%d, w=%d, h=%d, rect=%d", newX, newY, newWidth, newHeight, numRectangles);
Packit 1fb8d4
Packit 1fb8d4
	if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
Packit 1fb8d4
	{
Packit Service 5a9772
		return tsmf_window_resize(mdecoder, newX, newY, newWidth, newHeight, numRectangles,
Packit Service 5a9772
		                          rectangles) == 0;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL tsmf_gstreamer_ack(ITSMFDecoder* decoder, BOOL (*cb)(void*, BOOL), void* stream)
Packit 1fb8d4
{
Packit Service 5a9772
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
Packit 1fb8d4
	DEBUG_TSMF("");
Packit 1fb8d4
	mdecoder->ack_cb = NULL;
Packit 1fb8d4
	mdecoder->stream = stream;
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL tsmf_gstreamer_sync(ITSMFDecoder* decoder, void (*cb)(void*), void* stream)
Packit 1fb8d4
{
Packit Service 5a9772
	TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
Packit 1fb8d4
	DEBUG_TSMF("");
Packit 1fb8d4
	mdecoder->sync_cb = NULL;
Packit 1fb8d4
	mdecoder->stream = stream;
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#ifdef BUILTIN_CHANNELS
Packit Service 5a9772
#define freerdp_tsmf_client_subsystem_entry gstreamer_freerdp_tsmf_client_decoder_subsystem_entry
Packit 1fb8d4
#else
Packit Service 5a9772
#define freerdp_tsmf_client_subsystem_entry FREERDP_API freerdp_tsmf_client_decoder_subsystem_entry
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
ITSMFDecoder* freerdp_tsmf_client_subsystem_entry(void)
Packit 1fb8d4
{
Packit Service 5a9772
	TSMFGstreamerDecoder* decoder;
Packit 1fb8d4
Packit Service 5a9772
#if GST_CHECK_VERSION(0, 10, 31)
Packit 1fb8d4
	if (!gst_is_initialized())
Packit 1fb8d4
	{
Packit 1fb8d4
		gst_init(NULL, NULL);
Packit 1fb8d4
	}
Packit 1fb8d4
#else
Packit 1fb8d4
	gst_init(NULL, NULL);
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
	decoder = calloc(1, sizeof(TSMFGstreamerDecoder));
Packit 1fb8d4
Packit 1fb8d4
	if (!decoder)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	decoder->iface.SetFormat = tsmf_gstreamer_set_format;
Packit 1fb8d4
	decoder->iface.Decode = NULL;
Packit 1fb8d4
	decoder->iface.GetDecodedData = NULL;
Packit 1fb8d4
	decoder->iface.GetDecodedFormat = NULL;
Packit 1fb8d4
	decoder->iface.GetDecodedDimension = NULL;
Packit 1fb8d4
	decoder->iface.GetRunningTime = tsmf_gstreamer_get_running_time;
Packit 1fb8d4
	decoder->iface.UpdateRenderingArea = tsmf_gstreamer_update_rendering_area;
Packit 1fb8d4
	decoder->iface.Free = tsmf_gstreamer_free;
Packit 1fb8d4
	decoder->iface.Control = tsmf_gstreamer_control;
Packit 1fb8d4
	decoder->iface.DecodeEx = tsmf_gstreamer_decodeEx;
Packit 1fb8d4
	decoder->iface.ChangeVolume = tsmf_gstreamer_change_volume;
Packit 1fb8d4
	decoder->iface.BufferLevel = tsmf_gstreamer_buffer_level;
Packit 1fb8d4
	decoder->iface.SetAckFunc = tsmf_gstreamer_ack;
Packit 1fb8d4
	decoder->iface.SetSyncFunc = tsmf_gstreamer_sync;
Packit 1fb8d4
	decoder->paused = FALSE;
Packit 1fb8d4
	decoder->gstVolume = 0.5;
Packit 1fb8d4
	decoder->gstMuted = FALSE;
Packit Service 5a9772
	decoder->state = GST_STATE_VOID_PENDING; /* No real state yet */
Packit 1fb8d4
	decoder->last_sample_start_time = 0;
Packit 1fb8d4
	decoder->last_sample_end_time = 0;
Packit 1fb8d4
	decoder->seek_offset = 0;
Packit 1fb8d4
	decoder->seeking = FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (tsmf_platform_create(decoder) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		free(decoder);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	return (ITSMFDecoder*)decoder;
Packit 1fb8d4
}