From c9c22fa33105997c71b08b6bdb6dbef7ef1c042d Mon Sep 17 00:00:00 2001 From: Irwan Halim Date: Wed, 13 Jun 2012 14:45:58 -0500 Subject: [PATCH] Add GStreamer plugin for Multi-media redirection --- CMakeLists.txt | 2 + channels/drdynvc/tsmf/CMakeLists.txt | 5 + .../drdynvc/tsmf/gstreamer/CMakeLists.txt | 34 + .../drdynvc/tsmf/gstreamer/tsmf_gstreamer.c | 1595 +++++++++++++++++ channels/drdynvc/tsmf/tsmf_codec.c | 118 ++ channels/drdynvc/tsmf/tsmf_constants.h | 10 + channels/drdynvc/tsmf/tsmf_decoder.h | 24 +- channels/drdynvc/tsmf/tsmf_ifman.c | 60 +- channels/drdynvc/tsmf/tsmf_media.c | 372 +++- channels/drdynvc/tsmf/tsmf_media.h | 4 + client/X11/xf_window.c | 25 + client/X11/xfreerdp.c | 26 + cmake/FindGstreamer.cmake | 2 + 13 files changed, 2232 insertions(+), 45 deletions(-) create mode 100644 channels/drdynvc/tsmf/gstreamer/CMakeLists.txt create mode 100644 channels/drdynvc/tsmf/gstreamer/tsmf_gstreamer.c create mode 100644 cmake/FindGstreamer.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 6729528f7..20f6efe2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ # Copyright 2011 O.S. Systems Software Ltda. # Copyright 2011 Otavio Salvador # Copyright 2011 Marc-Andre Moreau +# 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. @@ -117,6 +118,7 @@ if(NOT WIN32) if(NOT APPLE) find_suggested_package(FFmpeg) + find_suggested_package(Gstreamer) find_suggested_package(ALSA) else(NOT APPLE) find_optional_package(FFmpeg) diff --git a/channels/drdynvc/tsmf/CMakeLists.txt b/channels/drdynvc/tsmf/CMakeLists.txt index e28cac6e7..eaaec0932 100644 --- a/channels/drdynvc/tsmf/CMakeLists.txt +++ b/channels/drdynvc/tsmf/CMakeLists.txt @@ -4,6 +4,7 @@ # Copyright 2011 O.S. Systems Software Ltda. # Copyright 2011 Otavio Salvador # Copyright 2011 Marc-Andre Moreau +# Copyright 2012 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -47,6 +48,10 @@ if(WITH_FFMPEG) add_subdirectory(ffmpeg) endif() +if(GSTREAMER_FOUND) + add_subdirectory(gstreamer) +endif() + if(WITH_ALSA) add_subdirectory(alsa) endif() diff --git a/channels/drdynvc/tsmf/gstreamer/CMakeLists.txt b/channels/drdynvc/tsmf/gstreamer/CMakeLists.txt new file mode 100644 index 000000000..a957667f5 --- /dev/null +++ b/channels/drdynvc/tsmf/gstreamer/CMakeLists.txt @@ -0,0 +1,34 @@ +# FreeRDP: A Remote Desktop Protocol Client +# FreeRDP cmake build script for gstreamer plugin +# +# (C) Copyright 2012 Hewlett-Packard Development Company, L.P. +# +# 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. + + + +set(TSMF_GSTREAMER_SRCS + tsmf_gstreamer.c +) + +include_directories(..) +include_directories(${GSTREAMER_INCLUDE_DIRS}) + +add_library(tsmf_gstreamer ${TSMF_GSTREAMER_SRCS}) +set_target_properties(tsmf_gstreamer PROPERTIES PREFIX "") + +target_link_libraries(tsmf_gstreamer freerdp-utils) +target_link_libraries(tsmf_gstreamer ${GSTREAMER_LIBRARIES} gstapp-0.10 gstinterfaces-0.10 Xrandr X11 Xext) + +install(TARGETS tsmf_gstreamer DESTINATION ${FREERDP_PLUGIN_PATH}) + diff --git a/channels/drdynvc/tsmf/gstreamer/tsmf_gstreamer.c b/channels/drdynvc/tsmf/gstreamer/tsmf_gstreamer.c new file mode 100644 index 000000000..eba53fc94 --- /dev/null +++ b/channels/drdynvc/tsmf/gstreamer/tsmf_gstreamer.c @@ -0,0 +1,1595 @@ +/* + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} + +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; +} + +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, ¤t_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. + */ + printf(""); + + 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, "%llu\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, "%llu", &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, "%llu", &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; +} diff --git a/channels/drdynvc/tsmf/tsmf_codec.c b/channels/drdynvc/tsmf/tsmf_codec.c index bd5a31703..7bc61c38e 100644 --- a/channels/drdynvc/tsmf/tsmf_codec.c +++ b/channels/drdynvc/tsmf/tsmf_codec.c @@ -3,6 +3,7 @@ * Video Redirection Virtual Channel - Codec * * Copyright 2010-2011 Vic Lee + * Copyright 2012 Hewlett-Packard Development Company, L.P. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -103,6 +104,20 @@ static const TSMFMediaTypeMap tsmf_sub_type_map[] = TSMF_SUB_TYPE_MP2V }, + /* 31564D57-0000-0010-8000-00AA00389B71 */ + { + { 0x57, 0x4D, 0x56, 0x31, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }, + "MEDIASUBTYPE_WMV1", + TSMF_SUB_TYPE_WMV1 + }, + + /* 32564D57-0000-0010-8000-00AA00389B71 */ + { + { 0x57, 0x4D, 0x56, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }, + "MEDIASUBTYPE_WMV2", + TSMF_SUB_TYPE_WMV2 + }, + /* 33564D57-0000-0010-8000-00AA00389B71 */ { { 0x57, 0x4D, 0x56, 0x33, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }, @@ -131,12 +146,62 @@ static const TSMFMediaTypeMap tsmf_sub_type_map[] = TSMF_SUB_TYPE_AVC1 }, + /* 3334504D-0000-0010-8000-00AA00389B71 */ + { + { 0x4D, 0x50, 0x34, 0x33, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }, + "MEDIASUBTYPE_MP43", + TSMF_SUB_TYPE_MP43 + }, + + /* 5634504D-0000-0010-8000-00AA00389B71 */ + { + { 0x4D, 0x50, 0x34, 0x56, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }, + "MEDIASUBTYPE_MP4S", + TSMF_SUB_TYPE_MP4S + }, + + /* 3234504D-0000-0010-8000-00AA00389B71 */ + { + { 0x4D, 0x50, 0x34, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }, + "MEDIASUBTYPE_MP42", + TSMF_SUB_TYPE_MP42 + }, + + /* E436EB81-524F-11CE-9F53-0020AF0BA770 */ + /* + { + { 0x81, 0xEB, 0x36, 0xE4, 0x4F, 0x52, 0xCE, 0x11, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 }, + "MEDIASUBTYPE_MP1V", + TSMF_SUB_TYPE_MP1V + }, + */ + + /* 00000050-0000-0010-8000-00AA00389B71 */ + /* + { + { 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }, + "MEDIASUBTYPE_MP1A", + TSMF_SUB_TYPE_MP1A + }, + */ + /* E06D802C-DB46-11CF-B4D1-00805F6CBBEA */ + /* { { 0x2C, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA }, "MEDIASUBTYPE_DOLBY_AC3", TSMF_SUB_TYPE_AC3 }, + */ + + /* 32595559-0000-0010-8000-00AA00389B71 */ + /* + { + { 0x59, 0x55, 0x59, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }, + "MEDIASUBTYPE_YUY2", + TSMF_SUB_TYPE_YUY2 + }, + */ { { 0 }, @@ -176,6 +241,13 @@ static const TSMFMediaTypeMap tsmf_format_type_map[] = TSMF_FORMAT_TYPE_VIDEOINFO2 }, + /* 05589F82-C356-11CE-BF01-00AA0055595A */ + { + { 0x82, 0x9F, 0x58, 0x05, 0x56, 0xC3, 0xCE, 0x11, 0xBF, 0x01, 0x00, 0xAA, 0x00, 0x55, 0x59, 0x5A }, + "FORMAT_MPEG1_VIDEO", + TSMF_FORMAT_TYPE_MPEG1VIDEOINFO + }, + { { 0 }, "Unknown", @@ -257,6 +329,40 @@ static uint32 tsmf_codec_parse_VIDEOINFOHEADER2(TS_AM_MEDIA_TYPE* mediatype, STR return 72; } +/* http://msdn.microsoft.com/en-us/library/dd390700.aspx */ +static uint32 tsmf_codec_parse_VIDEOINFOHEADER(TS_AM_MEDIA_TYPE* mediatype, STREAM* s) +{ +/* +typedef struct tagVIDEOINFOHEADER { + RECT rcSource; //16 + RECT rcTarget; //16 32 + DWORD dwBitRate; //4 36 + DWORD dwBitErrorRate; //4 40 + REFERENCE_TIME AvgTimePerFrame; //8 48 + BITMAPINFOHEADER bmiHeader; +} VIDEOINFOHEADER; +*/ + uint64 AvgTimePerFrame; + + /* VIDEOINFOHEADER.rcSource, RECT(LONG left, LONG top, LONG right, LONG bottom) */ + stream_seek_uint32(s); + stream_seek_uint32(s); + stream_read_uint32(s, mediatype->Width); + stream_read_uint32(s, mediatype->Height); + /* VIDEOINFOHEADER.rcTarget */ + stream_seek(s, 16); + /* VIDEOINFOHEADER.dwBitRate */ + stream_read_uint32(s, mediatype->BitRate); + /* VIDEOINFOHEADER.dwBitErrorRate */ + stream_seek_uint32(s); + /* VIDEOINFOHEADER.AvgTimePerFrame */ + stream_read_uint64(s, AvgTimePerFrame); + mediatype->SamplesPerSecond.Numerator = 1000000; + mediatype->SamplesPerSecond.Denominator = (int)(AvgTimePerFrame / 10LL); + + return 48; +} + boolean tsmf_codec_parse_media_type(TS_AM_MEDIA_TYPE* mediatype, STREAM* s) { int i; @@ -358,6 +464,18 @@ boolean tsmf_codec_parse_media_type(TS_AM_MEDIA_TYPE* mediatype, STREAM* s) break; + case TSMF_FORMAT_TYPE_MPEG1VIDEOINFO: + /* http://msdn.microsoft.com/en-us/library/dd390700.aspx */ + + i = tsmf_codec_parse_VIDEOINFOHEADER(mediatype, s); + i += tsmf_codec_parse_BITMAPINFOHEADER(mediatype, s, true); + if (cbFormat > i) + { + mediatype->ExtraDataSize = cbFormat - i; + mediatype->ExtraData = stream_get_tail(s); + } + break; + case TSMF_FORMAT_TYPE_MPEG2VIDEOINFO: /* http://msdn.microsoft.com/en-us/library/dd390707.aspx */ diff --git a/channels/drdynvc/tsmf/tsmf_constants.h b/channels/drdynvc/tsmf/tsmf_constants.h index e836dd72f..a93b2dd13 100644 --- a/channels/drdynvc/tsmf/tsmf_constants.h +++ b/channels/drdynvc/tsmf/tsmf_constants.h @@ -3,6 +3,7 @@ * Video Redirection Virtual Channel - Constants * * Copyright 2010-2011 Vic Lee + * Copyright 2012 Hewlett-Packard Development Company, L.P. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -108,6 +109,14 @@ #define TSMF_SUB_TYPE_H264 9 #define TSMF_SUB_TYPE_AVC1 10 #define TSMF_SUB_TYPE_AC3 11 +#define TSMF_SUB_TYPE_WMV2 12 +#define TSMF_SUB_TYPE_WMV1 13 +#define TSMF_SUB_TYPE_MP1V 14 +#define TSMF_SUB_TYPE_MP1A 15 +#define TSMF_SUB_TYPE_YUY2 16 +#define TSMF_SUB_TYPE_MP43 17 +#define TSMF_SUB_TYPE_MP4S 18 +#define TSMF_SUB_TYPE_MP42 19 /* FormatType */ #define TSMF_FORMAT_TYPE_UNKNOWN 0 @@ -115,6 +124,7 @@ #define TSMF_FORMAT_TYPE_WAVEFORMATEX 2 #define TSMF_FORMAT_TYPE_MPEG2VIDEOINFO 3 #define TSMF_FORMAT_TYPE_VIDEOINFO2 4 +#define TSMF_FORMAT_TYPE_MPEG1VIDEOINFO 5 #endif diff --git a/channels/drdynvc/tsmf/tsmf_decoder.h b/channels/drdynvc/tsmf/tsmf_decoder.h index 2aee84fec..dd911dbcf 100644 --- a/channels/drdynvc/tsmf/tsmf_decoder.h +++ b/channels/drdynvc/tsmf/tsmf_decoder.h @@ -3,6 +3,7 @@ * Video Redirection Virtual Channel - Decoder * * Copyright 2010-2011 Vic Lee + * Copyright 2012 Hewlett-Packard Development Company, L.P. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +24,14 @@ #include "drdynvc_types.h" #include "tsmf_types.h" +typedef enum _ITSMFControlMsg +{ + Control_Pause, + Control_Restart, + Control_Flush, + Control_EndOfStream +} ITSMFControlMsg; + typedef struct _ITSMFDecoder ITSMFDecoder; struct _ITSMFDecoder @@ -38,7 +47,20 @@ struct _ITSMFDecoder /* Get the width and height of decoded video frame */ boolean (*GetDecodedDimension) (ITSMFDecoder* decoder, uint32* width, uint32* height); /* Free the decoder */ - void (*Free) (ITSMFDecoder* decoder); + void (*Free) (ITSMFDecoder * decoder); + /* Optional Contol function */ + void (*Control) (ITSMFDecoder * decoder, ITSMFControlMsg control_msg, uint32 *arg); + /* Decode a sample with extended interface. */ + int (*DecodeEx) (ITSMFDecoder * decoder, const uint8 * data, uint32 data_size, uint32 extensions, + uint64 start_time, uint64 end_time, uint64 duration); + /* Get current play time */ + uint64 (*GetRunningTime) (ITSMFDecoder * decoder); + /* Update Gstreamer Rendering Area */ + void (*UpdateRenderingArea) (ITSMFDecoder * decoder, int newX, int newY, int newWidth, int newHeight, int numRectangles, RDP_RECT *rectangles); + /* Change Gstreamer Audio Volume */ + void (*ChangeVolume) (ITSMFDecoder * decoder, uint32 newVolume, uint32 muted); + /* Check buffer level */ + uint32 (*BufferLevel) (ITSMFDecoder * decoder); }; #define TSMF_DECODER_EXPORT_FUNC_NAME "TSMFDecoderEntry" diff --git a/channels/drdynvc/tsmf/tsmf_ifman.c b/channels/drdynvc/tsmf/tsmf_ifman.c index 881b18f94..1881d15d5 100644 --- a/channels/drdynvc/tsmf/tsmf_ifman.c +++ b/channels/drdynvc/tsmf/tsmf_ifman.c @@ -3,6 +3,7 @@ * Video Redirection Virtual Channel - Interface Manipulation * * Copyright 2010-2011 Vic Lee + * Copyright 2012 Hewlett-Packard Development Company, L.P. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -118,14 +119,23 @@ int tsmf_ifman_check_format_support_request(TSMF_IFMAN* ifman) return 0; } +static TSMF_PRESENTATION* pexisted = 0; + int tsmf_ifman_on_new_presentation(TSMF_IFMAN* ifman) { int error = 0; TSMF_PRESENTATION* presentation; DEBUG_DVC(""); + if (pexisted) + { + ifman->output_pending = false; + return 0; + + } presentation = tsmf_presentation_new(stream_get_tail(ifman->input), ifman->channel_callback); + pexisted = presentation; if (presentation == NULL) error = 1; else @@ -208,6 +218,8 @@ int tsmf_ifman_shutdown_presentation(TSMF_IFMAN* ifman) if (presentation) tsmf_presentation_free(presentation); + pexisted = 0; + stream_check_size(ifman->output, 4); stream_write_uint32(ifman->output, 0); /* Result */ ifman->output_interface_id = TSMF_INTERFACE_DEFAULT | STREAM_ID_STUB; @@ -216,14 +228,42 @@ int tsmf_ifman_shutdown_presentation(TSMF_IFMAN* ifman) int tsmf_ifman_on_stream_volume(TSMF_IFMAN* ifman) { - DEBUG_DVC(""); + DEBUG_DVC("on stream volume"); + TSMF_PRESENTATION* presentation; + presentation = tsmf_presentation_find_by_id(stream_get_tail(ifman->input)); + if (presentation) + { + stream_seek(ifman->input, 16); + uint32 newVolume; + uint32 muted; + stream_read_uint32(ifman->input, newVolume); + DEBUG_DVC("on stream volume: new volume=[%d]", newVolume); + stream_read_uint32(ifman->input, muted); + DEBUG_DVC("on stream volume: muted=[%d]", muted); + tsmf_presentation_volume_changed(presentation, newVolume, muted); + } + else + DEBUG_WARN("unknown presentation id"); + ifman->output_pending = true; return 0; } int tsmf_ifman_on_channel_volume(TSMF_IFMAN* ifman) { - DEBUG_DVC(""); + DEBUG_DVC("on channel volume"); + TSMF_PRESENTATION* presentation; + presentation = tsmf_presentation_find_by_id(stream_get_tail(ifman->input)); + if (presentation) + { + stream_seek(ifman->input, 16); + uint32 channelVolume; + uint32 changedChannel; + stream_read_uint32(ifman->input, channelVolume); + DEBUG_DVC("on channel volume: channel volume=[%d]", channelVolume); + stream_read_uint32(ifman->input, changedChannel); + DEBUG_DVC("on stream volume: changed channel=[%d]", changedChannel); + } ifman->output_pending = true; return 0; } @@ -434,6 +474,14 @@ int tsmf_ifman_on_playback_paused(TSMF_IFMAN* ifman) { DEBUG_DVC(""); ifman->output_pending = true; + + /* Added pause control so gstreamer pipeline can be paused accordingly */ + TSMF_PRESENTATION* presentation; + presentation = tsmf_presentation_find_by_id(stream_get_tail(ifman->input)); + if (presentation) + tsmf_presentation_paused(presentation); + else + DEBUG_WARN("unknown presentation id"); return 0; } @@ -441,6 +489,14 @@ int tsmf_ifman_on_playback_restarted(TSMF_IFMAN* ifman) { DEBUG_DVC(""); ifman->output_pending = true; + + /* Added restart control so gstreamer pipeline can be resumed accordingly */ + TSMF_PRESENTATION* presentation; + presentation = tsmf_presentation_find_by_id(stream_get_tail(ifman->input)); + if (presentation) + tsmf_presentation_restarted(presentation); + else + DEBUG_WARN("unknown presentation id"); return 0; } diff --git a/channels/drdynvc/tsmf/tsmf_media.c b/channels/drdynvc/tsmf/tsmf_media.c index eac7106af..fb34c9409 100644 --- a/channels/drdynvc/tsmf/tsmf_media.c +++ b/channels/drdynvc/tsmf/tsmf_media.c @@ -3,6 +3,7 @@ * Video Redirection Virtual Channel - Media Container * * Copyright 2010-2011 Vic Lee + * Copyright 2012 Hewlett-Packard Development Company, L.P. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -123,6 +126,9 @@ struct _TSMF_SAMPLE }; static LIST* presentation_list = NULL; +static uint64 last_played_audio_time = 0; +static pthread_mutex_t tsmf_mutex = PTHREAD_MUTEX_INITIALIZER; +static int TERMINATING = 0; static uint64 get_current_time(void) { @@ -145,33 +151,41 @@ static TSMF_SAMPLE* tsmf_stream_pop_sample(TSMF_STREAM* stream, int sync) if (sync) { - if (stream->major_type == TSMF_MAJOR_TYPE_AUDIO) + if (stream->decoder) { - /* Check if some other stream has earlier sample that needs to be played first */ - if (stream->last_end_time > AUDIO_TOLERANCE) + if (stream->decoder->GetDecodedData) { - freerdp_mutex_lock(presentation->mutex); - for (item = presentation->stream_list->head; item; item = item->next) + if (stream->major_type == TSMF_MAJOR_TYPE_AUDIO) { - s = (TSMF_STREAM*) item->data; - if (s != stream && !s->eos && s->last_end_time && - s->last_end_time < stream->last_end_time - AUDIO_TOLERANCE) + /* Check if some other stream has earlier sample that needs to be played first */ + if (stream->last_end_time > AUDIO_TOLERANCE) { - pending = true; - break; + freerdp_mutex_lock(presentation->mutex); + for (item = presentation->stream_list->head; item; item = item->next) + { + s = (TSMF_STREAM*) item->data; + if (s != stream && !s->eos && s->last_end_time && + s->last_end_time < stream->last_end_time - AUDIO_TOLERANCE) + { + pending = true; + break; + } + } + freerdp_mutex_unlock(presentation->mutex); } } - freerdp_mutex_unlock(presentation->mutex); - } - } - else - { - if (stream->last_end_time > presentation->audio_end_time) - { - pending = true; + else + { + if (stream->last_end_time > presentation->audio_end_time) + { + pending = true; + } + } + } } } + if (pending) return NULL; @@ -224,6 +238,15 @@ static void tsmf_stream_process_ack(TSMF_STREAM* stream) TSMF_PRESENTATION* tsmf_presentation_new(const uint8* guid, IWTSVirtualChannelCallback* pChannelCallback) { + pthread_t thid = pthread_self(); + FILE* fout = NULL; + fout = fopen("/tmp/tsmf.tid", "wt"); + if (fout) + { + fprintf(fout, "%d\n", (int) thid); + fclose(fout); + } + TSMF_PRESENTATION* presentation; presentation = tsmf_presentation_find_by_id(guid); @@ -423,7 +446,14 @@ static void tsmf_sample_playback(TSMF_SAMPLE* sample) TSMF_STREAM* stream = sample->stream; if (stream->decoder) - ret = stream->decoder->Decode(stream->decoder, sample->data, sample->data_size, sample->extensions); + { + if (stream->decoder->DecodeEx) + ret = stream->decoder->DecodeEx(stream->decoder, sample->data, sample->data_size, sample->extensions, + sample->start_time, sample->end_time, sample->duration); + else + ret = stream->decoder->Decode(stream->decoder, sample->data, sample->data_size, sample->extensions); + } + if (!ret) { tsmf_sample_ack(sample); @@ -450,32 +480,139 @@ static void tsmf_sample_playback(TSMF_SAMPLE* sample) ret = false ; if (stream->decoder->GetDecodedDimension) - ret = stream->decoder->GetDecodedDimension(stream->decoder, &width, &height); - if (ret && (width != stream->width || height != stream->height)) { - DEBUG_DVC("video dimension changed to %d x %d", width, height); - stream->width = width; - stream->height = height; + ret = stream->decoder->GetDecodedDimension(stream->decoder, &width, &height); + if (ret && (width != stream->width || height != stream->height)) + { + DEBUG_DVC("video dimension changed to %d x %d", width, height); + stream->width = width; + stream->height = height; + } } } if (stream->decoder->GetDecodedData) { sample->data = stream->decoder->GetDecodedData(stream->decoder, &sample->decoded_size); + switch (sample->stream->major_type) + { + case TSMF_MAJOR_TYPE_VIDEO: + tsmf_sample_playback_video(sample); + tsmf_sample_ack(sample); + tsmf_sample_free(sample); + break; + case TSMF_MAJOR_TYPE_AUDIO: + tsmf_sample_playback_audio(sample); + tsmf_sample_queue_ack(sample); + break; + } } - - switch (sample->stream->major_type) + else { - case TSMF_MAJOR_TYPE_VIDEO: - tsmf_sample_playback_video(sample); - tsmf_sample_ack(sample); - tsmf_sample_free(sample); - break; - case TSMF_MAJOR_TYPE_AUDIO: - tsmf_sample_playback_audio(sample); - tsmf_sample_queue_ack(sample); - break; - } + TSMF_STREAM * stream = sample->stream; + uint64 ack_anticipation_time = get_current_time(); + uint64 currentRunningTime = sample->start_time; + uint32 bufferLevel = 0; + if (stream->decoder->GetRunningTime) + { + currentRunningTime = stream->decoder->GetRunningTime(stream->decoder); + } + if (stream->decoder->BufferLevel) + { + bufferLevel = stream->decoder->BufferLevel(stream->decoder); + } + switch (sample->stream->major_type) + { + case TSMF_MAJOR_TYPE_VIDEO: + { + TSMF_PRESENTATION * presentation = sample->stream->presentation; + /* + * Tell gstreamer that presentation screen area has moved. + * So it can render on the new area. + */ + if (presentation->last_x != presentation->output_x || presentation->last_y != presentation->output_y || + presentation->last_width != presentation->output_width || presentation->last_height != presentation->output_height) + { + presentation->last_x = presentation->output_x; + presentation->last_y = presentation->output_y; + presentation->last_width = presentation->output_width; + presentation->last_height = presentation->output_height; + if(stream->decoder->UpdateRenderingArea) + { + stream->decoder->UpdateRenderingArea(stream->decoder, presentation->output_x, presentation->output_y, + presentation->output_width, presentation->output_height, presentation->output_num_rects, presentation->output_rects); + } + } + if ( presentation->last_num_rects != presentation->output_num_rects || (presentation->last_rects && presentation->output_rects && + memcmp(presentation->last_rects, presentation->output_rects, presentation->last_num_rects * sizeof(RDP_RECT)) != 0)) + { + if (presentation->last_rects) + { + xfree(presentation->last_rects); + presentation->last_rects = NULL; + } + presentation->last_num_rects = presentation->output_num_rects; + if (presentation->last_num_rects > 0) + { + presentation->last_rects = xzalloc(presentation->last_num_rects * sizeof(RDP_RECT)); + memcpy(presentation->last_rects, presentation->output_rects, presentation->last_num_rects * sizeof(RDP_RECT)); + } + if(stream->decoder->UpdateRenderingArea) + { + stream->decoder->UpdateRenderingArea(stream->decoder, presentation->output_x, presentation->output_y, + presentation->output_width, presentation->output_height, presentation->output_num_rects, presentation->output_rects); + } + } + + if (bufferLevel < 24) + { + ack_anticipation_time += sample->duration; + } + else + { + if (currentRunningTime > sample->start_time) + { + ack_anticipation_time += sample->duration; + } + else if(currentRunningTime == 0) + { + ack_anticipation_time += sample->duration; + } + else + { + ack_anticipation_time += (sample->start_time - currentRunningTime); + } + } + break; + } + case TSMF_MAJOR_TYPE_AUDIO: + { + last_played_audio_time = currentRunningTime; + if (bufferLevel < 2) + { + ack_anticipation_time += sample->duration; + } + else + { + if (currentRunningTime > sample->start_time) + { + ack_anticipation_time += sample->duration; + } + else if(currentRunningTime == 0) + { + ack_anticipation_time += sample->duration; + } + else + { + ack_anticipation_time += (sample->start_time - currentRunningTime); + } + } + break; + } + } + sample->ack_time = ack_anticipation_time; + tsmf_sample_queue_ack(sample); + } } static void* tsmf_stream_playback_func(void* arg) @@ -489,13 +626,19 @@ static void* tsmf_stream_playback_func(void* arg) if (stream->major_type == TSMF_MAJOR_TYPE_AUDIO && stream->sample_rate && stream->channels && stream->bits_per_sample) { - stream->audio = tsmf_load_audio_device( - presentation->audio_name && presentation->audio_name[0] ? presentation->audio_name : NULL, - presentation->audio_device && presentation->audio_device[0] ? presentation->audio_device : NULL); - if (stream->audio) + if (stream->decoder) { - stream->audio->SetFormat(stream->audio, - stream->sample_rate, stream->channels, stream->bits_per_sample); + if (stream->decoder->GetDecodedData) + { + stream->audio = tsmf_load_audio_device( + presentation->audio_name && presentation->audio_name[0] ? presentation->audio_name : NULL, + presentation->audio_device && presentation->audio_device[0] ? presentation->audio_device : NULL); + if (stream->audio) + { + stream->audio->SetFormat(stream->audio, + stream->sample_rate, stream->channels, stream->bits_per_sample); + } + } } } while (!freerdp_thread_is_stopped(stream->thread)) @@ -535,10 +678,99 @@ static void tsmf_stream_start(TSMF_STREAM* stream) static void tsmf_stream_stop(TSMF_STREAM* stream) { + if (!stream) + return; + + if (!stream->decoder) + return; + if (freerdp_thread_is_running(stream->thread)) { freerdp_thread_stop(stream->thread); } + if (stream->decoder->Control) + { + stream->decoder->Control(stream->decoder, Control_Flush, NULL); + } +} + +static void tsmf_stream_pause(TSMF_STREAM* stream) +{ + if (!stream) + return; + + if (!stream->decoder) + return; + + if (stream->decoder->Control) + { + stream->decoder->Control(stream->decoder, Control_Pause, NULL); + } +} + +static void tsmf_stream_restart(TSMF_STREAM* stream) +{ + if (!stream) + return; + + if (!stream->decoder) + return; + + if (stream->decoder->Control) + { + stream->decoder->Control(stream->decoder, Control_Restart, NULL); + } +} + +static void tsmf_stream_change_volume(TSMF_STREAM* stream, uint32 newVolume, uint32 muted) +{ + if (!stream) + return; + + if (!stream->decoder) + return; + + if (stream->decoder->ChangeVolume) + { + stream->decoder->ChangeVolume(stream->decoder, newVolume, muted); + } +} + +void tsmf_presentation_volume_changed(TSMF_PRESENTATION* presentation, uint32 newVolume, uint32 muted) +{ + LIST_ITEM* item; + TSMF_STREAM* stream; + + for (item = presentation->stream_list->head; item; item = item->next) + { + stream = (TSMF_STREAM*) item->data; + tsmf_stream_change_volume(stream, newVolume, muted); + } + +} + +void tsmf_presentation_paused(TSMF_PRESENTATION* presentation) +{ + LIST_ITEM* item; + TSMF_STREAM* stream; + + for (item = presentation->stream_list->head; item; item = item->next) + { + stream = (TSMF_STREAM*) item->data; + tsmf_stream_pause(stream); + } +} + +void tsmf_presentation_restarted(TSMF_PRESENTATION* presentation) +{ + LIST_ITEM* item; + TSMF_STREAM* stream; + + for (item = presentation->stream_list->head; item; item = item->next) + { + stream = (TSMF_STREAM*) item->data; + tsmf_stream_restart(stream); + } } void tsmf_presentation_start(TSMF_PRESENTATION* presentation) @@ -645,7 +877,9 @@ void tsmf_presentation_free(TSMF_PRESENTATION* presentation) TSMF_STREAM* stream; tsmf_presentation_stop(presentation); + freerdp_mutex_lock(presentation->mutex); list_remove(presentation_list, presentation); + freerdp_mutex_unlock(presentation->mutex); while (list_size(presentation->stream_list) > 0) { @@ -757,17 +991,28 @@ void tsmf_stream_free(TSMF_STREAM* stream) list_free(stream->sample_ack_list); if (stream->decoder) + { stream->decoder->Free(stream->decoder); + stream->decoder = 0; + } freerdp_thread_free(stream->thread); xfree(stream); + stream = 0; } void tsmf_stream_push_sample(TSMF_STREAM* stream, IWTSVirtualChannelCallback* pChannelCallback, uint32 sample_id, uint64 start_time, uint64 end_time, uint64 duration, uint32 extensions, uint32 data_size, uint8* data) { + pthread_mutex_lock(&tsmf_mutex); + if (TERMINATING) + { + pthread_mutex_unlock(&tsmf_mutex); + return; + } + pthread_mutex_unlock(&tsmf_mutex); TSMF_SAMPLE* sample; sample = xnew(TSMF_SAMPLE); @@ -788,8 +1033,51 @@ void tsmf_stream_push_sample(TSMF_STREAM* stream, IWTSVirtualChannelCallback* pC freerdp_thread_unlock(stream->thread); } +static void tsmf_signal_handler(int s) +{ + pthread_mutex_lock(&tsmf_mutex); + TERMINATING = 1; + pthread_mutex_unlock(&tsmf_mutex); + LIST_ITEM* p_item; + TSMF_PRESENTATION* presentation; + LIST_ITEM* s_item; + TSMF_STREAM* _stream; + if (presentation_list) + { + for (p_item = presentation_list->head; p_item; p_item = p_item->next) + { + presentation = (TSMF_PRESENTATION*) p_item->data; + for (s_item = presentation->stream_list->head; s_item; s_item = s_item->next) + { + _stream = (TSMF_STREAM*) s_item->data; + tsmf_stream_free(_stream); + } + tsmf_presentation_free(presentation); + } + } + + unlink("/tmp/tsmf.tid"); + + if (s == SIGINT) + { + signal(s, SIG_DFL); + kill(getpid(), s); + } + else if (s == SIGUSR1) + { + signal(s, SIG_DFL); + } +} + void tsmf_media_init(void) { + struct sigaction sigtrap; + sigtrap.sa_handler = tsmf_signal_handler; + sigemptyset(&sigtrap.sa_mask); + sigtrap.sa_flags = 0; + sigaction(SIGINT, &sigtrap, 0); + sigaction(SIGUSR1, &sigtrap, 0); + if (presentation_list == NULL) presentation_list = list_new(); } diff --git a/channels/drdynvc/tsmf/tsmf_media.h b/channels/drdynvc/tsmf/tsmf_media.h index eac3e0035..ae58d9132 100644 --- a/channels/drdynvc/tsmf/tsmf_media.h +++ b/channels/drdynvc/tsmf/tsmf_media.h @@ -3,6 +3,7 @@ * Video Redirection Virtual Channel - Media Container * * Copyright 2010-2011 Vic Lee + * Copyright 2012 Hewlett-Packard Development Company, L.P. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +36,9 @@ TSMF_PRESENTATION* tsmf_presentation_new(const uint8* guid, IWTSVirtualChannelCa TSMF_PRESENTATION* tsmf_presentation_find_by_id(const uint8* guid); void tsmf_presentation_start(TSMF_PRESENTATION* presentation); void tsmf_presentation_stop(TSMF_PRESENTATION* presentation); +void tsmf_presentation_paused(TSMF_PRESENTATION* presentation); +void tsmf_presentation_restarted(TSMF_PRESENTATION* presentation); +void tsmf_presentation_volume_changed(TSMF_PRESENTATION* presentation, uint32 newVolume, uint32 muted); void tsmf_presentation_set_geometry_info(TSMF_PRESENTATION* presentation, uint32 x, uint32 y, uint32 width, uint32 height, int num_rects, RDP_RECT* rects); diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c index 158b6b18a..e93d75ef8 100644 --- a/client/X11/xf_window.c +++ b/client/X11/xf_window.c @@ -3,6 +3,7 @@ * X11 Windows * * Copyright 2011 Marc-Andre Moreau + * 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. @@ -21,6 +22,9 @@ #include #include #include +#include +#include +#include #include #include @@ -61,6 +65,9 @@ #define PROP_MOTIF_WM_HINTS_ELEMENTS 5 +/*to be accessed by gstreamer plugin*/ +#define SHARED_MEM_KEY 7777 + struct _PropMotifWmHints { unsigned long flags; @@ -294,6 +301,24 @@ xfWindow* xf_CreateDesktopWindow(xfInfo* xfi, char* name, int width, int height, CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap | CWBorderPixel | CWWinGravity | CWBitGravity, &xfi->attribs); + int shmid = shmget(SHARED_MEM_KEY, sizeof(int), IPC_CREAT | 0666); + if (shmid < 0) + { + DEBUG_X11("xf_CreateDesktopWindow: failed to get access to shared memory - shmget()\n"); + } + else + { + int *xfwin = shmat(shmid, NULL, 0); + if (xfwin == (int *) -1) + { + DEBUG_X11("xf_CreateDesktopWindow: failed to assign pointer to the memory address - shmat()\n"); + } + else + { + *xfwin = (int)window->handle; + } + } + class_hints = XAllocClassHint(); if (class_hints != NULL) diff --git a/client/X11/xfreerdp.c b/client/X11/xfreerdp.c index c8ee69048..e6528471a 100644 --- a/client/X11/xfreerdp.c +++ b/client/X11/xfreerdp.c @@ -3,6 +3,7 @@ * X11 Client * * Copyright 2011 Marc-Andre Moreau + * 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. @@ -1156,6 +1157,31 @@ int xfreerdp_run(freerdp* instance) xf_process_channel_event(channels, instance); } + FILE *fin = fopen("/tmp/tsmf.tid", "rt"); + if(fin) + { + int thid = 0; + fscanf(fin, "%d", &thid); + fclose(fin); + pthread_kill((pthread_t) thid, SIGUSR1); + + FILE *fin1 = fopen("/tmp/tsmf.tid", "rt"); + int timeout = 5; + while (fin1) + { + fclose(fin1); + sleep(1); + timeout--; + if (timeout <= 0) + { + unlink("/tmp/tsmf.tid"); + pthread_kill((pthread_t) thid, SIGKILL); + break; + } + fin1 = fopen("/tmp/tsmf.tid", "rt"); + } + } + if (!ret) ret = freerdp_error_info(instance); diff --git a/cmake/FindGstreamer.cmake b/cmake/FindGstreamer.cmake new file mode 100644 index 000000000..dccb20082 --- /dev/null +++ b/cmake/FindGstreamer.cmake @@ -0,0 +1,2 @@ +pkg_check_modules(GSTREAMER gstreamer-plugins-base-0.10 gstreamer-0.10) +