2011-09-19 18:54:09 +04:00
|
|
|
/**
|
2012-10-09 07:02:04 +04:00
|
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
2011-09-19 18:54:09 +04:00
|
|
|
* Video Redirection Virtual Channel - Media Container
|
|
|
|
*
|
|
|
|
* Copyright 2010-2011 Vic Lee
|
2012-06-13 23:45:58 +04:00
|
|
|
* Copyright 2012 Hewlett-Packard Development Company, L.P.
|
2015-07-15 10:50:35 +03:00
|
|
|
* Copyright 2015 Thincast Technologies GmbH
|
|
|
|
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
2011-09-19 18:54:09 +04:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2012-08-15 01:09:01 +04:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2012-06-13 23:45:58 +04:00
|
|
|
#include <signal.h>
|
2012-07-29 06:24:14 +04:00
|
|
|
|
|
|
|
#ifdef HAVE_UNISTD_H
|
2012-06-13 23:45:58 +04:00
|
|
|
#include <unistd.h>
|
2012-07-29 06:24:14 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
2011-09-19 18:54:09 +04:00
|
|
|
#include <sys/time.h>
|
2012-07-29 06:24:14 +04:00
|
|
|
#endif
|
|
|
|
|
2012-11-20 08:49:08 +04:00
|
|
|
#include <winpr/crt.h>
|
2013-03-21 05:42:52 +04:00
|
|
|
#include <winpr/synch.h>
|
2015-07-03 14:26:15 +03:00
|
|
|
#include <winpr/string.h>
|
2013-03-21 05:42:52 +04:00
|
|
|
#include <winpr/thread.h>
|
2014-04-26 20:31:24 +04:00
|
|
|
#include <winpr/stream.h>
|
2013-03-21 05:42:52 +04:00
|
|
|
#include <winpr/collections.h>
|
2012-11-20 08:49:08 +04:00
|
|
|
|
2012-10-09 04:33:58 +04:00
|
|
|
#include <freerdp/client/tsmf.h>
|
2011-09-19 18:54:09 +04:00
|
|
|
|
|
|
|
#include "tsmf_constants.h"
|
|
|
|
#include "tsmf_types.h"
|
|
|
|
#include "tsmf_decoder.h"
|
|
|
|
#include "tsmf_audio.h"
|
|
|
|
#include "tsmf_main.h"
|
|
|
|
#include "tsmf_codec.h"
|
|
|
|
#include "tsmf_media.h"
|
|
|
|
|
|
|
|
#define AUDIO_TOLERANCE 10000000LL
|
|
|
|
|
2015-07-08 21:10:21 +03:00
|
|
|
/* 1 second = 10,000,000 100ns units*/
|
|
|
|
#define VIDEO_ADJUST_MAX 10*1000*1000
|
2015-07-08 00:39:29 +03:00
|
|
|
|
|
|
|
#define MAX_ACK_TIME 666667
|
|
|
|
|
|
|
|
#define AUDIO_MIN_BUFFER_LEVEL 3
|
|
|
|
#define AUDIO_MAX_BUFFER_LEVEL 6
|
|
|
|
|
|
|
|
#define VIDEO_MIN_BUFFER_LEVEL 10
|
|
|
|
#define VIDEO_MAX_BUFFER_LEVEL 30
|
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
struct _TSMF_PRESENTATION
|
|
|
|
{
|
2012-10-09 11:01:37 +04:00
|
|
|
BYTE presentation_id[GUID_SIZE];
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
const char* audio_name;
|
|
|
|
const char* audio_device;
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
IWTSVirtualChannelCallback* channel_callback;
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT64 audio_start_time;
|
|
|
|
UINT64 audio_end_time;
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2013-06-19 20:33:46 +04:00
|
|
|
UINT32 volume;
|
|
|
|
UINT32 muted;
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
wArrayList* stream_list;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
int x;
|
|
|
|
int y;
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
|
|
|
|
int nr_rects;
|
2016-09-26 13:12:37 +03:00
|
|
|
void* rects;
|
2011-09-19 18:54:09 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
struct _TSMF_STREAM
|
|
|
|
{
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT32 stream_id;
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_PRESENTATION* presentation;
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
ITSMFDecoder* decoder;
|
2011-09-19 18:54:09 +04:00
|
|
|
|
|
|
|
int major_type;
|
|
|
|
int eos;
|
2015-07-08 00:39:29 +03:00
|
|
|
UINT32 eos_message_id;
|
|
|
|
IWTSVirtualChannelCallback* eos_channel_callback;
|
|
|
|
int delayed_stop;
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT32 width;
|
|
|
|
UINT32 height;
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
ITSMFAudioDevice* audio;
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT32 sample_rate;
|
|
|
|
UINT32 channels;
|
|
|
|
UINT32 bits_per_sample;
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
/* The start time of last played sample */
|
2016-09-26 13:02:33 +03:00
|
|
|
UINT64 last_start_time;
|
2011-09-19 18:54:09 +04:00
|
|
|
/* The end_time of last played sample */
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT64 last_end_time;
|
2011-09-19 18:54:09 +04:00
|
|
|
/* Next sample should not start before this system time. */
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT64 next_start_time;
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
UINT32 minBufferLevel;
|
|
|
|
UINT32 maxBufferLevel;
|
|
|
|
UINT32 currentBufferLevel;
|
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
HANDLE play_thread;
|
|
|
|
HANDLE ack_thread;
|
2013-03-21 05:42:52 +04:00
|
|
|
HANDLE stopEvent;
|
2014-05-23 15:50:52 +04:00
|
|
|
HANDLE ready;
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
wQueue* sample_list;
|
|
|
|
wQueue* sample_ack_list;
|
2015-07-15 10:50:35 +03:00
|
|
|
rdpContext* rdpcontext;
|
2015-07-08 00:39:29 +03:00
|
|
|
|
|
|
|
BOOL seeking;
|
2011-09-19 18:54:09 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
struct _TSMF_SAMPLE
|
|
|
|
{
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT32 sample_id;
|
|
|
|
UINT64 start_time;
|
|
|
|
UINT64 end_time;
|
|
|
|
UINT64 duration;
|
|
|
|
UINT32 extensions;
|
|
|
|
UINT32 data_size;
|
2016-09-26 13:12:37 +03:00
|
|
|
BYTE* data;
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT32 decoded_size;
|
|
|
|
UINT32 pixfmt;
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
BOOL invalidTimestamps;
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream;
|
2016-09-26 13:12:37 +03:00
|
|
|
IWTSVirtualChannelCallback* channel_callback;
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT64 ack_time;
|
2011-09-19 18:54:09 +04:00
|
|
|
};
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
static wArrayList* presentation_list = NULL;
|
2012-06-13 23:45:58 +04:00
|
|
|
static int TERMINATING = 0;
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2018-02-20 14:15:30 +03:00
|
|
|
static void _tsmf_presentation_free(void* obj);
|
|
|
|
static void _tsmf_stream_free(void* obj);
|
2014-05-23 15:50:52 +04:00
|
|
|
|
2012-10-09 11:26:39 +04:00
|
|
|
static UINT64 get_current_time(void)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
|
|
|
struct timeval tp;
|
|
|
|
gettimeofday(&tp, 0);
|
2012-10-09 11:26:39 +04:00
|
|
|
return ((UINT64)tp.tv_sec) * 10000000LL + ((UINT64)tp.tv_usec) * 10LL;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
static TSMF_SAMPLE* tsmf_stream_pop_sample(TSMF_STREAM* stream, int sync)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-04-26 20:31:24 +04:00
|
|
|
UINT32 index;
|
|
|
|
UINT32 count;
|
2016-09-26 13:12:37 +03:00
|
|
|
TSMF_STREAM* s;
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_SAMPLE* sample;
|
2012-10-09 10:38:39 +04:00
|
|
|
BOOL pending = FALSE;
|
2017-01-26 12:44:19 +03:00
|
|
|
TSMF_PRESENTATION* presentation = NULL;
|
2014-11-10 22:02:54 +03:00
|
|
|
|
|
|
|
if (!stream)
|
|
|
|
return NULL;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2017-01-26 12:44:19 +03:00
|
|
|
presentation = stream->presentation;
|
|
|
|
|
2014-05-25 00:58:54 +04:00
|
|
|
if (Queue_Count(stream->sample_list) < 1)
|
2011-09-19 18:54:09 +04:00
|
|
|
return NULL;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (sync)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-05-25 00:58:54 +04:00
|
|
|
if (stream->decoder)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-05-25 00:58:54 +04:00
|
|
|
if (stream->decoder->GetDecodedData)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-05-25 00:58:54 +04:00
|
|
|
if (stream->major_type == TSMF_MAJOR_TYPE_AUDIO)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2012-06-13 23:45:58 +04:00
|
|
|
/* Check if some other stream has earlier sample that needs to be played first */
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Start time is more reliable than end time as some stream types seem to have incorrect
|
|
|
|
* end times from the server
|
|
|
|
*/
|
2015-07-08 00:39:29 +03:00
|
|
|
if (stream->last_start_time > AUDIO_TOLERANCE)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Lock(presentation->stream_list);
|
|
|
|
count = ArrayList_Count(presentation->stream_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
for (index = 0; index < count; index++)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
s = (TSMF_STREAM*) ArrayList_GetItem(presentation->stream_list, index);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Start time is more reliable than end time as some stream types seem to have incorrect
|
|
|
|
* end times from the server
|
|
|
|
*/
|
2015-07-08 00:39:29 +03:00
|
|
|
if (s != stream && !s->eos && s->last_start_time &&
|
2016-09-26 13:12:37 +03:00
|
|
|
s->last_start_time < stream->last_start_time - AUDIO_TOLERANCE)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2015-07-08 00:39:29 +03:00
|
|
|
DEBUG_TSMF("Pending due to audio tolerance");
|
2014-05-23 15:50:52 +04:00
|
|
|
pending = TRUE;
|
|
|
|
break;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Unlock(presentation->stream_list);
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
else
|
|
|
|
{
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Start time is more reliable than end time as some stream types seem to have incorrect
|
|
|
|
* end times from the server
|
|
|
|
*/
|
2015-07-08 00:39:29 +03:00
|
|
|
if (stream->last_start_time > presentation->audio_start_time)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2015-07-08 00:39:29 +03:00
|
|
|
DEBUG_TSMF("Pending due to stream start time > audio start time");
|
2012-10-09 10:31:28 +04:00
|
|
|
pending = TRUE;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
}
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (pending)
|
2011-09-19 18:54:09 +04:00
|
|
|
return NULL;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
sample = (TSMF_SAMPLE*) Queue_Dequeue(stream->sample_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Only update stream last end time if the sample end time is valid and greater than the current stream end time */
|
2016-09-26 13:12:37 +03:00
|
|
|
if (sample && (sample->end_time > stream->last_end_time)
|
|
|
|
&& (!sample->invalidTimestamps))
|
2011-09-19 18:54:09 +04:00
|
|
|
stream->last_end_time = sample->end_time;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Only update stream last start time if the sample start time is valid and greater than the current stream start time */
|
2016-09-26 13:12:37 +03:00
|
|
|
if (sample && (sample->start_time > stream->last_start_time)
|
|
|
|
&& (!sample->invalidTimestamps))
|
2015-07-08 00:39:29 +03:00
|
|
|
stream->last_start_time = sample->start_time;
|
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
return sample;
|
|
|
|
}
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
static void tsmf_sample_free(void* arg)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_SAMPLE* sample = arg;
|
|
|
|
|
|
|
|
if (!sample)
|
|
|
|
return;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-05-11 10:07:39 +03:00
|
|
|
free(sample->data);
|
2012-10-09 07:21:26 +04:00
|
|
|
free(sample);
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
static BOOL tsmf_sample_ack(TSMF_SAMPLE* sample)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
if (!sample)
|
2015-06-29 16:21:53 +03:00
|
|
|
return FALSE;
|
2014-11-10 22:02:54 +03:00
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
return tsmf_playback_ack(sample->channel_callback, sample->sample_id,
|
|
|
|
sample->duration, sample->data_size);
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
static BOOL tsmf_sample_queue_ack(TSMF_SAMPLE* sample)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
if (!sample)
|
2015-06-29 16:21:53 +03:00
|
|
|
return FALSE;
|
2014-11-10 22:02:54 +03:00
|
|
|
|
|
|
|
if (!sample->stream)
|
2015-06-29 16:21:53 +03:00
|
|
|
return FALSE;
|
2014-11-10 22:02:54 +03:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
return Queue_Enqueue(sample->stream->sample_ack_list, sample);
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Returns TRUE if no more samples are currently available
|
|
|
|
* Returns FALSE otherwise
|
|
|
|
*/
|
2014-11-10 22:02:54 +03:00
|
|
|
static BOOL tsmf_stream_process_ack(void* arg, BOOL force)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream = arg;
|
|
|
|
TSMF_SAMPLE* sample;
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT64 ack_time;
|
2014-05-23 15:50:52 +04:00
|
|
|
BOOL rc = FALSE;
|
2014-11-10 22:02:54 +03:00
|
|
|
|
|
|
|
if (!stream)
|
2015-07-08 00:39:29 +03:00
|
|
|
return TRUE;
|
2014-11-10 22:02:54 +03:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
Queue_Lock(stream->sample_ack_list);
|
2014-11-10 22:02:54 +03:00
|
|
|
sample = (TSMF_SAMPLE*) Queue_Peek(stream->sample_ack_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (!sample)
|
2015-07-08 00:39:29 +03:00
|
|
|
{
|
|
|
|
rc = TRUE;
|
2014-05-23 15:50:52 +04:00
|
|
|
goto finally;
|
2015-07-08 00:39:29 +03:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (!force)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Do some min/max ack limiting if we have access to Buffer level information */
|
2016-10-19 12:10:35 +03:00
|
|
|
if (stream->decoder && stream->decoder->BufferLevel)
|
2015-07-08 00:39:29 +03:00
|
|
|
{
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Try to keep buffer level below max by withholding acks */
|
2015-07-08 00:39:29 +03:00
|
|
|
if (stream->currentBufferLevel > stream->maxBufferLevel)
|
|
|
|
goto finally;
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Try to keep buffer level above min by pushing acks through quickly */
|
2015-07-08 00:39:29 +03:00
|
|
|
else if (stream->currentBufferLevel < stream->minBufferLevel)
|
|
|
|
goto dequeue;
|
|
|
|
}
|
|
|
|
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Time based acks only */
|
2014-05-23 15:50:52 +04:00
|
|
|
ack_time = get_current_time();
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (sample->ack_time > ack_time)
|
2014-05-23 15:50:52 +04:00
|
|
|
goto finally;
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
dequeue:
|
2014-05-23 15:50:52 +04:00
|
|
|
sample = Queue_Dequeue(stream->sample_ack_list);
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
if (sample)
|
|
|
|
{
|
2015-09-03 20:58:16 +03:00
|
|
|
tsmf_sample_ack(sample);
|
2015-06-29 16:21:53 +03:00
|
|
|
tsmf_sample_free(sample);
|
|
|
|
}
|
2014-11-10 22:02:54 +03:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
finally:
|
|
|
|
Queue_Unlock(stream->sample_ack_list);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
TSMF_PRESENTATION* tsmf_presentation_new(const BYTE* guid,
|
|
|
|
IWTSVirtualChannelCallback* pChannelCallback)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_PRESENTATION* presentation;
|
|
|
|
|
|
|
|
if (!guid || !pChannelCallback)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
presentation = (TSMF_PRESENTATION*) calloc(1, sizeof(TSMF_PRESENTATION));
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2014-05-25 00:58:54 +04:00
|
|
|
if (!presentation)
|
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "calloc failed");
|
2014-05-25 00:58:54 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-04-26 20:31:24 +04:00
|
|
|
CopyMemory(presentation->presentation_id, guid, GUID_SIZE);
|
2011-09-19 18:54:09 +04:00
|
|
|
presentation->channel_callback = pChannelCallback;
|
2013-06-19 20:33:46 +04:00
|
|
|
presentation->volume = 5000; /* 50% */
|
2015-07-08 00:39:29 +03:00
|
|
|
presentation->muted = 0;
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-05-28 11:49:38 +03:00
|
|
|
if (!(presentation->stream_list = ArrayList_New(TRUE)))
|
2015-05-18 12:28:00 +03:00
|
|
|
goto error_stream_list;
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
ArrayList_Object(presentation->stream_list)->fnObjectFree =
|
2018-02-20 14:15:30 +03:00
|
|
|
_tsmf_stream_free;
|
2014-11-10 22:02:54 +03:00
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
if (ArrayList_Add(presentation_list, presentation) < 0)
|
|
|
|
goto error_add;
|
2014-11-10 22:02:54 +03:00
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
return presentation;
|
2015-05-18 12:28:00 +03:00
|
|
|
error_add:
|
|
|
|
ArrayList_Free(presentation->stream_list);
|
|
|
|
error_stream_list:
|
|
|
|
free(presentation);
|
|
|
|
return NULL;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
static char* guid_to_string(const BYTE* guid, char* str, size_t len)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2019-02-07 16:32:55 +03:00
|
|
|
size_t i;
|
2014-11-10 22:02:54 +03:00
|
|
|
|
|
|
|
if (!guid || !str)
|
|
|
|
return NULL;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2019-02-07 16:32:55 +03:00
|
|
|
for (i = 0; i < GUID_SIZE && (len > 2 * i); i++)
|
2016-12-14 00:47:08 +03:00
|
|
|
sprintf_s(str + (2 * i), len - 2 * i, "%02"PRIX8"", guid[i]);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
TSMF_PRESENTATION* tsmf_presentation_find_by_id(const BYTE* guid)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
2014-04-26 20:31:24 +04:00
|
|
|
UINT32 index;
|
|
|
|
UINT32 count;
|
|
|
|
BOOL found = FALSE;
|
2014-11-10 22:02:54 +03:00
|
|
|
char guid_str[GUID_SIZE * 2 + 1];
|
|
|
|
TSMF_PRESENTATION* presentation;
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Lock(presentation_list);
|
|
|
|
count = ArrayList_Count(presentation_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
for (index = 0; index < count; index++)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
presentation = (TSMF_PRESENTATION*) ArrayList_GetItem(presentation_list, index);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (memcmp(presentation->presentation_id, guid, GUID_SIZE) == 0)
|
2014-04-26 20:31:24 +04:00
|
|
|
{
|
|
|
|
found = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Unlock(presentation_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (!found)
|
2016-09-26 13:12:37 +03:00
|
|
|
WLog_WARN(TAG, "presentation id %s not found", guid_to_string(guid, guid_str,
|
|
|
|
sizeof(guid_str)));
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-04-26 20:31:24 +04:00
|
|
|
return (found) ? presentation : NULL;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
static BOOL tsmf_sample_playback_video(TSMF_SAMPLE* sample)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT64 t;
|
2014-11-11 00:33:34 +03:00
|
|
|
TSMF_VIDEO_FRAME_EVENT event;
|
2014-11-08 02:33:45 +03:00
|
|
|
TSMF_STREAM* stream = sample->stream;
|
|
|
|
TSMF_PRESENTATION* presentation = stream->presentation;
|
2016-09-26 13:12:37 +03:00
|
|
|
TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*)
|
|
|
|
sample->channel_callback;
|
2014-11-08 02:33:45 +03:00
|
|
|
TsmfClientContext* tsmf = (TsmfClientContext*) callback->plugin->pInterface;
|
2016-12-14 00:47:08 +03:00
|
|
|
DEBUG_TSMF("MessageId %"PRIu32" EndTime %"PRIu64" data_size %"PRIu32" consumed.",
|
|
|
|
sample->sample_id, sample->end_time, sample->data_size);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (sample->data)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
|
|
|
t = get_current_time();
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Start time is more reliable than end time as some stream types seem to have incorrect
|
|
|
|
* end times from the server
|
|
|
|
*/
|
2014-05-25 00:58:54 +04:00
|
|
|
if (stream->next_start_time > t &&
|
2016-09-26 13:12:37 +03:00
|
|
|
((sample->start_time >= presentation->audio_start_time) ||
|
|
|
|
((sample->start_time < stream->last_start_time)
|
|
|
|
&& (!sample->invalidTimestamps))))
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2012-12-14 09:58:48 +04:00
|
|
|
USleep((stream->next_start_time - t) / 10);
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
stream->next_start_time = t + sample->duration - 50000;
|
2014-11-11 00:33:34 +03:00
|
|
|
ZeroMemory(&event, sizeof(TSMF_VIDEO_FRAME_EVENT));
|
|
|
|
event.frameData = sample->data;
|
|
|
|
event.frameSize = sample->decoded_size;
|
|
|
|
event.framePixFmt = sample->pixfmt;
|
|
|
|
event.frameWidth = sample->stream->width;
|
|
|
|
event.frameHeight = sample->stream->height;
|
2015-04-15 11:29:27 +03:00
|
|
|
event.x = presentation->x;
|
|
|
|
event.y = presentation->y;
|
|
|
|
event.width = presentation->width;
|
|
|
|
event.height = presentation->height;
|
|
|
|
|
|
|
|
if (presentation->nr_rects > 0)
|
|
|
|
{
|
|
|
|
event.numVisibleRects = presentation->nr_rects;
|
2017-05-30 11:46:43 +03:00
|
|
|
event.visibleRects = (RECTANGLE_16*) calloc(event.numVisibleRects, sizeof(RECTANGLE_16));
|
2017-01-17 12:03:05 +03:00
|
|
|
|
2015-04-15 17:42:54 +03:00
|
|
|
if (!event.visibleRects)
|
2015-04-15 14:08:51 +03:00
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "can't allocate memory for copy rectangles");
|
2017-01-18 14:39:40 +03:00
|
|
|
return FALSE;
|
2015-04-15 14:08:51 +03:00
|
|
|
}
|
2017-01-17 12:03:05 +03:00
|
|
|
|
2015-04-15 17:42:54 +03:00
|
|
|
memcpy(event.visibleRects, presentation->rects, presentation->nr_rects * sizeof(RDP_RECT));
|
2015-05-14 16:57:37 +03:00
|
|
|
presentation->nr_rects = 0;
|
2015-04-15 11:29:27 +03:00
|
|
|
}
|
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
#if 0
|
|
|
|
/* Dump a .ppm image for every 30 frames. Assuming the frame is in YUV format, we
|
|
|
|
extract the Y values to create a grayscale image. */
|
|
|
|
static int frame_id = 0;
|
|
|
|
char buf[100];
|
2016-09-26 13:12:37 +03:00
|
|
|
FILE* fp;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if ((frame_id % 30) == 0)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2015-07-03 14:26:15 +03:00
|
|
|
sprintf_s(buf, sizeof(buf), "/tmp/FreeRDP_Frame_%d.ppm", frame_id);
|
2011-09-19 18:54:09 +04:00
|
|
|
fp = fopen(buf, "wb");
|
|
|
|
fwrite("P5\n", 1, 3, fp);
|
2016-12-14 00:47:08 +03:00
|
|
|
sprintf_s(buf, sizeof(buf), "%"PRIu32" %"PRIu32"\n", sample->stream->width,
|
2016-09-26 13:12:37 +03:00
|
|
|
sample->stream->height);
|
2011-09-19 18:54:09 +04:00
|
|
|
fwrite(buf, 1, strlen(buf), fp);
|
|
|
|
fwrite("255\n", 1, 4, fp);
|
|
|
|
fwrite(sample->data, 1, sample->stream->width * sample->stream->height, fp);
|
|
|
|
fflush(fp);
|
|
|
|
fclose(fp);
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
frame_id++;
|
|
|
|
#endif
|
2015-03-26 13:38:09 +03:00
|
|
|
/* The frame data ownership is passed to the event object, and is freed after the event is processed. */
|
|
|
|
sample->data = NULL;
|
|
|
|
sample->decoded_size = 0;
|
|
|
|
|
|
|
|
if (tsmf->FrameEvent)
|
|
|
|
tsmf->FrameEvent(tsmf, &event);
|
|
|
|
|
|
|
|
free(event.frameData);
|
2015-04-15 11:29:27 +03:00
|
|
|
|
2017-01-17 12:03:05 +03:00
|
|
|
if (event.visibleRects != NULL)
|
2015-05-14 16:57:37 +03:00
|
|
|
free(event.visibleRects);
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
return TRUE;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
static BOOL tsmf_sample_playback_audio(TSMF_SAMPLE* sample)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT64 latency = 0;
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream = sample->stream;
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL ret;
|
2016-12-14 00:47:08 +03:00
|
|
|
DEBUG_TSMF("MessageId %"PRIu32" EndTime %"PRIu64" consumed.",
|
|
|
|
sample->sample_id, sample->end_time);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-03-17 05:10:58 +03:00
|
|
|
if (stream->audio && sample->data)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
ret = sample->stream->audio->Play(sample->stream->audio, sample->data,
|
|
|
|
sample->decoded_size);
|
2018-02-20 14:15:30 +03:00
|
|
|
free(sample->data);
|
2011-09-19 18:54:09 +04:00
|
|
|
sample->data = NULL;
|
|
|
|
sample->decoded_size = 0;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-03-17 05:10:58 +03:00
|
|
|
if (stream->audio->GetLatency)
|
2011-09-19 18:54:09 +04:00
|
|
|
latency = stream->audio->GetLatency(stream->audio);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-06-29 16:21:53 +03:00
|
|
|
ret = TRUE;
|
2011-09-19 18:54:09 +04:00
|
|
|
latency = 0;
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
sample->ack_time = latency + get_current_time();
|
2015-07-08 00:39:29 +03:00
|
|
|
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Only update stream times if the sample timestamps are valid */
|
2015-07-08 00:39:29 +03:00
|
|
|
if (!sample->invalidTimestamps)
|
|
|
|
{
|
|
|
|
stream->last_start_time = sample->start_time + latency;
|
|
|
|
stream->last_end_time = sample->end_time + latency;
|
|
|
|
stream->presentation->audio_start_time = sample->start_time + latency;
|
|
|
|
stream->presentation->audio_end_time = sample->end_time + latency;
|
|
|
|
}
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
return ret;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
static BOOL tsmf_sample_playback(TSMF_SAMPLE* sample)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2012-10-09 10:38:39 +04:00
|
|
|
BOOL ret = FALSE;
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT32 width;
|
|
|
|
UINT32 height;
|
|
|
|
UINT32 pixfmt = 0;
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream = sample->stream;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->decoder)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-25 00:58:54 +04:00
|
|
|
if (stream->decoder->DecodeEx)
|
2014-11-10 22:02:54 +03:00
|
|
|
{
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Try to "sync" video buffers to audio buffers by looking at the running time for each stream
|
|
|
|
* The difference between the two running times causes an offset between audio and video actual
|
|
|
|
* render times. So, we try to adjust timestamps on the video buffer to match those on the audio buffer.
|
|
|
|
*/
|
2015-07-08 00:39:29 +03:00
|
|
|
if (stream->major_type == TSMF_MAJOR_TYPE_VIDEO)
|
2016-09-26 13:02:33 +03:00
|
|
|
{
|
2015-07-08 00:39:29 +03:00
|
|
|
TSMF_STREAM* temp_stream = NULL;
|
|
|
|
TSMF_PRESENTATION* presentation = stream->presentation;
|
|
|
|
ArrayList_Lock(presentation->stream_list);
|
|
|
|
int count = ArrayList_Count(presentation->stream_list);
|
|
|
|
int index = 0;
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
for (index = 0; index < count; index++)
|
|
|
|
{
|
2016-02-03 13:30:15 +03:00
|
|
|
UINT64 time_diff;
|
2016-09-26 13:12:37 +03:00
|
|
|
temp_stream = (TSMF_STREAM*) ArrayList_GetItem(presentation->stream_list,
|
|
|
|
index);
|
2016-02-03 13:30:15 +03:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
if (temp_stream->major_type == TSMF_MAJOR_TYPE_AUDIO)
|
|
|
|
{
|
|
|
|
UINT64 video_time = (UINT64) stream->decoder->GetRunningTime(stream->decoder);
|
2016-09-26 13:12:37 +03:00
|
|
|
UINT64 audio_time = (UINT64) temp_stream->decoder->GetRunningTime(
|
|
|
|
temp_stream->decoder);
|
2015-09-18 00:19:55 +03:00
|
|
|
UINT64 max_adjust = VIDEO_ADJUST_MAX;
|
|
|
|
|
|
|
|
if (video_time < audio_time)
|
|
|
|
max_adjust = -VIDEO_ADJUST_MAX;
|
|
|
|
|
2016-02-03 13:30:15 +03:00
|
|
|
if (video_time > audio_time)
|
|
|
|
time_diff = video_time - audio_time;
|
|
|
|
else
|
|
|
|
time_diff = audio_time - video_time;
|
|
|
|
|
|
|
|
time_diff = time_diff < VIDEO_ADJUST_MAX ? time_diff : max_adjust;
|
|
|
|
sample->start_time += time_diff;
|
|
|
|
sample->end_time += time_diff;
|
2015-07-08 00:39:29 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
ArrayList_Unlock(presentation->stream_list);
|
|
|
|
}
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
ret = stream->decoder->DecodeEx(stream->decoder, sample->data,
|
|
|
|
sample->data_size, sample->extensions,
|
|
|
|
sample->start_time, sample->end_time, sample->duration);
|
2014-11-10 22:02:54 +03:00
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
else
|
2014-11-10 22:02:54 +03:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
ret = stream->decoder->Decode(stream->decoder, sample->data, sample->data_size,
|
|
|
|
sample->extensions);
|
2014-11-10 22:02:54 +03:00
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (!ret)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2015-09-03 20:58:16 +03:00
|
|
|
WLog_ERR(TAG, "decode error, queue ack anyways");
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
if (!tsmf_sample_queue_ack(sample))
|
2015-09-03 20:58:16 +03:00
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "error queuing sample for ack");
|
2015-06-29 16:21:53 +03:00
|
|
|
return FALSE;
|
2015-09-03 20:58:16 +03:00
|
|
|
}
|
2015-06-29 16:21:53 +03:00
|
|
|
|
|
|
|
return TRUE;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2012-10-09 07:21:26 +04:00
|
|
|
free(sample->data);
|
2011-09-19 18:54:09 +04:00
|
|
|
sample->data = NULL;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->major_type == TSMF_MAJOR_TYPE_VIDEO)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-05-25 00:58:54 +04:00
|
|
|
if (stream->decoder->GetDecodedFormat)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
|
|
|
pixfmt = stream->decoder->GetDecodedFormat(stream->decoder);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
if (pixfmt == ((UINT32) - 1))
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2015-06-29 16:21:53 +03:00
|
|
|
WLog_ERR(TAG, "unable to decode video format");
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
if (!tsmf_sample_queue_ack(sample))
|
|
|
|
{
|
2015-09-03 20:58:16 +03:00
|
|
|
WLog_ERR(TAG, "error queuing sample for ack");
|
2015-06-29 16:21:53 +03:00
|
|
|
}
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
return FALSE;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
sample->pixfmt = pixfmt;
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->decoder->GetDecodedDimension)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2012-06-13 23:45:58 +04:00
|
|
|
ret = stream->decoder->GetDecodedDimension(stream->decoder, &width, &height);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (ret && (width != stream->width || height != stream->height))
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2016-12-14 00:47:08 +03:00
|
|
|
DEBUG_TSMF("video dimension changed to %"PRIu32" x %"PRIu32"", width, height);
|
2012-06-13 23:45:58 +04:00
|
|
|
stream->width = width;
|
|
|
|
stream->height = height;
|
|
|
|
}
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->decoder->GetDecodedData)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
sample->data = stream->decoder->GetDecodedData(stream->decoder,
|
|
|
|
&sample->decoded_size);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
switch (sample->stream->major_type)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
|
|
|
case TSMF_MAJOR_TYPE_VIDEO:
|
2015-06-29 16:21:53 +03:00
|
|
|
ret = tsmf_sample_playback_video(sample) &&
|
2016-09-26 13:12:37 +03:00
|
|
|
tsmf_sample_queue_ack(sample);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
case TSMF_MAJOR_TYPE_AUDIO:
|
2015-06-29 16:21:53 +03:00
|
|
|
ret = tsmf_sample_playback_audio(sample) &&
|
2016-09-26 13:12:37 +03:00
|
|
|
tsmf_sample_queue_ack(sample);
|
2012-06-13 23:45:58 +04:00
|
|
|
break;
|
|
|
|
}
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
else
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream = sample->stream;
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT64 ack_anticipation_time = get_current_time();
|
2014-05-23 15:50:52 +04:00
|
|
|
BOOL buffer_filled = TRUE;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Classify the buffer as filled once it reaches minimum level */
|
2015-07-08 00:39:29 +03:00
|
|
|
if (stream->decoder->BufferLevel)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2015-07-08 00:39:29 +03:00
|
|
|
if (stream->currentBufferLevel < stream->minBufferLevel)
|
|
|
|
buffer_filled = FALSE;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2019-01-04 18:56:57 +03:00
|
|
|
ack_anticipation_time += (sample->duration / 2 < MAX_ACK_TIME) ?
|
|
|
|
sample->duration / 2 : MAX_ACK_TIME;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
switch (sample->stream->major_type)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
|
|
|
case TSMF_MAJOR_TYPE_VIDEO:
|
2016-09-26 13:12:37 +03:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
case TSMF_MAJOR_TYPE_AUDIO:
|
2016-09-26 13:12:37 +03:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
sample->ack_time = ack_anticipation_time;
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-09-03 20:58:16 +03:00
|
|
|
if (!tsmf_sample_queue_ack(sample))
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "error queuing sample for ack");
|
|
|
|
ret = FALSE;
|
|
|
|
}
|
2014-05-23 15:50:52 +04:00
|
|
|
}
|
2015-06-29 16:21:53 +03:00
|
|
|
|
|
|
|
return ret;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2018-03-07 14:03:10 +03:00
|
|
|
static DWORD WINAPI tsmf_stream_ack_func(LPVOID arg)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-05-23 15:50:52 +04:00
|
|
|
HANDLE hdl[2];
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream = (TSMF_STREAM*) arg;
|
2015-08-27 15:25:09 +03:00
|
|
|
UINT error = CHANNEL_RC_OK;
|
2016-12-14 00:47:08 +03:00
|
|
|
DEBUG_TSMF("in %"PRIu32"", stream->stream_id);
|
2014-05-23 15:50:52 +04:00
|
|
|
hdl[0] = stream->stopEvent;
|
|
|
|
hdl[1] = Queue_Event(stream->sample_ack_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
while (1)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
2015-07-08 00:39:29 +03:00
|
|
|
DWORD ev = WaitForMultipleObjects(2, hdl, FALSE, 1000);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
if (ev == WAIT_FAILED)
|
|
|
|
{
|
|
|
|
error = GetLastError();
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"!", error);
|
2014-05-23 15:50:52 +04:00
|
|
|
break;
|
2015-07-08 00:39:29 +03:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
if (stream->decoder)
|
|
|
|
if (stream->decoder->BufferLevel)
|
|
|
|
stream->currentBufferLevel = stream->decoder->BufferLevel(stream->decoder);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
if (stream->eos)
|
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
while ((stream->currentBufferLevel > 0)
|
2017-02-20 11:54:51 +03:00
|
|
|
&& !(tsmf_stream_process_ack(stream, TRUE)))
|
2015-07-08 00:39:29 +03:00
|
|
|
{
|
|
|
|
DEBUG_TSMF("END OF STREAM PROCESSING!");
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2016-10-19 12:10:35 +03:00
|
|
|
if (stream->decoder && stream->decoder->BufferLevel)
|
2015-07-08 00:39:29 +03:00
|
|
|
stream->currentBufferLevel = stream->decoder->BufferLevel(stream->decoder);
|
|
|
|
else
|
|
|
|
stream->currentBufferLevel = 1;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
USleep(1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
tsmf_send_eos_response(stream->eos_channel_callback, stream->eos_message_id);
|
|
|
|
stream->eos = 0;
|
|
|
|
|
|
|
|
if (stream->delayed_stop)
|
|
|
|
{
|
|
|
|
DEBUG_TSMF("Finishing delayed stream stop, now that eos has processed.");
|
|
|
|
tsmf_stream_flush(stream);
|
|
|
|
|
2016-10-19 12:10:35 +03:00
|
|
|
if (stream->decoder && stream->decoder->Control)
|
2015-07-08 00:39:29 +03:00
|
|
|
stream->decoder->Control(stream->decoder, Control_Stop, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Stream stopped force all of the acks to happen */
|
2015-07-08 00:39:29 +03:00
|
|
|
if (ev == WAIT_OBJECT_0)
|
2015-07-15 10:50:35 +03:00
|
|
|
{
|
2015-07-08 00:39:29 +03:00
|
|
|
DEBUG_TSMF("ack: Stream stopped!");
|
2016-09-26 13:12:37 +03:00
|
|
|
|
|
|
|
while (1)
|
2015-07-08 00:39:29 +03:00
|
|
|
{
|
|
|
|
if (tsmf_stream_process_ack(stream, TRUE))
|
|
|
|
break;
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
USleep(1000);
|
|
|
|
}
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
break;
|
2015-07-15 10:50:35 +03:00
|
|
|
}
|
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
if (tsmf_stream_process_ack(stream, FALSE))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (stream->currentBufferLevel > stream->minBufferLevel)
|
|
|
|
USleep(1000);
|
2014-05-23 15:50:52 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-15 10:50:35 +03:00
|
|
|
if (error && stream->rdpcontext)
|
2016-09-26 13:12:37 +03:00
|
|
|
setChannelError(stream->rdpcontext, error,
|
|
|
|
"tsmf_stream_ack_func reported an error");
|
2015-07-15 10:50:35 +03:00
|
|
|
|
2016-12-14 00:47:08 +03:00
|
|
|
DEBUG_TSMF("out %"PRIu32"", stream->stream_id);
|
2018-03-07 14:03:10 +03:00
|
|
|
ExitThread(error);
|
|
|
|
return error;
|
2014-05-23 15:50:52 +04:00
|
|
|
}
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2018-03-07 14:03:10 +03:00
|
|
|
static DWORD WINAPI tsmf_stream_playback_func(LPVOID arg)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
2014-05-24 17:55:55 +04:00
|
|
|
HANDLE hdl[2];
|
2015-07-08 00:39:29 +03:00
|
|
|
TSMF_SAMPLE* sample = NULL;
|
2016-09-26 13:12:37 +03:00
|
|
|
TSMF_STREAM* stream = (TSMF_STREAM*) arg;
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_PRESENTATION* presentation = stream->presentation;
|
2015-08-27 15:25:09 +03:00
|
|
|
UINT error = CHANNEL_RC_OK;
|
2015-09-03 20:58:16 +03:00
|
|
|
DWORD status;
|
2016-12-14 00:47:08 +03:00
|
|
|
DEBUG_TSMF("in %"PRIu32"", stream->stream_id);
|
2016-09-26 13:02:33 +03:00
|
|
|
|
2014-05-25 00:58:54 +04:00
|
|
|
if (stream->major_type == TSMF_MAJOR_TYPE_AUDIO &&
|
2016-09-26 13:12:37 +03:00
|
|
|
stream->sample_rate && stream->channels && stream->bits_per_sample)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-05-25 00:58:54 +04:00
|
|
|
if (stream->decoder)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-05-25 00:58:54 +04:00
|
|
|
if (stream->decoder->GetDecodedData)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
|
|
|
stream->audio = tsmf_load_audio_device(
|
2016-09-26 13:12:37 +03:00
|
|
|
presentation->audio_name
|
|
|
|
&& presentation->audio_name[0] ? presentation->audio_name : NULL,
|
|
|
|
presentation->audio_device
|
|
|
|
&& presentation->audio_device[0] ? presentation->audio_device : NULL);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->audio)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
stream->audio->SetFormat(stream->audio, stream->sample_rate, stream->channels,
|
|
|
|
stream->bits_per_sample);
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
}
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
}
|
2014-05-24 17:55:55 +04:00
|
|
|
|
|
|
|
hdl[0] = stream->stopEvent;
|
|
|
|
hdl[1] = Queue_Event(stream->sample_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-30 16:49:21 +03:00
|
|
|
while (1)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2015-07-08 00:39:29 +03:00
|
|
|
status = WaitForMultipleObjects(2, hdl, FALSE, 1000);
|
2015-07-30 16:49:21 +03:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
if (status == WAIT_FAILED)
|
|
|
|
{
|
|
|
|
error = GetLastError();
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"!", error);
|
2015-07-08 00:39:29 +03:00
|
|
|
break;
|
|
|
|
}
|
2015-07-30 16:49:21 +03:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
status = WaitForSingleObject(stream->stopEvent, 0);
|
2015-07-30 16:49:21 +03:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
if (status == WAIT_FAILED)
|
|
|
|
{
|
|
|
|
error = GetLastError();
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
|
2015-07-08 00:39:29 +03:00
|
|
|
break;
|
|
|
|
}
|
2015-07-30 16:49:21 +03:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
if (status == WAIT_OBJECT_0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (stream->decoder)
|
|
|
|
if (stream->decoder->BufferLevel)
|
|
|
|
stream->currentBufferLevel = stream->decoder->BufferLevel(stream->decoder);
|
|
|
|
|
|
|
|
sample = tsmf_stream_pop_sample(stream, 0);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
if (sample && !tsmf_sample_playback(sample))
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "error playing sample");
|
2015-07-15 10:50:35 +03:00
|
|
|
error = ERROR_INTERNAL_ERROR;
|
2015-06-29 16:21:53 +03:00
|
|
|
break;
|
|
|
|
}
|
2015-07-08 00:39:29 +03:00
|
|
|
|
|
|
|
if (stream->currentBufferLevel > stream->minBufferLevel)
|
|
|
|
USleep(1000);
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->audio)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
|
|
|
stream->audio->Free(stream->audio);
|
|
|
|
stream->audio = NULL;
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-15 10:50:35 +03:00
|
|
|
if (error && stream->rdpcontext)
|
2016-09-26 13:12:37 +03:00
|
|
|
setChannelError(stream->rdpcontext, error,
|
|
|
|
"tsmf_stream_playback_func reported an error");
|
2015-07-15 10:50:35 +03:00
|
|
|
|
2016-12-14 00:47:08 +03:00
|
|
|
DEBUG_TSMF("out %"PRIu32"", stream->stream_id);
|
2018-03-07 14:03:10 +03:00
|
|
|
ExitThread(error);
|
|
|
|
return error;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
static BOOL tsmf_stream_start(TSMF_STREAM* stream)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
if (!stream || !stream->presentation || !stream->decoder
|
|
|
|
|| !stream->decoder->Control)
|
2015-06-29 16:21:53 +03:00
|
|
|
return TRUE;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
stream->eos = 0;
|
|
|
|
return stream->decoder->Control(stream->decoder, Control_Restart, NULL);
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
static BOOL tsmf_stream_stop(TSMF_STREAM* stream)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2015-06-29 16:21:53 +03:00
|
|
|
if (!stream || !stream->decoder || !stream->decoder->Control)
|
|
|
|
return TRUE;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 21:10:21 +03:00
|
|
|
/* If stopping after eos - we delay until the eos has been processed
|
|
|
|
* this allows us to process any buffers that have been acked even though
|
|
|
|
* they have not actually been completely processes by the decoder
|
|
|
|
*/
|
2015-07-08 00:39:29 +03:00
|
|
|
if (stream->eos)
|
|
|
|
{
|
|
|
|
DEBUG_TSMF("Setting up a delayed stop for once the eos has been processed.");
|
|
|
|
stream->delayed_stop = 1;
|
|
|
|
return TRUE;
|
|
|
|
}
|
2015-07-08 21:10:21 +03:00
|
|
|
/* Otherwise force stop immediately */
|
2015-07-08 00:39:29 +03:00
|
|
|
else
|
|
|
|
{
|
|
|
|
DEBUG_TSMF("Stop with no pending eos response, so do it immediately.");
|
|
|
|
tsmf_stream_flush(stream);
|
|
|
|
return stream->decoder->Control(stream->decoder, Control_Stop, NULL);
|
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
static BOOL tsmf_stream_pause(TSMF_STREAM* stream)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2015-06-29 16:21:53 +03:00
|
|
|
if (!stream || !stream->decoder || !stream->decoder->Control)
|
|
|
|
return TRUE;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
return stream->decoder->Control(stream->decoder, Control_Pause, NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
static BOOL tsmf_stream_restart(TSMF_STREAM* stream)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2015-06-29 16:21:53 +03:00
|
|
|
if (!stream || !stream->decoder || !stream->decoder->Control)
|
|
|
|
return TRUE;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
stream->eos = 0;
|
|
|
|
return stream->decoder->Control(stream->decoder, Control_Restart, NULL);
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
static BOOL tsmf_stream_change_volume(TSMF_STREAM* stream, UINT32 newVolume,
|
|
|
|
UINT32 muted)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-05-25 00:58:54 +04:00
|
|
|
if (!stream || !stream->decoder)
|
2015-06-29 16:21:53 +03:00
|
|
|
return TRUE;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->decoder != NULL && stream->decoder->ChangeVolume)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2015-06-29 16:21:53 +03:00
|
|
|
return stream->decoder->ChangeVolume(stream->decoder, newVolume, muted);
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
else if (stream->audio != NULL && stream->audio->ChangeVolume)
|
|
|
|
{
|
2015-06-29 16:21:53 +03:00
|
|
|
return stream->audio->ChangeVolume(stream->audio, newVolume, muted);
|
2014-05-25 00:58:54 +04:00
|
|
|
}
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
return TRUE;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
BOOL tsmf_presentation_volume_changed(TSMF_PRESENTATION* presentation,
|
|
|
|
UINT32 newVolume, UINT32 muted)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-04-26 20:31:24 +04:00
|
|
|
UINT32 index;
|
|
|
|
UINT32 count;
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream;
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL ret = TRUE;
|
2013-06-19 20:33:46 +04:00
|
|
|
presentation->volume = newVolume;
|
|
|
|
presentation->muted = muted;
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Lock(presentation->stream_list);
|
2015-06-29 16:21:53 +03:00
|
|
|
count = ArrayList_Count(presentation->stream_list);
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2014-05-25 00:58:54 +04:00
|
|
|
for (index = 0; index < count; index++)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
stream = (TSMF_STREAM*) ArrayList_GetItem(presentation->stream_list, index);
|
2015-06-29 16:21:53 +03:00
|
|
|
ret &= tsmf_stream_change_volume(stream, newVolume, muted);
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Unlock(presentation->stream_list);
|
2015-06-29 16:21:53 +03:00
|
|
|
return ret;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL tsmf_presentation_paused(TSMF_PRESENTATION* presentation)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-04-26 20:31:24 +04:00
|
|
|
UINT32 index;
|
|
|
|
UINT32 count;
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream;
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL ret = TRUE;
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Lock(presentation->stream_list);
|
|
|
|
count = ArrayList_Count(presentation->stream_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
for (index = 0; index < count; index++)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
stream = (TSMF_STREAM*) ArrayList_GetItem(presentation->stream_list, index);
|
2015-06-29 16:21:53 +03:00
|
|
|
ret &= tsmf_stream_pause(stream);
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Unlock(presentation->stream_list);
|
2015-06-29 16:21:53 +03:00
|
|
|
return ret;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL tsmf_presentation_restarted(TSMF_PRESENTATION* presentation)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2014-04-26 20:31:24 +04:00
|
|
|
UINT32 index;
|
|
|
|
UINT32 count;
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream;
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL ret = TRUE;
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Lock(presentation->stream_list);
|
|
|
|
count = ArrayList_Count(presentation->stream_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
for (index = 0; index < count; index++)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
stream = (TSMF_STREAM*) ArrayList_GetItem(presentation->stream_list, index);
|
2015-06-29 16:21:53 +03:00
|
|
|
ret &= tsmf_stream_restart(stream);
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Unlock(presentation->stream_list);
|
2015-06-29 16:21:53 +03:00
|
|
|
return ret;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL tsmf_presentation_start(TSMF_PRESENTATION* presentation)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-04-26 20:31:24 +04:00
|
|
|
UINT32 index;
|
|
|
|
UINT32 count;
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream;
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL ret = TRUE;
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Lock(presentation->stream_list);
|
|
|
|
count = ArrayList_Count(presentation->stream_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
for (index = 0; index < count; index++)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
stream = (TSMF_STREAM*) ArrayList_GetItem(presentation->stream_list, index);
|
2015-06-29 16:21:53 +03:00
|
|
|
ret &= tsmf_stream_start(stream);
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Unlock(presentation->stream_list);
|
2015-06-29 16:21:53 +03:00
|
|
|
return ret;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2015-08-27 15:25:09 +03:00
|
|
|
/**
|
|
|
|
* Function description
|
|
|
|
*
|
|
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
|
|
*/
|
|
|
|
UINT tsmf_presentation_sync(TSMF_PRESENTATION* presentation)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-04-26 20:31:24 +04:00
|
|
|
UINT32 index;
|
|
|
|
UINT32 count;
|
2015-09-03 20:58:16 +03:00
|
|
|
UINT error;
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Lock(presentation->stream_list);
|
|
|
|
count = ArrayList_Count(presentation->stream_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
for (index = 0; index < count; index++)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
TSMF_STREAM* stream = (TSMF_STREAM*) ArrayList_GetItem(
|
|
|
|
presentation->stream_list, index);
|
|
|
|
|
2015-07-30 16:49:21 +03:00
|
|
|
if (WaitForSingleObject(stream->ready, 500) == WAIT_FAILED)
|
2015-09-03 20:58:16 +03:00
|
|
|
{
|
|
|
|
error = GetLastError();
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
|
2015-09-03 20:58:16 +03:00
|
|
|
return error;
|
|
|
|
}
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Unlock(presentation->stream_list);
|
2015-09-03 20:58:16 +03:00
|
|
|
return CHANNEL_RC_OK;
|
2014-05-23 15:50:52 +04:00
|
|
|
}
|
2014-04-26 20:31:24 +04:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL tsmf_presentation_stop(TSMF_PRESENTATION* presentation)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
|
|
|
UINT32 index;
|
|
|
|
UINT32 count;
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream;
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL ret = TRUE;
|
2014-05-23 15:50:52 +04:00
|
|
|
ArrayList_Lock(presentation->stream_list);
|
|
|
|
count = ArrayList_Count(presentation->stream_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
for (index = 0; index < count; index++)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
stream = (TSMF_STREAM*) ArrayList_GetItem(presentation->stream_list, index);
|
2015-06-29 16:21:53 +03:00
|
|
|
ret &= tsmf_stream_stop(stream);
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
ArrayList_Unlock(presentation->stream_list);
|
2015-07-08 00:39:29 +03:00
|
|
|
presentation->audio_start_time = 0;
|
|
|
|
presentation->audio_end_time = 0;
|
2015-06-29 16:21:53 +03:00
|
|
|
return ret;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL tsmf_presentation_set_geometry_info(TSMF_PRESENTATION* presentation,
|
2016-09-26 13:12:37 +03:00
|
|
|
UINT32 x, UINT32 y, UINT32 width, UINT32 height, int num_rects, RDP_RECT* rects)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-05-23 15:50:52 +04:00
|
|
|
UINT32 index;
|
|
|
|
UINT32 count;
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream;
|
2016-09-26 13:12:37 +03:00
|
|
|
void* tmp_rects = NULL;
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL ret = TRUE;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
/* The server may send messages with invalid width / height.
|
|
|
|
* Ignore those messages. */
|
2014-05-25 00:58:54 +04:00
|
|
|
if (!width || !height)
|
2015-06-29 16:21:53 +03:00
|
|
|
return TRUE;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2016-09-26 13:02:33 +03:00
|
|
|
/* Streams can be added/removed from the presentation and the server will resend geometry info when a new stream is
|
2015-09-16 20:46:45 +03:00
|
|
|
* added to the presentation. Also, num_rects is used to indicate whether or not the window is visible.
|
|
|
|
* So, always process a valid message with unchanged position/size and/or no visibility rects.
|
2015-07-08 21:10:21 +03:00
|
|
|
*/
|
2014-05-25 00:58:54 +04:00
|
|
|
presentation->x = x;
|
|
|
|
presentation->y = y;
|
|
|
|
presentation->width = width;
|
|
|
|
presentation->height = height;
|
2015-03-11 06:04:36 +03:00
|
|
|
tmp_rects = realloc(presentation->rects, sizeof(RDP_RECT) * num_rects);
|
2015-04-15 11:29:27 +03:00
|
|
|
|
2017-01-17 12:03:05 +03:00
|
|
|
if (!tmp_rects && num_rects)
|
2017-01-18 14:39:40 +03:00
|
|
|
return FALSE;
|
2015-04-15 17:42:54 +03:00
|
|
|
|
2014-05-25 00:58:54 +04:00
|
|
|
presentation->nr_rects = num_rects;
|
2015-03-11 06:04:36 +03:00
|
|
|
presentation->rects = tmp_rects;
|
2019-02-07 16:32:55 +03:00
|
|
|
if (presentation->rects)
|
|
|
|
CopyMemory(presentation->rects, rects, sizeof(RDP_RECT) * num_rects);
|
2014-05-23 15:50:52 +04:00
|
|
|
ArrayList_Lock(presentation->stream_list);
|
|
|
|
count = ArrayList_Count(presentation->stream_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
for (index = 0; index < count; index++)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
stream = (TSMF_STREAM*) ArrayList_GetItem(presentation->stream_list, index);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (!stream->decoder)
|
2014-05-23 15:50:52 +04:00
|
|
|
continue;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->decoder->UpdateRenderingArea)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
2016-09-26 13:12:37 +03:00
|
|
|
ret = stream->decoder->UpdateRenderingArea(stream->decoder, x, y, width, height,
|
|
|
|
num_rects, rects);
|
2014-05-23 15:50:52 +04:00
|
|
|
}
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
ArrayList_Unlock(presentation->stream_list);
|
2015-06-29 16:21:53 +03:00
|
|
|
return ret;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
void tsmf_presentation_set_audio_device(TSMF_PRESENTATION* presentation,
|
|
|
|
const char* name, const char* device)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
|
|
|
presentation->audio_name = name;
|
|
|
|
presentation->audio_device = device;
|
|
|
|
}
|
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
BOOL tsmf_stream_flush(TSMF_STREAM* stream)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL ret = TRUE;
|
|
|
|
|
2013-03-22 00:45:25 +04:00
|
|
|
//TSMF_SAMPLE* sample;
|
2013-03-21 05:42:52 +04:00
|
|
|
/* TODO: free lists */
|
2014-05-25 00:58:54 +04:00
|
|
|
if (stream->audio)
|
2015-06-29 16:21:53 +03:00
|
|
|
ret = stream->audio->Flush(stream->audio);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
stream->eos = 0;
|
2015-07-08 00:39:29 +03:00
|
|
|
stream->eos_message_id = 0;
|
|
|
|
stream->eos_channel_callback = NULL;
|
|
|
|
stream->delayed_stop = 0;
|
2011-09-19 18:54:09 +04:00
|
|
|
stream->last_end_time = 0;
|
|
|
|
stream->next_start_time = 0;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->major_type == TSMF_MAJOR_TYPE_AUDIO)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
|
|
|
stream->presentation->audio_start_time = 0;
|
|
|
|
stream->presentation->audio_end_time = 0;
|
|
|
|
}
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
return TRUE;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2018-02-20 14:15:30 +03:00
|
|
|
void _tsmf_presentation_free(void* obj)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2018-02-20 14:15:30 +03:00
|
|
|
TSMF_PRESENTATION* presentation = (TSMF_PRESENTATION*)obj;
|
|
|
|
|
|
|
|
if (presentation)
|
|
|
|
{
|
|
|
|
tsmf_presentation_stop(presentation);
|
|
|
|
ArrayList_Clear(presentation->stream_list);
|
|
|
|
ArrayList_Free(presentation->stream_list);
|
|
|
|
free(presentation->rects);
|
|
|
|
ZeroMemory(presentation, sizeof(TSMF_PRESENTATION));
|
|
|
|
free(presentation);
|
|
|
|
}
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
void tsmf_presentation_free(TSMF_PRESENTATION* presentation)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-05-23 15:50:52 +04:00
|
|
|
ArrayList_Remove(presentation_list, presentation);
|
|
|
|
}
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
TSMF_STREAM* tsmf_stream_new(TSMF_PRESENTATION* presentation, UINT32 stream_id,
|
|
|
|
rdpContext* rdpcontext)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream;
|
2011-09-19 18:54:09 +04:00
|
|
|
stream = tsmf_stream_find_by_id(presentation, stream_id);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "duplicated stream id %"PRIu32"!", stream_id);
|
2011-09-19 18:54:09 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
stream = (TSMF_STREAM*) calloc(1, sizeof(TSMF_STREAM));
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2014-05-25 00:58:54 +04:00
|
|
|
if (!stream)
|
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "Calloc failed");
|
2014-05-25 00:58:54 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
stream->minBufferLevel = VIDEO_MIN_BUFFER_LEVEL;
|
|
|
|
stream->maxBufferLevel = VIDEO_MAX_BUFFER_LEVEL;
|
|
|
|
stream->currentBufferLevel = 1;
|
|
|
|
stream->seeking = FALSE;
|
|
|
|
stream->eos = 0;
|
|
|
|
stream->eos_message_id = 0;
|
|
|
|
stream->eos_channel_callback = NULL;
|
2011-09-19 18:54:09 +04:00
|
|
|
stream->stream_id = stream_id;
|
|
|
|
stream->presentation = presentation;
|
2013-03-21 05:42:52 +04:00
|
|
|
stream->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
if (!stream->stopEvent)
|
|
|
|
goto error_stopEvent;
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
stream->ready = CreateEvent(NULL, TRUE, TRUE, NULL);
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
if (!stream->ready)
|
|
|
|
goto error_ready;
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2013-03-21 05:42:52 +04:00
|
|
|
stream->sample_list = Queue_New(TRUE, -1, -1);
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
if (!stream->sample_list)
|
|
|
|
goto error_sample_list;
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
stream->sample_list->object.fnObjectFree = tsmf_sample_free;
|
2013-03-21 05:42:52 +04:00
|
|
|
stream->sample_ack_list = Queue_New(TRUE, -1, -1);
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
if (!stream->sample_ack_list)
|
|
|
|
goto error_sample_ack_list;
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
stream->sample_ack_list->object.fnObjectFree = tsmf_sample_free;
|
2018-03-07 14:03:10 +03:00
|
|
|
stream->play_thread = CreateThread(NULL, 0, tsmf_stream_playback_func,
|
2017-01-17 12:03:05 +03:00
|
|
|
stream, CREATE_SUSPENDED, NULL);
|
2015-05-18 12:28:00 +03:00
|
|
|
|
|
|
|
if (!stream->play_thread)
|
|
|
|
goto error_play_thread;
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2018-03-07 14:03:10 +03:00
|
|
|
stream->ack_thread = CreateThread(NULL, 0, tsmf_stream_ack_func, stream,
|
2017-01-17 12:03:05 +03:00
|
|
|
CREATE_SUSPENDED, NULL);
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
if (!stream->ack_thread)
|
|
|
|
goto error_ack_thread;
|
2014-11-10 22:02:54 +03:00
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
if (ArrayList_Add(presentation->stream_list, stream) < 0)
|
|
|
|
goto error_add;
|
2014-11-10 22:02:54 +03:00
|
|
|
|
2015-07-15 10:50:35 +03:00
|
|
|
stream->rdpcontext = rdpcontext;
|
2011-09-19 18:54:09 +04:00
|
|
|
return stream;
|
2015-05-18 12:28:00 +03:00
|
|
|
error_add:
|
|
|
|
SetEvent(stream->stopEvent);
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-07-30 16:49:21 +03:00
|
|
|
if (WaitForSingleObject(stream->ack_thread, INFINITE) == WAIT_FAILED)
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", GetLastError());
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
error_ack_thread:
|
|
|
|
SetEvent(stream->stopEvent);
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-07-30 16:49:21 +03:00
|
|
|
if (WaitForSingleObject(stream->play_thread, INFINITE) == WAIT_FAILED)
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", GetLastError());
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
error_play_thread:
|
|
|
|
Queue_Free(stream->sample_ack_list);
|
|
|
|
error_sample_ack_list:
|
|
|
|
Queue_Free(stream->sample_list);
|
|
|
|
error_sample_list:
|
|
|
|
CloseHandle(stream->ready);
|
|
|
|
error_ready:
|
|
|
|
CloseHandle(stream->stopEvent);
|
|
|
|
error_stopEvent:
|
|
|
|
free(stream);
|
|
|
|
return NULL;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2017-01-17 12:03:05 +03:00
|
|
|
void tsmf_stream_start_threads(TSMF_STREAM* stream)
|
2015-05-14 16:57:37 +03:00
|
|
|
{
|
|
|
|
ResumeThread(stream->play_thread);
|
|
|
|
ResumeThread(stream->ack_thread);
|
|
|
|
}
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
TSMF_STREAM* tsmf_stream_find_by_id(TSMF_PRESENTATION* presentation,
|
|
|
|
UINT32 stream_id)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-04-26 20:31:24 +04:00
|
|
|
UINT32 index;
|
|
|
|
UINT32 count;
|
|
|
|
BOOL found = FALSE;
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream;
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Lock(presentation->stream_list);
|
|
|
|
count = ArrayList_Count(presentation->stream_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
for (index = 0; index < count; index++)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
stream = (TSMF_STREAM*) ArrayList_GetItem(presentation->stream_list, index);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->stream_id == stream_id)
|
2014-04-26 20:31:24 +04:00
|
|
|
{
|
|
|
|
found = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Unlock(presentation->stream_list);
|
|
|
|
return (found) ? stream : NULL;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
static void tsmf_stream_resync(void* arg)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_STREAM* stream = arg;
|
2014-05-23 15:50:52 +04:00
|
|
|
ResetEvent(stream->ready);
|
|
|
|
}
|
2011-09-19 18:54:09 +04:00
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
BOOL tsmf_stream_set_format(TSMF_STREAM* stream, const char* name, wStream* s)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
|
|
|
TS_AM_MEDIA_TYPE mediatype;
|
2015-06-29 16:21:53 +03:00
|
|
|
BOOL ret = TRUE;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->decoder)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-09-12 18:19:32 +04:00
|
|
|
WLog_ERR(TAG, "duplicated call");
|
2015-06-29 16:21:53 +03:00
|
|
|
return FALSE;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
if (!tsmf_codec_parse_media_type(&mediatype, s))
|
2015-09-03 20:58:16 +03:00
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "unable to parse media type");
|
2015-06-29 16:21:53 +03:00
|
|
|
return FALSE;
|
2015-09-03 20:58:16 +03:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (mediatype.MajorType == TSMF_MAJOR_TYPE_VIDEO)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2016-12-14 00:47:08 +03:00
|
|
|
DEBUG_TSMF("video width %"PRIu32" height %"PRIu32" bit_rate %"PRIu32" frame_rate %f codec_data %"PRIu32"",
|
2016-09-26 13:12:37 +03:00
|
|
|
mediatype.Width, mediatype.Height, mediatype.BitRate,
|
|
|
|
(double) mediatype.SamplesPerSecond.Numerator / (double)
|
|
|
|
mediatype.SamplesPerSecond.Denominator,
|
|
|
|
mediatype.ExtraDataSize);
|
2015-07-08 00:39:29 +03:00
|
|
|
stream->minBufferLevel = VIDEO_MIN_BUFFER_LEVEL;
|
|
|
|
stream->maxBufferLevel = VIDEO_MAX_BUFFER_LEVEL;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
else if (mediatype.MajorType == TSMF_MAJOR_TYPE_AUDIO)
|
|
|
|
{
|
2016-12-14 00:47:08 +03:00
|
|
|
DEBUG_TSMF("audio channel %"PRIu32" sample_rate %"PRIu32" bits_per_sample %"PRIu32" codec_data %"PRIu32"",
|
2016-09-26 13:12:37 +03:00
|
|
|
mediatype.Channels, mediatype.SamplesPerSecond.Numerator,
|
|
|
|
mediatype.BitsPerSample,
|
|
|
|
mediatype.ExtraDataSize);
|
2014-05-25 00:58:54 +04:00
|
|
|
stream->sample_rate = mediatype.SamplesPerSecond.Numerator;
|
|
|
|
stream->channels = mediatype.Channels;
|
|
|
|
stream->bits_per_sample = mediatype.BitsPerSample;
|
|
|
|
|
|
|
|
if (stream->bits_per_sample == 0)
|
|
|
|
stream->bits_per_sample = 16;
|
2015-07-08 00:39:29 +03:00
|
|
|
|
|
|
|
stream->minBufferLevel = AUDIO_MIN_BUFFER_LEVEL;
|
|
|
|
stream->maxBufferLevel = AUDIO_MAX_BUFFER_LEVEL;
|
2014-05-25 00:58:54 +04:00
|
|
|
}
|
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
stream->major_type = mediatype.MajorType;
|
|
|
|
stream->width = mediatype.Width;
|
|
|
|
stream->height = mediatype.Height;
|
|
|
|
stream->decoder = tsmf_load_decoder(name, &mediatype);
|
2016-09-26 13:12:37 +03:00
|
|
|
ret &= tsmf_stream_change_volume(stream, stream->presentation->volume,
|
|
|
|
stream->presentation->muted);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (!stream->decoder)
|
2015-06-29 16:21:53 +03:00
|
|
|
return FALSE;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->decoder->SetAckFunc)
|
2016-09-26 13:12:37 +03:00
|
|
|
ret &= stream->decoder->SetAckFunc(stream->decoder, tsmf_stream_process_ack,
|
|
|
|
stream);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->decoder->SetSyncFunc)
|
2016-09-26 13:12:37 +03:00
|
|
|
ret &= stream->decoder->SetSyncFunc(stream->decoder, tsmf_stream_resync,
|
|
|
|
stream);
|
|
|
|
|
2015-06-29 16:21:53 +03:00
|
|
|
return ret;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
void tsmf_stream_end(TSMF_STREAM* stream, UINT32 message_id,
|
|
|
|
IWTSVirtualChannelCallback* pChannelCallback)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
if (!stream)
|
|
|
|
return;
|
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
stream->eos = 1;
|
2015-07-08 00:39:29 +03:00
|
|
|
stream->eos_message_id = message_id;
|
|
|
|
stream->eos_channel_callback = pChannelCallback;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2018-02-20 14:15:30 +03:00
|
|
|
void _tsmf_stream_free(void* obj)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2018-02-20 14:15:30 +03:00
|
|
|
TSMF_STREAM* stream = (TSMF_STREAM*)obj;
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
if (!stream)
|
|
|
|
return;
|
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
tsmf_stream_stop(stream);
|
2014-05-23 15:50:52 +04:00
|
|
|
SetEvent(stream->stopEvent);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->play_thread)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
2015-07-30 16:49:21 +03:00
|
|
|
if (WaitForSingleObject(stream->play_thread, INFINITE) == WAIT_FAILED)
|
2015-09-03 20:58:16 +03:00
|
|
|
{
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", GetLastError());
|
2015-09-03 20:58:16 +03:00
|
|
|
return;
|
|
|
|
}
|
2015-07-30 16:49:21 +03:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
CloseHandle(stream->play_thread);
|
|
|
|
stream->play_thread = NULL;
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->ack_thread)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
2015-07-30 16:49:21 +03:00
|
|
|
if (WaitForSingleObject(stream->ack_thread, INFINITE) == WAIT_FAILED)
|
2015-09-03 20:58:16 +03:00
|
|
|
{
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", GetLastError());
|
2015-09-03 20:58:16 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
CloseHandle(stream->ack_thread);
|
|
|
|
stream->ack_thread = NULL;
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2013-03-21 05:42:52 +04:00
|
|
|
Queue_Free(stream->sample_list);
|
|
|
|
Queue_Free(stream->sample_ack_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (stream->decoder && stream->decoder->Free)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
2011-09-19 18:54:09 +04:00
|
|
|
stream->decoder->Free(stream->decoder);
|
2014-05-23 15:50:52 +04:00
|
|
|
stream->decoder = NULL;
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-05-23 15:50:52 +04:00
|
|
|
CloseHandle(stream->stopEvent);
|
|
|
|
CloseHandle(stream->ready);
|
2014-11-10 22:02:54 +03:00
|
|
|
ZeroMemory(stream, sizeof(TSMF_STREAM));
|
2012-10-09 07:21:26 +04:00
|
|
|
free(stream);
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
void tsmf_stream_free(TSMF_STREAM* stream)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_PRESENTATION* presentation = stream->presentation;
|
2014-05-23 15:50:52 +04:00
|
|
|
ArrayList_Remove(presentation->stream_list, stream);
|
|
|
|
}
|
2012-07-29 06:24:14 +04:00
|
|
|
|
2016-09-26 13:12:37 +03:00
|
|
|
BOOL 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, BYTE* data)
|
2014-05-23 15:50:52 +04:00
|
|
|
{
|
2014-11-10 22:02:54 +03:00
|
|
|
TSMF_SAMPLE* sample;
|
2014-05-23 15:50:52 +04:00
|
|
|
SetEvent(stream->ready);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (TERMINATING)
|
2015-06-29 16:21:53 +03:00
|
|
|
return TRUE;
|
2014-05-25 00:58:54 +04:00
|
|
|
|
2014-11-10 22:02:54 +03:00
|
|
|
sample = (TSMF_SAMPLE*) calloc(1, sizeof(TSMF_SAMPLE));
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2014-05-25 00:58:54 +04:00
|
|
|
if (!sample)
|
|
|
|
{
|
2015-06-29 16:21:53 +03:00
|
|
|
WLog_ERR(TAG, "calloc sample failed!");
|
|
|
|
return FALSE;
|
2014-05-25 00:58:54 +04:00
|
|
|
}
|
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
sample->sample_id = sample_id;
|
|
|
|
sample->start_time = start_time;
|
|
|
|
sample->end_time = end_time;
|
|
|
|
sample->duration = duration;
|
|
|
|
sample->extensions = extensions;
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-07-08 00:39:29 +03:00
|
|
|
if ((sample->extensions & 0x00000080) || (sample->extensions & 0x00000040))
|
|
|
|
sample->invalidTimestamps = TRUE;
|
|
|
|
else
|
|
|
|
sample->invalidTimestamps = FALSE;
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2011-09-19 18:54:09 +04:00
|
|
|
sample->stream = stream;
|
|
|
|
sample->channel_callback = pChannelCallback;
|
|
|
|
sample->data_size = data_size;
|
2014-05-25 00:58:54 +04:00
|
|
|
sample->data = calloc(1, data_size + TSMF_BUFFER_PADDING_SIZE);
|
|
|
|
|
|
|
|
if (!sample->data)
|
|
|
|
{
|
2015-06-29 16:21:53 +03:00
|
|
|
WLog_ERR(TAG, "calloc sample->data failed!");
|
2014-05-25 00:58:54 +04:00
|
|
|
free(sample);
|
2015-06-29 16:21:53 +03:00
|
|
|
return FALSE;
|
2014-05-25 00:58:54 +04:00
|
|
|
}
|
|
|
|
|
2013-03-21 05:42:52 +04:00
|
|
|
CopyMemory(sample->data, data, data_size);
|
2015-06-29 16:21:53 +03:00
|
|
|
return Queue_Enqueue(stream->sample_list, sample);
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|
|
|
|
|
2012-07-29 06:24:14 +04:00
|
|
|
#ifndef _WIN32
|
|
|
|
|
2012-06-13 23:45:58 +04:00
|
|
|
static void tsmf_signal_handler(int s)
|
|
|
|
{
|
2012-09-19 01:33:52 +04:00
|
|
|
TERMINATING = 1;
|
2014-04-26 20:31:24 +04:00
|
|
|
ArrayList_Free(presentation_list);
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (s == SIGINT)
|
2012-06-13 23:45:58 +04:00
|
|
|
{
|
|
|
|
signal(s, SIG_DFL);
|
|
|
|
kill(getpid(), s);
|
|
|
|
}
|
2014-05-25 00:58:54 +04:00
|
|
|
else if (s == SIGUSR1)
|
|
|
|
{
|
|
|
|
signal(s, SIG_DFL);
|
|
|
|
}
|
2012-06-13 23:45:58 +04:00
|
|
|
}
|
|
|
|
|
2012-07-29 06:24:14 +04:00
|
|
|
#endif
|
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
BOOL tsmf_media_init(void)
|
2011-09-19 18:54:09 +04:00
|
|
|
{
|
2012-07-29 06:24:14 +04:00
|
|
|
#ifndef _WIN32
|
2012-06-13 23:45:58 +04:00
|
|
|
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);
|
2012-07-29 06:24:14 +04:00
|
|
|
#endif
|
2014-05-25 00:58:54 +04:00
|
|
|
|
|
|
|
if (!presentation_list)
|
2014-04-26 20:31:24 +04:00
|
|
|
{
|
|
|
|
presentation_list = ArrayList_New(TRUE);
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
if (!presentation_list)
|
|
|
|
return FALSE;
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2018-02-20 14:15:30 +03:00
|
|
|
ArrayList_Object(presentation_list)->fnObjectFree = _tsmf_presentation_free;
|
2014-04-26 20:31:24 +04:00
|
|
|
}
|
2016-09-26 13:12:37 +03:00
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
return TRUE;
|
2011-09-19 18:54:09 +04:00
|
|
|
}
|