/* * FreeRDP: A Remote Desktop Protocol Implementation * 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 #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; } #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, ¤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. */ 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; }