2012-06-13 23:45:58 +04:00
|
|
|
/*
|
2012-10-09 07:02:04 +04:00
|
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
2012-06-13 23:45:58 +04:00
|
|
|
* Video Redirection Virtual Channel - GStreamer Decoder
|
|
|
|
*
|
2014-05-23 15:46:37 +04:00
|
|
|
* (C) Copyright 2012 HP Development Company, LLC
|
|
|
|
* (C) Copyright 2014 Thincast Technologies GmbH
|
|
|
|
* (C) Copyright 2014 Armin Novak <armin.novak@thincast.com>
|
|
|
|
*
|
2012-06-13 23:45:58 +04:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2012-08-15 01:09:01 +04:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
#include <assert.h>
|
2012-10-09 22:26:07 +04:00
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
2012-10-09 22:26:07 +04:00
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
#include <gst/gst.h>
|
|
|
|
#include <gst/app/gstappsrc.h>
|
|
|
|
#include <gst/app/gstappsink.h>
|
|
|
|
|
|
|
|
#include "tsmf_constants.h"
|
|
|
|
#include "tsmf_decoder.h"
|
2014-05-23 15:46:37 +04:00
|
|
|
#include "tsmf_platform.h"
|
2012-06-13 23:45:58 +04:00
|
|
|
|
2012-10-09 22:52:07 +04:00
|
|
|
#ifdef HAVE_INTTYPES_H
|
|
|
|
#include <inttypes.h>
|
|
|
|
#endif
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
static BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder);
|
|
|
|
static void tsmf_gstreamer_clean_up(TSMFGstreamerDecoder* mdecoder);
|
|
|
|
static int tsmf_gstreamer_pipeline_set_state(TSMFGstreamerDecoder* mdecoder,
|
2014-05-23 15:46:37 +04:00
|
|
|
GstState desired_state);
|
2012-06-13 23:45:58 +04:00
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
const char* get_type(TSMFGstreamerDecoder* mdecoder)
|
2014-05-23 15:46:37 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
if (!mdecoder)
|
|
|
|
return NULL;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
2014-05-23 15:46:37 +04:00
|
|
|
return "VIDEO";
|
|
|
|
else
|
|
|
|
return "AUDIO";
|
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
static void tsmf_gstreamer_enough_data(GstAppSrc *src, gpointer user_data)
|
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = user_data;
|
|
|
|
(void) mdecoder;
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("%s", get_type(mdecoder));
|
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
static void tsmf_gstreamer_need_data(GstAppSrc *src, guint length, gpointer user_data)
|
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = user_data;
|
|
|
|
(void) mdecoder;
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("%s length=%lu", get_type(mdecoder), length);
|
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
static gboolean tsmf_gstreamer_seek_data(GstAppSrc *src, guint64 offset, gpointer user_data)
|
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = user_data;
|
|
|
|
(void) mdecoder;
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("%s offset=%llu", get_type(mdecoder), offset);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder->paused)
|
2014-05-25 01:32:47 +04:00
|
|
|
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
gst_app_src_end_of_stream((GstAppSrc*) mdecoder->src);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder->paused)
|
2014-05-25 01:32:47 +04:00
|
|
|
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder->sync_cb)
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->sync_cb(mdecoder->stream);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
return TRUE;
|
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
|
2012-10-09 11:26:39 +04:00
|
|
|
static inline const GstClockTime tsmf_gstreamer_timestamp_ms_to_gst(UINT64 ms_timestamp)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-23 15:46:37 +04:00
|
|
|
/*
|
2012-06-13 23:45:58 +04:00
|
|
|
* Convert Microsoft 100ns timestamps to Gstreamer 1ns units.
|
|
|
|
*/
|
|
|
|
return (GstClockTime)(ms_timestamp * 100);
|
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
int tsmf_gstreamer_pipeline_set_state(TSMFGstreamerDecoder* mdecoder, GstState desired_state)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
|
|
|
GstStateChangeReturn state_change;
|
2014-11-10 22:02:54 +03:00
|
|
|
const char* name;
|
|
|
|
const char* sname = get_type(mdecoder);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder)
|
2014-05-23 15:46:37 +04:00
|
|
|
return 0;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder->pipe)
|
2012-06-13 23:45:58 +04:00
|
|
|
return 0; /* Just in case this is called during startup or shutdown when we don't expect it */
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (desired_state == mdecoder->state)
|
2012-06-13 23:45:58 +04:00
|
|
|
return 0; /* Redundant request - Nothing to do */
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-25 00:22:33 +04:00
|
|
|
name = gst_element_state_get_name(desired_state); /* For debug */
|
2014-05-25 12:08:50 +04:00
|
|
|
DEBUG_TSMF("%s to %s", sname, name);
|
2014-05-23 15:46:37 +04:00
|
|
|
state_change = gst_element_set_state(mdecoder->pipe, desired_state);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (state_change == GST_STATE_CHANGE_FAILURE)
|
2014-11-10 22:02:54 +03:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "%s: (%s) GST_STATE_CHANGE_FAILURE.", sname, name);
|
2014-11-10 22:02:54 +03:00
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
else if (state_change == GST_STATE_CHANGE_ASYNC)
|
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "%s: (%s) GST_STATE_CHANGE_ASYNC.", sname, name);
|
2014-05-23 17:35:25 +04:00
|
|
|
mdecoder->state = desired_state;
|
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
else
|
2014-11-10 22:02:54 +03:00
|
|
|
{
|
2014-05-23 17:35:25 +04:00
|
|
|
mdecoder->state = desired_state;
|
2014-11-10 22:02:54 +03:00
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
static GstBuffer* tsmf_get_buffer_from_data(const void* raw_data, gsize size)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
GstBuffer* buffer;
|
2014-05-23 15:46:37 +04:00
|
|
|
gpointer data;
|
2014-11-10 22:02:54 +03:00
|
|
|
|
|
|
|
if (!raw_data)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (size < 1)
|
|
|
|
return NULL;
|
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
data = g_malloc(size);
|
2014-05-25 00:22:33 +04:00
|
|
|
|
|
|
|
if (!data)
|
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "Could not allocate %"G_GSIZE_FORMAT" bytes of data.", size);
|
2014-05-25 00:22:33 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
CopyMemory(data, raw_data, size);
|
|
|
|
|
2014-05-25 00:22:33 +04:00
|
|
|
#if GST_VERSION_MAJOR > 0
|
|
|
|
buffer = gst_buffer_new_wrapped(data, size);
|
|
|
|
#else
|
|
|
|
buffer = gst_buffer_new();
|
2012-06-13 23:45:58 +04:00
|
|
|
|
2014-05-25 00:22:33 +04:00
|
|
|
if (!buffer)
|
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "Could not create GstBuffer");
|
2014-05-25 00:22:33 +04:00
|
|
|
free(data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GST_BUFFER_MALLOCDATA(buffer) = data;
|
|
|
|
GST_BUFFER_SIZE(buffer) = size;
|
|
|
|
GST_BUFFER_DATA(buffer) = GST_BUFFER_MALLOCDATA(buffer);
|
|
|
|
#endif
|
2014-11-10 22:02:54 +03:00
|
|
|
|
2014-05-25 00:22:33 +04:00
|
|
|
return buffer;
|
2014-05-23 15:46:37 +04:00
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* media_type)
|
2014-05-23 15:46:37 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*) decoder;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder)
|
2014-05-23 15:46:37 +04:00
|
|
|
return FALSE;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("");
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
switch (media_type->MajorType)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
|
|
|
case TSMF_MAJOR_TYPE_VIDEO:
|
|
|
|
mdecoder->media_type = TSMF_MAJOR_TYPE_VIDEO;
|
|
|
|
break;
|
|
|
|
case TSMF_MAJOR_TYPE_AUDIO:
|
|
|
|
mdecoder->media_type = TSMF_MAJOR_TYPE_AUDIO;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return FALSE;
|
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
switch (media_type->SubType)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
|
|
|
case TSMF_SUB_TYPE_WVC1:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("video/x-wmv",
|
|
|
|
"width", G_TYPE_INT, media_type->Width,
|
|
|
|
"height", G_TYPE_INT, media_type->Height,
|
|
|
|
"wmvversion", G_TYPE_INT, 3,
|
2014-09-17 11:06:17 +04:00
|
|
|
"format", G_TYPE_STRING, "WVC1",
|
2014-05-23 15:46:37 +04:00
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_MP4S:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("video/x-divx",
|
|
|
|
"divxversion", G_TYPE_INT, 5,
|
|
|
|
"bitrate", G_TYPE_UINT, media_type->BitRate,
|
|
|
|
"width", G_TYPE_INT, media_type->Width,
|
|
|
|
"height", G_TYPE_INT, media_type->Height,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_MP42:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("video/x-msmpeg",
|
|
|
|
"msmpegversion", G_TYPE_INT, 42,
|
|
|
|
"bitrate", G_TYPE_UINT, media_type->BitRate,
|
|
|
|
"width", G_TYPE_INT, media_type->Width,
|
|
|
|
"height", G_TYPE_INT, media_type->Height,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_MP43:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("video/x-msmpeg",
|
|
|
|
"bitrate", G_TYPE_UINT, media_type->BitRate,
|
|
|
|
"width", G_TYPE_INT, media_type->Width,
|
|
|
|
"height", G_TYPE_INT, media_type->Height,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_WMA9:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("audio/x-wma",
|
|
|
|
"wmaversion", G_TYPE_INT, 3,
|
|
|
|
"rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
|
|
|
|
"channels", G_TYPE_INT, media_type->Channels,
|
|
|
|
"bitrate", G_TYPE_INT, media_type->BitRate,
|
|
|
|
"depth", G_TYPE_INT, media_type->BitsPerSample,
|
|
|
|
"width", G_TYPE_INT, media_type->BitsPerSample,
|
|
|
|
"block_align", G_TYPE_INT, media_type->BlockAlign,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_WMA2:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("audio/x-wma",
|
|
|
|
"wmaversion", G_TYPE_INT, 2,
|
|
|
|
"rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
|
|
|
|
"channels", G_TYPE_INT, media_type->Channels,
|
|
|
|
"bitrate", G_TYPE_INT, media_type->BitRate,
|
|
|
|
"depth", G_TYPE_INT, media_type->BitsPerSample,
|
|
|
|
"width", G_TYPE_INT, media_type->BitsPerSample,
|
|
|
|
"block_align", G_TYPE_INT, media_type->BlockAlign,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_MP3:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("audio/mpeg",
|
|
|
|
"mpegversion", G_TYPE_INT, 1,
|
|
|
|
"layer", G_TYPE_INT, 3,
|
|
|
|
"rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
|
|
|
|
"channels", G_TYPE_INT, media_type->Channels,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_WMV1:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("video/x-wmv",
|
|
|
|
"bitrate", G_TYPE_UINT, media_type->BitRate,
|
|
|
|
"width", G_TYPE_INT, media_type->Width,
|
|
|
|
"height", G_TYPE_INT, media_type->Height,
|
|
|
|
"wmvversion", G_TYPE_INT, 1,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_WMV2:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("video/x-wmv",
|
|
|
|
"width", G_TYPE_INT, media_type->Width,
|
|
|
|
"height", G_TYPE_INT, media_type->Height,
|
|
|
|
"wmvversion", G_TYPE_INT, 2,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_WMV3:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("video/x-wmv",
|
|
|
|
"bitrate", G_TYPE_UINT, media_type->BitRate,
|
|
|
|
"width", G_TYPE_INT, media_type->Width,
|
|
|
|
"height", G_TYPE_INT, media_type->Height,
|
|
|
|
"wmvversion", G_TYPE_INT, 3,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_AVC1:
|
|
|
|
case TSMF_SUB_TYPE_H264:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("video/x-h264",
|
|
|
|
"width", G_TYPE_INT, media_type->Width,
|
|
|
|
"height", G_TYPE_INT, media_type->Height,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_AC3:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("audio/x-ac3",
|
|
|
|
"rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
|
|
|
|
"channels", G_TYPE_INT, media_type->Channels,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_AAC:
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
/* For AAC the pFormat is a HEAACWAVEINFO struct, and the codec data
|
|
|
|
is at the end of it. See
|
|
|
|
http://msdn.microsoft.com/en-us/library/dd757806.aspx */
|
2014-05-23 17:35:25 +04:00
|
|
|
if (media_type->ExtraData)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
|
|
|
media_type->ExtraData += 12;
|
|
|
|
media_type->ExtraDataSize -= 12;
|
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("audio/mpeg",
|
|
|
|
"rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
|
|
|
|
"channels", G_TYPE_INT, media_type->Channels,
|
|
|
|
"mpegversion", G_TYPE_INT, 4,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_MP1A:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("audio/mpeg",
|
|
|
|
"mpegversion", G_TYPE_INT, 1,
|
|
|
|
"channels", G_TYPE_INT, media_type->Channels,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_MP1V:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("video/mpeg",
|
|
|
|
"mpegversion", G_TYPE_INT, 1,
|
|
|
|
"width", G_TYPE_INT, media_type->Width,
|
|
|
|
"height", G_TYPE_INT, media_type->Height,
|
|
|
|
"systemstream", G_TYPE_BOOLEAN, FALSE,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_YUY2:
|
2014-05-26 09:46:11 +04:00
|
|
|
#if GST_VERSION_MAJOR > 0
|
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("video/x-raw",
|
|
|
|
"format", G_TYPE_STRING, "YUY2",
|
|
|
|
"width", G_TYPE_INT, media_type->Width,
|
|
|
|
"height", G_TYPE_INT, media_type->Height,
|
|
|
|
NULL);
|
|
|
|
#else
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("video/x-raw-yuv",
|
|
|
|
"format", G_TYPE_STRING, "YUY2",
|
|
|
|
"width", G_TYPE_INT, media_type->Width,
|
|
|
|
"height", G_TYPE_INT, media_type->Height,
|
|
|
|
NULL);
|
2014-05-26 09:46:11 +04:00
|
|
|
#endif
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_MP2V:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("video/mpeg",
|
|
|
|
"mpegversion", G_TYPE_INT, 2,
|
|
|
|
"systemstream", G_TYPE_BOOLEAN, FALSE,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
case TSMF_SUB_TYPE_MP2A:
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->gst_caps = gst_caps_new_simple("audio/mpeg",
|
|
|
|
"mpegversion", G_TYPE_INT, 2,
|
|
|
|
"rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
|
|
|
|
"channels", G_TYPE_INT, media_type->Channels,
|
|
|
|
NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
default:
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "unknown format:(%d).", media_type->SubType);
|
2012-06-13 23:45:58 +04:00
|
|
|
return FALSE;
|
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (media_type->ExtraDataSize > 0)
|
2014-05-23 15:46:37 +04:00
|
|
|
{
|
2014-05-25 00:22:33 +04:00
|
|
|
GstBuffer *buffer;
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("Extra data available (%d)", media_type->ExtraDataSize);
|
2014-05-25 00:22:33 +04:00
|
|
|
buffer = tsmf_get_buffer_from_data(media_type->ExtraData, media_type->ExtraDataSize);
|
|
|
|
|
|
|
|
if (!buffer)
|
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "could not allocate GstBuffer!");
|
2014-05-25 00:22:33 +04:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_caps_set_simple(mdecoder->gst_caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL);
|
2014-05-23 15:46:37 +04:00
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("%p format '%s'", mdecoder, gst_caps_to_string(mdecoder->gst_caps));
|
|
|
|
tsmf_platform_set_format(mdecoder);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
/* Create the pipeline... */
|
2014-05-23 17:35:25 +04:00
|
|
|
if (!tsmf_gstreamer_pipeline_build(mdecoder))
|
2014-05-23 15:46:37 +04:00
|
|
|
return FALSE;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-09-12 18:19:32 +04:00
|
|
|
void tsmf_gstreamer_clean_up(TSMFGstreamerDecoder* mdecoder)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-23 17:35:25 +04:00
|
|
|
if (!mdecoder || !mdecoder->pipe)
|
2014-05-23 15:46:37 +04:00
|
|
|
return;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder->pipe && GST_OBJECT_REFCOUNT_VALUE(mdecoder->pipe) > 0)
|
2014-05-23 15:46:37 +04:00
|
|
|
{
|
2014-05-25 01:32:47 +04:00
|
|
|
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
|
2014-05-23 15:46:37 +04:00
|
|
|
gst_object_unref(mdecoder->pipe);
|
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
tsmf_window_destroy(mdecoder);
|
|
|
|
mdecoder->ready = FALSE;
|
|
|
|
mdecoder->pipe = NULL;
|
|
|
|
mdecoder->src = NULL;
|
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder)
|
2014-05-23 15:46:37 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
const char* appsrc = "appsrc name=source ! decodebin name=decoder !";
|
|
|
|
const char* video = "autovideoconvert ! videoscale !";
|
|
|
|
const char* audio = "audioconvert ! audiorate ! audioresample ! volume name=audiovolume !";
|
2014-05-25 12:08:50 +04:00
|
|
|
char pipeline[1024];
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-25 12:08:50 +04:00
|
|
|
if (!mdecoder)
|
|
|
|
return FALSE;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-26 09:46:11 +04:00
|
|
|
/* 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. */
|
2014-05-25 12:08:50 +04:00
|
|
|
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());
|
2014-05-26 09:46:11 +04:00
|
|
|
|
|
|
|
DEBUG_TSMF("pipeline=%s", pipeline);
|
2014-05-25 12:08:50 +04:00
|
|
|
mdecoder->pipe = gst_parse_launch(pipeline, NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
|
2014-05-25 12:08:50 +04:00
|
|
|
if (!mdecoder->pipe)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "Failed to create new pipe");
|
2014-05-25 12:08:50 +04:00
|
|
|
return FALSE;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-25 12:08:50 +04:00
|
|
|
mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "source");
|
2014-05-26 09:46:11 +04:00
|
|
|
|
2014-05-25 12:08:50 +04:00
|
|
|
if (!mdecoder->src)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "Failed to get appsrc");
|
2014-05-25 12:08:50 +04:00
|
|
|
return FALSE;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-25 12:08:50 +04:00
|
|
|
mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "outsink");
|
2014-05-26 09:46:11 +04:00
|
|
|
|
2014-05-25 12:08:50 +04:00
|
|
|
if (!mdecoder->outsink)
|
2014-05-23 15:46:37 +04:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "Failed to get sink");
|
2012-06-13 23:45:58 +04:00
|
|
|
return FALSE;
|
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-25 12:08:50 +04:00
|
|
|
if (mdecoder->media_type != TSMF_MAJOR_TYPE_VIDEO)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-25 12:08:50 +04:00
|
|
|
mdecoder->volume = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiovolume");
|
2014-05-26 09:46:11 +04:00
|
|
|
|
2014-05-23 17:35:25 +04:00
|
|
|
if (!mdecoder->volume)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "Failed to get volume");
|
2014-05-23 15:46:37 +04:00
|
|
|
return FALSE;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
tsmf_platform_register_handler(mdecoder);
|
|
|
|
/* AppSrc settings */
|
|
|
|
GstAppSrcCallbacks callbacks =
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-23 15:46:37 +04:00
|
|
|
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);
|
2014-05-26 09:46:11 +04:00
|
|
|
gst_app_src_set_caps((GstAppSrc *) mdecoder->src, mdecoder->gst_caps);
|
2014-05-23 15:46:37 +04:00
|
|
|
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;
|
2014-05-26 10:25:01 +04:00
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(mdecoder->pipe), GST_DEBUG_GRAPH_SHOW_ALL, get_type(mdecoder));
|
2014-05-26 10:25:01 +04:00
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
static BOOL tsmf_gstreamer_decodeEx(ITSMFDecoder* decoder, const BYTE *data, UINT32 data_size, UINT32 extensions,
|
2014-05-23 15:46:37 +04:00
|
|
|
UINT64 start_time, UINT64 end_time, UINT64 duration)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-23 15:46:37 +04:00
|
|
|
GstBuffer *gst_buf;
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
2014-05-23 15:46:37 +04:00
|
|
|
UINT64 sample_time = tsmf_gstreamer_timestamp_ms_to_gst(start_time);
|
|
|
|
UINT64 sample_duration = tsmf_gstreamer_timestamp_ms_to_gst(duration);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "Decoder not initialized!");
|
2012-06-13 23:45:58 +04:00
|
|
|
return FALSE;
|
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
/*
|
|
|
|
* 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
|
2012-06-13 23:45:58 +04:00
|
|
|
* have more than enough buffering.
|
2014-05-23 15:46:37 +04:00
|
|
|
*/
|
|
|
|
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);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder->gst_caps == NULL)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "tsmf_gstreamer_set_format not called or invalid format.");
|
2012-06-13 23:45:58 +04:00
|
|
|
return FALSE;
|
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder->src)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "failed to construct pipeline correctly. Unable to push buffer to source element.");
|
2014-05-23 15:46:37 +04:00
|
|
|
return FALSE;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
gst_buf = tsmf_get_buffer_from_data(data, data_size);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (gst_buf == NULL)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "tsmf_get_buffer_from_data(%p, %d) failed.", data, data_size);
|
2012-06-13 23:45:58 +04:00
|
|
|
return FALSE;
|
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder->pipeline_start_time_valid)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-23 15:46:37 +04:00
|
|
|
long long diff = start_time;
|
|
|
|
diff -= mdecoder->last_sample_end_time;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (diff < 0)
|
2014-05-23 15:46:37 +04:00
|
|
|
diff *= -1;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
/* The pipe is initialized, but there is a discontinuity.
|
|
|
|
* Seek to the start position... */
|
2014-05-23 17:35:25 +04:00
|
|
|
if (diff > 50)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("%s seeking to %lld", get_type(mdecoder), start_time);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
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))
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "seek failed");
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->pipeline_start_time_valid = 0;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 15:46:37 +04:00
|
|
|
else
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("%s start time %llu", get_type(mdecoder), sample_time);
|
|
|
|
mdecoder->pipeline_start_time_valid = 1;
|
2013-06-19 20:33:46 +04:00
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-25 00:22:33 +04:00
|
|
|
#if GST_VERSION_MAJOR > 0
|
2014-05-23 15:46:37 +04:00
|
|
|
GST_BUFFER_PTS(gst_buf) = sample_time;
|
2014-05-25 00:22:33 +04:00
|
|
|
#else
|
|
|
|
GST_BUFFER_TIMESTAMP(gst_buf) = sample_time;
|
|
|
|
#endif
|
2014-05-23 15:46:37 +04:00
|
|
|
GST_BUFFER_DURATION(gst_buf) = sample_duration;
|
2012-06-13 23:45:58 +04:00
|
|
|
gst_app_src_push_buffer(GST_APP_SRC(mdecoder->src), gst_buf);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder->ack_cb)
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder->ack_cb(mdecoder->stream, TRUE);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
mdecoder->last_sample_end_time = end_time;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (GST_STATE(mdecoder->pipe) != GST_STATE_PLAYING)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-25 01:32:47 +04:00
|
|
|
DEBUG_TSMF("%s: state=%s", get_type(mdecoder), gst_element_state_get_name(GST_STATE(mdecoder->pipe)));
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder->paused && !mdecoder->shutdown && mdecoder->ready)
|
2012-06-13 23:45:58 +04:00
|
|
|
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
|
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
static void tsmf_gstreamer_change_volume(ITSMFDecoder* decoder, UINT32 newVolume, UINT32 muted)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder || !mdecoder->pipe)
|
2012-06-13 23:45:58 +04:00
|
|
|
return;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
2012-06-13 23:45:58 +04:00
|
|
|
return;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2013-06-19 20:33:46 +04:00
|
|
|
mdecoder->gstMuted = (BOOL) muted;
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("mute=[%d]", mdecoder->gstMuted);
|
2013-06-19 20:33:46 +04:00
|
|
|
mdecoder->gstVolume = (double) newVolume / (double) 10000;
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("gst_new_vol=[%f]", mdecoder->gstVolume);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder->volume)
|
2012-06-13 23:45:58 +04:00
|
|
|
return;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!G_IS_OBJECT(mdecoder->volume))
|
2012-06-13 23:45:58 +04:00
|
|
|
return;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
g_object_set(mdecoder->volume, "mute", mdecoder->gstMuted, NULL);
|
|
|
|
g_object_set(mdecoder->volume, "volume", mdecoder->gstVolume, NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
static void tsmf_gstreamer_control(ITSMFDecoder* decoder, ITSMFControlMsg control_msg, UINT32 *arg)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder)
|
2012-06-13 23:45:58 +04:00
|
|
|
return;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (control_msg == Control_Pause)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("Control_Pause %s", get_type(mdecoder));
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder->paused)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "%s: Ignoring control PAUSE, already received!", get_type(mdecoder));
|
2014-05-23 15:46:37 +04:00
|
|
|
return;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
|
|
|
|
mdecoder->paused = TRUE;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
2014-05-23 15:46:37 +04:00
|
|
|
tsmf_window_pause(mdecoder);
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
else if (control_msg == Control_Resume)
|
|
|
|
{
|
|
|
|
DEBUG_TSMF("Control_Resume %s", get_type(mdecoder));
|
|
|
|
|
|
|
|
if (!mdecoder->paused && !mdecoder->shutdown)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "%s: Ignoring control RESUME, already received!", get_type(mdecoder));
|
2014-05-23 17:35:25 +04:00
|
|
|
return;
|
|
|
|
}
|
2014-05-23 15:46:37 +04:00
|
|
|
|
2014-05-23 17:35:25 +04:00
|
|
|
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)
|
|
|
|
{
|
2014-05-26 09:46:11 +04:00
|
|
|
DEBUG_TSMF("Control_Stop %s", get_type(mdecoder));
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder->shutdown)
|
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "%s: Ignoring control STOP, already received!", get_type(mdecoder));
|
2014-05-23 17:35:25 +04:00
|
|
|
return;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
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
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "Unknown control message %08x", control_msg);
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
static BOOL tsmf_gstreamer_buffer_filled(ITSMFDecoder* decoder)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("");
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder)
|
2014-05-23 15:46:37 +04:00
|
|
|
return FALSE;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
guint buff_max = 0;
|
2012-06-13 23:45:58 +04:00
|
|
|
guint clbuff = 0;
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("%s buffer fill %u/%u", get_type(mdecoder), clbuff, buff_max);
|
|
|
|
return clbuff >= buff_max ? TRUE : FALSE;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
static void tsmf_gstreamer_free(ITSMFDecoder* decoder)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
2014-05-26 09:46:11 +04:00
|
|
|
DEBUG_TSMF("%s", get_type(mdecoder));
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
|
|
|
mdecoder->shutdown = 1;
|
2014-05-23 15:46:37 +04:00
|
|
|
tsmf_gstreamer_clean_up(mdecoder);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder->gst_caps)
|
2012-06-13 23:45:58 +04:00
|
|
|
gst_caps_unref(mdecoder->gst_caps);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
tsmf_platform_free(mdecoder);
|
2014-11-10 22:02:54 +03:00
|
|
|
ZeroMemory(mdecoder, sizeof(TSMFGstreamerDecoder));
|
2012-06-13 23:45:58 +04:00
|
|
|
free(mdecoder);
|
2014-05-23 15:46:37 +04:00
|
|
|
mdecoder = NULL;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
static UINT64 tsmf_gstreamer_get_running_time(ITSMFDecoder* decoder)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder)
|
2012-06-13 23:45:58 +04:00
|
|
|
return 0;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!mdecoder->outsink)
|
2012-06-13 23:45:58 +04:00
|
|
|
return mdecoder->last_sample_end_time;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (GST_STATE(mdecoder->pipe) != GST_STATE_PLAYING)
|
2012-06-13 23:45:58 +04:00
|
|
|
return 0;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
GstFormat fmt = GST_FORMAT_TIME;
|
|
|
|
gint64 pos = 0;
|
2014-05-25 00:22:33 +04:00
|
|
|
#if GST_VERSION_MAJOR > 0
|
2014-05-23 15:46:37 +04:00
|
|
|
gst_element_query_position(mdecoder->outsink, fmt, &pos);
|
2014-05-25 00:22:33 +04:00
|
|
|
#else
|
2014-05-25 00:23:51 +04:00
|
|
|
gst_element_query_position(mdecoder->outsink, &fmt, &pos);
|
2014-05-25 00:22:33 +04:00
|
|
|
#endif
|
2012-06-13 23:45:58 +04:00
|
|
|
return pos/100;
|
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
static void tsmf_gstreamer_update_rendering_area(ITSMFDecoder* decoder,
|
2014-05-23 15:46:37 +04:00
|
|
|
int newX, int newY, int newWidth, int newHeight, int numRectangles,
|
|
|
|
RDP_RECT *rectangles)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("x=%d, y=%d, w=%d, h=%d, rect=%d", newX, newY, newWidth,
|
|
|
|
newHeight, numRectangles);
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
2014-11-10 22:02:54 +03:00
|
|
|
{
|
2014-05-23 15:46:37 +04:00
|
|
|
tsmf_window_resize(mdecoder, newX, newY, newWidth, newHeight,
|
|
|
|
numRectangles, rectangles);
|
2014-11-10 22:02:54 +03:00
|
|
|
}
|
2014-05-23 15:46:37 +04:00
|
|
|
}
|
2013-06-19 20:33:46 +04:00
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
BOOL tsmf_gstreamer_ack(ITSMFDecoder* decoder, BOOL (*cb)(void *, BOOL), void *stream)
|
2014-05-23 15:46:37 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("");
|
|
|
|
mdecoder->ack_cb = cb;
|
|
|
|
mdecoder->stream = stream;
|
|
|
|
return TRUE;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
BOOL tsmf_gstreamer_sync(ITSMFDecoder* decoder, void (*cb)(void *), void *stream)
|
2014-05-23 15:46:37 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
2014-05-23 15:46:37 +04:00
|
|
|
DEBUG_TSMF("");
|
|
|
|
mdecoder->sync_cb = NULL;
|
|
|
|
mdecoder->stream = stream;
|
|
|
|
return TRUE;
|
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
|
2012-11-20 07:31:15 +04:00
|
|
|
#ifdef STATIC_CHANNELS
|
2014-09-18 03:19:37 +04:00
|
|
|
#define freerdp_tsmf_client_subsystem_entry gstreamer_freerdp_tsmf_client_decoder_subsystem_entry
|
2012-11-20 07:31:15 +04:00
|
|
|
#endif
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
ITSMFDecoder* freerdp_tsmf_client_subsystem_entry(void)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-23 15:46:37 +04:00
|
|
|
TSMFGstreamerDecoder *decoder;
|
2014-05-23 17:35:25 +04:00
|
|
|
|
|
|
|
if (!gst_is_initialized())
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-23 15:46:37 +04:00
|
|
|
gst_init(NULL, NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-23 17:35:25 +04:00
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
decoder = calloc(1, sizeof(TSMFGstreamerDecoder));
|
|
|
|
|
|
|
|
if (!decoder)
|
|
|
|
return NULL;
|
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
decoder->iface.SetFormat = tsmf_gstreamer_set_format;
|
|
|
|
decoder->iface.Decode = NULL;
|
|
|
|
decoder->iface.GetDecodedData = NULL;
|
|
|
|
decoder->iface.GetDecodedFormat = NULL;
|
|
|
|
decoder->iface.GetDecodedDimension = NULL;
|
|
|
|
decoder->iface.GetRunningTime = tsmf_gstreamer_get_running_time;
|
|
|
|
decoder->iface.UpdateRenderingArea = tsmf_gstreamer_update_rendering_area;
|
|
|
|
decoder->iface.Free = tsmf_gstreamer_free;
|
|
|
|
decoder->iface.Control = tsmf_gstreamer_control;
|
|
|
|
decoder->iface.DecodeEx = tsmf_gstreamer_decodeEx;
|
|
|
|
decoder->iface.ChangeVolume = tsmf_gstreamer_change_volume;
|
2014-05-23 15:46:37 +04:00
|
|
|
decoder->iface.BufferFilled = tsmf_gstreamer_buffer_filled;
|
|
|
|
decoder->iface.SetAckFunc = tsmf_gstreamer_ack;
|
|
|
|
decoder->iface.SetSyncFunc = tsmf_gstreamer_sync;
|
2012-06-13 23:45:58 +04:00
|
|
|
decoder->paused = FALSE;
|
2013-06-19 20:33:46 +04:00
|
|
|
decoder->gstVolume = 0.5;
|
2012-06-13 23:45:58 +04:00
|
|
|
decoder->gstMuted = FALSE;
|
|
|
|
decoder->state = GST_STATE_VOID_PENDING; /* No real state yet */
|
2014-11-10 22:02:54 +03:00
|
|
|
|
2014-05-23 15:46:37 +04:00
|
|
|
tsmf_platform_create(decoder);
|
2014-11-10 22:02:54 +03:00
|
|
|
|
|
|
|
return (ITSMFDecoder*) decoder;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|