FreeRDP/channels/tsmf/client/gstreamer/tsmf_gstreamer.c
2012-09-22 18:08:22 -04:00

1603 lines
47 KiB
C

/*
* FreeRDP: A Remote Desktop Protocol client.
* Video Redirection Virtual Channel - GStreamer Decoder
*
* (C) Copyright 2012 HP Development Company, LLC
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/shape.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>
#include <gst/interfaces/xoverlay.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include "tsmf_constants.h"
#include "tsmf_decoder.h"
#define SHARED_MEM_KEY 7777
#define TRY_DECODEBIN 0
typedef struct _TSMFGstreamerDecoder
{
ITSMFDecoder iface;
int media_type; /* TSMF_MAJOR_TYPE_AUDIO or TSMF_MAJOR_TYPE_VIDEO */
TS_AM_MEDIA_TYPE tsmf_media_type; /* TSMF description of the media type, (without ExtraData) */
pthread_t eventloop_thread;
GstCaps *gst_caps; /* Gstreamer description of the media type */
GstState state;
GstElement *pipe;
GstElement *src;
GstElement *queue;
GstElement *decbin;
GstElement *outbin;
GstElement *outconv;
GstElement *outsink;
GstElement *aVolume;
boolean paused;
uint64 last_sample_end_time;
Display *disp;
int *xfwin;
Window subwin;
int xOffset;
int yOffset;
bool offsetObtained;
int linked;
double gstVolume;
bool gstMuted;
int pipeline_start_time_valid; /* We've set the start time and have not reset the pipeline */
int shutdown; /* The decoder stream is shutting down */
pthread_mutex_t gst_mutex;
} TSMFGstreamerDecoder;
const char *NAME_GST_STATE_PLAYING = "GST_STATE_PLAYING";
const char *NAME_GST_STATE_PAUSED = "GST_STATE_PAUSED";
const char *NAME_GST_STATE_READY = "GST_STATE_READY";
const char *NAME_GST_STATE_NULL = "GST_STATE_NULL";
const char *NAME_GST_STATE_VOID_PENDING = "GST_STATE_VOID_PENDING";
const char *NAME_GST_STATE_OTHER = "GST_STATE_?";
static inline const GstClockTime tsmf_gstreamer_timestamp_ms_to_gst(uint64 ms_timestamp)
{
/*
* Convert Microsoft 100ns timestamps to Gstreamer 1ns units.
*/
return (GstClockTime)(ms_timestamp * 100);
}
static const char *tsmf_gstreamer_state_name(GstState state)
{
const char *name;
if (state == GST_STATE_PLAYING) name = NAME_GST_STATE_PLAYING;
else if (state == GST_STATE_PAUSED) name = NAME_GST_STATE_PAUSED;
else if (state == GST_STATE_READY) name = NAME_GST_STATE_READY;
else if (state == GST_STATE_NULL) name = NAME_GST_STATE_NULL;
else if (state == GST_STATE_VOID_PENDING) name = NAME_GST_STATE_VOID_PENDING;
else name = NAME_GST_STATE_OTHER;
return name;
}
#if 0
static void *tsmf_gstreamer_eventloop_thread_func(void * arg)
{
TSMFGstreamerDecoder * mdecoder = (TSMFGstreamerDecoder *) arg;
GstBus *bus;
GstMessage *message = NULL;
GstState old, new, pending;
int loop;
DEBUG_DVC("tsmf_gstreamer_eventloop_thread_func: ");
bus = gst_element_get_bus(mdecoder->pipe);
loop = 1;
while (loop)
{
message = gst_bus_poll (bus, GST_MESSAGE_ANY, -1);
if (mdecoder->shutdown)
{
loop =0; /* We are done with this stream */
}
else
{
switch (message->type)
{
case GST_MESSAGE_EOS:
DEBUG_DVC("tsmf_gstreamer_eventloop_thread_func: GST_MESSAGE_EOS");
gst_message_unref (message);
break;
case GST_MESSAGE_WARNING:
case GST_MESSAGE_ERROR:
{
DEBUG_DVC("tsmf_gstreamer_eventloop_thread_func: GST_MESSAGE_ERROR");
/*GError *err;
gchar *debug;
gst_message_parse_error(message, &err, &debug);
g_print("ERROR: %s\nDEBUG:%s\n", err->message, debug);
g_error_free(err);
g_free(debug);
gst_message_unref(message);*/
break;
}
case GST_MESSAGE_STATE_CHANGED:
{
gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC(message));
gst_message_parse_state_changed (message, &old, &new, &pending);
DEBUG_DVC("tsmf_gstreamer_eventloop_thread_func: GST_MESSAGE_STATE_CHANGED %s old %s new %s pending %s",
name,
gst_element_state_get_name(old),
gst_element_state_get_name(new),
gst_element_state_get_name(pending));
g_free (name);
gst_message_unref(message);
break;
}
case GST_MESSAGE_REQUEST_STATE:
{
GstState state;
gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC(message));
gst_message_parse_request_state(message, &state);
DEBUG_DVC("GST_MESSAGE_REQUEST_STATE: Setting %s state to %s", name, gst_element_state_get_name(state));
gst_element_set_state (mdecoder->pipe, state);
g_free (name);
gst_message_unref(message);
break;
}
default:
gst_message_unref(message);
break;
}
}
}
mdecoder->eventloop_thread = 0;
DEBUG_DVC("tsmf_gstreamer_eventloop_thread_func: EXITED");
return 0;
}
static int tsmf_gstreamer_start_eventloop_thread(TSMFGstreamerDecoder *mdecoder)
{
pthread_create(&(mdecoder->eventloop_thread), 0, tsmf_gstreamer_eventloop_thread_func, mdecoder);
pthread_detach(mdecoder->eventloop_thread);
return 0;
}
#endif
static int tsmf_gstreamer_stop_eventloop_thread(TSMFGstreamerDecoder *mdecoder)
{
DEBUG_DVC("tsmf_gstreamer_stop_eventloop_thread: ");
if (!mdecoder)
return 0;
if (mdecoder->eventloop_thread != 0)
{
pthread_cancel(mdecoder->eventloop_thread);
}
return 0;
}
static int tsmf_gstreamer_pipeline_set_state(TSMFGstreamerDecoder * mdecoder, GstState desired_state)
{
if (!mdecoder)
return 0;
GstStateChangeReturn state_change;
int keep_waiting;
int timeout;
GstState current_state;
GstState pending_state;
const char *name;
const char *current_name;
const char *pending_name;
if (!mdecoder->pipe)
return 0; /* Just in case this is called during startup or shutdown when we don't expect it */
if (desired_state == mdecoder->state)
return 0; /* Redundant request - Nothing to do */
name = tsmf_gstreamer_state_name(desired_state); /* For debug */
keep_waiting = 1;
state_change = gst_element_set_state (mdecoder->pipe, desired_state);
timeout = 1000;
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
DEBUG_DVC("tsmf_gstreamer_pipeline_set_state_VIDEO:");
else
DEBUG_DVC("tsmf_gstreamer_pipeline_set_state_AUDIO:");
while (keep_waiting)
{
if (state_change == GST_STATE_CHANGE_FAILURE)
{
DEBUG_WARN("tsmf_gstreamer_pipeline_set_state(%s) GST_STATE_CHANGE_FAILURE.", name);
keep_waiting = 0;
}
else if (state_change == GST_STATE_CHANGE_SUCCESS)
{
DEBUG_DVC("tsmf_gstreamer_pipeline_set_state(%s) GST_STATE_CHANGE_SUCCESS.", name);
mdecoder->state = desired_state;
keep_waiting = 0;
}
else if (state_change == GST_STATE_CHANGE_NO_PREROLL)
{
DEBUG_DVC("tsmf_gstreamer_pipeline_set_state(%s) GST_STATE_CHANGE_NO_PREROLL.", name);
keep_waiting = 0;
}
else if (state_change == GST_STATE_CHANGE_ASYNC)
{
DEBUG_DVC("tsmf_gstreamer_pipeline_set_state(%s) GST_STATE_CHANGE_ASYNC.", name);
state_change = gst_element_get_state(mdecoder->pipe, &current_state, &pending_state, 10 * GST_MSECOND);
current_name = tsmf_gstreamer_state_name(current_state);
pending_name = tsmf_gstreamer_state_name(pending_state);
if (current_state == desired_state)
{
DEBUG_DVC("tsmf_gstreamer_pipeline_set_state(%s) GST_STATE_CHANGE_SUCCESS.", name);
mdecoder->state = desired_state;
keep_waiting = 0;
}
else if (pending_state != desired_state)
{
DEBUG_DVC("tsmf_gstreamer_pipeline_set_state(%s) changing to %s instead.", name, pending_name);
keep_waiting = 0;
}
else
{
DEBUG_DVC("tsmf_gstreamer_pipeline_set_state(%s) Waiting - current %s pending %s.", name, current_name, pending_name);
}
}
/*
To avoid RDP session hang. set timeout for changing gstreamer state to 5 seconds.
*/
usleep(10000);
timeout--;
if (timeout <= 0)
{
DEBUG_WARN("tsmf_gstreamer_pipeline_set_state: TIMED OUT - failed to change state");
keep_waiting = 0;
break;
}
}
//sleep(1);
return 0;
}
static boolean tsmf_gstreamer_set_format(ITSMFDecoder * decoder, TS_AM_MEDIA_TYPE * media_type)
{
TSMFGstreamerDecoder * mdecoder = (TSMFGstreamerDecoder *) decoder;
if (!mdecoder)
return false;
GstBuffer *gst_buf_cap_codec_data; /* Buffer to hold extra descriptive codec-specific caps data */
DEBUG_DVC("tsmf_gstreamer_set_format: ");
switch (media_type->MajorType)
{
case TSMF_MAJOR_TYPE_VIDEO:
mdecoder->media_type = TSMF_MAJOR_TYPE_VIDEO;
mdecoder->tsmf_media_type = *media_type; /* Structure copy */
break;
case TSMF_MAJOR_TYPE_AUDIO:
mdecoder->media_type = TSMF_MAJOR_TYPE_AUDIO;
mdecoder->tsmf_media_type = *media_type; /* Structure copy */
break;
default:
return FALSE;
}
switch (media_type->SubType)
{
case TSMF_SUB_TYPE_WVC1:
gst_buf_cap_codec_data = gst_buffer_try_new_and_alloc(media_type->ExtraDataSize);
if (gst_buf_cap_codec_data != NULL)
{
memcpy(GST_BUFFER_MALLOCDATA(gst_buf_cap_codec_data),
media_type->ExtraData, media_type->ExtraDataSize);
}
else
{
DEBUG_WARN("tsmf_gstreamer_set_format: gst_buffer_try_new_and_alloc(%d) failed.",
media_type->ExtraDataSize);
}
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,
"codec_data", GST_TYPE_BUFFER, gst_buf_cap_codec_data,
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
//"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
NULL);
break;
case TSMF_SUB_TYPE_MP4S:
if (media_type->ExtraDataSize > 0)
{
gst_buf_cap_codec_data = gst_buffer_try_new_and_alloc(media_type->ExtraDataSize);
if (gst_buf_cap_codec_data != NULL)
{
memcpy(GST_BUFFER_MALLOCDATA(gst_buf_cap_codec_data), media_type->ExtraData, media_type->ExtraDataSize);
}
else
{
DEBUG_WARN("tsmf_gstreamer_set_format: gst_buffer_try_new_and_alloc(%d) failed.", media_type->ExtraDataSize);
}
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,
"codec_data", GST_TYPE_BUFFER, gst_buf_cap_codec_data,
NULL);
}
else
{
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);
}
break;
case TSMF_SUB_TYPE_MP42:
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);
break;
case TSMF_SUB_TYPE_MP43:
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,
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('M', 'P', '4', '3'),
NULL);
break;
case TSMF_SUB_TYPE_WMA9:
if (media_type->ExtraDataSize > 0)
{
gst_buf_cap_codec_data = gst_buffer_try_new_and_alloc(media_type->ExtraDataSize);
if (gst_buf_cap_codec_data != NULL)
{
memcpy(GST_BUFFER_MALLOCDATA(gst_buf_cap_codec_data), media_type->ExtraData, media_type->ExtraDataSize);
}
else
{
DEBUG_WARN("tsmf_gstreamer_set_format: gst_buffer_try_new_and_alloc(%d) failed.", media_type->ExtraDataSize);
}
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,
"codec_data", GST_TYPE_BUFFER, gst_buf_cap_codec_data,
NULL);
}
else
{
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);
}
break;
case TSMF_SUB_TYPE_WMA2:
gst_buf_cap_codec_data = gst_buffer_try_new_and_alloc(media_type->ExtraDataSize);
if (gst_buf_cap_codec_data != NULL)
{
memcpy(GST_BUFFER_MALLOCDATA(gst_buf_cap_codec_data),
media_type->ExtraData, media_type->ExtraDataSize);
}
else
{
DEBUG_WARN("tsmf_gstreamer_set_format: gst_buffer_try_new_and_alloc(%d) failed.",
media_type->ExtraDataSize);
}
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,
"codec_data", GST_TYPE_BUFFER, gst_buf_cap_codec_data,
NULL);
break;
case TSMF_SUB_TYPE_MP3:
mdecoder->gst_caps = gst_caps_new_simple ("audio/mpeg",
"mpegversion", G_TYPE_INT, 1,
"mpegaudioversion", G_TYPE_INT, 1,
"layer", G_TYPE_INT, 3,
"rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
"channels", G_TYPE_INT, media_type->Channels,
"parsed", G_TYPE_BOOLEAN, TRUE,
NULL);
break;
case TSMF_SUB_TYPE_WMV1:
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,
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'M', 'V', '1'),
NULL);
break;
case TSMF_SUB_TYPE_WMV2:
gst_buf_cap_codec_data = gst_buffer_try_new_and_alloc(media_type->ExtraDataSize);
if (gst_buf_cap_codec_data != NULL)
{
memcpy(GST_BUFFER_MALLOCDATA(gst_buf_cap_codec_data),
media_type->ExtraData, media_type->ExtraDataSize);
}
else
{
DEBUG_WARN("tsmf_gstreamer_set_format: gst_buffer_try_new_and_alloc(%d) failed.",
media_type->ExtraDataSize);
}
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,
"codec_data", GST_TYPE_BUFFER, gst_buf_cap_codec_data,
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'M', 'V', '2'),
NULL);
break;
case TSMF_SUB_TYPE_WMV3:
gst_buf_cap_codec_data = gst_buffer_try_new_and_alloc(media_type->ExtraDataSize);
if (gst_buf_cap_codec_data != NULL)
{
memcpy(GST_BUFFER_MALLOCDATA(gst_buf_cap_codec_data),
media_type->ExtraData, media_type->ExtraDataSize);
}
else
{
DEBUG_WARN("tsmf_gstreamer_set_format: gst_buffer_try_new_and_alloc(%d) failed.",
media_type->ExtraDataSize);
}
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,
"codec_data", GST_TYPE_BUFFER, gst_buf_cap_codec_data,
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'M', 'V', '3'),
//"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
NULL);
break;
case TSMF_SUB_TYPE_AVC1:
case TSMF_SUB_TYPE_H264:
if (media_type->ExtraDataSize > 0)
{
gst_buf_cap_codec_data = gst_buffer_try_new_and_alloc(media_type->ExtraDataSize);
if (gst_buf_cap_codec_data != NULL)
{
memcpy(GST_BUFFER_MALLOCDATA(gst_buf_cap_codec_data), media_type->ExtraData, media_type->ExtraDataSize);
}
else
{
DEBUG_WARN("tsmf_gstreamer_set_format: gst_buffer_try_new_and_alloc(%d) failed.", media_type->ExtraDataSize);
}
mdecoder->gst_caps = gst_caps_new_simple ("video/x-h264",
"width", G_TYPE_INT, media_type->Width,
"height", G_TYPE_INT, media_type->Height,
"codec_data", GST_TYPE_BUFFER, gst_buf_cap_codec_data,
//"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
NULL);
}
else
{
mdecoder->gst_caps = gst_caps_new_simple ("video/x-h264",
"width", G_TYPE_INT, media_type->Width,
"height", G_TYPE_INT, media_type->Height,
//"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
NULL);
}
break;
case TSMF_SUB_TYPE_AC3:
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);
break;
case TSMF_SUB_TYPE_AAC:
/* 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 */
if (media_type->ExtraData)
{
media_type->ExtraData += 12;
media_type->ExtraDataSize -= 12;
}
gst_buf_cap_codec_data = gst_buffer_try_new_and_alloc(media_type->ExtraDataSize);
if (gst_buf_cap_codec_data != NULL)
{
memcpy(GST_BUFFER_MALLOCDATA(gst_buf_cap_codec_data),
media_type->ExtraData, media_type->ExtraDataSize);
}
else
{
DEBUG_WARN("tsmf_gstreamer_set_format: gst_buffer_try_new_and_alloc(%d) failed.",
media_type->ExtraDataSize);
}
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,
"codec_data", GST_TYPE_BUFFER, gst_buf_cap_codec_data,
NULL);
break;
case TSMF_SUB_TYPE_MP1A:
if (media_type->ExtraDataSize > 0)
{
gst_buf_cap_codec_data = gst_buffer_try_new_and_alloc(media_type->ExtraDataSize);
if (gst_buf_cap_codec_data != NULL)
{
memcpy(GST_BUFFER_MALLOCDATA(gst_buf_cap_codec_data), media_type->ExtraData, media_type->ExtraDataSize);
}
else
{
DEBUG_WARN("tsmf_gstreamer_set_format: gst_buffer_try_new_and_alloc(%d) failed.", media_type->ExtraDataSize);
}
mdecoder->gst_caps = gst_caps_new_simple ("audio/mpeg",
"mpegversion", G_TYPE_INT, 1,
"channels", G_TYPE_INT, media_type->Channels,
"codec_data", GST_TYPE_BUFFER, gst_buf_cap_codec_data,
NULL);
}
else
{
mdecoder->gst_caps = gst_caps_new_simple ("audio/mpeg",
"mpegversion", G_TYPE_INT, 1,
"channels", G_TYPE_INT, media_type->Channels,
NULL);
}
break;
case TSMF_SUB_TYPE_MP1V:
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);
break;
case TSMF_SUB_TYPE_YUY2:
mdecoder->gst_caps = gst_caps_new_simple ("video/x-raw-yuv",
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'),
//"bitrate", G_TYPE_UINT, media_type->BitRate,
"width", G_TYPE_INT, media_type->Width,
"height", G_TYPE_INT, media_type->Height,
NULL);
break;
case TSMF_SUB_TYPE_MP2V:
mdecoder->gst_caps = gst_caps_new_simple ("video/mpeg",
//"bitrate", G_TYPE_UINT, media_type->BitRate,
//"width", G_TYPE_INT, media_type->Width,
//"height", G_TYPE_INT, media_type->Height,
"mpegversion", G_TYPE_INT, 2,
"systemstream", G_TYPE_BOOLEAN, FALSE,
NULL);
break;
case TSMF_SUB_TYPE_MP2A:
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);
break;
#if 0
case TSMF_SUB_TYPE_AC3:
break;
#endif
default:
DEBUG_WARN("tsmf_gstreamer_set_format: unknown format:(%d).", media_type->SubType);
return FALSE;
}
return TRUE;
}
static void tsmf_gstreamer_pipeline_send_end_of_stream(TSMFGstreamerDecoder * mdecoder)
{
DEBUG_DVC("tsmf_gstreamer_pipeline_send_end_of_stream: ");
if (mdecoder && mdecoder->src)
{
gst_app_src_end_of_stream(GST_APP_SRC(mdecoder->src));
}
return;
}
#ifdef __arm__
/* code from TI to check whether OMX is being lock or not */
static boolean tsmf_gstreamer_pipeline_omx_available()
{
bool ret = TRUE;
int shm_fd = 0;
struct shm_info
{
pid_t pid;
}shm_info;
struct shm_info *info = NULL;
shm_fd = shm_open ("gstomx", (O_CREAT | O_RDWR), (S_IREAD | S_IWRITE));
if (shm_fd < 0)
{
DEBUG_DVC("ERROR: failed to open shm");
goto exit;
}
/* set file size */
ftruncate(shm_fd, sizeof(struct shm_info));
if ((info = mmap(0, sizeof(struct shm_info), (PROT_READ | PROT_WRITE), MAP_SHARED, shm_fd, 0)) == MAP_FAILED)
{
DEBUG_DVC("ERROR: failed to map");
goto exit;
}
if (info->pid)
{
DEBUG_DVC ("ERROR: omxcore is in use by '%d'", info->pid);
ret = FALSE;
}
else
{
DEBUG_DVC ("omxcore is available for use");
}
exit:
if (info)
munmap (info, sizeof(struct shm_info));
if (shm_fd)
close (shm_fd);
return ret;
}
#endif
static void tsmf_gstreamer_clean_up(TSMFGstreamerDecoder * mdecoder)
{
//Cleaning up elements
if (!mdecoder)
return;
if (mdecoder->src)
{
gst_object_unref(mdecoder->src);
mdecoder->src = NULL;
}
if (mdecoder->queue)
{
gst_object_unref(mdecoder->queue);
mdecoder->queue = NULL;
}
if (mdecoder->decbin)
{
gst_object_unref(mdecoder->decbin);
mdecoder->decbin = NULL;
}
if(mdecoder->outbin)
{
gst_object_unref(mdecoder->outbin);
mdecoder->outbin = NULL;
}
if (mdecoder->outconv)
{
gst_object_unref(mdecoder->outconv);
mdecoder->outconv = NULL;
}
if (mdecoder->outsink)
{
gst_object_unref(mdecoder->outsink);
mdecoder->outsink = NULL;
}
if (mdecoder->aVolume)
{
gst_object_unref(mdecoder->aVolume);
mdecoder->aVolume = NULL;
}
}
static boolean tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder * mdecoder)
{
if (!mdecoder)
return false;
GstPad *out_pad;
mdecoder->pipe = gst_pipeline_new (NULL);
if (!mdecoder->pipe)
{
DEBUG_WARN("tsmf_gstreamer_pipeline_build: Failed to create new pipe");
return FALSE;
}
bool OMXavailable = FALSE;
#ifdef __arm__
OMXavailable = tsmf_gstreamer_pipeline_omx_available();
#endif
/*
* On Atlas without this printf, we'll see Illegal instruction only with optimization level set to -O2.
*/
const char *blank = "";
printf("%s", blank);
bool hwaccelflu = FALSE;
bool hwaccelomx = FALSE;
switch (mdecoder->tsmf_media_type.SubType)
{
case TSMF_SUB_TYPE_WMA2:
mdecoder->decbin = gst_element_factory_make ("fluwmadec", NULL);
if (!mdecoder->decbin)
mdecoder->decbin = gst_element_factory_make ("ffdec_wmav2", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: WMA2");
break;
case TSMF_SUB_TYPE_WMA9:
mdecoder->decbin = gst_element_factory_make ("fluwmadec", NULL);
if (!mdecoder->decbin)
mdecoder->decbin = gst_element_factory_make ("ffdec_wmapro", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: WMA9 - WMA PRO version 3");
break;
case TSMF_SUB_TYPE_MP3:
mdecoder->decbin = gst_element_factory_make ("flump3dec", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: MP3");
break;
case TSMF_SUB_TYPE_MP4S:
if (OMXavailable)
{
mdecoder->decbin = gst_element_factory_make ("omx_mpeg4dec", NULL);
if (mdecoder->decbin)
{
hwaccelomx = TRUE;
}
}
else
mdecoder->decbin = NULL;
if(!mdecoder->decbin)
mdecoder->decbin = gst_element_factory_make ("flumpeg4vdec", NULL);
if(!mdecoder->decbin)
mdecoder->decbin = gst_element_factory_make ("ffdec_mpeg4", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: MP4S");
break;
case TSMF_SUB_TYPE_MP42:
mdecoder->decbin = gst_element_factory_make ("ffdec_msmpeg4v2", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: MP42");
break;
case TSMF_SUB_TYPE_MP43:
mdecoder->decbin = gst_element_factory_make ("ffdec_msmpeg4", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: MP43");
break;
case TSMF_SUB_TYPE_MP2V:
if (OMXavailable)
{
mdecoder->decbin = gst_element_factory_make ("omx_mpeg2dec", NULL);
if (mdecoder->decbin)
{
hwaccelomx = TRUE;
}
}
else
mdecoder->decbin = NULL;
if (!mdecoder->decbin)
mdecoder->decbin = gst_element_factory_make ("ffdec_mpeg2video", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: MPEG2 Video");
break;
case TSMF_SUB_TYPE_WMV1:
mdecoder->decbin = gst_element_factory_make ("ffdec_wmv1", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: WMV1");
break;
case TSMF_SUB_TYPE_WMV2:
mdecoder->decbin = gst_element_factory_make ("ffdec_wmv2", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: WMV2");
break;
case TSMF_SUB_TYPE_WVC1:
case TSMF_SUB_TYPE_WMV3:
mdecoder->decbin = gst_element_factory_make ("fluvadec", NULL);
if (mdecoder->decbin)
{
hwaccelflu = TRUE;
}
else
{
if (OMXavailable)
{
mdecoder->decbin = gst_element_factory_make ("omx_vc1dec", NULL);
if (mdecoder->decbin)
hwaccelomx = TRUE;
}
else
mdecoder->decbin = NULL;
}
if (!mdecoder->decbin)
mdecoder->decbin = gst_element_factory_make ("fluwmvdec", NULL);
if (!mdecoder->decbin)
mdecoder->decbin = gst_element_factory_make ("ffdec_wmv3", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: WMV3");
break;
case TSMF_SUB_TYPE_AVC1:
case TSMF_SUB_TYPE_H264:
mdecoder->decbin = gst_element_factory_make ("fluvadec", NULL);
if (mdecoder->decbin)
{
hwaccelflu = TRUE;
}
else
{
if (OMXavailable)
{
mdecoder->decbin = gst_element_factory_make ("omx_h264dec", NULL);
if (mdecoder->decbin)
hwaccelomx = TRUE;
}
else
mdecoder->decbin = NULL;
}
if (!mdecoder->decbin)
mdecoder->decbin = gst_element_factory_make ("fluh264dec", NULL);
if (!mdecoder->decbin)
mdecoder->decbin = gst_element_factory_make ("ffdec_h264", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: H264");
break;
case TSMF_SUB_TYPE_AC3:
mdecoder->decbin = gst_element_factory_make ("ffdec_ac3", NULL);
//mdecoder->decbin = gst_element_factory_make ("ffdec_ac3", NULL);//no fluendo equivalent?
DEBUG_DVC("tsmf_gstreamer_pipeline_build: AC3");
break;
case TSMF_SUB_TYPE_AAC:
mdecoder->decbin = gst_element_factory_make ("fluaacdec", NULL);
if (!mdecoder->decbin)
mdecoder->decbin = gst_element_factory_make ("faad", NULL);
if (!mdecoder->decbin)
mdecoder->decbin = gst_element_factory_make ("ffdec_aac", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: AAC");
break;
case TSMF_SUB_TYPE_MP2A:
mdecoder->decbin = gst_element_factory_make ("fluaacdec", NULL);
if (!mdecoder->decbin)
mdecoder->decbin = gst_element_factory_make ("faad", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: MP2A");
break;
case TSMF_SUB_TYPE_MP1A:
mdecoder->decbin = gst_element_factory_make ("flump3dec", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: MP1A");
break;
case TSMF_SUB_TYPE_MP1V:
mdecoder->decbin = gst_element_factory_make ("ffdec_mpegvideo", NULL);
DEBUG_DVC("tsmf_gstreamer_pipeline_build: MP1V");
break;
default:
DEBUG_WARN("tsmf_gstreamer_pipeline_build: Unsupported media type %d", mdecoder->tsmf_media_type.SubType);
return FALSE;
}
if (!mdecoder->decbin)
{
DEBUG_WARN("tsmf_gstreamer_pipeline_build: Failed to load decoder plugin");
return FALSE;
}
switch (mdecoder->media_type)
{
case TSMF_MAJOR_TYPE_VIDEO:
{
mdecoder->outbin = gst_bin_new ("videobin");
if (hwaccelflu)
{
mdecoder->outconv = gst_element_factory_make ("queue", "queuetosink");
mdecoder->outsink = gst_element_factory_make ("fluvasink", "videosink");
}
else if(hwaccelomx)
{
mdecoder->outconv = gst_element_factory_make ("queue", "queuetosink");
mdecoder->outsink = gst_element_factory_make ("gemxvimagesink", "videosink");
}
else
{
mdecoder->outconv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
mdecoder->outsink = gst_element_factory_make ("xvimagesink", "videosink");
}
DEBUG_DVC("tsmf_gstreamer_pipeline_build: building Video Pipe");
if (mdecoder->xfwin == (int *) -1)
DEBUG_WARN("tsmf_gstreamer_entry: failed to assign pointer to the memory address - shmat()");
else
{
if (!mdecoder->disp)
mdecoder->disp = XOpenDisplay(NULL);
if (!mdecoder->subwin)
{
mdecoder->subwin = XCreateSimpleWindow(mdecoder->disp, *mdecoder->xfwin, 0, 0, 1, 1, 0, 0, 0);
XMapWindow(mdecoder->disp, mdecoder->subwin);
XSync(mdecoder->disp, FALSE);
}
}
mdecoder->aVolume = 0;
break;
}
case TSMF_MAJOR_TYPE_AUDIO:
{
mdecoder->outbin = gst_bin_new ("audiobin");
mdecoder->outconv = gst_element_factory_make ("audioconvert", "aconv");
mdecoder->outsink = gst_element_factory_make ("alsasink", NULL);
mdecoder->aVolume = gst_element_factory_make ("volume", "AudioVol");
if (mdecoder->aVolume)
{
g_object_set(mdecoder->aVolume, "mute", mdecoder->gstMuted, NULL);
g_object_set(mdecoder->aVolume, "volume", mdecoder->gstVolume, NULL);
}
DEBUG_DVC("tsmf_gstreamer_pipeline_build: building Audio Pipe");
break;
}
default:
break;
}
if (!mdecoder->outconv)
{
DEBUG_WARN("tsmf_gstreamer_pipeline_build: Failed to load media converter");
tsmf_gstreamer_clean_up(mdecoder);
return FALSE;
}
if (!mdecoder->outsink)
{
DEBUG_WARN("tsmf_gstreamer_pipeline_build: Failed to load xvimagesink plugin");
tsmf_gstreamer_clean_up(mdecoder);
return FALSE;
}
mdecoder->src = gst_element_factory_make ("appsrc", NULL);
mdecoder->queue = gst_element_factory_make ("queue2", NULL);
g_object_set(mdecoder->queue, "use-buffering", FALSE, NULL);
g_object_set(mdecoder->queue, "use-rate-estimate", FALSE, NULL);
g_object_set(mdecoder->outsink, "async", FALSE, NULL);
g_object_set(mdecoder->src, "format", GST_FORMAT_TIME, NULL);
gst_app_src_set_stream_type((GstAppSrc *) mdecoder->src, GST_APP_STREAM_TYPE_STREAM);
gst_app_src_set_max_bytes((GstAppSrc *) mdecoder->src, 4*1024*1024); /* 32 Mbits */
gst_app_src_set_caps((GstAppSrc *) mdecoder->src, mdecoder->gst_caps);
out_pad = gst_element_get_static_pad(mdecoder->outconv, "sink");
gboolean linkResult = FALSE;
gst_bin_add(GST_BIN(mdecoder->outbin), mdecoder->outconv);
gst_bin_add(GST_BIN(mdecoder->outbin), mdecoder->outsink);
if (mdecoder->aVolume)
{
gst_bin_add(GST_BIN(mdecoder->outbin), mdecoder->aVolume);
linkResult = gst_element_link_many(mdecoder->outconv, mdecoder->aVolume, mdecoder->outsink, NULL);
}
else
{
linkResult = gst_element_link(mdecoder->outconv, mdecoder->outsink);
}
if (!linkResult)
{
DEBUG_WARN("tsmf_gstreamer_pipeline_build: Failed to link these elements: converter->sink");
tsmf_gstreamer_clean_up(mdecoder);
return FALSE;
}
gst_element_add_pad(mdecoder->outbin, gst_ghost_pad_new ("sink", out_pad));
gst_object_unref(out_pad);
gst_bin_add(GST_BIN(mdecoder->pipe), mdecoder->src);
gst_bin_add(GST_BIN(mdecoder->pipe), mdecoder->queue);
gst_bin_add(GST_BIN(mdecoder->pipe), mdecoder->decbin);
gst_bin_add(GST_BIN(mdecoder->pipe), mdecoder->outbin);
linkResult = gst_element_link_many(mdecoder->src, mdecoder->queue, mdecoder->decbin, NULL);
if (!linkResult)
{
DEBUG_WARN("tsmf_gstreamer_pipeline_build: Failed to link these elements: source->decoder");
tsmf_gstreamer_clean_up(mdecoder);
return FALSE;
}
mdecoder->linked = gst_element_link(mdecoder->decbin, mdecoder->outbin);
if (!mdecoder->linked)
{
DEBUG_WARN("tsmf_gstreamer_pipeline_build: Failed to link these elements: decoder->output_bin");
tsmf_gstreamer_clean_up(mdecoder);
return FALSE;
}
if (GST_IS_X_OVERLAY (mdecoder->outsink))
{
//gst_x_overlay_set_window_handle (GST_X_OVERLAY (mdecoder->outsink), *mdecoder->xfwin);
if(mdecoder->subwin)
{
//gdk_threads_enter();
gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (mdecoder->outsink), mdecoder->subwin);
//gdk_threads_leave();
}
}
g_object_set(mdecoder->outsink, "preroll-queue-len", 10, NULL);
return TRUE;
}
static boolean tsmf_gstreamer_decodeEx(ITSMFDecoder * decoder, const uint8 * data, uint32 data_size, uint32 extensions,
uint64 start_time, uint64 end_time, uint64 duration)
{
TSMFGstreamerDecoder * mdecoder = (TSMFGstreamerDecoder *) decoder;
if (!mdecoder)
{
return FALSE;
}
int mutexret = pthread_mutex_lock(&mdecoder->gst_mutex);
if(mutexret != 0)
return FALSE;
if (mdecoder->shutdown)
{
pthread_mutex_unlock(&mdecoder->gst_mutex);
return FALSE;
}
GstBuffer *gst_buf;
/*
* This function is always called from a stream-specific thread.
* It should be alright to block here if necessary.
* We don't expect to block here often, since the pipeline should
* have more than enough buffering.
*/
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
DEBUG_DVC("tsmf_gstreamer_decodeEx_VIDEO. Start:(%llu) End:(%llu) Duration:(%llu) Last End:(%llu)", start_time, end_time, duration, mdecoder->last_sample_end_time);
else
DEBUG_DVC("tsmf_gstreamer_decodeEX_AUDIO. Start:(%llu) End:(%llu) Duration:(%llu) Last End:(%llu)", start_time, end_time, duration, mdecoder->last_sample_end_time);
if (mdecoder->gst_caps == NULL)
{
DEBUG_WARN("tsmf_gstreamer_decodeEx: tsmf_gstreamer_set_format not called or invalid format.");
pthread_mutex_unlock(&mdecoder->gst_mutex);
return FALSE;
}
if (mdecoder->pipe == NULL)
{
if (!tsmf_gstreamer_pipeline_build(mdecoder))
{
if (mdecoder->pipe)
{
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
gst_object_unref(mdecoder->pipe);
mdecoder->pipe = NULL;
}
pthread_mutex_unlock(&mdecoder->gst_mutex);
return FALSE;
}
//tsmf_gstreamer_start_eventloop_thread(mdecoder);
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_READY);
mdecoder->pipeline_start_time_valid = 0;
}
else
{
/*
* this is to fix gstreamer's seeking forward/backward issue with live stream.
* set the seeking tolerance to 1 second.
*/
if (start_time > (mdecoder->last_sample_end_time + 10000000) || (end_time + 10000000) < mdecoder->last_sample_end_time)
{
DEBUG_DVC("tsmf_gstreamer_decodeEx: start_time=[%llu] > last_sample_end_time=[%llu]", start_time, mdecoder->last_sample_end_time);
DEBUG_DVC("tsmf_gstreamer_decodeEx: Stream seek detected - flushing element.");
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
gst_object_unref(mdecoder->pipe);
mdecoder->pipe = NULL;
if (!tsmf_gstreamer_pipeline_build(mdecoder))
{
if (mdecoder->pipe)
{
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
gst_object_unref(mdecoder->pipe);
mdecoder->pipe = NULL;
}
pthread_mutex_unlock(&mdecoder->gst_mutex);
return FALSE;
}
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_READY);
mdecoder->pipeline_start_time_valid = 0;
/*
* This is to fix the discrepancy between audio/video start time during a seek
*/
FILE *fout = NULL;
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
fout = fopen("/tmp/tsmf_vseek.info", "wt");
else
fout = fopen("/tmp/tsmf_aseek.info", "wt");
if (fout)
{
fprintf(fout, "%"PRIu64"\n", start_time);
fclose(fout);
}
}
}
if (!mdecoder->src)
{
pthread_mutex_unlock(&mdecoder->gst_mutex);
DEBUG_WARN("tsmf_gstreamer_decodeEx: failed to construct pipeline correctly. Unable to push buffer to source element.");
return FALSE;
}
if (GST_STATE(mdecoder->pipe) != GST_STATE_PAUSED && GST_STATE(mdecoder->pipe) != GST_STATE_PLAYING)
{
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
{
FILE *fout = fopen("/tmp/tsmf_video.ready", "wt");
if (fout)
fclose(fout);
FILE *fin = fopen("/tmp/tsmf_aseek.info", "rt");
if (fin)
{
uint64 AStartTime = 0;
fscanf(fin, "%"PRIu64, &AStartTime);
fclose(fin);
if (start_time > AStartTime)
{
uint64 streamDelay = (start_time - AStartTime) / 10;
usleep(streamDelay);
}
unlink("/tmp/tsmf_aseek.info");
}
}
else if (mdecoder->media_type == TSMF_MAJOR_TYPE_AUDIO)
{
int timeout = 0;
FILE *fin = fopen("/tmp/tsmf_video.ready", "rt");
while (fin == NULL)
{
timeout++;
usleep(1000);
//wait up to 1.5 second
if (timeout >= 1500)
break;
fin = fopen("/tmp/tsmf_video.ready", "rt");
}
if (fin)
{
fclose(fin);
unlink("/tmp/tsmf_video.ready");
fin = NULL;
}
fin = fopen("/tmp/tsmf_vseek.info", "rt");
if (fin)
{
uint64 VStartTime = 0;
fscanf(fin, "%"PRIu64, &VStartTime);
fclose(fin);
if (start_time > VStartTime)
{
uint64 streamDelay = (start_time - VStartTime) / 10;
usleep(streamDelay);
}
unlink("/tmp/tsmf_vseek.info");
}
}
}
gst_buf = gst_buffer_try_new_and_alloc(data_size);
if (gst_buf == NULL)
{
pthread_mutex_unlock(&mdecoder->gst_mutex);
DEBUG_WARN("tsmf_gstreamer_decodeEx: gst_buffer_try_new_and_alloc(%d) failed.", data_size);
return FALSE;
}
gst_buffer_set_caps(gst_buf, mdecoder->gst_caps);
memcpy(GST_BUFFER_MALLOCDATA(gst_buf), data, data_size);
GST_BUFFER_TIMESTAMP(gst_buf) = tsmf_gstreamer_timestamp_ms_to_gst(start_time);
GST_BUFFER_DURATION(gst_buf) = tsmf_gstreamer_timestamp_ms_to_gst(duration);
gst_app_src_push_buffer(GST_APP_SRC(mdecoder->src), gst_buf);
mdecoder->last_sample_end_time = end_time;
if (!mdecoder->pipeline_start_time_valid)
{
gst_element_set_base_time(mdecoder->pipe, tsmf_gstreamer_timestamp_ms_to_gst(start_time));
gst_element_set_start_time(mdecoder->pipe, tsmf_gstreamer_timestamp_ms_to_gst(start_time));
mdecoder->pipeline_start_time_valid = 1;
}
if(GST_STATE(mdecoder->pipe) != GST_STATE_PLAYING)
{
if (!mdecoder->paused)
{
if (mdecoder->subwin)
{
XMapWindow(mdecoder->disp, mdecoder->subwin);
XSync(mdecoder->disp, FALSE);
}
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
}
}
pthread_mutex_unlock(&mdecoder->gst_mutex);
return TRUE;
}
static void tsmf_gstreamer_change_volume(ITSMFDecoder * decoder, uint32 newVolume, uint32 muted)
{
TSMFGstreamerDecoder * mdecoder = (TSMFGstreamerDecoder *) decoder;
if (!mdecoder)
return;
if (mdecoder->shutdown)
return;
if (!mdecoder->aVolume)
return;
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
return;
if (!G_IS_OBJECT(mdecoder->aVolume))
return;
mdecoder->gstMuted = (bool) muted;
DEBUG_DVC("tsmf_gstreamer_change_volume: mute=[%d]", mdecoder->gstMuted);
g_object_set(mdecoder->aVolume, "mute", mdecoder->gstMuted, NULL);
mdecoder->gstVolume = (double) newVolume / (double) 10000;
DEBUG_DVC("tsmf_gstreamer_change_volume: gst_new_vol=[%f]", mdecoder->gstVolume);
g_object_set(mdecoder->aVolume, "volume", mdecoder->gstVolume, NULL);
}
static void tsmf_gstreamer_control(ITSMFDecoder * decoder, ITSMFControlMsg control_msg, uint32 *arg)
{
TSMFGstreamerDecoder * mdecoder = (TSMFGstreamerDecoder *) decoder;
if (!mdecoder)
return;
if (control_msg == Control_Pause)
{
DEBUG_DVC("tsmf_gstreamer_control: Control_Pause");
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
mdecoder->paused = TRUE;
if (mdecoder->subwin)
{
XUnmapWindow(mdecoder->disp, mdecoder->subwin);
XSync(mdecoder->disp, FALSE);
}
}
else if (control_msg == Control_Restart)
{
DEBUG_DVC("tsmf_gstreamer_control: Control_Restart");
mdecoder->paused = FALSE;
if (mdecoder->subwin)
{
XMapWindow(mdecoder->disp, mdecoder->subwin);
XSync(mdecoder->disp, FALSE);
}
if (mdecoder->pipeline_start_time_valid)
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
}
else if (control_msg == Control_Flush)
{
DEBUG_DVC("tsmf_gstreamer_control: Control_Flush");
/* Reset stamps, flush buffers, etc */
if (mdecoder->pipe)
{
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
gst_object_unref(mdecoder->pipe);
mdecoder->pipe = NULL;
}
mdecoder->pipeline_start_time_valid = 0;
mdecoder->paused = FALSE;
if (mdecoder->subwin)
{
XUnmapWindow(mdecoder->disp, mdecoder->subwin);
XSync(mdecoder->disp, FALSE);
}
}
else if (control_msg == Control_EndOfStream)
{
mdecoder->paused = FALSE;
DEBUG_DVC("tsmf_gstreamer_control: Control_EndOfStream");
/*
* The EOS may take some time to flow through the pipeline
* If the server sees the client "End of Stream Processed"
* notification too soon, it may shut down the stream
* and clip the end of files.
* If that's the case, then we'll need to change the TSMF layer
* to send the "End of Stream Processed" only after the stream
* is truly EOS.
* (It's unlikely we can simply "wait" here for it to happen
* since we don't want to hold up acks, etc.)
*/
tsmf_gstreamer_pipeline_send_end_of_stream(mdecoder);
}
}
static guint tsmf_gstreamer_buffer_level(ITSMFDecoder * decoder)
{
TSMFGstreamerDecoder * mdecoder = (TSMFGstreamerDecoder *) decoder;
DEBUG_DVC("tsmf_gstreamer_buffer_level\n");
if (!mdecoder)
return 0;
if (mdecoder->shutdown)
return 0;
if (!G_IS_OBJECT(mdecoder->queue))
return 0;
guint clbuff = 0;
g_object_get(mdecoder->queue, "current-level-buffers", &clbuff, NULL);
return clbuff;
}
static void tsmf_gstreamer_free(ITSMFDecoder * decoder)
{
TSMFGstreamerDecoder * mdecoder = (TSMFGstreamerDecoder *) decoder;
DEBUG_DVC("tsmf_gstreamer_free\n");
if (mdecoder)
{
pthread_mutex_lock(&mdecoder->gst_mutex);
mdecoder->shutdown = 1;
if (mdecoder->pipe)
{
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
gst_object_unref(mdecoder->pipe);
mdecoder->pipe = NULL;
}
tsmf_gstreamer_stop_eventloop_thread(mdecoder);
if (mdecoder->gst_caps)
gst_caps_unref(mdecoder->gst_caps);
if (mdecoder->subwin)
{
DEBUG_DVC("destroy subwindow\n");
XDestroyWindow(mdecoder->disp, mdecoder->subwin);
XSync(mdecoder->disp, FALSE);
}
if (mdecoder->disp)
XCloseDisplay(mdecoder->disp);
unlink("/tmp/tsmf_aseek.info");
unlink("/tmp/tsmf_vseek.info");
unlink("/tmp/tsmf_video.ready");
pthread_mutex_unlock(&mdecoder->gst_mutex);
free(mdecoder);
mdecoder = 0;
}
}
static uint64 tsmf_gstreamer_get_running_time(ITSMFDecoder * decoder)
{
TSMFGstreamerDecoder *mdecoder = (TSMFGstreamerDecoder *) decoder;
if (!mdecoder)
return 0;
if (!mdecoder->outsink)
return mdecoder->last_sample_end_time;
if(GST_STATE(mdecoder->pipe) != GST_STATE_PLAYING)
return 0;
GstFormat fmt = GST_FORMAT_TIME;
gint64 pos = 0;
gst_element_query_position (mdecoder->outsink, &fmt, &pos);
DEBUG_DVC("tsmf_gstreamer_current_pos=[%llu]", pos);
return pos/100;
}
static void tsmf_gstreamer_update_rendering_area(ITSMFDecoder * decoder, int newX, int newY, int newWidth, int newHeight, int numRectangles, RDP_RECT *rectangles)
{
DEBUG_DVC("tsmf_gstreamer_update_rendering_area");
TSMFGstreamerDecoder *mdecoder = (TSMFGstreamerDecoder *) decoder;
if (!mdecoder)
return;
if (mdecoder->shutdown)
return;
if (GST_IS_X_OVERLAY (mdecoder->outsink))
{
if (!mdecoder->disp)
mdecoder->disp = XOpenDisplay(NULL);
//multi-mon test
int anewX = newX;
int anewY = newY;
if (!mdecoder->offsetObtained)
{
XSync(mdecoder->disp, FALSE);
RROutput primary_output;
XRRScreenResources *res = 0;
int screen = 0;
res = XRRGetScreenResourcesCurrent(mdecoder->disp, RootWindow(mdecoder->disp, screen));
if (res)
{
DEBUG_DVC("number of output:%d", res->ncrtc);
primary_output = XRRGetOutputPrimary(mdecoder->disp, DefaultRootWindow(mdecoder->disp));
DEBUG_DVC("primary_output:%d", (int)primary_output);
int i = 0;
for (i = 0; i < res->ncrtc; i++)
{
XRRCrtcInfo *info = XRRGetCrtcInfo(mdecoder->disp, res, res->crtcs[i]);
if (info)
{
if (info->noutput > 0)
{
if (info->outputs[0] == primary_output)
{
mdecoder->xOffset = info->x;
mdecoder->yOffset = info->y;
}
DEBUG_DVC("output %d ID: %lu (x,y): (%d,%d) (w,h): (%d,%d) primary: %d", i, info->outputs[0], info->x, info->y, info->width, info->height, (info->outputs[0] == primary_output));
}
}
}
}
mdecoder->offsetObtained = TRUE;
}
anewX += mdecoder->xOffset;
anewY += mdecoder->yOffset;
XSync(mdecoder->disp, FALSE);
//end of multi-mon test
if(mdecoder->subwin)
{
XMoveWindow(mdecoder->disp, mdecoder->subwin, anewX, anewY);
XResizeWindow(mdecoder->disp, mdecoder->subwin, newWidth, newHeight);
XSync(mdecoder->disp, FALSE);
XShapeCombineRectangles (mdecoder->disp, mdecoder->subwin, ShapeBounding, 0, 0,(XRectangle*) rectangles, numRectangles, ShapeSet, Unsorted);
XSync(mdecoder->disp, FALSE);
//Sending Expose Event so freeRDP can do a redraw.
XExposeEvent xpose;
xpose.type = Expose;
xpose.display = mdecoder->disp;
xpose.window = *mdecoder->xfwin;
xpose.x = 0;
xpose.y = 0;
XSendEvent(mdecoder->disp, *mdecoder->xfwin, TRUE, ExposureMask, (XEvent *)&xpose);
XSync(mdecoder->disp, FALSE);
}
gst_x_overlay_expose (GST_X_OVERLAY (mdecoder->outsink));
}
}
static int initialized = 0;
ITSMFDecoder *
TSMFDecoderEntry(void)
{
TSMFGstreamerDecoder * decoder;
if (!initialized)
{
gst_init(0, 0);
initialized = 1;
}
decoder = malloc(sizeof(TSMFGstreamerDecoder));
memset(decoder, 0, sizeof(TSMFGstreamerDecoder));
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;
decoder->iface.BufferLevel = tsmf_gstreamer_buffer_level;
decoder->paused = FALSE;
decoder->subwin = 0;
decoder->xOffset = 0;
decoder->yOffset = 0;
decoder->offsetObtained = FALSE;
decoder->gstVolume = 1.0;
decoder->gstMuted = FALSE;
decoder->state = GST_STATE_VOID_PENDING; /* No real state yet */
pthread_mutex_init(&decoder->gst_mutex, NULL);
int shmid = shmget(SHARED_MEM_KEY, sizeof(int), 0666);
if (shmid < 0)
{
DEBUG_WARN("tsmf_gstreamer_entry: failed to get access to shared memory - shmget()");
}
else
{
decoder->xfwin = shmat(shmid, NULL, 0);
}
XInitThreads();
return (ITSMFDecoder *) decoder;
}