FreeRDP
tsmf_gstreamer.c
1 /*
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * Video Redirection Virtual Channel - GStreamer Decoder
4  *
5  * (C) Copyright 2012 HP Development Company, LLC
6  * (C) Copyright 2014 Thincast Technologies GmbH
7  * (C) Copyright 2014 Armin Novak <armin.novak@thincast.com>
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
22 #include <freerdp/config.h>
23 
24 #include <winpr/assert.h>
25 
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include <winpr/string.h>
33 #include <winpr/platform.h>
34 
35 #include <gst/gst.h>
36 
37 #include <gst/app/gstappsrc.h>
38 #include <gst/app/gstappsink.h>
39 
40 #include "tsmf_constants.h"
41 #include "tsmf_decoder.h"
42 #include "tsmf_platform.h"
43 
44 /* 1 second = 10,000,000 100ns units*/
45 #define SEEK_TOLERANCE 10 * 1000 * 1000
46 
47 static BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder);
48 static void tsmf_gstreamer_clean_up(TSMFGstreamerDecoder* mdecoder);
49 static int tsmf_gstreamer_pipeline_set_state(TSMFGstreamerDecoder* mdecoder,
50  GstState desired_state);
51 static BOOL tsmf_gstreamer_buffer_level(ITSMFDecoder* decoder);
52 
53 static const char* get_type(TSMFGstreamerDecoder* mdecoder)
54 {
55  if (!mdecoder)
56  return NULL;
57 
58  switch (mdecoder->media_type)
59  {
60  case TSMF_MAJOR_TYPE_VIDEO:
61  return "VIDEO";
62  case TSMF_MAJOR_TYPE_AUDIO:
63  return "AUDIO";
64  default:
65  return "UNKNOWN";
66  }
67 }
68 
69 static void cb_child_added(GstChildProxy* child_proxy, GObject* object,
70  TSMFGstreamerDecoder* mdecoder)
71 {
72  DEBUG_TSMF("NAME: %s", G_OBJECT_TYPE_NAME(object));
73 
74  if (!g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstXvImageSink") ||
75  !g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstXImageSink") ||
76  !g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstFluVAAutoSink"))
77  {
78  gst_base_sink_set_max_lateness((GstBaseSink*)object, 10000000); /* nanoseconds */
79  g_object_set(G_OBJECT(object), "sync", TRUE, NULL); /* synchronize on the clock */
80  g_object_set(G_OBJECT(object), "async", TRUE, NULL); /* no async state changes */
81  }
82 
83  else if (!g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstAlsaSink") ||
84  !g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstPulseSink"))
85  {
86  gst_base_sink_set_max_lateness((GstBaseSink*)object, 10000000); /* nanoseconds */
87  g_object_set(G_OBJECT(object), "slave-method", 1, NULL);
88  g_object_set(G_OBJECT(object), "buffer-time", (gint64)20000, NULL); /* microseconds */
89  g_object_set(G_OBJECT(object), "drift-tolerance", (gint64)20000, NULL); /* microseconds */
90  g_object_set(G_OBJECT(object), "latency-time", (gint64)10000, NULL); /* microseconds */
91  g_object_set(G_OBJECT(object), "sync", TRUE, NULL); /* synchronize on the clock */
92  g_object_set(G_OBJECT(object), "async", TRUE, NULL); /* no async state changes */
93  }
94 }
95 
96 static void tsmf_gstreamer_enough_data(GstAppSrc* src, gpointer user_data)
97 {
98  TSMFGstreamerDecoder* mdecoder = user_data;
99  (void)mdecoder;
100  DEBUG_TSMF("%s", get_type(mdecoder));
101 }
102 
103 static void tsmf_gstreamer_need_data(GstAppSrc* src, guint length, gpointer user_data)
104 {
105  TSMFGstreamerDecoder* mdecoder = user_data;
106  (void)mdecoder;
107  DEBUG_TSMF("%s length=%u", get_type(mdecoder), length);
108 }
109 
110 static gboolean tsmf_gstreamer_seek_data(GstAppSrc* src, guint64 offset, gpointer user_data)
111 {
112  TSMFGstreamerDecoder* mdecoder = user_data;
113  (void)mdecoder;
114  DEBUG_TSMF("%s offset=%" PRIu64 "", get_type(mdecoder), offset);
115 
116  return TRUE;
117 }
118 
119 static BOOL tsmf_gstreamer_change_volume(ITSMFDecoder* decoder, UINT32 newVolume, UINT32 muted)
120 {
121  TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
122 
123  if (!mdecoder || !mdecoder->pipe)
124  return TRUE;
125 
126  if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
127  return TRUE;
128 
129  mdecoder->gstMuted = (BOOL)muted;
130  DEBUG_TSMF("mute=[%" PRId32 "]", mdecoder->gstMuted);
131  mdecoder->gstVolume = (double)newVolume / (double)10000;
132  DEBUG_TSMF("gst_new_vol=[%f]", mdecoder->gstVolume);
133 
134  if (!mdecoder->volume)
135  return TRUE;
136 
137  if (!G_IS_OBJECT(mdecoder->volume))
138  return TRUE;
139 
140  g_object_set(mdecoder->volume, "mute", mdecoder->gstMuted, NULL);
141  g_object_set(mdecoder->volume, "volume", mdecoder->gstVolume, NULL);
142 
143  return TRUE;
144 }
145 
146 static inline GstClockTime tsmf_gstreamer_timestamp_ms_to_gst(UINT64 ms_timestamp)
147 {
148  /*
149  * Convert Microsoft 100ns timestamps to Gstreamer 1ns units.
150  */
151  return (GstClockTime)(ms_timestamp * 100);
152 }
153 
154 int tsmf_gstreamer_pipeline_set_state(TSMFGstreamerDecoder* mdecoder, GstState desired_state)
155 {
156  GstStateChangeReturn state_change;
157  const char* name;
158  const char* sname = get_type(mdecoder);
159 
160  if (!mdecoder)
161  return 0;
162 
163  if (!mdecoder->pipe)
164  return 0; /* Just in case this is called during startup or shutdown when we don't expect it
165  */
166 
167  if (desired_state == mdecoder->state)
168  return 0; /* Redundant request - Nothing to do */
169 
170  name = gst_element_state_get_name(desired_state); /* For debug */
171  DEBUG_TSMF("%s to %s", sname, name);
172  state_change = gst_element_set_state(mdecoder->pipe, desired_state);
173 
174  if (state_change == GST_STATE_CHANGE_FAILURE)
175  {
176  WLog_ERR(TAG, "%s: (%s) GST_STATE_CHANGE_FAILURE.", sname, name);
177  }
178  else if (state_change == GST_STATE_CHANGE_ASYNC)
179  {
180  WLog_ERR(TAG, "%s: (%s) GST_STATE_CHANGE_ASYNC.", sname, name);
181  mdecoder->state = desired_state;
182  }
183  else
184  {
185  mdecoder->state = desired_state;
186  }
187 
188  return 0;
189 }
190 
191 static GstBuffer* tsmf_get_buffer_from_data(const void* raw_data, gsize size)
192 {
193  GstBuffer* buffer;
194  gpointer data;
195 
196  if (!raw_data)
197  return NULL;
198 
199  if (size < 1)
200  return NULL;
201 
202  data = g_malloc(size);
203 
204  if (!data)
205  {
206  WLog_ERR(TAG, "Could not allocate %" G_GSIZE_FORMAT " bytes of data.", size);
207  return NULL;
208  }
209 
210  CopyMemory(data, raw_data, size);
211 
212 #if GST_VERSION_MAJOR > 0
213  buffer = gst_buffer_new_wrapped(data, size);
214 #else
215  buffer = gst_buffer_new();
216 
217  if (!buffer)
218  {
219  WLog_ERR(TAG, "Could not create GstBuffer");
220  free(data);
221  return NULL;
222  }
223 
224  GST_BUFFER_MALLOCDATA(buffer) = data;
225  GST_BUFFER_SIZE(buffer) = size;
226  GST_BUFFER_DATA(buffer) = GST_BUFFER_MALLOCDATA(buffer);
227 #endif
228 
229  return buffer;
230 }
231 
232 static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* media_type)
233 {
234  TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
235 
236  if (!mdecoder)
237  return FALSE;
238 
239  DEBUG_TSMF("");
240 
241  switch (media_type->MajorType)
242  {
243  case TSMF_MAJOR_TYPE_VIDEO:
244  mdecoder->media_type = TSMF_MAJOR_TYPE_VIDEO;
245  break;
246  case TSMF_MAJOR_TYPE_AUDIO:
247  mdecoder->media_type = TSMF_MAJOR_TYPE_AUDIO;
248  break;
249  default:
250  return FALSE;
251  }
252 
253  switch (media_type->SubType)
254  {
255  case TSMF_SUB_TYPE_WVC1:
256  mdecoder->gst_caps = gst_caps_new_simple(
257  "video/x-wmv", "bitrate", G_TYPE_UINT, media_type->BitRate, "width", G_TYPE_INT,
258  media_type->Width, "height", G_TYPE_INT, media_type->Height, "wmvversion",
259  G_TYPE_INT, 3,
260 #if GST_VERSION_MAJOR > 0
261  "format", G_TYPE_STRING, "WVC1",
262 #else
263  "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('W', 'V', 'C', '1'),
264 #endif
265  "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
266  media_type->SamplesPerSecond.Denominator, "pixel-aspect-ratio", GST_TYPE_FRACTION,
267  1, 1, NULL);
268  break;
269  case TSMF_SUB_TYPE_MP4S:
270  mdecoder->gst_caps = gst_caps_new_simple(
271  "video/x-divx", "divxversion", G_TYPE_INT, 5, "bitrate", G_TYPE_UINT,
272  media_type->BitRate, "width", G_TYPE_INT, media_type->Width, "height", G_TYPE_INT,
273  media_type->Height,
274 #if GST_VERSION_MAJOR > 0
275  "format", G_TYPE_STRING, "MP42",
276 #else
277  "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('M', 'P', '4', '2'),
278 #endif
279  "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
280  media_type->SamplesPerSecond.Denominator, NULL);
281  break;
282  case TSMF_SUB_TYPE_MP42:
283  mdecoder->gst_caps = gst_caps_new_simple(
284  "video/x-msmpeg", "msmpegversion", G_TYPE_INT, 42, "bitrate", G_TYPE_UINT,
285  media_type->BitRate, "width", G_TYPE_INT, media_type->Width, "height", G_TYPE_INT,
286  media_type->Height,
287 #if GST_VERSION_MAJOR > 0
288  "format", G_TYPE_STRING, "MP42",
289 #else
290  "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('M', 'P', '4', '2'),
291 #endif
292  "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
293  media_type->SamplesPerSecond.Denominator, NULL);
294  break;
295  case TSMF_SUB_TYPE_MP43:
296  mdecoder->gst_caps = gst_caps_new_simple(
297  "video/x-msmpeg", "msmpegversion", G_TYPE_INT, 43, "bitrate", G_TYPE_UINT,
298  media_type->BitRate, "width", G_TYPE_INT, media_type->Width, "height", G_TYPE_INT,
299  media_type->Height,
300 #if GST_VERSION_MAJOR > 0
301  "format", G_TYPE_STRING, "MP43",
302 #else
303  "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('M', 'P', '4', '3'),
304 #endif
305  "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
306  media_type->SamplesPerSecond.Denominator, NULL);
307  break;
308  case TSMF_SUB_TYPE_M4S2:
309  mdecoder->gst_caps = gst_caps_new_simple(
310  "video/mpeg", "mpegversion", G_TYPE_INT, 4, "width", G_TYPE_INT, media_type->Width,
311  "height", G_TYPE_INT, media_type->Height,
312 #if GST_VERSION_MAJOR > 0
313  "format", G_TYPE_STRING, "M4S2",
314 #else
315  "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('M', '4', 'S', '2'),
316 #endif
317  "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
318  media_type->SamplesPerSecond.Denominator, NULL);
319  break;
320  case TSMF_SUB_TYPE_WMA9:
321  mdecoder->gst_caps = gst_caps_new_simple(
322  "audio/x-wma", "wmaversion", G_TYPE_INT, 3, "rate", G_TYPE_INT,
323  media_type->SamplesPerSecond.Numerator, "channels", G_TYPE_INT,
324  media_type->Channels, "bitrate", G_TYPE_INT, media_type->BitRate, "depth",
325  G_TYPE_INT, media_type->BitsPerSample, "width", G_TYPE_INT,
326  media_type->BitsPerSample, "block_align", G_TYPE_INT, media_type->BlockAlign, NULL);
327  break;
328  case TSMF_SUB_TYPE_WMA1:
329  mdecoder->gst_caps = gst_caps_new_simple(
330  "audio/x-wma", "wmaversion", G_TYPE_INT, 1, "rate", G_TYPE_INT,
331  media_type->SamplesPerSecond.Numerator, "channels", G_TYPE_INT,
332  media_type->Channels, "bitrate", G_TYPE_INT, media_type->BitRate, "depth",
333  G_TYPE_INT, media_type->BitsPerSample, "width", G_TYPE_INT,
334  media_type->BitsPerSample, "block_align", G_TYPE_INT, media_type->BlockAlign, NULL);
335  break;
336  case TSMF_SUB_TYPE_WMA2:
337  mdecoder->gst_caps = gst_caps_new_simple(
338  "audio/x-wma", "wmaversion", G_TYPE_INT, 2, "rate", G_TYPE_INT,
339  media_type->SamplesPerSecond.Numerator, "channels", G_TYPE_INT,
340  media_type->Channels, "bitrate", G_TYPE_INT, media_type->BitRate, "depth",
341  G_TYPE_INT, media_type->BitsPerSample, "width", G_TYPE_INT,
342  media_type->BitsPerSample, "block_align", G_TYPE_INT, media_type->BlockAlign, NULL);
343  break;
344  case TSMF_SUB_TYPE_MP3:
345  mdecoder->gst_caps =
346  gst_caps_new_simple("audio/mpeg", "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT,
347  3, "rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
348  "channels", G_TYPE_INT, media_type->Channels, NULL);
349  break;
350  case TSMF_SUB_TYPE_WMV1:
351  mdecoder->gst_caps = gst_caps_new_simple(
352  "video/x-wmv", "bitrate", G_TYPE_UINT, media_type->BitRate, "width", G_TYPE_INT,
353  media_type->Width, "height", G_TYPE_INT, media_type->Height, "wmvversion",
354  G_TYPE_INT, 1,
355 #if GST_VERSION_MAJOR > 0
356  "format", G_TYPE_STRING, "WMV1",
357 #else
358  "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('W', 'M', 'V', '1'),
359 #endif
360  "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
361  media_type->SamplesPerSecond.Denominator, NULL);
362  break;
363  case TSMF_SUB_TYPE_WMV2:
364  mdecoder->gst_caps = gst_caps_new_simple(
365  "video/x-wmv", "width", G_TYPE_INT, media_type->Width, "height", G_TYPE_INT,
366  media_type->Height, "wmvversion", G_TYPE_INT, 2,
367 #if GST_VERSION_MAJOR > 0
368  "format", G_TYPE_STRING, "WMV2",
369 #else
370  "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('W', 'M', 'V', '2'),
371 #endif
372  "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
373  media_type->SamplesPerSecond.Denominator, "pixel-aspect-ratio", GST_TYPE_FRACTION,
374  1, 1, NULL);
375  break;
376  case TSMF_SUB_TYPE_WMV3:
377  mdecoder->gst_caps = gst_caps_new_simple(
378  "video/x-wmv", "bitrate", G_TYPE_UINT, media_type->BitRate, "width", G_TYPE_INT,
379  media_type->Width, "height", G_TYPE_INT, media_type->Height, "wmvversion",
380  G_TYPE_INT, 3,
381 #if GST_VERSION_MAJOR > 0
382  "format", G_TYPE_STRING, "WMV3",
383 #else
384  "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('W', 'M', 'V', '3'),
385 #endif
386  "framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
387  media_type->SamplesPerSecond.Denominator, "pixel-aspect-ratio", GST_TYPE_FRACTION,
388  1, 1, NULL);
389  break;
390  case TSMF_SUB_TYPE_AVC1:
391  case TSMF_SUB_TYPE_H264:
392  mdecoder->gst_caps = gst_caps_new_simple(
393  "video/x-h264", "width", G_TYPE_INT, media_type->Width, "height", G_TYPE_INT,
394  media_type->Height, "framerate", GST_TYPE_FRACTION,
395  media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
396  "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, "stream-format", G_TYPE_STRING,
397  "byte-stream", "alignment", G_TYPE_STRING, "nal", NULL);
398  break;
399  case TSMF_SUB_TYPE_AC3:
400  mdecoder->gst_caps = gst_caps_new_simple(
401  "audio/x-ac3", "rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
402  "channels", G_TYPE_INT, media_type->Channels, NULL);
403  break;
404  case TSMF_SUB_TYPE_AAC:
405 
406  /* For AAC the pFormat is a HEAACWAVEINFO struct, and the codec data
407  is at the end of it. See
408  http://msdn.microsoft.com/en-us/library/dd757806.aspx */
409  if (media_type->ExtraData)
410  {
411  if (media_type->ExtraDataSize < 12)
412  return FALSE;
413  media_type->ExtraData += 12;
414  media_type->ExtraDataSize -= 12;
415  }
416 
417  mdecoder->gst_caps = gst_caps_new_simple(
418  "audio/mpeg", "rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
419  "channels", G_TYPE_INT, media_type->Channels, "mpegversion", G_TYPE_INT, 4,
420  "framed", G_TYPE_BOOLEAN, TRUE, "stream-format", G_TYPE_STRING, "raw", NULL);
421  break;
422  case TSMF_SUB_TYPE_MP1A:
423  mdecoder->gst_caps =
424  gst_caps_new_simple("audio/mpeg", "mpegversion", G_TYPE_INT, 1, "channels",
425  G_TYPE_INT, media_type->Channels, NULL);
426  break;
427  case TSMF_SUB_TYPE_MP1V:
428  mdecoder->gst_caps =
429  gst_caps_new_simple("video/mpeg", "mpegversion", G_TYPE_INT, 1, "width", G_TYPE_INT,
430  media_type->Width, "height", G_TYPE_INT, media_type->Height,
431  "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
432  break;
433  case TSMF_SUB_TYPE_YUY2:
434 #if GST_VERSION_MAJOR > 0
435  mdecoder->gst_caps = gst_caps_new_simple(
436  "video/x-raw", "format", G_TYPE_STRING, "YUY2", "width", G_TYPE_INT,
437  media_type->Width, "height", G_TYPE_INT, media_type->Height, NULL);
438 #else
439  mdecoder->gst_caps = gst_caps_new_simple(
440  "video/x-raw-yuv", "format", G_TYPE_STRING, "YUY2", "width", G_TYPE_INT,
441  media_type->Width, "height", G_TYPE_INT, media_type->Height, "framerate",
442  GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator,
443  media_type->SamplesPerSecond.Denominator, NULL);
444 #endif
445  break;
446  case TSMF_SUB_TYPE_MP2V:
447  mdecoder->gst_caps = gst_caps_new_simple("video/mpeg", "mpegversion", G_TYPE_INT, 2,
448  "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
449  break;
450  case TSMF_SUB_TYPE_MP2A:
451  mdecoder->gst_caps =
452  gst_caps_new_simple("audio/mpeg", "mpegversion", G_TYPE_INT, 1, "rate", G_TYPE_INT,
453  media_type->SamplesPerSecond.Numerator, "channels", G_TYPE_INT,
454  media_type->Channels, NULL);
455  break;
456  case TSMF_SUB_TYPE_FLAC:
457  mdecoder->gst_caps = gst_caps_new_simple("audio/x-flac", "", NULL);
458  break;
459  default:
460  WLog_ERR(TAG, "unknown format:(%d).", media_type->SubType);
461  return FALSE;
462  }
463 
464  if (media_type->ExtraDataSize > 0)
465  {
466  GstBuffer* buffer;
467  DEBUG_TSMF("Extra data available (%" PRIu32 ")", media_type->ExtraDataSize);
468  buffer = tsmf_get_buffer_from_data(media_type->ExtraData, media_type->ExtraDataSize);
469 
470  if (!buffer)
471  {
472  WLog_ERR(TAG, "could not allocate GstBuffer!");
473  return FALSE;
474  }
475 
476  gst_caps_set_simple(mdecoder->gst_caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL);
477  }
478 
479  DEBUG_TSMF("%p format '%s'", (void*)mdecoder, gst_caps_to_string(mdecoder->gst_caps));
480  tsmf_platform_set_format(mdecoder);
481 
482  /* Create the pipeline... */
483  if (!tsmf_gstreamer_pipeline_build(mdecoder))
484  return FALSE;
485 
486  return TRUE;
487 }
488 
489 void tsmf_gstreamer_clean_up(TSMFGstreamerDecoder* mdecoder)
490 {
491  if (!mdecoder || !mdecoder->pipe)
492  return;
493 
494  if (mdecoder->pipe && GST_OBJECT_REFCOUNT_VALUE(mdecoder->pipe) > 0)
495  {
496  tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
497  gst_object_unref(mdecoder->pipe);
498  }
499 
500  mdecoder->ready = FALSE;
501  mdecoder->paused = FALSE;
502 
503  mdecoder->pipe = NULL;
504  mdecoder->src = NULL;
505  mdecoder->queue = NULL;
506 }
507 
508 BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder)
509 {
510 #if GST_VERSION_MAJOR > 0
511  const char* video =
512  "appsrc name=videosource ! queue2 name=videoqueue ! decodebin name=videodecoder !";
513  const char* audio =
514  "appsrc name=audiosource ! queue2 name=audioqueue ! decodebin name=audiodecoder ! "
515  "audioconvert ! audiorate ! audioresample ! volume name=audiovolume !";
516 #else
517  const char* video =
518  "appsrc name=videosource ! queue2 name=videoqueue ! decodebin2 name=videodecoder !";
519  const char* audio =
520  "appsrc name=audiosource ! queue2 name=audioqueue ! decodebin2 name=audiodecoder ! "
521  "audioconvert ! audiorate ! audioresample ! volume name=audiovolume !";
522 #endif
523  char pipeline[1024];
524 
525  if (!mdecoder)
526  return FALSE;
527 
528  /* TODO: Construction of the pipeline from a string allows easy overwrite with arguments.
529  * The only fixed elements necessary are appsrc and the volume element for audio streams.
530  * The rest could easily be provided in gstreamer pipeline notation from command line. */
531  if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
532  sprintf_s(pipeline, sizeof(pipeline), "%s %s name=videosink", video,
533  tsmf_platform_get_video_sink());
534  else
535  sprintf_s(pipeline, sizeof(pipeline), "%s %s name=audiosink", audio,
536  tsmf_platform_get_audio_sink());
537 
538  DEBUG_TSMF("pipeline=%s", pipeline);
539  mdecoder->pipe = gst_parse_launch(pipeline, NULL);
540 
541  if (!mdecoder->pipe)
542  {
543  WLog_ERR(TAG, "Failed to create new pipe");
544  return FALSE;
545  }
546 
547  if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
548  mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videosource");
549  else
550  mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiosource");
551 
552  if (!mdecoder->src)
553  {
554  WLog_ERR(TAG, "Failed to get appsrc");
555  return FALSE;
556  }
557 
558  if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
559  mdecoder->queue = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videoqueue");
560  else
561  mdecoder->queue = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audioqueue");
562 
563  if (!mdecoder->queue)
564  {
565  WLog_ERR(TAG, "Failed to get queue");
566  return FALSE;
567  }
568 
569  if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
570  mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videosink");
571  else
572  mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiosink");
573 
574  if (!mdecoder->outsink)
575  {
576  WLog_ERR(TAG, "Failed to get sink");
577  return FALSE;
578  }
579 
580  g_signal_connect(mdecoder->outsink, "child-added", G_CALLBACK(cb_child_added), mdecoder);
581 
582  if (mdecoder->media_type == TSMF_MAJOR_TYPE_AUDIO)
583  {
584  mdecoder->volume = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiovolume");
585 
586  if (!mdecoder->volume)
587  {
588  WLog_ERR(TAG, "Failed to get volume");
589  return FALSE;
590  }
591 
592  tsmf_gstreamer_change_volume((ITSMFDecoder*)mdecoder, mdecoder->gstVolume * ((double)10000),
593  mdecoder->gstMuted);
594  }
595 
596  tsmf_platform_register_handler(mdecoder);
597  /* AppSrc settings */
598  GstAppSrcCallbacks callbacks = {
599  tsmf_gstreamer_need_data, tsmf_gstreamer_enough_data, tsmf_gstreamer_seek_data, { NULL }
600  };
601  g_object_set(mdecoder->src, "format", GST_FORMAT_TIME, NULL);
602  g_object_set(mdecoder->src, "is-live", FALSE, NULL);
603  g_object_set(mdecoder->src, "block", FALSE, NULL);
604  g_object_set(mdecoder->src, "blocksize", 1024, NULL);
605  gst_app_src_set_caps((GstAppSrc*)mdecoder->src, mdecoder->gst_caps);
606  gst_app_src_set_callbacks((GstAppSrc*)mdecoder->src, &callbacks, mdecoder, NULL);
607  gst_app_src_set_stream_type((GstAppSrc*)mdecoder->src, GST_APP_STREAM_TYPE_SEEKABLE);
608  gst_app_src_set_latency((GstAppSrc*)mdecoder->src, 0, -1);
609  gst_app_src_set_max_bytes((GstAppSrc*)mdecoder->src, (guint64)0); // unlimited
610  g_object_set(G_OBJECT(mdecoder->queue), "use-buffering", FALSE, NULL);
611  g_object_set(G_OBJECT(mdecoder->queue), "use-rate-estimate", FALSE, NULL);
612  g_object_set(G_OBJECT(mdecoder->queue), "max-size-buffers", 0, NULL);
613  g_object_set(G_OBJECT(mdecoder->queue), "max-size-bytes", 0, NULL);
614  g_object_set(G_OBJECT(mdecoder->queue), "max-size-time", (guint64)0, NULL);
615 
616  /* Only set these properties if not an autosink, otherwise we will set properties when real
617  * sinks are added */
618  if (!g_strcmp0(G_OBJECT_TYPE_NAME(mdecoder->outsink), "GstAutoVideoSink") &&
619  !g_strcmp0(G_OBJECT_TYPE_NAME(mdecoder->outsink), "GstAutoAudioSink"))
620  {
621  if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
622  {
623  gst_base_sink_set_max_lateness((GstBaseSink*)mdecoder->outsink,
624  10000000); /* nanoseconds */
625  }
626  else
627  {
628  gst_base_sink_set_max_lateness((GstBaseSink*)mdecoder->outsink,
629  10000000); /* nanoseconds */
630  g_object_set(G_OBJECT(mdecoder->outsink), "buffer-time", (gint64)20000,
631  NULL); /* microseconds */
632  g_object_set(G_OBJECT(mdecoder->outsink), "drift-tolerance", (gint64)20000,
633  NULL); /* microseconds */
634  g_object_set(G_OBJECT(mdecoder->outsink), "latency-time", (gint64)10000,
635  NULL); /* microseconds */
636  g_object_set(G_OBJECT(mdecoder->outsink), "slave-method", 1, NULL);
637  }
638  g_object_set(G_OBJECT(mdecoder->outsink), "sync", TRUE,
639  NULL); /* synchronize on the clock */
640  g_object_set(G_OBJECT(mdecoder->outsink), "async", TRUE, NULL); /* no async state changes */
641  }
642 
643  tsmf_window_create(mdecoder);
644  tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_READY);
645  tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
646  mdecoder->pipeline_start_time_valid = 0;
647  mdecoder->shutdown = 0;
648  mdecoder->paused = FALSE;
649 
650  GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(mdecoder->pipe), GST_DEBUG_GRAPH_SHOW_ALL,
651  get_type(mdecoder));
652 
653  return TRUE;
654 }
655 
656 static BOOL tsmf_gstreamer_decodeEx(ITSMFDecoder* decoder, const BYTE* data, UINT32 data_size,
657  UINT32 extensions, UINT64 start_time, UINT64 end_time,
658  UINT64 duration)
659 {
660  GstBuffer* gst_buf;
661  TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
662  UINT64 sample_time = tsmf_gstreamer_timestamp_ms_to_gst(start_time);
663  BOOL useTimestamps = TRUE;
664 
665  if (!mdecoder)
666  {
667  WLog_ERR(TAG, "Decoder not initialized!");
668  return FALSE;
669  }
670 
671  /*
672  * This function is always called from a stream-specific thread.
673  * It should be alright to block here if necessary.
674  * We don't expect to block here often, since the pipeline should
675  * have more than enough buffering.
676  */
677  DEBUG_TSMF(
678  "%s. Start:(%" PRIu64 ") End:(%" PRIu64 ") Duration:(%" PRIu64 ") Last Start:(%" PRIu64 ")",
679  get_type(mdecoder), start_time, end_time, duration, mdecoder->last_sample_start_time);
680 
681  if (mdecoder->shutdown)
682  {
683  WLog_ERR(TAG, "decodeEx called on shutdown decoder");
684  return TRUE;
685  }
686 
687  if (mdecoder->gst_caps == NULL)
688  {
689  WLog_ERR(TAG, "tsmf_gstreamer_set_format not called or invalid format.");
690  return FALSE;
691  }
692 
693  if (!mdecoder->pipe)
694  tsmf_gstreamer_pipeline_build(mdecoder);
695 
696  if (!mdecoder->src)
697  {
698  WLog_ERR(
699  TAG,
700  "failed to construct pipeline correctly. Unable to push buffer to source element.");
701  return FALSE;
702  }
703 
704  gst_buf = tsmf_get_buffer_from_data(data, data_size);
705 
706  if (gst_buf == NULL)
707  {
708  WLog_ERR(TAG, "tsmf_get_buffer_from_data(%p, %" PRIu32 ") failed.", (void*)data, data_size);
709  return FALSE;
710  }
711 
712  /* Relative timestamping will sometimes be set to 0
713  * so we ignore these timestamps just to be safe(bit 8)
714  */
715  if (extensions & 0x00000080)
716  {
717  DEBUG_TSMF("Ignoring the timestamps - relative - bit 8");
718  useTimestamps = FALSE;
719  }
720 
721  /* If no timestamps exist then we dont want to look at the timestamp values (bit 7) */
722  if (extensions & 0x00000040)
723  {
724  DEBUG_TSMF("Ignoring the timestamps - none - bit 7");
725  useTimestamps = FALSE;
726  }
727 
728  /* If performing a seek */
729  if (mdecoder->seeking)
730  {
731  mdecoder->seeking = FALSE;
732  tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
733  mdecoder->pipeline_start_time_valid = 0;
734  }
735 
736  if (mdecoder->pipeline_start_time_valid)
737  {
738  DEBUG_TSMF("%s start time %" PRIu64 "", get_type(mdecoder), start_time);
739 
740  /* Adjusted the condition for a seek to be based on start time only
741  * WMV1 and WMV2 files in particular have bad end time and duration values
742  * there seems to be no real side effects of just using the start time instead
743  */
744  UINT64 minTime = mdecoder->last_sample_start_time - (UINT64)SEEK_TOLERANCE;
745  UINT64 maxTime = mdecoder->last_sample_start_time + (UINT64)SEEK_TOLERANCE;
746 
747  /* Make sure the minTime stops at 0 , should we be at the beginning of the stream */
748  if (mdecoder->last_sample_start_time < (UINT64)SEEK_TOLERANCE)
749  minTime = 0;
750 
751  /* If the start_time is valid and different from the previous start time by more than the
752  * seek tolerance, then we have a seek condition */
753  if (((start_time > maxTime) || (start_time < minTime)) && useTimestamps)
754  {
755  DEBUG_TSMF("tsmf_gstreamer_decodeEx: start_time=[%" PRIu64
756  "] > last_sample_start_time=[%" PRIu64 "] OR ",
757  start_time, mdecoder->last_sample_start_time);
758  DEBUG_TSMF("tsmf_gstreamer_decodeEx: start_time=[%" PRIu64
759  "] < last_sample_start_time=[%" PRIu64 "] with",
760  start_time, mdecoder->last_sample_start_time);
761  DEBUG_TSMF(
762  "tsmf_gstreamer_decodeEX: a tolerance of more than [%lu] from the last sample",
763  SEEK_TOLERANCE);
764  DEBUG_TSMF("tsmf_gstreamer_decodeEX: minTime=[%" PRIu64 "] maxTime=[%" PRIu64 "]",
765  minTime, maxTime);
766 
767  mdecoder->seeking = TRUE;
768 
769  /* since we cant make the gstreamer pipeline jump to the new start time after a seek -
770  * we just maintain a offset between realtime and gstreamer time
771  */
772  mdecoder->seek_offset = start_time;
773  }
774  }
775  else
776  {
777  DEBUG_TSMF("%s start time %" PRIu64 "", get_type(mdecoder), start_time);
778  /* Always set base/start time to 0. Will use seek offset to translate real buffer times
779  * back to 0. This allows the video to be started from anywhere and the ability to handle
780  * seeks without rebuilding the pipeline, etc. since that is costly
781  */
782  gst_element_set_base_time(mdecoder->pipe, tsmf_gstreamer_timestamp_ms_to_gst(0));
783  gst_element_set_start_time(mdecoder->pipe, tsmf_gstreamer_timestamp_ms_to_gst(0));
784  mdecoder->pipeline_start_time_valid = 1;
785 
786  /* Set the seek offset if buffer has valid timestamps. */
787  if (useTimestamps)
788  mdecoder->seek_offset = start_time;
789 
790  if (!gst_element_seek(mdecoder->pipe, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
791  GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
792  {
793  WLog_ERR(TAG, "seek failed");
794  }
795  }
796 
797 #if GST_VERSION_MAJOR > 0
798  if (useTimestamps)
799  GST_BUFFER_PTS(gst_buf) =
800  sample_time - tsmf_gstreamer_timestamp_ms_to_gst(mdecoder->seek_offset);
801  else
802  GST_BUFFER_PTS(gst_buf) = GST_CLOCK_TIME_NONE;
803 #else
804  if (useTimestamps)
805  GST_BUFFER_TIMESTAMP(gst_buf) =
806  sample_time - tsmf_gstreamer_timestamp_ms_to_gst(mdecoder->seek_offset);
807  else
808  GST_BUFFER_TIMESTAMP(gst_buf) = GST_CLOCK_TIME_NONE;
809 #endif
810  GST_BUFFER_DURATION(gst_buf) = GST_CLOCK_TIME_NONE;
811  GST_BUFFER_OFFSET(gst_buf) = GST_BUFFER_OFFSET_NONE;
812 #if GST_VERSION_MAJOR > 0
813 #else
814  gst_buffer_set_caps(gst_buf, mdecoder->gst_caps);
815 #endif
816  gst_app_src_push_buffer(GST_APP_SRC(mdecoder->src), gst_buf);
817 
818  /* Should only update the last timestamps if the current ones are valid */
819  if (useTimestamps)
820  {
821  mdecoder->last_sample_start_time = start_time;
822  mdecoder->last_sample_end_time = end_time;
823  }
824 
825  if (mdecoder->pipe && (GST_STATE(mdecoder->pipe) != GST_STATE_PLAYING))
826  {
827  DEBUG_TSMF("%s: state=%s", get_type(mdecoder),
828  gst_element_state_get_name(GST_STATE(mdecoder->pipe)));
829 
830  DEBUG_TSMF("%s Paused: %" PRIi32 " Shutdown: %i Ready: %" PRIi32 "", get_type(mdecoder),
831  mdecoder->paused, mdecoder->shutdown, mdecoder->ready);
832  if (!mdecoder->paused && !mdecoder->shutdown && mdecoder->ready)
833  tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
834  }
835 
836  return TRUE;
837 }
838 
839 static BOOL tsmf_gstreamer_control(ITSMFDecoder* decoder, ITSMFControlMsg control_msg, UINT32* arg)
840 {
841  TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
842 
843  if (!mdecoder)
844  {
845  WLog_ERR(TAG, "Control called with no decoder!");
846  return TRUE;
847  }
848 
849  if (control_msg == Control_Pause)
850  {
851  DEBUG_TSMF("Control_Pause %s", get_type(mdecoder));
852 
853  if (mdecoder->paused)
854  {
855  WLog_ERR(TAG, "%s: Ignoring Control_Pause, already received!", get_type(mdecoder));
856  return TRUE;
857  }
858 
859  tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
860  mdecoder->shutdown = 0;
861  mdecoder->paused = TRUE;
862  }
863  else if (control_msg == Control_Resume)
864  {
865  DEBUG_TSMF("Control_Resume %s", get_type(mdecoder));
866 
867  if (!mdecoder->paused && !mdecoder->shutdown)
868  {
869  WLog_ERR(TAG, "%s: Ignoring Control_Resume, already received!", get_type(mdecoder));
870  return TRUE;
871  }
872 
873  mdecoder->shutdown = 0;
874  mdecoder->paused = FALSE;
875  }
876  else if (control_msg == Control_Stop)
877  {
878  DEBUG_TSMF("Control_Stop %s", get_type(mdecoder));
879 
880  if (mdecoder->shutdown)
881  {
882  WLog_ERR(TAG, "%s: Ignoring Control_Stop, already received!", get_type(mdecoder));
883  return TRUE;
884  }
885 
886  /* Reset stamps, flush buffers, etc */
887  if (mdecoder->pipe)
888  {
889  tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
890  tsmf_window_destroy(mdecoder);
891  tsmf_gstreamer_clean_up(mdecoder);
892  }
893  mdecoder->seek_offset = 0;
894  mdecoder->pipeline_start_time_valid = 0;
895  mdecoder->shutdown = 1;
896  }
897  else if (control_msg == Control_Restart)
898  {
899  DEBUG_TSMF("Control_Restart %s", get_type(mdecoder));
900  mdecoder->shutdown = 0;
901  mdecoder->paused = FALSE;
902 
903  if (mdecoder->pipeline_start_time_valid)
904  tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
905  }
906  else
907  WLog_ERR(TAG, "Unknown control message %08x", control_msg);
908 
909  return TRUE;
910 }
911 
912 static BOOL tsmf_gstreamer_buffer_level(ITSMFDecoder* decoder)
913 {
914  TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
915  DEBUG_TSMF("");
916 
917  if (!mdecoder)
918  return FALSE;
919 
920  guint clbuff = 0;
921 
922  if (G_IS_OBJECT(mdecoder->queue))
923  g_object_get(mdecoder->queue, "current-level-buffers", &clbuff, NULL);
924 
925  DEBUG_TSMF("%s buffer level %u", get_type(mdecoder), clbuff);
926  return clbuff;
927 }
928 
929 static void tsmf_gstreamer_free(ITSMFDecoder* decoder)
930 {
931  TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
932  DEBUG_TSMF("%s", get_type(mdecoder));
933 
934  if (mdecoder)
935  {
936  tsmf_window_destroy(mdecoder);
937  tsmf_gstreamer_clean_up(mdecoder);
938 
939  if (mdecoder->gst_caps)
940  gst_caps_unref(mdecoder->gst_caps);
941 
942  tsmf_platform_free(mdecoder);
943  ZeroMemory(mdecoder, sizeof(TSMFGstreamerDecoder));
944  free(mdecoder);
945  mdecoder = NULL;
946  }
947 }
948 
949 static UINT64 tsmf_gstreamer_get_running_time(ITSMFDecoder* decoder)
950 {
951  TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
952 
953  if (!mdecoder)
954  return 0;
955 
956  if (!mdecoder->outsink)
957  return mdecoder->last_sample_start_time;
958 
959  if (!mdecoder->pipe)
960  return 0;
961 
962  GstFormat fmt = GST_FORMAT_TIME;
963  gint64 pos = 0;
964 #if GST_VERSION_MAJOR > 0
965  gst_element_query_position(mdecoder->pipe, fmt, &pos);
966 #else
967  gst_element_query_position(mdecoder->pipe, &fmt, &pos);
968 #endif
969  return (UINT64)(pos / 100 + mdecoder->seek_offset);
970 }
971 
972 static BOOL tsmf_gstreamer_update_rendering_area(ITSMFDecoder* decoder, int newX, int newY,
973  int newWidth, int newHeight, int numRectangles,
974  RDP_RECT* rectangles)
975 {
976  TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
977  DEBUG_TSMF("x=%d, y=%d, w=%d, h=%d, rect=%d", newX, newY, newWidth, newHeight, numRectangles);
978 
979  if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
980  {
981  return tsmf_window_resize(mdecoder, newX, newY, newWidth, newHeight, numRectangles,
982  rectangles) == 0;
983  }
984 
985  return TRUE;
986 }
987 
988 static BOOL tsmf_gstreamer_ack(ITSMFDecoder* decoder, BOOL (*cb)(void*, BOOL), void* stream)
989 {
990  TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
991  DEBUG_TSMF("");
992  mdecoder->ack_cb = NULL;
993  mdecoder->stream = stream;
994  return TRUE;
995 }
996 
997 static BOOL tsmf_gstreamer_sync(ITSMFDecoder* decoder, void (*cb)(void*), void* stream)
998 {
999  TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder*)decoder;
1000  DEBUG_TSMF("");
1001  mdecoder->sync_cb = NULL;
1002  mdecoder->stream = stream;
1003  return TRUE;
1004 }
1005 
1006 FREERDP_ENTRY_POINT(UINT VCAPITYPE gstreamer_freerdp_tsmf_client_decoder_subsystem_entry(void* ptr))
1007 {
1008  ITSMFDecoder** sptr = (ITSMFDecoder**)ptr;
1009  WINPR_ASSERT(sptr);
1010  *sptr = NULL;
1011 
1012 #if GST_CHECK_VERSION(0, 10, 31)
1013  if (!gst_is_initialized())
1014  {
1015  gst_init(NULL, NULL);
1016  }
1017 #else
1018  gst_init(NULL, NULL);
1019 #endif
1020 
1021  TSMFGstreamerDecoder* decoder;
1022  decoder = calloc(1, sizeof(TSMFGstreamerDecoder));
1023 
1024  if (!decoder)
1025  return ERROR_OUTOFMEMORY;
1026 
1027  decoder->iface.SetFormat = tsmf_gstreamer_set_format;
1028  decoder->iface.Decode = NULL;
1029  decoder->iface.GetDecodedData = NULL;
1030  decoder->iface.GetDecodedFormat = NULL;
1031  decoder->iface.GetDecodedDimension = NULL;
1032  decoder->iface.GetRunningTime = tsmf_gstreamer_get_running_time;
1033  decoder->iface.UpdateRenderingArea = tsmf_gstreamer_update_rendering_area;
1034  decoder->iface.Free = tsmf_gstreamer_free;
1035  decoder->iface.Control = tsmf_gstreamer_control;
1036  decoder->iface.DecodeEx = tsmf_gstreamer_decodeEx;
1037  decoder->iface.ChangeVolume = tsmf_gstreamer_change_volume;
1038  decoder->iface.BufferLevel = tsmf_gstreamer_buffer_level;
1039  decoder->iface.SetAckFunc = tsmf_gstreamer_ack;
1040  decoder->iface.SetSyncFunc = tsmf_gstreamer_sync;
1041  decoder->paused = FALSE;
1042  decoder->gstVolume = 0.5;
1043  decoder->gstMuted = FALSE;
1044  decoder->state = GST_STATE_VOID_PENDING; /* No real state yet */
1045  decoder->last_sample_start_time = 0;
1046  decoder->last_sample_end_time = 0;
1047  decoder->seek_offset = 0;
1048  decoder->seeking = FALSE;
1049 
1050  if (tsmf_platform_create(decoder) < 0)
1051  {
1052  free(decoder);
1053  return ERROR_INTERNAL_ERROR;
1054  }
1055 
1056  *sptr = &decoder->iface;
1057  return CHANNEL_RC_OK;
1058 }