Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
82
third_party/FreeRDP/channels/tsmf/client/CMakeLists.txt
vendored
Normal file
82
third_party/FreeRDP/channels/tsmf/client/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
# Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
define_channel_client("tsmf")
|
||||
|
||||
message(DEPRECATION "TSMF channel is no longer maintained. Use [MS-RDPEVOR] (/video) instead.")
|
||||
|
||||
find_package(PkgConfig)
|
||||
if(PkgConfig_FOUND)
|
||||
pkg_check_modules(gstreamer gstreamer-1.0)
|
||||
freerdp_client_pc_add_requires_private("gstreamer-1.0")
|
||||
endif()
|
||||
|
||||
if(WITH_GSTREAMER_1_0)
|
||||
if(gstreamer_FOUND)
|
||||
add_compile_definitions(WITH_GSTREAMER_1_0)
|
||||
else()
|
||||
message(WARNING "gstreamer not detected, disabling support")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
tsmf_audio.c
|
||||
tsmf_audio.h
|
||||
tsmf_codec.c
|
||||
tsmf_codec.h
|
||||
tsmf_constants.h
|
||||
tsmf_decoder.c
|
||||
tsmf_decoder.h
|
||||
tsmf_ifman.c
|
||||
tsmf_ifman.h
|
||||
tsmf_main.c
|
||||
tsmf_main.h
|
||||
tsmf_media.c
|
||||
tsmf_media.h
|
||||
tsmf_types.h
|
||||
)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp)
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
|
||||
if(WITH_VIDEO_FFMPEG)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "ffmpeg" "decoder")
|
||||
endif()
|
||||
|
||||
if(WITH_GSTREAMER_1_0)
|
||||
find_package(X11)
|
||||
if(X11_Xrandr_FOUND)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "gstreamer" "decoder")
|
||||
else()
|
||||
message(WARNING "Disabling tsmf gstreamer because XRandR wasn't found")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_OSS)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "oss" "audio")
|
||||
endif()
|
||||
|
||||
if(WITH_ALSA)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "alsa" "audio")
|
||||
endif()
|
||||
|
||||
if(WITH_PULSE)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "pulse" "audio")
|
||||
endif()
|
||||
30
third_party/FreeRDP/channels/tsmf/client/alsa/CMakeLists.txt
vendored
Normal file
30
third_party/FreeRDP/channels/tsmf/client/alsa/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
define_channel_client_subsystem("tsmf" "alsa" "audio")
|
||||
|
||||
find_package(ALSA REQUIRED)
|
||||
freerdp_client_pc_add_requires_private("alsa")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS tsmf_alsa.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp ${ALSA_LIBRARIES})
|
||||
|
||||
include_directories(..)
|
||||
include_directories(SYSTEM ${ALSA_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
238
third_party/FreeRDP/channels/tsmf/client/alsa/tsmf_alsa.c
vendored
Normal file
238
third_party/FreeRDP/channels/tsmf/client/alsa/tsmf_alsa.c
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - ALSA Audio Device
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
|
||||
#include "tsmf_audio.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ITSMFAudioDevice iface;
|
||||
|
||||
char device[32];
|
||||
snd_pcm_t* out_handle;
|
||||
UINT32 source_rate;
|
||||
UINT32 actual_rate;
|
||||
UINT32 source_channels;
|
||||
UINT32 actual_channels;
|
||||
UINT32 bytes_per_sample;
|
||||
} TSMFAlsaAudioDevice;
|
||||
|
||||
static BOOL tsmf_alsa_open_device(TSMFAlsaAudioDevice* alsa)
|
||||
{
|
||||
int error = 0;
|
||||
error = snd_pcm_open(&alsa->out_handle, alsa->device, SND_PCM_STREAM_PLAYBACK, 0);
|
||||
|
||||
if (error < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to open device %s", alsa->device);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DEBUG_TSMF("open device %s", alsa->device);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_alsa_open(ITSMFAudioDevice* audio, const char* device)
|
||||
{
|
||||
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*)audio;
|
||||
|
||||
if (!device)
|
||||
{
|
||||
strncpy(alsa->device, "default", sizeof(alsa->device));
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(alsa->device, device, sizeof(alsa->device) - 1);
|
||||
}
|
||||
|
||||
return tsmf_alsa_open_device(alsa);
|
||||
}
|
||||
|
||||
static BOOL tsmf_alsa_set_format(ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels,
|
||||
UINT32 bits_per_sample)
|
||||
{
|
||||
int error = 0;
|
||||
snd_pcm_uframes_t frames = 0;
|
||||
snd_pcm_hw_params_t* hw_params = nullptr;
|
||||
snd_pcm_sw_params_t* sw_params = nullptr;
|
||||
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*)audio;
|
||||
|
||||
if (!alsa->out_handle)
|
||||
return FALSE;
|
||||
|
||||
snd_pcm_drop(alsa->out_handle);
|
||||
alsa->actual_rate = alsa->source_rate = sample_rate;
|
||||
alsa->actual_channels = alsa->source_channels = channels;
|
||||
alsa->bytes_per_sample = bits_per_sample / 8;
|
||||
error = snd_pcm_hw_params_malloc(&hw_params);
|
||||
|
||||
if (error < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_pcm_hw_params_malloc failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_any(alsa->out_handle, hw_params);
|
||||
snd_pcm_hw_params_set_access(alsa->out_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
snd_pcm_hw_params_set_format(alsa->out_handle, hw_params, SND_PCM_FORMAT_S16_LE);
|
||||
snd_pcm_hw_params_set_rate_near(alsa->out_handle, hw_params, &alsa->actual_rate, nullptr);
|
||||
snd_pcm_hw_params_set_channels_near(alsa->out_handle, hw_params, &alsa->actual_channels);
|
||||
frames = sample_rate;
|
||||
snd_pcm_hw_params_set_buffer_size_near(alsa->out_handle, hw_params, &frames);
|
||||
snd_pcm_hw_params(alsa->out_handle, hw_params);
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
error = snd_pcm_sw_params_malloc(&sw_params);
|
||||
|
||||
if (error < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_pcm_sw_params_malloc");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
snd_pcm_sw_params_current(alsa->out_handle, sw_params);
|
||||
snd_pcm_sw_params_set_start_threshold(alsa->out_handle, sw_params, frames / 2);
|
||||
snd_pcm_sw_params(alsa->out_handle, sw_params);
|
||||
snd_pcm_sw_params_free(sw_params);
|
||||
snd_pcm_prepare(alsa->out_handle);
|
||||
DEBUG_TSMF("sample_rate %" PRIu32 " channels %" PRIu32 " bits_per_sample %" PRIu32 "",
|
||||
sample_rate, channels, bits_per_sample);
|
||||
DEBUG_TSMF("hardware buffer %lu frames", frames);
|
||||
|
||||
if ((alsa->actual_rate != alsa->source_rate) ||
|
||||
(alsa->actual_channels != alsa->source_channels))
|
||||
{
|
||||
DEBUG_TSMF("actual rate %" PRIu32 " / channel %" PRIu32 " is different "
|
||||
"from source rate %" PRIu32 " / channel %" PRIu32 ", resampling required.",
|
||||
alsa->actual_rate, alsa->actual_channels, alsa->source_rate,
|
||||
alsa->source_channels);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_alsa_play(ITSMFAudioDevice* audio, const BYTE* src, UINT32 data_size)
|
||||
{
|
||||
const BYTE* pindex = nullptr;
|
||||
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*)audio;
|
||||
DEBUG_TSMF("data_size %" PRIu32 "", data_size);
|
||||
|
||||
if (alsa->out_handle)
|
||||
{
|
||||
const size_t rbytes_per_frame = 1ULL * alsa->actual_channels * alsa->bytes_per_sample;
|
||||
pindex = src;
|
||||
const BYTE* end = pindex + data_size;
|
||||
|
||||
while (pindex < end)
|
||||
{
|
||||
const size_t len = (size_t)(end - pindex);
|
||||
const size_t frames = len / rbytes_per_frame;
|
||||
snd_pcm_sframes_t error = snd_pcm_writei(alsa->out_handle, pindex, frames);
|
||||
|
||||
if (error == -EPIPE)
|
||||
{
|
||||
snd_pcm_recover(alsa->out_handle, -EPIPE, 0);
|
||||
error = 0;
|
||||
}
|
||||
else if (error < 0)
|
||||
{
|
||||
DEBUG_TSMF("error len %ld", error);
|
||||
snd_pcm_close(alsa->out_handle);
|
||||
alsa->out_handle = 0;
|
||||
tsmf_alsa_open_device(alsa);
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG_TSMF("%d frames played.", error);
|
||||
|
||||
if (error == 0)
|
||||
break;
|
||||
|
||||
pindex += (size_t)error * rbytes_per_frame;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT64 tsmf_alsa_get_latency(ITSMFAudioDevice* audio)
|
||||
{
|
||||
UINT64 latency = 0;
|
||||
snd_pcm_sframes_t frames = 0;
|
||||
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*)audio;
|
||||
|
||||
if (alsa->out_handle && alsa->actual_rate > 0 &&
|
||||
snd_pcm_delay(alsa->out_handle, &frames) == 0 && frames > 0)
|
||||
{
|
||||
latency = ((UINT64)frames) * 10000000LL / (UINT64)alsa->actual_rate;
|
||||
}
|
||||
|
||||
return latency;
|
||||
}
|
||||
|
||||
static BOOL tsmf_alsa_flush(WINPR_ATTR_UNUSED ITSMFAudioDevice* audio)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void tsmf_alsa_free(ITSMFAudioDevice* audio)
|
||||
{
|
||||
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*)audio;
|
||||
DEBUG_TSMF("");
|
||||
|
||||
if (alsa->out_handle)
|
||||
{
|
||||
snd_pcm_drain(alsa->out_handle);
|
||||
snd_pcm_close(alsa->out_handle);
|
||||
}
|
||||
|
||||
free(alsa);
|
||||
}
|
||||
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE alsa_freerdp_tsmf_client_audio_subsystem_entry(void* ptr))
|
||||
{
|
||||
ITSMFAudioDevice** sptr = (ITSMFAudioDevice**)ptr;
|
||||
WINPR_ASSERT(sptr);
|
||||
*sptr = nullptr;
|
||||
|
||||
TSMFAlsaAudioDevice* alsa = calloc(1, sizeof(TSMFAlsaAudioDevice));
|
||||
if (!alsa)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
alsa->iface.Open = tsmf_alsa_open;
|
||||
alsa->iface.SetFormat = tsmf_alsa_set_format;
|
||||
alsa->iface.Play = tsmf_alsa_play;
|
||||
alsa->iface.GetLatency = tsmf_alsa_get_latency;
|
||||
alsa->iface.Flush = tsmf_alsa_flush;
|
||||
alsa->iface.Free = tsmf_alsa_free;
|
||||
*sptr = &alsa->iface;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
35
third_party/FreeRDP/channels/tsmf/client/ffmpeg/CMakeLists.txt
vendored
Normal file
35
third_party/FreeRDP/channels/tsmf/client/ffmpeg/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
define_channel_client_subsystem("tsmf" "ffmpeg" "decoder")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS tsmf_ffmpeg.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp ${FFMPEG_LIBRARIES})
|
||||
if(APPLE)
|
||||
# For this to work on apple, we need to add some frameworks
|
||||
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
|
||||
find_library(COREVIDEO_LIBRARY CoreVideo)
|
||||
find_library(COREVIDEODECODE_LIBRARY VideoDecodeAcceleration)
|
||||
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS ${COREFOUNDATION_LIBRARY} ${COREVIDEO_LIBRARY} ${COREVIDEODECODE_LIBRARY})
|
||||
endif()
|
||||
|
||||
include_directories(..)
|
||||
include_directories(SYSTEM ${FFMPEG_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
723
third_party/FreeRDP/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c
vendored
Normal file
723
third_party/FreeRDP/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c
vendored
Normal file
@@ -0,0 +1,723 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - FFmpeg Decoder
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/client/tsmf.h>
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/cpu.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
|
||||
#include "tsmf_constants.h"
|
||||
#include "tsmf_decoder.h"
|
||||
#include "tsmf_audio.h"
|
||||
|
||||
/* Compatibility with older FFmpeg */
|
||||
#if LIBAVUTIL_VERSION_MAJOR < 50
|
||||
#define AVMEDIA_TYPE_VIDEO 0
|
||||
#define AVMEDIA_TYPE_AUDIO 1
|
||||
#endif
|
||||
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 54
|
||||
#define MAX_AUDIO_FRAME_SIZE AVCODEC_MAX_AUDIO_FRAME_SIZE
|
||||
#else
|
||||
#define MAX_AUDIO_FRAME_SIZE 192000
|
||||
#endif
|
||||
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 55
|
||||
#define AV_CODEC_ID_VC1 CODEC_ID_VC1
|
||||
#define AV_CODEC_ID_WMAV2 CODEC_ID_WMAV2
|
||||
#define AV_CODEC_ID_WMAPRO CODEC_ID_WMAPRO
|
||||
#define AV_CODEC_ID_MP3 CODEC_ID_MP3
|
||||
#define AV_CODEC_ID_MP2 CODEC_ID_MP2
|
||||
#define AV_CODEC_ID_MPEG2VIDEO CODEC_ID_MPEG2VIDEO
|
||||
#define AV_CODEC_ID_WMV3 CODEC_ID_WMV3
|
||||
#define AV_CODEC_ID_AAC CODEC_ID_AAC
|
||||
#define AV_CODEC_ID_H264 CODEC_ID_H264
|
||||
#define AV_CODEC_ID_AC3 CODEC_ID_AC3
|
||||
#endif
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(56, 34, 2)
|
||||
#define AV_CODEC_CAP_TRUNCATED CODEC_CAP_TRUNCATED
|
||||
#define AV_CODEC_FLAG_TRUNCATED CODEC_FLAG_TRUNCATED
|
||||
#endif
|
||||
|
||||
#if LIBAVUTIL_VERSION_MAJOR < 52
|
||||
#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ITSMFDecoder iface;
|
||||
|
||||
int media_type;
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 55
|
||||
enum CodecID codec_id;
|
||||
#else
|
||||
enum AVCodecID codec_id;
|
||||
#endif
|
||||
AVCodecContext* codec_context;
|
||||
const AVCodec* codec;
|
||||
AVFrame* frame;
|
||||
int prepared;
|
||||
|
||||
BYTE* decoded_data;
|
||||
UINT32 decoded_size;
|
||||
UINT32 decoded_size_max;
|
||||
} TSMFFFmpegDecoder;
|
||||
|
||||
static BOOL tsmf_ffmpeg_init_context(ITSMFDecoder* decoder)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
mdecoder->codec_context = avcodec_alloc_context3(nullptr);
|
||||
|
||||
if (!mdecoder->codec_context)
|
||||
{
|
||||
WLog_ERR(TAG, "avcodec_alloc_context failed.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_ffmpeg_init_video_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYPE* media_type)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
mdecoder->codec_context->width = WINPR_ASSERTING_INT_CAST(int, media_type->Width);
|
||||
mdecoder->codec_context->height = WINPR_ASSERTING_INT_CAST(int, media_type->Height);
|
||||
mdecoder->codec_context->bit_rate = WINPR_ASSERTING_INT_CAST(int, media_type->BitRate);
|
||||
mdecoder->codec_context->time_base.den =
|
||||
WINPR_ASSERTING_INT_CAST(int, media_type->SamplesPerSecond.Numerator);
|
||||
mdecoder->codec_context->time_base.num =
|
||||
WINPR_ASSERTING_INT_CAST(int, media_type->SamplesPerSecond.Denominator);
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 55
|
||||
mdecoder->frame = avcodec_alloc_frame();
|
||||
#else
|
||||
mdecoder->frame = av_frame_alloc();
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_ffmpeg_init_audio_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYPE* media_type)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
mdecoder->codec_context->sample_rate =
|
||||
WINPR_ASSERTING_INT_CAST(int, media_type->SamplesPerSecond.Numerator);
|
||||
mdecoder->codec_context->bit_rate = WINPR_ASSERTING_INT_CAST(int, media_type->BitRate);
|
||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100)
|
||||
mdecoder->codec_context->ch_layout.nb_channels =
|
||||
WINPR_ASSERTING_INT_CAST(int, media_type->Channels);
|
||||
#else
|
||||
mdecoder->codec_context->channels = WINPR_ASSERTING_INT_CAST(int, media_type->Channels);
|
||||
#endif
|
||||
mdecoder->codec_context->block_align = WINPR_ASSERTING_INT_CAST(int, media_type->BlockAlign);
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 55
|
||||
#ifdef AV_CPU_FLAG_SSE2
|
||||
mdecoder->codec_context->dsp_mask = AV_CPU_FLAG_SSE2 | AV_CPU_FLAG_MMX2;
|
||||
#else
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 53
|
||||
mdecoder->codec_context->dsp_mask = FF_MM_SSE2 | FF_MM_MMXEXT;
|
||||
#else
|
||||
mdecoder->codec_context->dsp_mask = FF_MM_SSE2 | FF_MM_MMX2;
|
||||
#endif
|
||||
#endif
|
||||
#else /* LIBAVCODEC_VERSION_MAJOR < 55 */
|
||||
#ifdef AV_CPU_FLAG_SSE2
|
||||
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57, 17, 100)
|
||||
av_set_cpu_flags_mask(AV_CPU_FLAG_SSE2 | AV_CPU_FLAG_MMXEXT);
|
||||
#else
|
||||
av_force_cpu_flags(AV_CPU_FLAG_SSE2 | AV_CPU_FLAG_MMXEXT);
|
||||
#endif
|
||||
#else
|
||||
av_set_cpu_flags_mask(FF_MM_SSE2 | FF_MM_MMX2);
|
||||
#endif
|
||||
#endif /* LIBAVCODEC_VERSION_MAJOR < 55 */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_ffmpeg_init_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYPE* media_type)
|
||||
{
|
||||
BYTE* p = nullptr;
|
||||
UINT32 size = 0;
|
||||
const BYTE* s = nullptr;
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_QUALIFIERS
|
||||
mdecoder->codec = avcodec_find_decoder(mdecoder->codec_id);
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
|
||||
if (!mdecoder->codec)
|
||||
{
|
||||
WLog_ERR(TAG, "avcodec_find_decoder failed.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mdecoder->codec_context->codec_id = mdecoder->codec_id;
|
||||
mdecoder->codec_context->codec_type = mdecoder->media_type;
|
||||
|
||||
switch (mdecoder->media_type)
|
||||
{
|
||||
case AVMEDIA_TYPE_VIDEO:
|
||||
if (!tsmf_ffmpeg_init_video_stream(decoder, media_type))
|
||||
return FALSE;
|
||||
|
||||
break;
|
||||
|
||||
case AVMEDIA_TYPE_AUDIO:
|
||||
if (!tsmf_ffmpeg_init_audio_stream(decoder, media_type))
|
||||
return FALSE;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "unknown media_type %d", mdecoder->media_type);
|
||||
break;
|
||||
}
|
||||
|
||||
if (media_type->ExtraData)
|
||||
{
|
||||
/* Add a padding to avoid invalid memory read in some codec */
|
||||
mdecoder->codec_context->extradata_size =
|
||||
WINPR_ASSERTING_INT_CAST(int, media_type->ExtraDataSize + 8);
|
||||
if (mdecoder->codec_context->extradata_size == 0)
|
||||
return FALSE;
|
||||
mdecoder->codec_context->extradata = calloc(1, mdecoder->codec_context->extradata_size);
|
||||
|
||||
if (!mdecoder->codec_context->extradata)
|
||||
return FALSE;
|
||||
|
||||
if (media_type->SubType == TSMF_SUB_TYPE_AVC1 &&
|
||||
media_type->FormatType == TSMF_FORMAT_TYPE_MPEG2VIDEOINFO)
|
||||
{
|
||||
size_t required = 6;
|
||||
/* The extradata format that FFmpeg uses is following CodecPrivate in Matroska.
|
||||
See http://haali.su/mkv/codecs.pdf */
|
||||
p = mdecoder->codec_context->extradata;
|
||||
if ((mdecoder->codec_context->extradata_size < 0) ||
|
||||
((size_t)mdecoder->codec_context->extradata_size < required))
|
||||
return FALSE;
|
||||
*p++ = 1; /* Reserved? */
|
||||
*p++ = media_type->ExtraData[8]; /* Profile */
|
||||
*p++ = 0; /* Profile */
|
||||
*p++ = media_type->ExtraData[12]; /* Level */
|
||||
*p++ = 0xff; /* Flag? */
|
||||
*p++ = 0xe0 | 0x01; /* Reserved | #sps */
|
||||
s = media_type->ExtraData + 20;
|
||||
size = ((UINT32)(*s)) * 256 + ((UINT32)(*(s + 1)));
|
||||
required += size + 2;
|
||||
if ((mdecoder->codec_context->extradata_size < 0) ||
|
||||
((size_t)mdecoder->codec_context->extradata_size < required))
|
||||
return FALSE;
|
||||
memcpy(p, s, size + 2);
|
||||
s += size + 2;
|
||||
p += size + 2;
|
||||
required++;
|
||||
if ((mdecoder->codec_context->extradata_size < 0) ||
|
||||
((size_t)mdecoder->codec_context->extradata_size < required))
|
||||
return FALSE;
|
||||
*p++ = 1; /* #pps */
|
||||
size = ((UINT32)(*s)) * 256 + ((UINT32)(*(s + 1)));
|
||||
required += size + 2;
|
||||
if ((mdecoder->codec_context->extradata_size < 0) ||
|
||||
((size_t)mdecoder->codec_context->extradata_size < required))
|
||||
return FALSE;
|
||||
memcpy(p, s, size + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(mdecoder->codec_context->extradata, media_type->ExtraData,
|
||||
media_type->ExtraDataSize);
|
||||
if ((mdecoder->codec_context->extradata_size < 0) ||
|
||||
((size_t)mdecoder->codec_context->extradata_size <
|
||||
media_type->ExtraDataSize + 8ull))
|
||||
return FALSE;
|
||||
memset(mdecoder->codec_context->extradata + media_type->ExtraDataSize, 0, 8);
|
||||
}
|
||||
}
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 18, 100)
|
||||
if (mdecoder->codec->capabilities & AV_CODEC_CAP_TRUNCATED)
|
||||
mdecoder->codec_context->flags |= AV_CODEC_FLAG_TRUNCATED;
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_ffmpeg_prepare(ITSMFDecoder* decoder)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
|
||||
if (avcodec_open2(mdecoder->codec_context, mdecoder->codec, nullptr) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "avcodec_open2 failed.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mdecoder->prepared = 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_ffmpeg_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* media_type)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
|
||||
WINPR_ASSERT(mdecoder);
|
||||
WINPR_ASSERT(media_type);
|
||||
|
||||
switch (media_type->MajorType)
|
||||
{
|
||||
case TSMF_MAJOR_TYPE_VIDEO:
|
||||
mdecoder->media_type = AVMEDIA_TYPE_VIDEO;
|
||||
break;
|
||||
|
||||
case TSMF_MAJOR_TYPE_AUDIO:
|
||||
mdecoder->media_type = AVMEDIA_TYPE_AUDIO;
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch (media_type->SubType)
|
||||
{
|
||||
case TSMF_SUB_TYPE_WVC1:
|
||||
mdecoder->codec_id = AV_CODEC_ID_VC1;
|
||||
break;
|
||||
|
||||
case TSMF_SUB_TYPE_WMA2:
|
||||
mdecoder->codec_id = AV_CODEC_ID_WMAV2;
|
||||
break;
|
||||
|
||||
case TSMF_SUB_TYPE_WMA9:
|
||||
mdecoder->codec_id = AV_CODEC_ID_WMAPRO;
|
||||
break;
|
||||
|
||||
case TSMF_SUB_TYPE_MP3:
|
||||
mdecoder->codec_id = AV_CODEC_ID_MP3;
|
||||
break;
|
||||
|
||||
case TSMF_SUB_TYPE_MP2A:
|
||||
mdecoder->codec_id = AV_CODEC_ID_MP2;
|
||||
break;
|
||||
|
||||
case TSMF_SUB_TYPE_MP2V:
|
||||
mdecoder->codec_id = AV_CODEC_ID_MPEG2VIDEO;
|
||||
break;
|
||||
|
||||
case TSMF_SUB_TYPE_WMV3:
|
||||
mdecoder->codec_id = AV_CODEC_ID_WMV3;
|
||||
break;
|
||||
|
||||
case TSMF_SUB_TYPE_AAC:
|
||||
mdecoder->codec_id = AV_CODEC_ID_AAC;
|
||||
|
||||
/* For AAC the pFormat is a HEAACWAVEINFO struct, and the codec data
|
||||
is at the end of it. See
|
||||
http://msdn.microsoft.com/en-us/library/dd757806.aspx */
|
||||
if (media_type->ExtraData)
|
||||
{
|
||||
if (media_type->ExtraDataSize < 12)
|
||||
return FALSE;
|
||||
|
||||
media_type->ExtraData += 12;
|
||||
media_type->ExtraDataSize -= 12;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TSMF_SUB_TYPE_H264:
|
||||
case TSMF_SUB_TYPE_AVC1:
|
||||
mdecoder->codec_id = AV_CODEC_ID_H264;
|
||||
break;
|
||||
|
||||
case TSMF_SUB_TYPE_AC3:
|
||||
mdecoder->codec_id = AV_CODEC_ID_AC3;
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!tsmf_ffmpeg_init_context(decoder))
|
||||
return FALSE;
|
||||
|
||||
if (!tsmf_ffmpeg_init_stream(decoder, media_type))
|
||||
return FALSE;
|
||||
|
||||
if (!tsmf_ffmpeg_prepare(decoder))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_ffmpeg_decode_video(ITSMFDecoder* decoder, const BYTE* data, UINT32 data_size,
|
||||
UINT32 extensions)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
int decoded = 0;
|
||||
int len = 0;
|
||||
AVFrame* frame = nullptr;
|
||||
BOOL ret = TRUE;
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 52 || \
|
||||
(LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR <= 20)
|
||||
len = avcodec_decode_video(mdecoder->codec_context, mdecoder->frame, &decoded, data, data_size);
|
||||
#else
|
||||
{
|
||||
AVPacket pkt = WINPR_C_ARRAY_INIT;
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 133, 100)
|
||||
av_init_packet(&pkt);
|
||||
#endif
|
||||
pkt.data = WINPR_CAST_CONST_PTR_AWAY(data, BYTE*);
|
||||
pkt.size = WINPR_ASSERTING_INT_CAST(int, data_size);
|
||||
|
||||
if (extensions & TSMM_SAMPLE_EXT_CLEANPOINT)
|
||||
pkt.flags |= AV_PKT_FLAG_KEY;
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 101)
|
||||
len = avcodec_decode_video2(mdecoder->codec_context, mdecoder->frame, &decoded, &pkt);
|
||||
#else
|
||||
len = avcodec_send_packet(mdecoder->codec_context, &pkt);
|
||||
if (len > 0)
|
||||
{
|
||||
len = avcodec_receive_frame(mdecoder->codec_context, mdecoder->frame);
|
||||
if (len == AVERROR(EAGAIN))
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "data_size %" PRIu32 ", avcodec_decode_video failed (%d)", data_size, len);
|
||||
ret = FALSE;
|
||||
}
|
||||
else if (!decoded)
|
||||
{
|
||||
WLog_ERR(TAG, "data_size %" PRIu32 ", no frame is decoded.", data_size);
|
||||
ret = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_TSMF("linesize[0] %d linesize[1] %d linesize[2] %d linesize[3] %d "
|
||||
"pix_fmt %d width %d height %d",
|
||||
mdecoder->frame->linesize[0], mdecoder->frame->linesize[1],
|
||||
mdecoder->frame->linesize[2], mdecoder->frame->linesize[3],
|
||||
mdecoder->codec_context->pix_fmt, mdecoder->codec_context->width,
|
||||
mdecoder->codec_context->height);
|
||||
mdecoder->decoded_size = av_image_get_buffer_size(mdecoder->codec_context->pix_fmt,
|
||||
mdecoder->codec_context->width,
|
||||
mdecoder->codec_context->height, 1);
|
||||
mdecoder->decoded_data = calloc(1, mdecoder->decoded_size);
|
||||
|
||||
if (!mdecoder->decoded_data)
|
||||
return FALSE;
|
||||
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 55
|
||||
frame = avcodec_alloc_frame();
|
||||
#else
|
||||
frame = av_frame_alloc();
|
||||
#endif
|
||||
av_image_fill_arrays(frame->data, frame->linesize, mdecoder->decoded_data,
|
||||
mdecoder->codec_context->pix_fmt, mdecoder->codec_context->width,
|
||||
mdecoder->codec_context->height, 1);
|
||||
|
||||
const uint8_t* ptr[AV_NUM_DATA_POINTERS] = WINPR_C_ARRAY_INIT;
|
||||
for (size_t x = 0; x < AV_NUM_DATA_POINTERS; x++)
|
||||
ptr[x] = mdecoder->frame->data[x];
|
||||
|
||||
av_image_copy(frame->data, frame->linesize, ptr, mdecoder->frame->linesize,
|
||||
mdecoder->codec_context->pix_fmt, mdecoder->codec_context->width,
|
||||
mdecoder->codec_context->height);
|
||||
av_free(frame);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL tsmf_ffmpeg_decode_audio(ITSMFDecoder* decoder, const BYTE* data, UINT32 data_size,
|
||||
WINPR_ATTR_UNUSED UINT32 extensions)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
int len = 0;
|
||||
int frame_size = 0;
|
||||
|
||||
if (mdecoder->decoded_size_max == 0)
|
||||
mdecoder->decoded_size_max = MAX_AUDIO_FRAME_SIZE + 16;
|
||||
|
||||
mdecoder->decoded_data = calloc(1, mdecoder->decoded_size_max);
|
||||
|
||||
if (!mdecoder->decoded_data)
|
||||
return FALSE;
|
||||
|
||||
/* align the memory for SSE2 needs */
|
||||
BYTE* dst = (BYTE*)(((uintptr_t)mdecoder->decoded_data + 15) & ~0x0F);
|
||||
size_t dst_offset = (size_t)(dst - mdecoder->decoded_data);
|
||||
const BYTE* src = data;
|
||||
UINT32 src_size = data_size;
|
||||
|
||||
while (src_size > 0)
|
||||
{
|
||||
/* Ensure enough space for decoding */
|
||||
if (mdecoder->decoded_size_max - mdecoder->decoded_size < MAX_AUDIO_FRAME_SIZE)
|
||||
{
|
||||
BYTE* tmp_data = nullptr;
|
||||
tmp_data = realloc(mdecoder->decoded_data, mdecoder->decoded_size_max * 2ull + 16ull);
|
||||
|
||||
if (!tmp_data)
|
||||
return FALSE;
|
||||
|
||||
mdecoder->decoded_size_max = mdecoder->decoded_size_max * 2ull + 16ull;
|
||||
mdecoder->decoded_data = tmp_data;
|
||||
dst = (BYTE*)(((uintptr_t)mdecoder->decoded_data + 15) & ~0x0F);
|
||||
|
||||
const size_t diff = (size_t)(dst - mdecoder->decoded_data);
|
||||
if (diff != dst_offset)
|
||||
{
|
||||
/* re-align the memory if the alignment has changed after realloc */
|
||||
memmove(dst, mdecoder->decoded_data + dst_offset, mdecoder->decoded_size);
|
||||
dst_offset = diff;
|
||||
}
|
||||
|
||||
dst += mdecoder->decoded_size;
|
||||
}
|
||||
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 52 || \
|
||||
(LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR <= 20)
|
||||
frame_size = mdecoder->decoded_size_max - mdecoder->decoded_size;
|
||||
len = avcodec_decode_audio2(mdecoder->codec_context, (int16_t*)dst, &frame_size, src,
|
||||
src_size);
|
||||
#else
|
||||
{
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 55
|
||||
AVFrame* decoded_frame = avcodec_alloc_frame();
|
||||
#else
|
||||
AVFrame* decoded_frame = av_frame_alloc();
|
||||
#endif
|
||||
int got_frame = 0;
|
||||
AVPacket pkt = WINPR_C_ARRAY_INIT;
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 133, 100)
|
||||
av_init_packet(&pkt);
|
||||
#endif
|
||||
|
||||
pkt.data = WINPR_CAST_CONST_PTR_AWAY(src, BYTE*);
|
||||
pkt.size = WINPR_ASSERTING_INT_CAST(int, src_size);
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 101)
|
||||
len = avcodec_decode_audio4(mdecoder->codec_context, decoded_frame, &got_frame, &pkt);
|
||||
#else
|
||||
len = avcodec_send_packet(mdecoder->codec_context, &pkt);
|
||||
if (len > 0)
|
||||
{
|
||||
len = avcodec_receive_frame(mdecoder->codec_context, decoded_frame);
|
||||
if (len == AVERROR(EAGAIN))
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (len >= 0 && got_frame)
|
||||
{
|
||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100)
|
||||
const int channels = mdecoder->codec_context->ch_layout.nb_channels;
|
||||
#else
|
||||
const int channels = mdecoder->codec_context->channels;
|
||||
#endif
|
||||
frame_size =
|
||||
av_samples_get_buffer_size(nullptr, channels, decoded_frame->nb_samples,
|
||||
mdecoder->codec_context->sample_fmt, 1);
|
||||
memcpy(dst, decoded_frame->data[0], frame_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
frame_size = 0;
|
||||
}
|
||||
|
||||
av_free(decoded_frame);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
src += len;
|
||||
src_size -= len;
|
||||
}
|
||||
|
||||
if (frame_size > 0)
|
||||
{
|
||||
mdecoder->decoded_size += frame_size;
|
||||
dst += frame_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (mdecoder->decoded_size == 0)
|
||||
{
|
||||
free(mdecoder->decoded_data);
|
||||
mdecoder->decoded_data = nullptr;
|
||||
}
|
||||
else if (dst_offset)
|
||||
{
|
||||
/* move the aligned decoded data to original place */
|
||||
memmove(mdecoder->decoded_data, mdecoder->decoded_data + dst_offset,
|
||||
mdecoder->decoded_size);
|
||||
}
|
||||
|
||||
DEBUG_TSMF("data_size %" PRIu32 " decoded_size %" PRIu32 "", data_size, mdecoder->decoded_size);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_ffmpeg_decode(ITSMFDecoder* decoder, const BYTE* data, UINT32 data_size,
|
||||
UINT32 extensions)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
|
||||
if (mdecoder->decoded_data)
|
||||
{
|
||||
free(mdecoder->decoded_data);
|
||||
mdecoder->decoded_data = nullptr;
|
||||
}
|
||||
|
||||
mdecoder->decoded_size = 0;
|
||||
|
||||
switch (mdecoder->media_type)
|
||||
{
|
||||
case AVMEDIA_TYPE_VIDEO:
|
||||
return tsmf_ffmpeg_decode_video(decoder, data, data_size, extensions);
|
||||
|
||||
case AVMEDIA_TYPE_AUDIO:
|
||||
return tsmf_ffmpeg_decode_audio(decoder, data, data_size, extensions);
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "unknown media type.");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static BYTE* tsmf_ffmpeg_get_decoded_data(ITSMFDecoder* decoder, UINT32* size)
|
||||
{
|
||||
BYTE* buf = nullptr;
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
*size = mdecoder->decoded_size;
|
||||
buf = mdecoder->decoded_data;
|
||||
mdecoder->decoded_data = nullptr;
|
||||
mdecoder->decoded_size = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static UINT32 tsmf_ffmpeg_get_decoded_format(ITSMFDecoder* decoder)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
|
||||
switch (mdecoder->codec_context->pix_fmt)
|
||||
{
|
||||
case AV_PIX_FMT_YUV420P:
|
||||
return RDP_PIXFMT_I420;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "unsupported pixel format %d", mdecoder->codec_context->pix_fmt);
|
||||
return (UINT32)-1;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL tsmf_ffmpeg_get_decoded_dimension(ITSMFDecoder* decoder, UINT32* width, UINT32* height)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
|
||||
if (mdecoder->codec_context->width > 0 && mdecoder->codec_context->height > 0)
|
||||
{
|
||||
*width = mdecoder->codec_context->width;
|
||||
*height = mdecoder->codec_context->height;
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void tsmf_ffmpeg_free(ITSMFDecoder* decoder)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||
|
||||
if (mdecoder->frame)
|
||||
av_free(mdecoder->frame);
|
||||
|
||||
free(mdecoder->decoded_data);
|
||||
|
||||
if (mdecoder->codec_context)
|
||||
{
|
||||
free(mdecoder->codec_context->extradata);
|
||||
mdecoder->codec_context->extradata = nullptr;
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 69, 100)
|
||||
avcodec_free_context(&mdecoder->codec_context);
|
||||
#else
|
||||
if (mdecoder->prepared)
|
||||
avcodec_close(mdecoder->codec_context);
|
||||
|
||||
av_free(mdecoder->codec_context);
|
||||
#endif
|
||||
}
|
||||
|
||||
free(decoder);
|
||||
}
|
||||
|
||||
static INIT_ONCE g_Initialized = INIT_ONCE_STATIC_INIT;
|
||||
static BOOL CALLBACK InitializeAvCodecs(WINPR_ATTR_UNUSED PINIT_ONCE once,
|
||||
WINPR_ATTR_UNUSED PVOID param,
|
||||
WINPR_ATTR_UNUSED PVOID* context)
|
||||
{
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
|
||||
avcodec_register_all();
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE ffmpeg_freerdp_tsmf_client_decoder_subsystem_entry(void* ptr))
|
||||
{
|
||||
ITSMFDecoder** sptr = (ITSMFDecoder**)ptr;
|
||||
WINPR_ASSERT(sptr);
|
||||
*sptr = nullptr;
|
||||
|
||||
TSMFFFmpegDecoder* decoder = nullptr;
|
||||
if (!InitOnceExecuteOnce(&g_Initialized, InitializeAvCodecs, nullptr, nullptr))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
WLog_DBG(TAG, "TSMFDecoderEntry FFMPEG");
|
||||
decoder = (TSMFFFmpegDecoder*)calloc(1, sizeof(TSMFFFmpegDecoder));
|
||||
|
||||
if (!decoder)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
decoder->iface.SetFormat = tsmf_ffmpeg_set_format;
|
||||
decoder->iface.Decode = tsmf_ffmpeg_decode;
|
||||
decoder->iface.GetDecodedData = tsmf_ffmpeg_get_decoded_data;
|
||||
decoder->iface.GetDecodedFormat = tsmf_ffmpeg_get_decoded_format;
|
||||
decoder->iface.GetDecodedDimension = tsmf_ffmpeg_get_decoded_dimension;
|
||||
decoder->iface.Free = tsmf_ffmpeg_free;
|
||||
*sptr = &decoder->iface;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
61
third_party/FreeRDP/channels/tsmf/client/gstreamer/CMakeLists.txt
vendored
Normal file
61
third_party/FreeRDP/channels/tsmf/client/gstreamer/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script for gstreamer subsystem
|
||||
#
|
||||
# (C) Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
define_channel_client_subsystem("tsmf" "gstreamer" "decoder")
|
||||
|
||||
if(NOT gstreamer_FOUND)
|
||||
message(FATAL_ERROR "GStreamer library not found, but required for TSMF module.")
|
||||
endif()
|
||||
|
||||
set(SRC "tsmf_gstreamer.c")
|
||||
|
||||
pkg_check_modules(gstreamerbase gstreamer-base-1.0 REQUIRED)
|
||||
pkg_check_modules(gstreamervideo gstreamer-video-1.0 REQUIRED)
|
||||
pkg_check_modules(gstreamerapp gstreamer-app-1.0 REQUIRED)
|
||||
freerdp_client_pc_add_requires_private("gstreamer-base-1.0;gstreamer-video-1.0;gstreamer-app-1.0")
|
||||
|
||||
set(LIBS ${gstreamer_LIBRARIES} ${gstreamerbase_LIBRARIES} ${gstreamervideo_LIBRARIES} ${gstreamerapp_LIBRARIES})
|
||||
include_directories(
|
||||
SYSTEM ${gstreamer_INCLUDE_DIRS} ${gstreamerbase_INCLUDE_DIRS} ${gstreamervideo_INCLUDE_DIRS}
|
||||
${gstreamerapp_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(ANDROID)
|
||||
set(SRC ${SRC} tsmf_android.c)
|
||||
else()
|
||||
find_package(X11 REQUIRED)
|
||||
freerdp_client_pc_add_requires_private("x11")
|
||||
|
||||
list(APPEND SRC tsmf_X11.c)
|
||||
list(APPEND LIBS ${X11_LIBRARIES} ${X11_Xext_LIB})
|
||||
if(NOT APPLE)
|
||||
list(APPEND LIBS rt)
|
||||
endif()
|
||||
|
||||
if(X11_Xext_FOUND)
|
||||
add_compile_definitions(WITH_XEXT=1)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS "${SRC}")
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${LIBS} winpr)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
501
third_party/FreeRDP/channels/tsmf/client/gstreamer/tsmf_X11.c
vendored
Normal file
501
third_party/FreeRDP/channels/tsmf/client/gstreamer/tsmf_X11.c
vendored
Normal file
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - GStreamer Decoder X11 specifics
|
||||
*
|
||||
* (C) Copyright 2014 Thincast Technologies GmbH
|
||||
* (C) Copyright 2014 Armin Novak <armin.novak@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#ifndef __CYGWIN__
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/platform.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
#include <gst/video/videooverlay.h>
|
||||
#else
|
||||
#include <gst/interfaces/xoverlay.h>
|
||||
#endif
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include <X11/extensions/shape.h>
|
||||
|
||||
#include <freerdp/channels/tsmf.h>
|
||||
|
||||
#include "tsmf_platform.h"
|
||||
#include "tsmf_constants.h"
|
||||
#include "tsmf_decoder.h"
|
||||
|
||||
#if !defined(WITH_XEXT)
|
||||
#warning "Building TSMF without shape extension support"
|
||||
#endif
|
||||
|
||||
struct X11Handle
|
||||
{
|
||||
int shmid;
|
||||
int* xfwin;
|
||||
#if defined(WITH_XEXT)
|
||||
BOOL has_shape;
|
||||
#endif
|
||||
Display* disp;
|
||||
Window subwin;
|
||||
BOOL subwinMapped;
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
GstVideoOverlay* overlay;
|
||||
#else
|
||||
GstXOverlay* overlay;
|
||||
#endif
|
||||
int subwinWidth;
|
||||
int subwinHeight;
|
||||
int subwinX;
|
||||
int subwinY;
|
||||
};
|
||||
|
||||
static const char* get_shm_id()
|
||||
{
|
||||
static char shm_id[128];
|
||||
sprintf_s(shm_id, sizeof(shm_id), "/com.freerdp.xfreerdp.tsmf_%016X", GetCurrentProcessId());
|
||||
return shm_id;
|
||||
}
|
||||
|
||||
static GstBusSyncReply tsmf_platform_bus_sync_handler(GstBus* bus, GstMessage* message,
|
||||
gpointer user_data)
|
||||
{
|
||||
struct X11Handle* hdl;
|
||||
|
||||
TSMFGstreamerDecoder* decoder = user_data;
|
||||
|
||||
if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_ELEMENT)
|
||||
return GST_BUS_PASS;
|
||||
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
if (!gst_is_video_overlay_prepare_window_handle_message(message))
|
||||
return GST_BUS_PASS;
|
||||
#else
|
||||
if (!gst_structure_has_name(message->structure, "prepare-xwindow-id"))
|
||||
return GST_BUS_PASS;
|
||||
#endif
|
||||
|
||||
hdl = (struct X11Handle*)decoder->platform;
|
||||
|
||||
if (hdl->subwin)
|
||||
{
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
hdl->overlay = GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(message));
|
||||
gst_video_overlay_set_window_handle(hdl->overlay, hdl->subwin);
|
||||
gst_video_overlay_handle_events(hdl->overlay, FALSE);
|
||||
#else
|
||||
hdl->overlay = GST_X_OVERLAY(GST_MESSAGE_SRC(message));
|
||||
#if GST_CHECK_VERSION(0, 10, 31)
|
||||
gst_x_overlay_set_window_handle(hdl->overlay, hdl->subwin);
|
||||
#else
|
||||
gst_x_overlay_set_xwindow_id(hdl->overlay, hdl->subwin);
|
||||
#endif
|
||||
gst_x_overlay_handle_events(hdl->overlay, TRUE);
|
||||
#endif
|
||||
|
||||
if (hdl->subwinWidth != -1 && hdl->subwinHeight != -1 && hdl->subwinX != -1 &&
|
||||
hdl->subwinY != -1)
|
||||
{
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
if (!gst_video_overlay_set_render_rectangle(hdl->overlay, 0, 0, hdl->subwinWidth,
|
||||
hdl->subwinHeight))
|
||||
{
|
||||
WLog_ERR(TAG, "Could not resize overlay!");
|
||||
}
|
||||
|
||||
gst_video_overlay_expose(hdl->overlay);
|
||||
#else
|
||||
if (!gst_x_overlay_set_render_rectangle(hdl->overlay, 0, 0, hdl->subwinWidth,
|
||||
hdl->subwinHeight))
|
||||
{
|
||||
WLog_ERR(TAG, "Could not resize overlay!");
|
||||
}
|
||||
|
||||
gst_x_overlay_expose(hdl->overlay);
|
||||
#endif
|
||||
XLockDisplay(hdl->disp);
|
||||
XMoveResizeWindow(hdl->disp, hdl->subwin, hdl->subwinX, hdl->subwinY, hdl->subwinWidth,
|
||||
hdl->subwinHeight);
|
||||
XSync(hdl->disp, FALSE);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning("Window was not available before retrieving the overlay!");
|
||||
}
|
||||
|
||||
gst_message_unref(message);
|
||||
|
||||
return GST_BUS_DROP;
|
||||
}
|
||||
|
||||
const char* tsmf_platform_get_video_sink(void)
|
||||
{
|
||||
return "autovideosink";
|
||||
}
|
||||
|
||||
const char* tsmf_platform_get_audio_sink(void)
|
||||
{
|
||||
return "autoaudiosink";
|
||||
}
|
||||
|
||||
int tsmf_platform_create(TSMFGstreamerDecoder* decoder)
|
||||
{
|
||||
struct X11Handle* hdl;
|
||||
|
||||
if (!decoder)
|
||||
return -1;
|
||||
|
||||
if (decoder->platform)
|
||||
return -1;
|
||||
|
||||
hdl = calloc(1, sizeof(struct X11Handle));
|
||||
if (!hdl)
|
||||
{
|
||||
WLog_ERR(TAG, "Could not allocate handle.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
decoder->platform = hdl;
|
||||
hdl->shmid = shm_open(get_shm_id(), (O_RDWR | O_CREAT), (PROT_READ | PROT_WRITE));
|
||||
if (hdl->shmid == -1)
|
||||
{
|
||||
char ebuffer[256] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "failed to get access to shared memory - shmget(%s): %i - %s", get_shm_id(),
|
||||
errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
|
||||
return -2;
|
||||
}
|
||||
|
||||
hdl->xfwin = mmap(0, sizeof(void*), PROT_READ | PROT_WRITE, MAP_SHARED, hdl->shmid, 0);
|
||||
if (hdl->xfwin == MAP_FAILED)
|
||||
{
|
||||
WLog_ERR(TAG, "shmat failed!");
|
||||
return -3;
|
||||
}
|
||||
|
||||
hdl->disp = XOpenDisplay(nullptr);
|
||||
if (!hdl->disp)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to open display");
|
||||
return -4;
|
||||
}
|
||||
|
||||
hdl->subwinMapped = FALSE;
|
||||
hdl->subwinX = -1;
|
||||
hdl->subwinY = -1;
|
||||
hdl->subwinWidth = -1;
|
||||
hdl->subwinHeight = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsmf_platform_set_format(TSMFGstreamerDecoder* decoder)
|
||||
{
|
||||
if (!decoder)
|
||||
return -1;
|
||||
|
||||
if (decoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
{
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsmf_platform_register_handler(TSMFGstreamerDecoder* decoder)
|
||||
{
|
||||
GstBus* bus;
|
||||
|
||||
if (!decoder)
|
||||
return -1;
|
||||
|
||||
if (!decoder->pipe)
|
||||
return -1;
|
||||
|
||||
bus = gst_pipeline_get_bus(GST_PIPELINE(decoder->pipe));
|
||||
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
gst_bus_set_sync_handler(bus, (GstBusSyncHandler)tsmf_platform_bus_sync_handler, decoder,
|
||||
nullptr);
|
||||
#else
|
||||
gst_bus_set_sync_handler(bus, (GstBusSyncHandler)tsmf_platform_bus_sync_handler, decoder);
|
||||
#endif
|
||||
|
||||
if (!bus)
|
||||
{
|
||||
WLog_ERR(TAG, "gst_pipeline_get_bus failed!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
gst_object_unref(bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsmf_platform_free(TSMFGstreamerDecoder* decoder)
|
||||
{
|
||||
struct X11Handle* hdl = decoder->platform;
|
||||
|
||||
if (!hdl)
|
||||
return -1;
|
||||
|
||||
if (hdl->disp)
|
||||
XCloseDisplay(hdl->disp);
|
||||
|
||||
if (hdl->xfwin)
|
||||
munmap(0, sizeof(void*));
|
||||
|
||||
if (hdl->shmid >= 0)
|
||||
close(hdl->shmid);
|
||||
|
||||
free(hdl);
|
||||
decoder->platform = nullptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsmf_window_create(TSMFGstreamerDecoder* decoder)
|
||||
{
|
||||
struct X11Handle* hdl;
|
||||
|
||||
if (decoder->media_type != TSMF_MAJOR_TYPE_VIDEO)
|
||||
{
|
||||
decoder->ready = TRUE;
|
||||
return -3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!decoder)
|
||||
return -1;
|
||||
|
||||
if (!decoder->platform)
|
||||
return -1;
|
||||
|
||||
hdl = (struct X11Handle*)decoder->platform;
|
||||
|
||||
if (!hdl->subwin)
|
||||
{
|
||||
XLockDisplay(hdl->disp);
|
||||
hdl->subwin = XCreateSimpleWindow(hdl->disp, *(int*)hdl->xfwin, 0, 0, 1, 1, 0, 0, 0);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
|
||||
if (!hdl->subwin)
|
||||
{
|
||||
WLog_ERR(TAG, "Could not create subwindow!");
|
||||
}
|
||||
}
|
||||
|
||||
tsmf_window_map(decoder);
|
||||
|
||||
decoder->ready = TRUE;
|
||||
#if defined(WITH_XEXT)
|
||||
int event, error;
|
||||
XLockDisplay(hdl->disp);
|
||||
hdl->has_shape = XShapeQueryExtension(hdl->disp, &event, &error);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsmf_window_resize(TSMFGstreamerDecoder* decoder, int x, int y, int width, int height,
|
||||
int nr_rects, RDP_RECT* rects)
|
||||
{
|
||||
struct X11Handle* hdl;
|
||||
|
||||
if (!decoder)
|
||||
return -1;
|
||||
|
||||
if (decoder->media_type != TSMF_MAJOR_TYPE_VIDEO)
|
||||
{
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (!decoder->platform)
|
||||
return -1;
|
||||
|
||||
hdl = (struct X11Handle*)decoder->platform;
|
||||
DEBUG_TSMF("resize: x=%d, y=%d, w=%d, h=%d", x, y, width, height);
|
||||
|
||||
if (hdl->overlay)
|
||||
{
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
|
||||
if (!gst_video_overlay_set_render_rectangle(hdl->overlay, 0, 0, width, height))
|
||||
{
|
||||
WLog_ERR(TAG, "Could not resize overlay!");
|
||||
}
|
||||
|
||||
gst_video_overlay_expose(hdl->overlay);
|
||||
#else
|
||||
if (!gst_x_overlay_set_render_rectangle(hdl->overlay, 0, 0, width, height))
|
||||
{
|
||||
WLog_ERR(TAG, "Could not resize overlay!");
|
||||
}
|
||||
|
||||
gst_x_overlay_expose(hdl->overlay);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (hdl->subwin)
|
||||
{
|
||||
hdl->subwinX = x;
|
||||
hdl->subwinY = y;
|
||||
hdl->subwinWidth = width;
|
||||
hdl->subwinHeight = height;
|
||||
|
||||
XLockDisplay(hdl->disp);
|
||||
XMoveResizeWindow(hdl->disp, hdl->subwin, hdl->subwinX, hdl->subwinY, hdl->subwinWidth,
|
||||
hdl->subwinHeight);
|
||||
|
||||
/* Unmap the window if there are no visibility rects */
|
||||
if (nr_rects == 0)
|
||||
tsmf_window_unmap(decoder);
|
||||
else
|
||||
tsmf_window_map(decoder);
|
||||
|
||||
#if defined(WITH_XEXT)
|
||||
if (hdl->has_shape)
|
||||
{
|
||||
XRectangle* xrects = nullptr;
|
||||
|
||||
if (nr_rects == 0)
|
||||
{
|
||||
xrects = calloc(1, sizeof(XRectangle));
|
||||
xrects->x = x;
|
||||
xrects->y = y;
|
||||
xrects->width = width;
|
||||
xrects->height = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
xrects = calloc(nr_rects, sizeof(XRectangle));
|
||||
}
|
||||
|
||||
if (xrects)
|
||||
{
|
||||
for (int i = 0; i < nr_rects; i++)
|
||||
{
|
||||
xrects[i].x = rects[i].x - x;
|
||||
xrects[i].y = rects[i].y - y;
|
||||
xrects[i].width = rects[i].width;
|
||||
xrects[i].height = rects[i].height;
|
||||
}
|
||||
|
||||
XShapeCombineRectangles(hdl->disp, hdl->subwin, ShapeBounding, x, y, xrects,
|
||||
nr_rects, ShapeSet, 0);
|
||||
free(xrects);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
XSync(hdl->disp, FALSE);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsmf_window_map(TSMFGstreamerDecoder* decoder)
|
||||
{
|
||||
struct X11Handle* hdl;
|
||||
if (!decoder)
|
||||
return -1;
|
||||
|
||||
hdl = (struct X11Handle*)decoder->platform;
|
||||
|
||||
/* Only need to map the window if it is not currently mapped */
|
||||
if ((hdl->subwin) && (!hdl->subwinMapped))
|
||||
{
|
||||
XLockDisplay(hdl->disp);
|
||||
XMapWindow(hdl->disp, hdl->subwin);
|
||||
hdl->subwinMapped = TRUE;
|
||||
XSync(hdl->disp, FALSE);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsmf_window_unmap(TSMFGstreamerDecoder* decoder)
|
||||
{
|
||||
struct X11Handle* hdl;
|
||||
if (!decoder)
|
||||
return -1;
|
||||
|
||||
hdl = (struct X11Handle*)decoder->platform;
|
||||
|
||||
/* only need to unmap window if it is currently mapped */
|
||||
if ((hdl->subwin) && (hdl->subwinMapped))
|
||||
{
|
||||
XLockDisplay(hdl->disp);
|
||||
XUnmapWindow(hdl->disp, hdl->subwin);
|
||||
hdl->subwinMapped = FALSE;
|
||||
XSync(hdl->disp, FALSE);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsmf_window_destroy(TSMFGstreamerDecoder* decoder)
|
||||
{
|
||||
struct X11Handle* hdl;
|
||||
|
||||
if (!decoder)
|
||||
return -1;
|
||||
|
||||
decoder->ready = FALSE;
|
||||
|
||||
if (decoder->media_type != TSMF_MAJOR_TYPE_VIDEO)
|
||||
return -3;
|
||||
|
||||
if (!decoder->platform)
|
||||
return -1;
|
||||
|
||||
hdl = (struct X11Handle*)decoder->platform;
|
||||
|
||||
if (hdl->subwin)
|
||||
{
|
||||
XLockDisplay(hdl->disp);
|
||||
XDestroyWindow(hdl->disp, hdl->subwin);
|
||||
XSync(hdl->disp, FALSE);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
}
|
||||
|
||||
hdl->overlay = nullptr;
|
||||
hdl->subwin = 0;
|
||||
hdl->subwinMapped = FALSE;
|
||||
hdl->subwinX = -1;
|
||||
hdl->subwinY = -1;
|
||||
hdl->subwinWidth = -1;
|
||||
hdl->subwinHeight = -1;
|
||||
return 0;
|
||||
}
|
||||
1064
third_party/FreeRDP/channels/tsmf/client/gstreamer/tsmf_gstreamer.c
vendored
Normal file
1064
third_party/FreeRDP/channels/tsmf/client/gstreamer/tsmf_gstreamer.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
105
third_party/FreeRDP/channels/tsmf/client/gstreamer/tsmf_platform.h
vendored
Normal file
105
third_party/FreeRDP/channels/tsmf/client/gstreamer/tsmf_platform.h
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - GStreamer Decoder
|
||||
* platform specific functions
|
||||
*
|
||||
* (C) Copyright 2014 Thincast Technologies GmbH
|
||||
* (C) Copyright 2014 Armin Novak <armin.novak@thincast.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_TSMF_CLIENT_GST_PLATFORM_H
|
||||
#define FREERDP_CHANNEL_TSMF_CLIENT_GST_PLATFORM_H
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <tsmf_decoder.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ITSMFDecoder iface;
|
||||
|
||||
int media_type; /* TSMF_MAJOR_TYPE_AUDIO or TSMF_MAJOR_TYPE_VIDEO */
|
||||
|
||||
gint64 duration;
|
||||
|
||||
GstState state;
|
||||
GstCaps* gst_caps;
|
||||
|
||||
GstElement* pipe;
|
||||
GstElement* src;
|
||||
GstElement* queue;
|
||||
GstElement* outsink;
|
||||
GstElement* volume;
|
||||
|
||||
BOOL ready;
|
||||
BOOL paused;
|
||||
UINT64 last_sample_start_time;
|
||||
UINT64 last_sample_end_time;
|
||||
BOOL seeking;
|
||||
UINT64 seek_offset;
|
||||
|
||||
double gstVolume;
|
||||
BOOL gstMuted;
|
||||
|
||||
int pipeline_start_time_valid; /* We've set the start time and have not reset the pipeline */
|
||||
int shutdown; /* The decoder stream is shutting down */
|
||||
|
||||
void* platform;
|
||||
|
||||
BOOL (*ack_cb)(void*, BOOL);
|
||||
void (*sync_cb)(void*);
|
||||
void* stream;
|
||||
|
||||
} TSMFGstreamerDecoder;
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL const char* tsmf_platform_get_video_sink(void);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL const char* tsmf_platform_get_audio_sink(void);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL int tsmf_platform_create(TSMFGstreamerDecoder* decoder);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL int tsmf_platform_set_format(TSMFGstreamerDecoder* decoder);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL int tsmf_platform_register_handler(TSMFGstreamerDecoder* decoder);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL int tsmf_platform_free(TSMFGstreamerDecoder* decoder);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL int tsmf_window_create(TSMFGstreamerDecoder* decoder);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL int tsmf_window_resize(TSMFGstreamerDecoder* decoder, int x, int y, int width,
|
||||
int height, int nr_rect, RDP_RECT* visible);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL int tsmf_window_destroy(TSMFGstreamerDecoder* decoder);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL int tsmf_window_map(TSMFGstreamerDecoder* decoder);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL int tsmf_window_unmap(TSMFGstreamerDecoder* decoder);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL BOOL tsmf_gstreamer_add_pad(TSMFGstreamerDecoder* mdecoder);
|
||||
|
||||
FREERDP_LOCAL void tsmf_gstreamer_remove_pad(TSMFGstreamerDecoder* mdecoder);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_TSMF_CLIENT_GST_PLATFORM_H */
|
||||
29
third_party/FreeRDP/channels/tsmf/client/oss/CMakeLists.txt
vendored
Normal file
29
third_party/FreeRDP/channels/tsmf/client/oss/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright (c) 2015 Rozhuk Ivan <rozhuk.im@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
define_channel_client_subsystem("tsmf" "oss" "audio")
|
||||
|
||||
find_package(OSS REQUIRED)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS tsmf_oss.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr ${OSS_LIBRARIES})
|
||||
|
||||
include_directories(..)
|
||||
include_directories(SYSTEM ${OSS_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
237
third_party/FreeRDP/channels/tsmf/client/oss/tsmf_oss.c
vendored
Normal file
237
third_party/FreeRDP/channels/tsmf/client/oss/tsmf_oss.c
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - OSS Audio Device
|
||||
*
|
||||
* Copyright (c) 2015 Rozhuk Ivan <rozhuk.im@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#if defined(__OpenBSD__)
|
||||
#include <soundcard.h>
|
||||
#else
|
||||
#include <sys/soundcard.h>
|
||||
#endif
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
|
||||
#include "tsmf_audio.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ITSMFAudioDevice iface;
|
||||
|
||||
char dev_name[PATH_MAX];
|
||||
int pcm_handle;
|
||||
|
||||
UINT32 sample_rate;
|
||||
UINT32 channels;
|
||||
UINT32 bits_per_sample;
|
||||
|
||||
UINT32 data_size_last;
|
||||
} TSMFOssAudioDevice;
|
||||
|
||||
#define OSS_LOG_ERR(_text, _error) \
|
||||
do \
|
||||
{ \
|
||||
if ((_error) != 0) \
|
||||
{ \
|
||||
char ebuffer[256] = WINPR_C_ARRAY_INIT; \
|
||||
WLog_ERR(TAG, "%s: %i - %s", (_text), (_error), \
|
||||
winpr_strerror((_error), ebuffer, sizeof(ebuffer))); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static BOOL tsmf_oss_open(ITSMFAudioDevice* audio, const char* device)
|
||||
{
|
||||
int tmp = 0;
|
||||
TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
|
||||
|
||||
if (oss == nullptr || oss->pcm_handle != -1)
|
||||
return FALSE;
|
||||
|
||||
if (device == nullptr) /* Default device. */
|
||||
{
|
||||
strncpy(oss->dev_name, "/dev/dsp", sizeof(oss->dev_name));
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(oss->dev_name, device, sizeof(oss->dev_name) - 1);
|
||||
}
|
||||
|
||||
if ((oss->pcm_handle = open(oss->dev_name, O_WRONLY)) < 0)
|
||||
{
|
||||
OSS_LOG_ERR("sound dev open failed", errno);
|
||||
oss->pcm_handle = -1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const int rc = ioctl(oss->pcm_handle, SNDCTL_DSP_GETFMTS, &tmp);
|
||||
if (rc == -1)
|
||||
{
|
||||
OSS_LOG_ERR("SNDCTL_DSP_GETFMTS failed", errno);
|
||||
close(oss->pcm_handle);
|
||||
oss->pcm_handle = -1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((AFMT_S16_LE & tmp) == 0)
|
||||
{
|
||||
OSS_LOG_ERR("SNDCTL_DSP_GETFMTS - AFMT_S16_LE", EOPNOTSUPP);
|
||||
close(oss->pcm_handle);
|
||||
oss->pcm_handle = -1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "open: %s", oss->dev_name);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_oss_set_format(ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels,
|
||||
UINT32 bits_per_sample)
|
||||
{
|
||||
int tmp = 0;
|
||||
TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
|
||||
|
||||
if (oss == nullptr || oss->pcm_handle == -1)
|
||||
return FALSE;
|
||||
|
||||
oss->sample_rate = sample_rate;
|
||||
oss->channels = channels;
|
||||
oss->bits_per_sample = bits_per_sample;
|
||||
tmp = AFMT_S16_LE;
|
||||
|
||||
if (ioctl(oss->pcm_handle, SNDCTL_DSP_SETFMT, &tmp) == -1)
|
||||
OSS_LOG_ERR("SNDCTL_DSP_SETFMT failed", errno);
|
||||
|
||||
tmp = WINPR_ASSERTING_INT_CAST(int, channels);
|
||||
|
||||
if (ioctl(oss->pcm_handle, SNDCTL_DSP_CHANNELS, &tmp) == -1)
|
||||
OSS_LOG_ERR("SNDCTL_DSP_CHANNELS failed", errno);
|
||||
|
||||
tmp = WINPR_ASSERTING_INT_CAST(int, sample_rate);
|
||||
|
||||
if (ioctl(oss->pcm_handle, SNDCTL_DSP_SPEED, &tmp) == -1)
|
||||
OSS_LOG_ERR("SNDCTL_DSP_SPEED failed", errno);
|
||||
|
||||
tmp = WINPR_ASSERTING_INT_CAST(int, ((bits_per_sample / 8) * channels * sample_rate));
|
||||
|
||||
if (ioctl(oss->pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
|
||||
OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
|
||||
|
||||
DEBUG_TSMF("sample_rate %" PRIu32 " channels %" PRIu32 " bits_per_sample %" PRIu32 "",
|
||||
sample_rate, channels, bits_per_sample);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_oss_play(ITSMFAudioDevice* audio, const BYTE* data, UINT32 data_size)
|
||||
{
|
||||
UINT32 offset = 0;
|
||||
TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
|
||||
DEBUG_TSMF("tsmf_oss_play: data_size %" PRIu32 "", data_size);
|
||||
|
||||
if (oss == nullptr || oss->pcm_handle == -1)
|
||||
return FALSE;
|
||||
|
||||
if (data == nullptr || data_size == 0)
|
||||
return TRUE;
|
||||
|
||||
offset = 0;
|
||||
oss->data_size_last = data_size;
|
||||
|
||||
while (offset < data_size)
|
||||
{
|
||||
const ssize_t status = write(oss->pcm_handle, &data[offset], (data_size - offset));
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
OSS_LOG_ERR("write fail", errno);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
offset += status;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT64 tsmf_oss_get_latency(ITSMFAudioDevice* audio)
|
||||
{
|
||||
UINT64 latency = 0;
|
||||
TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
|
||||
|
||||
if (oss == nullptr)
|
||||
return 0;
|
||||
|
||||
// latency = ((oss->data_size_last / (oss->bits_per_sample / 8)) * oss->sample_rate);
|
||||
// WLog_INFO(TAG, "latency: %zu", latency);
|
||||
return latency;
|
||||
}
|
||||
|
||||
static BOOL tsmf_oss_flush(WINPR_ATTR_UNUSED ITSMFAudioDevice* audio)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void tsmf_oss_free(ITSMFAudioDevice* audio)
|
||||
{
|
||||
TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
|
||||
|
||||
if (oss == nullptr)
|
||||
return;
|
||||
|
||||
if (oss->pcm_handle != -1)
|
||||
{
|
||||
WLog_INFO(TAG, "close: %s", oss->dev_name);
|
||||
close(oss->pcm_handle);
|
||||
}
|
||||
|
||||
free(oss);
|
||||
}
|
||||
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_tsmf_client_audio_subsystem_entry(void* ptr))
|
||||
{
|
||||
ITSMFAudioDevice** sptr = (ITSMFAudioDevice**)ptr;
|
||||
WINPR_ASSERT(sptr);
|
||||
*sptr = nullptr;
|
||||
|
||||
TSMFOssAudioDevice* oss = calloc(1, sizeof(TSMFOssAudioDevice));
|
||||
if (!oss)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
oss->iface.Open = tsmf_oss_open;
|
||||
oss->iface.SetFormat = tsmf_oss_set_format;
|
||||
oss->iface.Play = tsmf_oss_play;
|
||||
oss->iface.GetLatency = tsmf_oss_get_latency;
|
||||
oss->iface.Flush = tsmf_oss_flush;
|
||||
oss->iface.Free = tsmf_oss_free;
|
||||
oss->pcm_handle = -1;
|
||||
*sptr = &oss->iface;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
30
third_party/FreeRDP/channels/tsmf/client/pulse/CMakeLists.txt
vendored
Normal file
30
third_party/FreeRDP/channels/tsmf/client/pulse/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
define_channel_client_subsystem("tsmf" "pulse" "audio")
|
||||
|
||||
find_package(PulseAudio REQUIRED)
|
||||
freerdp_client_pc_add_requires_private("libpulse")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS tsmf_pulse.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr ${PULSEAUDIO_LIBRARY} ${PULSEAUDIO_MAINLOOP_LIBRARY})
|
||||
|
||||
include_directories(..)
|
||||
include_directories(SYSTEM ${PULSEAUDIO_INCLUDE_DIR})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
427
third_party/FreeRDP/channels/tsmf/client/pulse/tsmf_pulse.c
vendored
Normal file
427
third_party/FreeRDP/channels/tsmf/client/pulse/tsmf_pulse.c
vendored
Normal file
@@ -0,0 +1,427 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - PulseAudio Device
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
#include <freerdp/utils/helpers.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#include "tsmf_audio.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ITSMFAudioDevice iface;
|
||||
|
||||
char device[32];
|
||||
pa_threaded_mainloop* mainloop;
|
||||
pa_context* context;
|
||||
pa_sample_spec sample_spec;
|
||||
pa_stream* stream;
|
||||
} TSMFPulseAudioDevice;
|
||||
|
||||
static void tsmf_pulse_context_state_callback(pa_context* context, void* userdata)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*)userdata;
|
||||
pa_context_state_t state = pa_context_get_state(context);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case PA_CONTEXT_READY:
|
||||
DEBUG_TSMF("PA_CONTEXT_READY");
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_FAILED:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
DEBUG_TSMF("state %d", state);
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_TSMF("state %d", state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_connect(TSMFPulseAudioDevice* pulse)
|
||||
{
|
||||
pa_context_state_t state = PA_CONTEXT_FAILED;
|
||||
|
||||
if (!pulse->context)
|
||||
return FALSE;
|
||||
|
||||
if (pa_context_connect(pulse->context, nullptr, 0, nullptr))
|
||||
{
|
||||
WLog_ERR(TAG, "pa_context_connect failed (%d)", pa_context_errno(pulse->context));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
|
||||
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_ERR(TAG, "pa_threaded_mainloop_start failed (%d)", pa_context_errno(pulse->context));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
state = pa_context_get_state(pulse->context);
|
||||
|
||||
if (state == PA_CONTEXT_READY)
|
||||
break;
|
||||
|
||||
if (!PA_CONTEXT_IS_GOOD(state))
|
||||
{
|
||||
DEBUG_TSMF("bad context state (%d)", pa_context_errno(pulse->context));
|
||||
break;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
|
||||
if (state == PA_CONTEXT_READY)
|
||||
{
|
||||
DEBUG_TSMF("connected");
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pa_context_disconnect(pulse->context);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_open(ITSMFAudioDevice* audio, const char* device)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*)audio;
|
||||
|
||||
if (device)
|
||||
{
|
||||
strncpy(pulse->device, device, sizeof(pulse->device) - 1);
|
||||
}
|
||||
|
||||
pulse->mainloop = pa_threaded_mainloop_new();
|
||||
|
||||
if (!pulse->mainloop)
|
||||
{
|
||||
WLog_ERR(TAG, "pa_threaded_mainloop_new failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop),
|
||||
freerdp_getApplicationDetailsString());
|
||||
|
||||
if (!pulse->context)
|
||||
{
|
||||
WLog_ERR(TAG, "pa_context_new failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback(pulse->context, tsmf_pulse_context_state_callback, pulse);
|
||||
|
||||
if (!tsmf_pulse_connect(pulse))
|
||||
{
|
||||
WLog_ERR(TAG, "tsmf_pulse_connect failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DEBUG_TSMF("open device %s", pulse->device);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void tsmf_pulse_stream_success_callback(WINPR_ATTR_UNUSED pa_stream* stream,
|
||||
WINPR_ATTR_UNUSED int success, void* userdata)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*)userdata;
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
}
|
||||
|
||||
static void tsmf_pulse_wait_for_operation(TSMFPulseAudioDevice* pulse, pa_operation* operation)
|
||||
{
|
||||
if (operation == nullptr)
|
||||
return;
|
||||
|
||||
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
|
||||
{
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
|
||||
pa_operation_unref(operation);
|
||||
}
|
||||
|
||||
static void tsmf_pulse_stream_state_callback(pa_stream* stream, void* userdata)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*)userdata;
|
||||
WINPR_ASSERT(pulse);
|
||||
|
||||
pa_stream_state_t state = pa_stream_get_state(stream);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case PA_STREAM_READY:
|
||||
DEBUG_TSMF("PA_STREAM_READY");
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_STREAM_FAILED:
|
||||
case PA_STREAM_TERMINATED:
|
||||
DEBUG_TSMF("state %d", state);
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_TSMF("state %d", state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void tsmf_pulse_stream_request_callback(WINPR_ATTR_UNUSED pa_stream* stream,
|
||||
WINPR_ATTR_UNUSED size_t length, void* userdata)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*)userdata;
|
||||
DEBUG_TSMF("%" PRIdz "", length);
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_close_stream(TSMFPulseAudioDevice* pulse)
|
||||
{
|
||||
if (!pulse->context || !pulse->stream)
|
||||
return FALSE;
|
||||
|
||||
DEBUG_TSMF("");
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
pa_stream_set_write_callback(pulse->stream, nullptr, nullptr);
|
||||
tsmf_pulse_wait_for_operation(
|
||||
pulse, pa_stream_drain(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
|
||||
pa_stream_disconnect(pulse->stream);
|
||||
pa_stream_unref(pulse->stream);
|
||||
pulse->stream = nullptr;
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_open_stream(TSMFPulseAudioDevice* pulse)
|
||||
{
|
||||
pa_stream_state_t state = PA_STREAM_FAILED;
|
||||
pa_buffer_attr buffer_attr = WINPR_C_ARRAY_INIT;
|
||||
|
||||
if (!pulse->context)
|
||||
return FALSE;
|
||||
|
||||
DEBUG_TSMF("");
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
pulse->stream = pa_stream_new(pulse->context, freerdp_getApplicationDetailsString(),
|
||||
&pulse->sample_spec, nullptr);
|
||||
|
||||
if (!pulse->stream)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_ERR(TAG, "pa_stream_new failed (%d)", pa_context_errno(pulse->context));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pa_stream_set_state_callback(pulse->stream, tsmf_pulse_stream_state_callback, pulse);
|
||||
pa_stream_set_write_callback(pulse->stream, tsmf_pulse_stream_request_callback, pulse);
|
||||
buffer_attr.maxlength = (uint32_t)pa_usec_to_bytes(500000, &pulse->sample_spec);
|
||||
buffer_attr.tlength = (uint32_t)pa_usec_to_bytes(250000, &pulse->sample_spec);
|
||||
buffer_attr.prebuf = (UINT32)-1;
|
||||
buffer_attr.minreq = (UINT32)-1;
|
||||
buffer_attr.fragsize = (UINT32)-1;
|
||||
|
||||
if (pa_stream_connect_playback(
|
||||
pulse->stream, pulse->device[0] ? pulse->device : nullptr, &buffer_attr,
|
||||
PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
|
||||
nullptr, nullptr) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_ERR(TAG, "pa_stream_connect_playback failed (%d)", pa_context_errno(pulse->context));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
state = pa_stream_get_state(pulse->stream);
|
||||
|
||||
if (state == PA_STREAM_READY)
|
||||
break;
|
||||
|
||||
if (!PA_STREAM_IS_GOOD(state))
|
||||
{
|
||||
WLog_ERR(TAG, "bad stream state (%d)", pa_context_errno(pulse->context));
|
||||
break;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
|
||||
if (state == PA_STREAM_READY)
|
||||
{
|
||||
DEBUG_TSMF("connected");
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
tsmf_pulse_close_stream(pulse);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_set_format(ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels,
|
||||
WINPR_ATTR_UNUSED UINT32 bits_per_sample)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*)audio;
|
||||
DEBUG_TSMF("sample_rate %" PRIu32 " channels %" PRIu32 " bits_per_sample %" PRIu32 "",
|
||||
sample_rate, channels, bits_per_sample);
|
||||
pulse->sample_spec.rate = sample_rate;
|
||||
|
||||
WINPR_ASSERT(channels <= UINT8_MAX);
|
||||
pulse->sample_spec.channels = (uint8_t)channels;
|
||||
pulse->sample_spec.format = PA_SAMPLE_S16LE;
|
||||
return tsmf_pulse_open_stream(pulse);
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_play(ITSMFAudioDevice* audio, const BYTE* data, UINT32 data_size)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*)audio;
|
||||
const BYTE* src = nullptr;
|
||||
size_t len = 0;
|
||||
int ret = 0;
|
||||
DEBUG_TSMF("data_size %" PRIu32 "", data_size);
|
||||
|
||||
if (pulse->stream)
|
||||
{
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
src = data;
|
||||
|
||||
while (data_size > 0)
|
||||
{
|
||||
while ((len = pa_stream_writable_size(pulse->stream)) == 0)
|
||||
{
|
||||
DEBUG_TSMF("waiting");
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
|
||||
if (len == (size_t)-1)
|
||||
break;
|
||||
|
||||
if (len > data_size)
|
||||
len = data_size;
|
||||
|
||||
ret = pa_stream_write(pulse->stream, src, len, nullptr, 0LL, PA_SEEK_RELATIVE);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
DEBUG_TSMF("pa_stream_write failed (%d)", pa_context_errno(pulse->context));
|
||||
break;
|
||||
}
|
||||
|
||||
src += len;
|
||||
data_size -= len;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT64 tsmf_pulse_get_latency(ITSMFAudioDevice* audio)
|
||||
{
|
||||
pa_usec_t usec = 0;
|
||||
UINT64 latency = 0;
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*)audio;
|
||||
|
||||
if (pulse->stream && pa_stream_get_latency(pulse->stream, &usec, nullptr) == 0)
|
||||
{
|
||||
latency = ((UINT64)usec) * 10LL;
|
||||
}
|
||||
|
||||
return latency;
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_flush(ITSMFAudioDevice* audio)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*)audio;
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
tsmf_pulse_wait_for_operation(
|
||||
pulse, pa_stream_flush(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void tsmf_pulse_free(ITSMFAudioDevice* audio)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*)audio;
|
||||
DEBUG_TSMF("");
|
||||
tsmf_pulse_close_stream(pulse);
|
||||
|
||||
if (pulse->mainloop)
|
||||
{
|
||||
pa_threaded_mainloop_stop(pulse->mainloop);
|
||||
}
|
||||
|
||||
if (pulse->context)
|
||||
{
|
||||
pa_context_disconnect(pulse->context);
|
||||
pa_context_unref(pulse->context);
|
||||
pulse->context = nullptr;
|
||||
}
|
||||
|
||||
if (pulse->mainloop)
|
||||
{
|
||||
pa_threaded_mainloop_free(pulse->mainloop);
|
||||
pulse->mainloop = nullptr;
|
||||
}
|
||||
|
||||
free(pulse);
|
||||
}
|
||||
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE pulse_freerdp_tsmf_client_audio_subsystem_entry(void* ptr))
|
||||
{
|
||||
ITSMFAudioDevice** sptr = (ITSMFAudioDevice**)ptr;
|
||||
WINPR_ASSERT(sptr);
|
||||
*sptr = nullptr;
|
||||
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*)calloc(1, sizeof(TSMFPulseAudioDevice));
|
||||
|
||||
if (!pulse)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
pulse->iface.Open = tsmf_pulse_open;
|
||||
pulse->iface.SetFormat = tsmf_pulse_set_format;
|
||||
pulse->iface.Play = tsmf_pulse_play;
|
||||
pulse->iface.GetLatency = tsmf_pulse_get_latency;
|
||||
pulse->iface.Flush = tsmf_pulse_flush;
|
||||
pulse->iface.Free = tsmf_pulse_free;
|
||||
*sptr = &pulse->iface;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
99
third_party/FreeRDP/channels/tsmf/client/tsmf_audio.c
vendored
Normal file
99
third_party/FreeRDP/channels/tsmf/client/tsmf_audio.c
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - Audio Device Manager
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tsmf_audio.h"
|
||||
|
||||
static ITSMFAudioDevice* tsmf_load_audio_device_by_name(const char* name, const char* device)
|
||||
{
|
||||
ITSMFAudioDevice* audio = nullptr;
|
||||
union
|
||||
{
|
||||
PVIRTUALCHANNELENTRY pvce;
|
||||
TSMF_AUDIO_DEVICE_ENTRY entry;
|
||||
} cnv;
|
||||
cnv.pvce = freerdp_load_channel_addin_entry("tsmf", name, "audio", 0);
|
||||
|
||||
if (!cnv.entry)
|
||||
return nullptr;
|
||||
|
||||
const UINT rc = cnv.entry(&audio);
|
||||
|
||||
if ((rc != CHANNEL_RC_OK) || !audio)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to call export function in %s", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!audio->Open(audio, device))
|
||||
{
|
||||
audio->Free(audio);
|
||||
audio = nullptr;
|
||||
WLog_ERR(TAG, "failed to open, name: %s, device: %s", name, device);
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_DBG(TAG, "name: %s, device: %s", name, device);
|
||||
}
|
||||
|
||||
return audio;
|
||||
}
|
||||
|
||||
ITSMFAudioDevice* tsmf_load_audio_device(const char* name, const char* device)
|
||||
{
|
||||
ITSMFAudioDevice* audio = nullptr;
|
||||
|
||||
if (name)
|
||||
{
|
||||
audio = tsmf_load_audio_device_by_name(name, device);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(WITH_PULSE)
|
||||
if (!audio)
|
||||
audio = tsmf_load_audio_device_by_name("pulse", device);
|
||||
#endif
|
||||
|
||||
#if defined(WITH_OSS)
|
||||
if (!audio)
|
||||
audio = tsmf_load_audio_device_by_name("oss", device);
|
||||
#endif
|
||||
|
||||
#if defined(WITH_ALSA)
|
||||
if (!audio)
|
||||
audio = tsmf_load_audio_device_by_name("alsa", device);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (audio == nullptr)
|
||||
{
|
||||
WLog_ERR(TAG, "no sound device.");
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_DBG(TAG, "name: %s, device: %s", name, device);
|
||||
}
|
||||
|
||||
return audio;
|
||||
}
|
||||
53
third_party/FreeRDP/channels/tsmf/client/tsmf_audio.h
vendored
Normal file
53
third_party/FreeRDP/channels/tsmf/client/tsmf_audio.h
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - Audio Device Manager
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_TSMF_CLIENT_AUDIO_H
|
||||
#define FREERDP_CHANNEL_TSMF_CLIENT_AUDIO_H
|
||||
|
||||
#include "tsmf_types.h"
|
||||
|
||||
typedef struct s_ITSMFAudioDevice ITSMFAudioDevice;
|
||||
|
||||
struct s_ITSMFAudioDevice
|
||||
{
|
||||
/* Open the audio device. */
|
||||
WINPR_ATTR_NODISCARD BOOL (*Open)(ITSMFAudioDevice* audio, const char* device);
|
||||
/* Set the audio data format. */
|
||||
WINPR_ATTR_NODISCARD BOOL (*SetFormat)(ITSMFAudioDevice* audio, UINT32 sample_rate,
|
||||
UINT32 channels, UINT32 bits_per_sample);
|
||||
/* Play audio data. */
|
||||
WINPR_ATTR_NODISCARD BOOL (*Play)(ITSMFAudioDevice* audio, const BYTE* data, UINT32 data_size);
|
||||
/* Get the latency of the last written sample, in 100ns */
|
||||
WINPR_ATTR_NODISCARD UINT64 (*GetLatency)(ITSMFAudioDevice* audio);
|
||||
/* Change the playback volume level */
|
||||
WINPR_ATTR_NODISCARD BOOL (*ChangeVolume)(ITSMFAudioDevice* audio, UINT32 newVolume,
|
||||
UINT32 muted);
|
||||
/* Flush queued audio data */
|
||||
WINPR_ATTR_NODISCARD BOOL (*Flush)(ITSMFAudioDevice* audio);
|
||||
/* Free the audio device */
|
||||
void (*Free)(ITSMFAudioDevice* audio);
|
||||
};
|
||||
|
||||
#define TSMF_AUDIO_DEVICE_EXPORT_FUNC_NAME "TSMFAudioDeviceEntry"
|
||||
typedef UINT(VCAPITYPE* TSMF_AUDIO_DEVICE_ENTRY)(ITSMFAudioDevice** dev);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL ITSMFAudioDevice* tsmf_load_audio_device(const char* name,
|
||||
const char* device);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_TSMF_CLIENT_AUDIO_H */
|
||||
615
third_party/FreeRDP/channels/tsmf/client/tsmf_codec.c
vendored
Normal file
615
third_party/FreeRDP/channels/tsmf/client/tsmf_codec.c
vendored
Normal file
@@ -0,0 +1,615 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - Codec
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/print.h>
|
||||
|
||||
#include "tsmf_decoder.h"
|
||||
#include "tsmf_constants.h"
|
||||
#include "tsmf_types.h"
|
||||
|
||||
#include "tsmf_codec.h"
|
||||
|
||||
#include <freerdp/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("tsmf.client")
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BYTE guid[16];
|
||||
const char* name;
|
||||
int type;
|
||||
} TSMFMediaTypeMap;
|
||||
|
||||
static const TSMFMediaTypeMap tsmf_major_type_map[] = {
|
||||
/* 73646976-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x76, 0x69, 0x64, 0x73, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIATYPE_Video",
|
||||
TSMF_MAJOR_TYPE_VIDEO },
|
||||
|
||||
/* 73647561-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x61, 0x75, 0x64, 0x73, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIATYPE_Audio",
|
||||
TSMF_MAJOR_TYPE_AUDIO },
|
||||
|
||||
{ { 0 }, "Unknown", TSMF_MAJOR_TYPE_UNKNOWN }
|
||||
};
|
||||
|
||||
static const TSMFMediaTypeMap tsmf_sub_type_map[] = {
|
||||
/* 31435657-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x57, 0x56, 0x43, 0x31, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_WVC1",
|
||||
TSMF_SUB_TYPE_WVC1 },
|
||||
|
||||
/* 00000160-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_WMAudioV1", /* V7, V8 has the same GUID */
|
||||
TSMF_SUB_TYPE_WMA1 },
|
||||
|
||||
/* 00000161-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x61, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_WMAudioV2", /* V7, V8 has the same GUID */
|
||||
TSMF_SUB_TYPE_WMA2 },
|
||||
|
||||
/* 00000162-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x62, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_WMAudioV9",
|
||||
TSMF_SUB_TYPE_WMA9 },
|
||||
|
||||
/* 00000055-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_MP3",
|
||||
TSMF_SUB_TYPE_MP3 },
|
||||
|
||||
/* E06D802B-DB46-11CF-B4D1-00805F6CBBEA */
|
||||
{ { 0x2B, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB,
|
||||
0xEA },
|
||||
"MEDIASUBTYPE_MPEG2_AUDIO",
|
||||
TSMF_SUB_TYPE_MP2A },
|
||||
|
||||
/* E06D8026-DB46-11CF-B4D1-00805F6CBBEA */
|
||||
{ { 0x26, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB,
|
||||
0xEA },
|
||||
"MEDIASUBTYPE_MPEG2_VIDEO",
|
||||
TSMF_SUB_TYPE_MP2V },
|
||||
|
||||
/* 31564D57-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x57, 0x4D, 0x56, 0x31, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_WMV1",
|
||||
TSMF_SUB_TYPE_WMV1 },
|
||||
|
||||
/* 32564D57-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x57, 0x4D, 0x56, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_WMV2",
|
||||
TSMF_SUB_TYPE_WMV2 },
|
||||
|
||||
/* 33564D57-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x57, 0x4D, 0x56, 0x33, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_WMV3",
|
||||
TSMF_SUB_TYPE_WMV3 },
|
||||
|
||||
/* 00001610-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x10, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_MPEG_HEAAC",
|
||||
TSMF_SUB_TYPE_AAC },
|
||||
|
||||
/* 34363248-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x48, 0x32, 0x36, 0x34, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_H264",
|
||||
TSMF_SUB_TYPE_H264 },
|
||||
|
||||
/* 31435641-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x41, 0x56, 0x43, 0x31, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_AVC1",
|
||||
TSMF_SUB_TYPE_AVC1 },
|
||||
|
||||
/* 3334504D-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x4D, 0x50, 0x34, 0x33, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_MP43",
|
||||
TSMF_SUB_TYPE_MP43 },
|
||||
|
||||
/* 5634504D-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x4D, 0x50, 0x34, 0x56, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_MP4S",
|
||||
TSMF_SUB_TYPE_MP4S },
|
||||
|
||||
/* 3234504D-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x4D, 0x50, 0x34, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_MP42",
|
||||
TSMF_SUB_TYPE_MP42 },
|
||||
|
||||
/* 3253344D-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x4D, 0x34, 0x53, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_MP42",
|
||||
TSMF_SUB_TYPE_M4S2 },
|
||||
|
||||
/* E436EB81-524F-11CE-9F53-0020AF0BA770 */
|
||||
{ { 0x81, 0xEB, 0x36, 0xE4, 0x4F, 0x52, 0xCE, 0x11, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7,
|
||||
0x70 },
|
||||
"MEDIASUBTYPE_MP1V",
|
||||
TSMF_SUB_TYPE_MP1V },
|
||||
|
||||
/* 00000050-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_MP1A",
|
||||
TSMF_SUB_TYPE_MP1A },
|
||||
|
||||
/* E06D802C-DB46-11CF-B4D1-00805F6CBBEA */
|
||||
{ { 0x2C, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB,
|
||||
0xEA },
|
||||
"MEDIASUBTYPE_DOLBY_AC3",
|
||||
TSMF_SUB_TYPE_AC3 },
|
||||
|
||||
/* 32595559-0000-0010-8000-00AA00389B71 */
|
||||
{ { 0x59, 0x55, 0x59, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_YUY2",
|
||||
TSMF_SUB_TYPE_YUY2 },
|
||||
|
||||
/* Opencodec IDS */
|
||||
{ { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_FLAC",
|
||||
TSMF_SUB_TYPE_FLAC },
|
||||
|
||||
{ { 0x61, 0x34, 0x70, 0x6D, 0x7A, 0x76, 0x4D, 0x49, 0xB4, 0x78, 0xF2, 0x9D, 0x25, 0xDC, 0x90,
|
||||
0x37 },
|
||||
"MEDIASUBTYPE_OGG",
|
||||
TSMF_SUB_TYPE_OGG },
|
||||
|
||||
{ { 0x4D, 0x34, 0x53, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_H263",
|
||||
TSMF_SUB_TYPE_H263 },
|
||||
|
||||
/* WebMMF codec IDS */
|
||||
{ { 0x56, 0x50, 0x38, 0x30, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
|
||||
0x71 },
|
||||
"MEDIASUBTYPE_VP8",
|
||||
TSMF_SUB_TYPE_VP8 },
|
||||
|
||||
{ { 0x0B, 0xD1, 0x2F, 0x8D, 0x41, 0x58, 0x6B, 0x4A, 0x89, 0x05, 0x58, 0x8F, 0xEC, 0x1A, 0xDE,
|
||||
0xD9 },
|
||||
"MEDIASUBTYPE_OGG",
|
||||
TSMF_SUB_TYPE_OGG },
|
||||
|
||||
{ { 0 }, "Unknown", TSMF_SUB_TYPE_UNKNOWN }
|
||||
|
||||
};
|
||||
|
||||
static const TSMFMediaTypeMap tsmf_format_type_map[] = {
|
||||
/* AED4AB2D-7326-43CB-9464-C879CAB9C43D */
|
||||
{ { 0x2D, 0xAB, 0xD4, 0xAE, 0x26, 0x73, 0xCB, 0x43, 0x94, 0x64, 0xC8, 0x79, 0xCA, 0xB9, 0xC4,
|
||||
0x3D },
|
||||
"FORMAT_MFVideoFormat",
|
||||
TSMF_FORMAT_TYPE_MFVIDEOFORMAT },
|
||||
|
||||
/* 05589F81-C356-11CE-BF01-00AA0055595A */
|
||||
{ { 0x81, 0x9F, 0x58, 0x05, 0x56, 0xC3, 0xCE, 0x11, 0xBF, 0x01, 0x00, 0xAA, 0x00, 0x55, 0x59,
|
||||
0x5A },
|
||||
"FORMAT_WaveFormatEx",
|
||||
TSMF_FORMAT_TYPE_WAVEFORMATEX },
|
||||
|
||||
/* E06D80E3-DB46-11CF-B4D1-00805F6CBBEA */
|
||||
{ { 0xE3, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB,
|
||||
0xEA },
|
||||
"FORMAT_MPEG2_VIDEO",
|
||||
TSMF_FORMAT_TYPE_MPEG2VIDEOINFO },
|
||||
|
||||
/* F72A76A0-EB0A-11D0-ACE4-0000C0CC16BA */
|
||||
{ { 0xA0, 0x76, 0x2A, 0xF7, 0x0A, 0xEB, 0xD0, 0x11, 0xAC, 0xE4, 0x00, 0x00, 0xC0, 0xCC, 0x16,
|
||||
0xBA },
|
||||
"FORMAT_VideoInfo2",
|
||||
TSMF_FORMAT_TYPE_VIDEOINFO2 },
|
||||
|
||||
/* 05589F82-C356-11CE-BF01-00AA0055595A */
|
||||
{ { 0x82, 0x9F, 0x58, 0x05, 0x56, 0xC3, 0xCE, 0x11, 0xBF, 0x01, 0x00, 0xAA, 0x00, 0x55, 0x59,
|
||||
0x5A },
|
||||
"FORMAT_MPEG1_VIDEO",
|
||||
TSMF_FORMAT_TYPE_MPEG1VIDEOINFO },
|
||||
|
||||
{ { 0 }, "Unknown", TSMF_FORMAT_TYPE_UNKNOWN }
|
||||
};
|
||||
|
||||
static void tsmf_print_guid(const BYTE* guid)
|
||||
{
|
||||
WINPR_UNUSED(guid);
|
||||
|
||||
#ifdef WITH_DEBUG_TSMF
|
||||
char guidString[37];
|
||||
|
||||
(void)snprintf(guidString, sizeof(guidString),
|
||||
"%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "-%02" PRIX8 "%02" PRIX8
|
||||
"-%02" PRIX8 "%02" PRIX8 "-%02" PRIX8 "%02" PRIX8 "-%02" PRIX8 "%02" PRIX8
|
||||
"%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "",
|
||||
guid[3], guid[2], guid[1], guid[0], guid[5], guid[4], guid[7], guid[6], guid[8],
|
||||
guid[9], guid[10], guid[11], guid[12], guid[13], guid[14], guid[15]);
|
||||
|
||||
WLog_INFO(TAG, "%s", guidString);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* http://msdn.microsoft.com/en-us/library/dd318229.aspx */
|
||||
static UINT32 tsmf_codec_parse_BITMAPINFOHEADER(TS_AM_MEDIA_TYPE* mediatype, wStream* s,
|
||||
BOOL bypass)
|
||||
{
|
||||
UINT32 biSize = 0;
|
||||
UINT32 biWidth = 0;
|
||||
UINT32 biHeight = 0;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 40))
|
||||
return 0;
|
||||
Stream_Read_UINT32(s, biSize);
|
||||
Stream_Read_UINT32(s, biWidth);
|
||||
Stream_Read_UINT32(s, biHeight);
|
||||
Stream_Seek(s, 28);
|
||||
|
||||
if (mediatype->Width == 0)
|
||||
mediatype->Width = biWidth;
|
||||
|
||||
if (mediatype->Height == 0)
|
||||
mediatype->Height = biHeight;
|
||||
|
||||
/* Assume there will be no color table for video? */
|
||||
if (biSize < 40)
|
||||
return 0;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, (biSize - 40)))
|
||||
return 0;
|
||||
|
||||
if (bypass && biSize > 40)
|
||||
Stream_Seek(s, biSize - 40);
|
||||
|
||||
return (bypass ? biSize : 40);
|
||||
}
|
||||
|
||||
/* http://msdn.microsoft.com/en-us/library/dd407326.aspx */
|
||||
static UINT32 tsmf_codec_parse_VIDEOINFOHEADER2(TS_AM_MEDIA_TYPE* mediatype, wStream* s)
|
||||
{
|
||||
UINT64 AvgTimePerFrame = 0;
|
||||
|
||||
/* VIDEOINFOHEADER2.rcSource, RECT(LONG left, LONG top, LONG right, LONG bottom) */
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 72))
|
||||
return 0;
|
||||
|
||||
Stream_Seek_UINT32(s);
|
||||
Stream_Seek_UINT32(s);
|
||||
Stream_Read_UINT32(s, mediatype->Width);
|
||||
Stream_Read_UINT32(s, mediatype->Height);
|
||||
/* VIDEOINFOHEADER2.rcTarget */
|
||||
Stream_Seek(s, 16);
|
||||
/* VIDEOINFOHEADER2.dwBitRate */
|
||||
Stream_Read_UINT32(s, mediatype->BitRate);
|
||||
/* VIDEOINFOHEADER2.dwBitErrorRate */
|
||||
Stream_Seek_UINT32(s);
|
||||
/* VIDEOINFOHEADER2.AvgTimePerFrame */
|
||||
Stream_Read_UINT64(s, AvgTimePerFrame);
|
||||
mediatype->SamplesPerSecond.Numerator = 1000000;
|
||||
mediatype->SamplesPerSecond.Denominator = (UINT32)(AvgTimePerFrame / 10ULL);
|
||||
/* Remaining fields before bmiHeader */
|
||||
Stream_Seek(s, 24);
|
||||
return 72;
|
||||
}
|
||||
|
||||
/* http://msdn.microsoft.com/en-us/library/dd390700.aspx */
|
||||
static UINT32 tsmf_codec_parse_VIDEOINFOHEADER(TS_AM_MEDIA_TYPE* mediatype, wStream* s)
|
||||
{
|
||||
/*
|
||||
typedef struct {
|
||||
RECT rcSource; //16
|
||||
RECT rcTarget; //16 32
|
||||
DWORD dwBitRate; //4 36
|
||||
DWORD dwBitErrorRate; //4 40
|
||||
REFERENCE_TIME AvgTimePerFrame; //8 48
|
||||
BITMAPINFOHEADER bmiHeader;
|
||||
} VIDEOINFOHEADER;
|
||||
*/
|
||||
UINT64 AvgTimePerFrame = 0;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 48))
|
||||
return 0;
|
||||
|
||||
/* VIDEOINFOHEADER.rcSource, RECT(LONG left, LONG top, LONG right, LONG bottom) */
|
||||
Stream_Seek_UINT32(s);
|
||||
Stream_Seek_UINT32(s);
|
||||
Stream_Read_UINT32(s, mediatype->Width);
|
||||
Stream_Read_UINT32(s, mediatype->Height);
|
||||
/* VIDEOINFOHEADER.rcTarget */
|
||||
Stream_Seek(s, 16);
|
||||
/* VIDEOINFOHEADER.dwBitRate */
|
||||
Stream_Read_UINT32(s, mediatype->BitRate);
|
||||
/* VIDEOINFOHEADER.dwBitErrorRate */
|
||||
Stream_Seek_UINT32(s);
|
||||
/* VIDEOINFOHEADER.AvgTimePerFrame */
|
||||
Stream_Read_UINT64(s, AvgTimePerFrame);
|
||||
mediatype->SamplesPerSecond.Numerator = 1000000;
|
||||
mediatype->SamplesPerSecond.Denominator = (UINT32)(AvgTimePerFrame / 10ULL);
|
||||
return 48;
|
||||
}
|
||||
|
||||
static BOOL tsmf_read_format_type(TS_AM_MEDIA_TYPE* mediatype, wStream* s, UINT32 cbFormat)
|
||||
{
|
||||
UINT32 i = 0;
|
||||
UINT32 j = 0;
|
||||
|
||||
switch (mediatype->FormatType)
|
||||
{
|
||||
case TSMF_FORMAT_TYPE_MFVIDEOFORMAT:
|
||||
/* http://msdn.microsoft.com/en-us/library/aa473808.aspx */
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 176))
|
||||
return FALSE;
|
||||
|
||||
Stream_Seek(s, 8); /* dwSize and ? */
|
||||
Stream_Read_UINT32(s, mediatype->Width); /* videoInfo.dwWidth */
|
||||
Stream_Read_UINT32(s, mediatype->Height); /* videoInfo.dwHeight */
|
||||
Stream_Seek(s, 32);
|
||||
/* videoInfo.FramesPerSecond */
|
||||
Stream_Read_UINT32(s, mediatype->SamplesPerSecond.Numerator);
|
||||
Stream_Read_UINT32(s, mediatype->SamplesPerSecond.Denominator);
|
||||
Stream_Seek(s, 80);
|
||||
Stream_Read_UINT32(s, mediatype->BitRate); /* compressedInfo.AvgBitrate */
|
||||
Stream_Seek(s, 36);
|
||||
|
||||
if (cbFormat > 176)
|
||||
{
|
||||
const size_t nsize = cbFormat - 176;
|
||||
if (mediatype->ExtraDataSize < nsize)
|
||||
return FALSE;
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, nsize))
|
||||
return FALSE;
|
||||
mediatype->ExtraDataSize = (UINT32)nsize;
|
||||
mediatype->ExtraData = Stream_Pointer(s);
|
||||
}
|
||||
break;
|
||||
|
||||
case TSMF_FORMAT_TYPE_WAVEFORMATEX:
|
||||
/* http://msdn.microsoft.com/en-us/library/dd757720.aspx */
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 18))
|
||||
return FALSE;
|
||||
|
||||
Stream_Seek_UINT16(s);
|
||||
Stream_Read_UINT16(s, mediatype->Channels);
|
||||
Stream_Read_UINT32(s, mediatype->SamplesPerSecond.Numerator);
|
||||
mediatype->SamplesPerSecond.Denominator = 1;
|
||||
Stream_Read_UINT32(s, mediatype->BitRate);
|
||||
mediatype->BitRate *= 8;
|
||||
Stream_Read_UINT16(s, mediatype->BlockAlign);
|
||||
Stream_Read_UINT16(s, mediatype->BitsPerSample);
|
||||
Stream_Read_UINT16(s, mediatype->ExtraDataSize);
|
||||
|
||||
if (mediatype->ExtraDataSize > 0)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, mediatype->ExtraDataSize))
|
||||
return FALSE;
|
||||
mediatype->ExtraData = Stream_Pointer(s);
|
||||
}
|
||||
break;
|
||||
|
||||
case TSMF_FORMAT_TYPE_MPEG1VIDEOINFO:
|
||||
/* http://msdn.microsoft.com/en-us/library/dd390700.aspx */
|
||||
i = tsmf_codec_parse_VIDEOINFOHEADER(mediatype, s);
|
||||
if (!i)
|
||||
return FALSE;
|
||||
j = tsmf_codec_parse_BITMAPINFOHEADER(mediatype, s, TRUE);
|
||||
if (!j)
|
||||
return FALSE;
|
||||
i += j;
|
||||
|
||||
if (cbFormat > i)
|
||||
{
|
||||
mediatype->ExtraDataSize = cbFormat - i;
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, mediatype->ExtraDataSize))
|
||||
return FALSE;
|
||||
mediatype->ExtraData = Stream_Pointer(s);
|
||||
}
|
||||
break;
|
||||
|
||||
case TSMF_FORMAT_TYPE_MPEG2VIDEOINFO:
|
||||
/* http://msdn.microsoft.com/en-us/library/dd390707.aspx */
|
||||
i = tsmf_codec_parse_VIDEOINFOHEADER2(mediatype, s);
|
||||
if (!i)
|
||||
return FALSE;
|
||||
j = tsmf_codec_parse_BITMAPINFOHEADER(mediatype, s, TRUE);
|
||||
if (!j)
|
||||
return FALSE;
|
||||
i += j;
|
||||
|
||||
if (cbFormat > i)
|
||||
{
|
||||
mediatype->ExtraDataSize = cbFormat - i;
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, mediatype->ExtraDataSize))
|
||||
return FALSE;
|
||||
mediatype->ExtraData = Stream_Pointer(s);
|
||||
}
|
||||
break;
|
||||
|
||||
case TSMF_FORMAT_TYPE_VIDEOINFO2:
|
||||
i = tsmf_codec_parse_VIDEOINFOHEADER2(mediatype, s);
|
||||
if (!i)
|
||||
return FALSE;
|
||||
j = tsmf_codec_parse_BITMAPINFOHEADER(mediatype, s, FALSE);
|
||||
if (!j)
|
||||
return FALSE;
|
||||
i += j;
|
||||
|
||||
if (cbFormat > i)
|
||||
{
|
||||
mediatype->ExtraDataSize = cbFormat - i;
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, mediatype->ExtraDataSize))
|
||||
return FALSE;
|
||||
mediatype->ExtraData = Stream_Pointer(s);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_INFO(TAG, "unhandled format type 0x%08x", (unsigned)mediatype->FormatType);
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL tsmf_codec_parse_media_type(TS_AM_MEDIA_TYPE* mediatype, wStream* s)
|
||||
{
|
||||
UINT32 cbFormat = 0;
|
||||
|
||||
ZeroMemory(mediatype, sizeof(TS_AM_MEDIA_TYPE));
|
||||
|
||||
/* MajorType */
|
||||
DEBUG_TSMF("MediaMajorType:");
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
|
||||
return FALSE;
|
||||
tsmf_print_guid(Stream_Pointer(s));
|
||||
|
||||
size_t i = 0;
|
||||
for (; tsmf_major_type_map[i].type != TSMF_MAJOR_TYPE_UNKNOWN; i++)
|
||||
{
|
||||
if (memcmp(tsmf_major_type_map[i].guid, Stream_Pointer(s), 16) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
mediatype->MajorType = tsmf_major_type_map[i].type;
|
||||
if (mediatype->MajorType == TSMF_MAJOR_TYPE_UNKNOWN)
|
||||
return FALSE;
|
||||
|
||||
DEBUG_TSMF("MediaMajorType %s", tsmf_major_type_map[i].name);
|
||||
Stream_Seek(s, 16);
|
||||
|
||||
/* SubType */
|
||||
DEBUG_TSMF("MediaSubType:");
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
|
||||
return FALSE;
|
||||
tsmf_print_guid(Stream_Pointer(s));
|
||||
|
||||
for (i = 0; tsmf_sub_type_map[i].type != TSMF_SUB_TYPE_UNKNOWN; i++)
|
||||
{
|
||||
if (memcmp(tsmf_sub_type_map[i].guid, Stream_Pointer(s), 16) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
mediatype->SubType = tsmf_sub_type_map[i].type;
|
||||
if (mediatype->SubType == TSMF_SUB_TYPE_UNKNOWN)
|
||||
return FALSE;
|
||||
|
||||
DEBUG_TSMF("MediaSubType %s", tsmf_sub_type_map[i].name);
|
||||
Stream_Seek(s, 16);
|
||||
|
||||
/* bFixedSizeSamples, bTemporalCompression, SampleSize */
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
|
||||
return FALSE;
|
||||
Stream_Seek(s, 12);
|
||||
|
||||
/* FormatType */
|
||||
DEBUG_TSMF("FormatType:");
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
|
||||
return FALSE;
|
||||
tsmf_print_guid(Stream_Pointer(s));
|
||||
|
||||
for (i = 0; tsmf_format_type_map[i].type != TSMF_FORMAT_TYPE_UNKNOWN; i++)
|
||||
{
|
||||
if (memcmp(tsmf_format_type_map[i].guid, Stream_Pointer(s), 16) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
mediatype->FormatType = tsmf_format_type_map[i].type;
|
||||
if (mediatype->FormatType == TSMF_FORMAT_TYPE_UNKNOWN)
|
||||
return FALSE;
|
||||
|
||||
DEBUG_TSMF("FormatType %s", tsmf_format_type_map[i].name);
|
||||
Stream_Seek(s, 16);
|
||||
|
||||
/* cbFormat */
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
return FALSE;
|
||||
Stream_Read_UINT32(s, cbFormat);
|
||||
DEBUG_TSMF("cbFormat %" PRIu32 "", cbFormat);
|
||||
#ifdef WITH_DEBUG_TSMF
|
||||
winpr_HexDump(TAG, WLOG_DEBUG, Stream_Pointer(s), cbFormat);
|
||||
#endif
|
||||
|
||||
const BOOL ret = tsmf_read_format_type(mediatype, s, cbFormat);
|
||||
|
||||
if (mediatype->SamplesPerSecond.Numerator == 0)
|
||||
mediatype->SamplesPerSecond.Numerator = 1;
|
||||
|
||||
if (mediatype->SamplesPerSecond.Denominator == 0)
|
||||
mediatype->SamplesPerSecond.Denominator = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL tsmf_codec_check_media_type(const char* decoder_name, wStream* s)
|
||||
{
|
||||
size_t pos = 0;
|
||||
BOOL ret = FALSE;
|
||||
TS_AM_MEDIA_TYPE mediatype;
|
||||
|
||||
static BOOL decoderAvailable = FALSE;
|
||||
static BOOL firstRun = TRUE;
|
||||
|
||||
if (firstRun)
|
||||
{
|
||||
firstRun = FALSE;
|
||||
if (tsmf_check_decoder_available(decoder_name))
|
||||
decoderAvailable = TRUE;
|
||||
}
|
||||
|
||||
pos = Stream_GetPosition(s);
|
||||
if (decoderAvailable)
|
||||
ret = tsmf_codec_parse_media_type(&mediatype, s);
|
||||
Stream_SetPosition(s, pos);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
ITSMFDecoder* decoder = tsmf_load_decoder(decoder_name, &mediatype);
|
||||
|
||||
if (!decoder)
|
||||
{
|
||||
WLog_WARN(TAG, "Format not supported by decoder %s", decoder_name);
|
||||
ret = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
decoder->Free(decoder);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
31
third_party/FreeRDP/channels/tsmf/client/tsmf_codec.h
vendored
Normal file
31
third_party/FreeRDP/channels/tsmf/client/tsmf_codec.h
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - Codec
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_TSMF_CLIENT_CODEC_H
|
||||
#define FREERDP_CHANNEL_TSMF_CLIENT_CODEC_H
|
||||
|
||||
#include "tsmf_types.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL BOOL tsmf_codec_parse_media_type(TS_AM_MEDIA_TYPE* mediatype, wStream* s);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL BOOL tsmf_codec_check_media_type(const char* decoder_name, wStream* s);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_TSMF_CLIENT_CODEC_H */
|
||||
139
third_party/FreeRDP/channels/tsmf/client/tsmf_constants.h
vendored
Normal file
139
third_party/FreeRDP/channels/tsmf/client/tsmf_constants.h
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - Constants
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_TSMF_CLIENT_CONSTANTS_H
|
||||
#define FREERDP_CHANNEL_TSMF_CLIENT_CONSTANTS_H
|
||||
|
||||
#define GUID_SIZE 16
|
||||
#define TSMF_BUFFER_PADDING_SIZE 8
|
||||
|
||||
/* Interface IDs defined in [MS-RDPEV]. There's no constant names in the MS
|
||||
documentation, so we create them on our own. */
|
||||
#define TSMF_INTERFACE_DEFAULT 0x00000000
|
||||
#define TSMF_INTERFACE_CLIENT_NOTIFICATIONS 0x00000001
|
||||
#define TSMF_INTERFACE_CAPABILITIES 0x00000002
|
||||
|
||||
/* Interface ID Mask */
|
||||
#define STREAM_ID_STUB 0x80000000
|
||||
#define STREAM_ID_PROXY 0x40000000
|
||||
#define STREAM_ID_NONE 0x00000000
|
||||
|
||||
/* Function ID */
|
||||
/* Common IDs for all interfaces are as follows. */
|
||||
#define RIMCALL_RELEASE 0x00000001
|
||||
#define RIMCALL_QUERYINTERFACE 0x00000002
|
||||
/* Capabilities Negotiator Interface IDs are as follows. */
|
||||
#define RIM_EXCHANGE_CAPABILITY_REQUEST 0x00000100
|
||||
/* The Client Notifications Interface ID is as follows. */
|
||||
#define PLAYBACK_ACK 0x00000100
|
||||
#define CLIENT_EVENT_NOTIFICATION 0x00000101
|
||||
/* Server Data Interface IDs are as follows. */
|
||||
#define EXCHANGE_CAPABILITIES_REQ 0x00000100
|
||||
#define SET_CHANNEL_PARAMS 0x00000101
|
||||
#define ADD_STREAM 0x00000102
|
||||
#define ON_SAMPLE 0x00000103
|
||||
#define SET_VIDEO_WINDOW 0x00000104
|
||||
#define ON_NEW_PRESENTATION 0x00000105
|
||||
#define SHUTDOWN_PRESENTATION_REQ 0x00000106
|
||||
#define SET_TOPOLOGY_REQ 0x00000107
|
||||
#define CHECK_FORMAT_SUPPORT_REQ 0x00000108
|
||||
#define ON_PLAYBACK_STARTED 0x00000109
|
||||
#define ON_PLAYBACK_PAUSED 0x0000010a
|
||||
#define ON_PLAYBACK_STOPPED 0x0000010b
|
||||
#define ON_PLAYBACK_RESTARTED 0x0000010c
|
||||
#define ON_PLAYBACK_RATE_CHANGED 0x0000010d
|
||||
#define ON_FLUSH 0x0000010e
|
||||
#define ON_STREAM_VOLUME 0x0000010f
|
||||
#define ON_CHANNEL_VOLUME 0x00000110
|
||||
#define ON_END_OF_STREAM 0x00000111
|
||||
#define SET_ALLOCATOR 0x00000112
|
||||
#define NOTIFY_PREROLL 0x00000113
|
||||
#define UPDATE_GEOMETRY_INFO 0x00000114
|
||||
#define REMOVE_STREAM 0x00000115
|
||||
#define SET_SOURCE_VIDEO_RECT 0x00000116
|
||||
|
||||
/* Supported platform */
|
||||
#define MMREDIR_CAPABILITY_PLATFORM_MF 0x00000001
|
||||
#define MMREDIR_CAPABILITY_PLATFORM_DSHOW 0x00000002
|
||||
#define MMREDIR_CAPABILITY_PLATFORM_OTHER 0x00000004
|
||||
|
||||
/* TSMM_CLIENT_EVENT Constants */
|
||||
#define TSMM_CLIENT_EVENT_ENDOFSTREAM 0x0064
|
||||
#define TSMM_CLIENT_EVENT_STOP_COMPLETED 0x00C8
|
||||
#define TSMM_CLIENT_EVENT_START_COMPLETED 0x00C9
|
||||
#define TSMM_CLIENT_EVENT_MONITORCHANGED 0x012C
|
||||
|
||||
/* TS_MM_DATA_SAMPLE.SampleExtensions */
|
||||
#define TSMM_SAMPLE_EXT_CLEANPOINT 0x00000001
|
||||
#define TSMM_SAMPLE_EXT_DISCONTINUITY 0x00000002
|
||||
#define TSMM_SAMPLE_EXT_INTERLACED 0x00000004
|
||||
#define TSMM_SAMPLE_EXT_BOTTOMFIELDFIRST 0x00000008
|
||||
#define TSMM_SAMPLE_EXT_REPEATFIELDFIRST 0x00000010
|
||||
#define TSMM_SAMPLE_EXT_SINGLEFIELD 0x00000020
|
||||
#define TSMM_SAMPLE_EXT_DERIVEDFROMTOPFIELD 0x00000040
|
||||
#define TSMM_SAMPLE_EXT_HAS_NO_TIMESTAMPS 0x00000080
|
||||
#define TSMM_SAMPLE_EXT_RELATIVE_TIMESTAMPS 0x00000100
|
||||
#define TSMM_SAMPLE_EXT_ABSOLUTE_TIMESTAMPS 0x00000200
|
||||
|
||||
/* MajorType */
|
||||
#define TSMF_MAJOR_TYPE_UNKNOWN 0
|
||||
#define TSMF_MAJOR_TYPE_VIDEO 1
|
||||
#define TSMF_MAJOR_TYPE_AUDIO 2
|
||||
|
||||
/* SubType */
|
||||
#define TSMF_SUB_TYPE_UNKNOWN 0
|
||||
#define TSMF_SUB_TYPE_WVC1 1
|
||||
#define TSMF_SUB_TYPE_WMA2 2
|
||||
#define TSMF_SUB_TYPE_WMA9 3
|
||||
#define TSMF_SUB_TYPE_MP3 4
|
||||
#define TSMF_SUB_TYPE_MP2A 5
|
||||
#define TSMF_SUB_TYPE_MP2V 6
|
||||
#define TSMF_SUB_TYPE_WMV3 7
|
||||
#define TSMF_SUB_TYPE_AAC 8
|
||||
#define TSMF_SUB_TYPE_H264 9
|
||||
#define TSMF_SUB_TYPE_AVC1 10
|
||||
#define TSMF_SUB_TYPE_AC3 11
|
||||
#define TSMF_SUB_TYPE_WMV2 12
|
||||
#define TSMF_SUB_TYPE_WMV1 13
|
||||
#define TSMF_SUB_TYPE_MP1V 14
|
||||
#define TSMF_SUB_TYPE_MP1A 15
|
||||
#define TSMF_SUB_TYPE_YUY2 16
|
||||
#define TSMF_SUB_TYPE_MP43 17
|
||||
#define TSMF_SUB_TYPE_MP4S 18
|
||||
#define TSMF_SUB_TYPE_MP42 19
|
||||
#define TSMF_SUB_TYPE_OGG 20
|
||||
#define TSMF_SUB_TYPE_SPEEX 21
|
||||
#define TSMF_SUB_TYPE_THEORA 22
|
||||
#define TSMF_SUB_TYPE_FLAC 23
|
||||
#define TSMF_SUB_TYPE_VP8 24
|
||||
#define TSMF_SUB_TYPE_VP9 25
|
||||
#define TSMF_SUB_TYPE_H263 26
|
||||
#define TSMF_SUB_TYPE_M4S2 27
|
||||
#define TSMF_SUB_TYPE_WMA1 28
|
||||
|
||||
/* FormatType */
|
||||
#define TSMF_FORMAT_TYPE_UNKNOWN 0
|
||||
#define TSMF_FORMAT_TYPE_MFVIDEOFORMAT 1
|
||||
#define TSMF_FORMAT_TYPE_WAVEFORMATEX 2
|
||||
#define TSMF_FORMAT_TYPE_MPEG2VIDEOINFO 3
|
||||
#define TSMF_FORMAT_TYPE_VIDEOINFO2 4
|
||||
#define TSMF_FORMAT_TYPE_MPEG1VIDEOINFO 5
|
||||
|
||||
#endif /* FREERDP_CHANNEL_TSMF_CLIENT_CONSTANTS_H */
|
||||
121
third_party/FreeRDP/channels/tsmf/client/tsmf_decoder.c
vendored
Normal file
121
third_party/FreeRDP/channels/tsmf/client/tsmf_decoder.c
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - Decoder
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/client/channels.h>
|
||||
|
||||
#include "tsmf_types.h"
|
||||
#include "tsmf_constants.h"
|
||||
#include "tsmf_decoder.h"
|
||||
|
||||
static ITSMFDecoder* tsmf_load_decoder_by_name(const char* name)
|
||||
{
|
||||
ITSMFDecoder* decoder = nullptr;
|
||||
union
|
||||
{
|
||||
PVIRTUALCHANNELENTRY pvce;
|
||||
TSMF_DECODER_ENTRY entry;
|
||||
} cnv;
|
||||
cnv.pvce = freerdp_load_channel_addin_entry("tsmf", name, "decoder", 0);
|
||||
|
||||
if (!cnv.entry)
|
||||
return nullptr;
|
||||
|
||||
const UINT rc = cnv.entry(&decoder);
|
||||
|
||||
if ((rc != CHANNEL_RC_OK) || !decoder)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to call export function in %s", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return decoder;
|
||||
}
|
||||
|
||||
static BOOL tsmf_decoder_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* media_type)
|
||||
{
|
||||
if (decoder->SetFormat(decoder, media_type))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ITSMFDecoder* tsmf_load_decoder(const char* name, TS_AM_MEDIA_TYPE* media_type)
|
||||
{
|
||||
ITSMFDecoder* decoder = nullptr;
|
||||
|
||||
if (name)
|
||||
decoder = tsmf_load_decoder_by_name(name);
|
||||
|
||||
#if defined(WITH_GSTREAMER_1_0)
|
||||
if (!decoder)
|
||||
decoder = tsmf_load_decoder_by_name("gstreamer");
|
||||
#endif
|
||||
|
||||
#if defined(WITH_VIDEO_FFMPEG)
|
||||
if (!decoder)
|
||||
decoder = tsmf_load_decoder_by_name("ffmpeg");
|
||||
#endif
|
||||
|
||||
if (decoder)
|
||||
{
|
||||
if (!tsmf_decoder_set_format(decoder, media_type))
|
||||
{
|
||||
decoder->Free(decoder);
|
||||
decoder = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return decoder;
|
||||
}
|
||||
|
||||
BOOL tsmf_check_decoder_available(const char* name)
|
||||
{
|
||||
ITSMFDecoder* decoder = nullptr;
|
||||
BOOL retValue = FALSE;
|
||||
|
||||
if (name)
|
||||
{
|
||||
decoder = tsmf_load_decoder_by_name(name);
|
||||
}
|
||||
#if defined(WITH_GSTREAMER_1_0)
|
||||
if (!decoder)
|
||||
decoder = tsmf_load_decoder_by_name("gstreamer");
|
||||
#endif
|
||||
|
||||
#if defined(WITH_VIDEO_FFMPEG)
|
||||
if (!decoder)
|
||||
decoder = tsmf_load_decoder_by_name("ffmpeg");
|
||||
#endif
|
||||
|
||||
if (decoder)
|
||||
{
|
||||
decoder->Free(decoder);
|
||||
decoder = nullptr;
|
||||
retValue = TRUE;
|
||||
}
|
||||
|
||||
return retValue;
|
||||
}
|
||||
87
third_party/FreeRDP/channels/tsmf/client/tsmf_decoder.h
vendored
Normal file
87
third_party/FreeRDP/channels/tsmf/client/tsmf_decoder.h
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - Decoder
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_TSMF_CLIENT_DECODER_H
|
||||
#define FREERDP_CHANNEL_TSMF_CLIENT_DECODER_H
|
||||
|
||||
#include "tsmf_types.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
Control_Pause,
|
||||
Control_Resume,
|
||||
Control_Restart,
|
||||
Control_Stop
|
||||
} ITSMFControlMsg;
|
||||
|
||||
typedef struct s_ITSMFDecoder ITSMFDecoder;
|
||||
|
||||
struct s_ITSMFDecoder
|
||||
{
|
||||
/* Set the decoder format. Return true if supported. */
|
||||
WINPR_ATTR_NODISCARD BOOL (*SetFormat)(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* media_type);
|
||||
/* Decode a sample. */
|
||||
WINPR_ATTR_NODISCARD BOOL (*Decode)(ITSMFDecoder* decoder, const BYTE* data, UINT32 data_size,
|
||||
UINT32 extensions);
|
||||
/* Get the decoded data */
|
||||
WINPR_ATTR_NODISCARD BYTE* (*GetDecodedData)(ITSMFDecoder* decoder, UINT32* size);
|
||||
/* Get the pixel format of decoded video frame */
|
||||
WINPR_ATTR_NODISCARD UINT32 (*GetDecodedFormat)(ITSMFDecoder* decoder);
|
||||
/* Get the width and height of decoded video frame */
|
||||
WINPR_ATTR_NODISCARD BOOL (*GetDecodedDimension)(ITSMFDecoder* decoder, UINT32* width,
|
||||
UINT32* height);
|
||||
/* Free the decoder */
|
||||
void (*Free)(ITSMFDecoder* decoder);
|
||||
/* Optional Control function */
|
||||
WINPR_ATTR_NODISCARD BOOL (*Control)(ITSMFDecoder* decoder, ITSMFControlMsg control_msg,
|
||||
UINT32* arg);
|
||||
/* Decode a sample with extended interface. */
|
||||
WINPR_ATTR_NODISCARD BOOL (*DecodeEx)(ITSMFDecoder* decoder, const BYTE* data, UINT32 data_size,
|
||||
UINT32 extensions, UINT64 start_time, UINT64 end_time,
|
||||
UINT64 duration);
|
||||
/* Get current play time */
|
||||
WINPR_ATTR_NODISCARD UINT64 (*GetRunningTime)(ITSMFDecoder* decoder);
|
||||
/* Update Gstreamer Rendering Area */
|
||||
WINPR_ATTR_NODISCARD BOOL (*UpdateRenderingArea)(ITSMFDecoder* decoder, UINT32 newX,
|
||||
UINT32 newY, UINT32 newWidth, UINT32 newHeight,
|
||||
UINT32 numRectangles,
|
||||
const RECTANGLE_32* rectangles);
|
||||
/* Change Gstreamer Audio Volume */
|
||||
WINPR_ATTR_NODISCARD BOOL (*ChangeVolume)(ITSMFDecoder* decoder, UINT32 newVolume,
|
||||
UINT32 muted);
|
||||
/* Check buffer level */
|
||||
WINPR_ATTR_NODISCARD BOOL (*BufferLevel)(ITSMFDecoder* decoder);
|
||||
/* Register a callback for frame ack. */
|
||||
WINPR_ATTR_NODISCARD BOOL (*SetAckFunc)(ITSMFDecoder* decoder, BOOL (*cb)(void*, BOOL),
|
||||
void* stream);
|
||||
/* Register a callback for stream seek detection. */
|
||||
WINPR_ATTR_NODISCARD BOOL (*SetSyncFunc)(ITSMFDecoder* decoder, void (*cb)(void*),
|
||||
void* stream);
|
||||
};
|
||||
|
||||
#define TSMF_DECODER_EXPORT_FUNC_NAME "TSMFDecoderEntry"
|
||||
typedef UINT(VCAPITYPE* TSMF_DECODER_ENTRY)(ITSMFDecoder** decoder);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL ITSMFDecoder* tsmf_load_decoder(const char* name,
|
||||
TS_AM_MEDIA_TYPE* media_type);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL tsmf_check_decoder_available(const char* name);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_TSMF_CLIENT_DECODER_H */
|
||||
839
third_party/FreeRDP/channels/tsmf/client/tsmf_ifman.c
vendored
Normal file
839
third_party/FreeRDP/channels/tsmf/client/tsmf_ifman.c
vendored
Normal file
@@ -0,0 +1,839 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - Interface Manipulation
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include "tsmf_types.h"
|
||||
#include "tsmf_constants.h"
|
||||
#include "tsmf_media.h"
|
||||
#include "tsmf_codec.h"
|
||||
|
||||
#include "tsmf_ifman.h"
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_rim_exchange_capability_request(TSMF_IFMAN* ifman)
|
||||
{
|
||||
UINT32 CapabilityValue = 0;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, 4))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(ifman->input, CapabilityValue);
|
||||
DEBUG_TSMF("server CapabilityValue %" PRIu32 "", CapabilityValue);
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(ifman->output, 8))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Write_UINT32(ifman->output, 1); /* CapabilityValue */
|
||||
Stream_Write_UINT32(ifman->output, 0); /* Result */
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_exchange_capability_request(TSMF_IFMAN* ifman)
|
||||
{
|
||||
UINT32 CapabilityType = 0;
|
||||
UINT32 cbCapabilityLength = 0;
|
||||
UINT32 numHostCapabilities = 0;
|
||||
|
||||
WINPR_ASSERT(ifman);
|
||||
if (!Stream_EnsureRemainingCapacity(ifman->output, ifman->input_size + 4))
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, ifman->input_size))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
const size_t xpos = Stream_GetPosition(ifman->output);
|
||||
Stream_Copy(ifman->input, ifman->output, ifman->input_size);
|
||||
Stream_SetPosition(ifman->output, xpos);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->output, 4))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(ifman->output, numHostCapabilities);
|
||||
|
||||
for (UINT32 i = 0; i < numHostCapabilities; i++)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->output, 8))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(ifman->output, CapabilityType);
|
||||
Stream_Read_UINT32(ifman->output, cbCapabilityLength);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->output, cbCapabilityLength))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
const size_t pos = Stream_GetPosition(ifman->output);
|
||||
|
||||
switch (CapabilityType)
|
||||
{
|
||||
case 1: /* Protocol version request */
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->output, 4))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
const UINT32 v = Stream_Get_UINT32(ifman->output);
|
||||
WINPR_UNUSED(v);
|
||||
DEBUG_TSMF("server protocol version %" PRIu32 "", v);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: /* Supported platform */
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->output, 4))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
const UINT32 v = Stream_Get_UINT32(ifman->output);
|
||||
WINPR_UNUSED(v);
|
||||
DEBUG_TSMF("server supported platform %" PRIu32 "", v);
|
||||
/* Claim that we support both MF and DShow platforms. */
|
||||
Stream_Write_UINT32(ifman->output, MMREDIR_CAPABILITY_PLATFORM_MF |
|
||||
MMREDIR_CAPABILITY_PLATFORM_DSHOW);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "skipping unknown capability type %" PRIu32 "", CapabilityType);
|
||||
break;
|
||||
}
|
||||
|
||||
Stream_SetPosition(ifman->output, pos + cbCapabilityLength);
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(ifman->output, 0); /* Result */
|
||||
ifman->output_interface_id = TSMF_INTERFACE_DEFAULT | STREAM_ID_STUB;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_check_format_support_request(TSMF_IFMAN* ifman)
|
||||
{
|
||||
UINT32 numMediaType = 0;
|
||||
UINT32 PlatformCookie = 0;
|
||||
UINT32 FormatSupported = 1;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, 12))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(ifman->input, PlatformCookie);
|
||||
Stream_Seek_UINT32(ifman->input); /* NoRolloverFlags (4 bytes) */
|
||||
Stream_Read_UINT32(ifman->input, numMediaType);
|
||||
DEBUG_TSMF("PlatformCookie %" PRIu32 " numMediaType %" PRIu32 "", PlatformCookie, numMediaType);
|
||||
|
||||
if (!tsmf_codec_check_media_type(ifman->decoder_name, ifman->input))
|
||||
FormatSupported = 0;
|
||||
|
||||
if (FormatSupported)
|
||||
DEBUG_TSMF("format ok.");
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(ifman->output, 12))
|
||||
return -1;
|
||||
|
||||
Stream_Write_UINT32(ifman->output, FormatSupported);
|
||||
Stream_Write_UINT32(ifman->output, PlatformCookie);
|
||||
Stream_Write_UINT32(ifman->output, 0); /* Result */
|
||||
ifman->output_interface_id = TSMF_INTERFACE_DEFAULT | STREAM_ID_STUB;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_on_new_presentation(TSMF_IFMAN* ifman)
|
||||
{
|
||||
UINT status = CHANNEL_RC_OK;
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
DEBUG_TSMF("");
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, GUID_SIZE))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
|
||||
if (presentation)
|
||||
{
|
||||
DEBUG_TSMF("Presentation already exists");
|
||||
ifman->output_pending = FALSE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
presentation = tsmf_presentation_new(Stream_Pointer(ifman->input), ifman->channel_callback);
|
||||
|
||||
if (!presentation)
|
||||
status = ERROR_OUTOFMEMORY;
|
||||
else
|
||||
tsmf_presentation_set_audio_device(presentation, ifman->audio_name, ifman->audio_device);
|
||||
|
||||
ifman->output_pending = TRUE;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_add_stream(TSMF_IFMAN* ifman, rdpContext* rdpcontext)
|
||||
{
|
||||
UINT32 StreamId = 0;
|
||||
UINT status = CHANNEL_RC_OK;
|
||||
TSMF_STREAM* stream = nullptr;
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
DEBUG_TSMF("");
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, GUID_SIZE + 8))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
Stream_Seek(ifman->input, GUID_SIZE);
|
||||
|
||||
if (!presentation)
|
||||
{
|
||||
WLog_ERR(TAG, "unknown presentation id");
|
||||
status = ERROR_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream_Read_UINT32(ifman->input, StreamId);
|
||||
Stream_Seek_UINT32(ifman->input); /* numMediaType */
|
||||
stream = tsmf_stream_new(presentation, StreamId, rdpcontext);
|
||||
|
||||
if (!stream)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to create stream");
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
if (!tsmf_stream_set_format(stream, ifman->decoder_name, ifman->input))
|
||||
{
|
||||
WLog_ERR(TAG, "failed to set stream format");
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
tsmf_stream_start_threads(stream);
|
||||
}
|
||||
|
||||
ifman->output_pending = TRUE;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_set_topology_request(TSMF_IFMAN* ifman)
|
||||
{
|
||||
DEBUG_TSMF("");
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(ifman->output, 8))
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
Stream_Write_UINT32(ifman->output, 1); /* TopologyReady */
|
||||
Stream_Write_UINT32(ifman->output, 0); /* Result */
|
||||
ifman->output_interface_id = TSMF_INTERFACE_DEFAULT | STREAM_ID_STUB;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_remove_stream(TSMF_IFMAN* ifman)
|
||||
{
|
||||
int status = CHANNEL_RC_OK;
|
||||
UINT32 StreamId = 0;
|
||||
TSMF_STREAM* stream = nullptr;
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
DEBUG_TSMF("");
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, 20))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
Stream_Seek(ifman->input, GUID_SIZE);
|
||||
|
||||
if (!presentation)
|
||||
{
|
||||
status = ERROR_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream_Read_UINT32(ifman->input, StreamId);
|
||||
stream = tsmf_stream_find_by_id(presentation, StreamId);
|
||||
|
||||
if (stream)
|
||||
tsmf_stream_free(stream);
|
||||
else
|
||||
status = ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
ifman->output_pending = TRUE;
|
||||
return status;
|
||||
}
|
||||
|
||||
static float tsmf_stream_read_float(wStream* s)
|
||||
{
|
||||
float fValue = NAN;
|
||||
UINT32 iValue = 0;
|
||||
Stream_Read_UINT32(s, iValue);
|
||||
CopyMemory(&fValue, &iValue, 4);
|
||||
return fValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_set_source_video_rect(TSMF_IFMAN* ifman)
|
||||
{
|
||||
UINT status = CHANNEL_RC_OK;
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
DEBUG_TSMF("");
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, 32))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
Stream_Seek(ifman->input, GUID_SIZE);
|
||||
|
||||
if (!presentation)
|
||||
{
|
||||
status = ERROR_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef WITH_DEBUG_TSMF
|
||||
const float Left = tsmf_stream_read_float(ifman->input); /* Left (4 bytes) */
|
||||
const float Top = tsmf_stream_read_float(ifman->input); /* Top (4 bytes) */
|
||||
const float Right = tsmf_stream_read_float(ifman->input); /* Right (4 bytes) */
|
||||
const float Bottom = tsmf_stream_read_float(ifman->input); /* Bottom (4 bytes) */
|
||||
DEBUG_TSMF("SetSourceVideoRect: Left: %f Top: %f Right: %f Bottom: %f", Left, Top, Right,
|
||||
Bottom);
|
||||
#endif
|
||||
}
|
||||
|
||||
ifman->output_pending = TRUE;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_shutdown_presentation(TSMF_IFMAN* ifman)
|
||||
{
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
DEBUG_TSMF("");
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, GUID_SIZE))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
|
||||
if (presentation)
|
||||
tsmf_presentation_free(presentation);
|
||||
else
|
||||
{
|
||||
WLog_ERR(TAG, "unknown presentation id");
|
||||
return ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(ifman->output, 4))
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
Stream_Write_UINT32(ifman->output, 0); /* Result */
|
||||
ifman->output_interface_id = TSMF_INTERFACE_DEFAULT | STREAM_ID_STUB;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_on_stream_volume(TSMF_IFMAN* ifman)
|
||||
{
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
UINT32 newVolume = 0;
|
||||
UINT32 muted = 0;
|
||||
DEBUG_TSMF("on stream volume");
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, GUID_SIZE + 8))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
|
||||
if (!presentation)
|
||||
{
|
||||
WLog_ERR(TAG, "unknown presentation id");
|
||||
return ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
Stream_Seek(ifman->input, 16);
|
||||
Stream_Read_UINT32(ifman->input, newVolume);
|
||||
DEBUG_TSMF("on stream volume: new volume=[%" PRIu32 "]", newVolume);
|
||||
Stream_Read_UINT32(ifman->input, muted);
|
||||
DEBUG_TSMF("on stream volume: muted=[%" PRIu32 "]", muted);
|
||||
|
||||
if (!tsmf_presentation_volume_changed(presentation, newVolume, muted))
|
||||
return ERROR_INVALID_OPERATION;
|
||||
|
||||
ifman->output_pending = TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_on_channel_volume(TSMF_IFMAN* ifman)
|
||||
{
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
DEBUG_TSMF("on channel volume");
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, GUID_SIZE + 8))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
|
||||
if (presentation)
|
||||
{
|
||||
UINT32 channelVolume = 0;
|
||||
UINT32 changedChannel = 0;
|
||||
Stream_Seek(ifman->input, 16);
|
||||
Stream_Read_UINT32(ifman->input, channelVolume);
|
||||
DEBUG_TSMF("on channel volume: channel volume=[%" PRIu32 "]", channelVolume);
|
||||
Stream_Read_UINT32(ifman->input, changedChannel);
|
||||
DEBUG_TSMF("on stream volume: changed channel=[%" PRIu32 "]", changedChannel);
|
||||
}
|
||||
|
||||
ifman->output_pending = TRUE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_set_video_window(TSMF_IFMAN* ifman)
|
||||
{
|
||||
DEBUG_TSMF("");
|
||||
ifman->output_pending = TRUE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_update_geometry_info(TSMF_IFMAN* ifman)
|
||||
{
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
UINT32 numGeometryInfo = 0;
|
||||
UINT32 Left = 0;
|
||||
UINT32 Top = 0;
|
||||
UINT32 Width = 0;
|
||||
UINT32 Height = 0;
|
||||
UINT32 cbVisibleRect = 0;
|
||||
RECTANGLE_32* rects = nullptr;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
size_t pos = 0;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, GUID_SIZE + 32))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
|
||||
if (!presentation)
|
||||
return ERROR_NOT_FOUND;
|
||||
|
||||
Stream_Seek(ifman->input, 16);
|
||||
Stream_Read_UINT32(ifman->input, numGeometryInfo);
|
||||
pos = Stream_GetPosition(ifman->input);
|
||||
Stream_Seek(ifman->input, 12); /* VideoWindowId (8 bytes), VideoWindowState (4 bytes) */
|
||||
Stream_Read_UINT32(ifman->input, Width);
|
||||
Stream_Read_UINT32(ifman->input, Height);
|
||||
Stream_Read_UINT32(ifman->input, Left);
|
||||
Stream_Read_UINT32(ifman->input, Top);
|
||||
Stream_SetPosition(ifman->input, pos + numGeometryInfo);
|
||||
Stream_Read_UINT32(ifman->input, cbVisibleRect);
|
||||
const UINT32 num_rects = cbVisibleRect / 16;
|
||||
DEBUG_TSMF("numGeometryInfo %" PRIu32 " Width %" PRIu32 " Height %" PRIu32 " Left %" PRIu32
|
||||
" Top %" PRIu32 " cbVisibleRect %" PRIu32 " num_rects %d",
|
||||
numGeometryInfo, Width, Height, Left, Top, cbVisibleRect, num_rects);
|
||||
|
||||
if (num_rects > 0)
|
||||
{
|
||||
rects = (RECTANGLE_32*)calloc(num_rects, sizeof(RECTANGLE_32));
|
||||
|
||||
for (size_t i = 0; i < num_rects; i++)
|
||||
{
|
||||
Stream_Read_UINT32(ifman->input, rects[i].top); /* Top */
|
||||
Stream_Read_UINT32(ifman->input, rects[i].left); /* Left */
|
||||
Stream_Read_UINT32(ifman->input, rects[i].height); /* Bottom */
|
||||
Stream_Read_UINT32(ifman->input, rects[i].width); /* Right */
|
||||
rects[i].width -= rects[i].left;
|
||||
rects[i].height -= rects[i].top;
|
||||
DEBUG_TSMF("rect %d: %" PRId16 " %" PRId16 " %" PRId16 " %" PRId16 "", i, rects[i].x,
|
||||
rects[i].y, rects[i].width, rects[i].height);
|
||||
}
|
||||
}
|
||||
|
||||
const BOOL rc = tsmf_presentation_set_geometry_info(presentation, Left, Top, Width, Height,
|
||||
num_rects, rects);
|
||||
free(rects);
|
||||
if (!rc)
|
||||
return ERROR_INVALID_OPERATION;
|
||||
|
||||
ifman->output_pending = TRUE;
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_set_allocator(TSMF_IFMAN* ifman)
|
||||
{
|
||||
DEBUG_TSMF("");
|
||||
ifman->output_pending = TRUE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_notify_preroll(TSMF_IFMAN* ifman)
|
||||
{
|
||||
DEBUG_TSMF("");
|
||||
tsmf_ifman_on_playback_paused(ifman);
|
||||
ifman->output_pending = TRUE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_on_sample(TSMF_IFMAN* ifman)
|
||||
{
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
TSMF_STREAM* stream = nullptr;
|
||||
UINT32 StreamId = 0;
|
||||
UINT64 SampleStartTime = 0;
|
||||
UINT64 SampleEndTime = 0;
|
||||
UINT64 ThrottleDuration = 0;
|
||||
UINT32 SampleExtensions = 0;
|
||||
UINT32 cbData = 0;
|
||||
UINT error = 0;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, 60))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(ifman->input, 16);
|
||||
Stream_Read_UINT32(ifman->input, StreamId);
|
||||
Stream_Seek_UINT32(ifman->input); /* numSample */
|
||||
Stream_Read_UINT64(ifman->input, SampleStartTime);
|
||||
Stream_Read_UINT64(ifman->input, SampleEndTime);
|
||||
Stream_Read_UINT64(ifman->input, ThrottleDuration);
|
||||
Stream_Seek_UINT32(ifman->input); /* SampleFlags */
|
||||
Stream_Read_UINT32(ifman->input, SampleExtensions);
|
||||
Stream_Read_UINT32(ifman->input, cbData);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, cbData))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
DEBUG_TSMF("MessageId %" PRIu32 " StreamId %" PRIu32 " SampleStartTime %" PRIu64
|
||||
" SampleEndTime %" PRIu64 " "
|
||||
"ThrottleDuration %" PRIu64 " SampleExtensions %" PRIu32 " cbData %" PRIu32 "",
|
||||
ifman->message_id, StreamId, SampleStartTime, SampleEndTime, ThrottleDuration,
|
||||
SampleExtensions, cbData);
|
||||
presentation = tsmf_presentation_find_by_id(ifman->presentation_id);
|
||||
|
||||
if (!presentation)
|
||||
{
|
||||
WLog_ERR(TAG, "unknown presentation id");
|
||||
return ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
stream = tsmf_stream_find_by_id(presentation, StreamId);
|
||||
|
||||
if (!stream)
|
||||
{
|
||||
WLog_ERR(TAG, "unknown stream id");
|
||||
return ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!tsmf_stream_push_sample(stream, ifman->channel_callback, ifman->message_id,
|
||||
SampleStartTime, SampleEndTime, ThrottleDuration, SampleExtensions,
|
||||
cbData, Stream_Pointer(ifman->input)))
|
||||
{
|
||||
WLog_ERR(TAG, "unable to push sample");
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
if ((error = tsmf_presentation_sync(presentation)))
|
||||
{
|
||||
WLog_ERR(TAG, "tsmf_presentation_sync failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
ifman->output_pending = TRUE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_on_flush(TSMF_IFMAN* ifman)
|
||||
{
|
||||
UINT32 StreamId = 0;
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
TSMF_STREAM* stream = nullptr;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, 20))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(ifman->input, 16);
|
||||
Stream_Read_UINT32(ifman->input, StreamId);
|
||||
DEBUG_TSMF("StreamId %" PRIu32 "", StreamId);
|
||||
presentation = tsmf_presentation_find_by_id(ifman->presentation_id);
|
||||
|
||||
if (!presentation)
|
||||
{
|
||||
WLog_ERR(TAG, "unknown presentation id");
|
||||
return ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* Flush message is for a stream, not the entire presentation
|
||||
* therefore we only flush the stream as intended per the MS-RDPEV spec
|
||||
*/
|
||||
stream = tsmf_stream_find_by_id(presentation, StreamId);
|
||||
|
||||
if (stream)
|
||||
{
|
||||
if (!tsmf_stream_flush(stream))
|
||||
return ERROR_INVALID_OPERATION;
|
||||
}
|
||||
else
|
||||
WLog_ERR(TAG, "unknown stream id");
|
||||
|
||||
ifman->output_pending = TRUE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_on_end_of_stream(TSMF_IFMAN* ifman)
|
||||
{
|
||||
UINT32 StreamId = 0;
|
||||
TSMF_STREAM* stream = nullptr;
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, 20))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
Stream_Seek(ifman->input, 16);
|
||||
Stream_Read_UINT32(ifman->input, StreamId);
|
||||
|
||||
if (presentation)
|
||||
{
|
||||
stream = tsmf_stream_find_by_id(presentation, StreamId);
|
||||
|
||||
if (stream)
|
||||
tsmf_stream_end(stream, ifman->message_id, ifman->channel_callback);
|
||||
}
|
||||
|
||||
DEBUG_TSMF("StreamId %" PRIu32 "", StreamId);
|
||||
ifman->output_pending = TRUE;
|
||||
ifman->output_interface_id = TSMF_INTERFACE_CLIENT_NOTIFICATIONS | STREAM_ID_PROXY;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_on_playback_started(TSMF_IFMAN* ifman)
|
||||
{
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
DEBUG_TSMF("");
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, ifman->input, 16))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
|
||||
if (presentation)
|
||||
tsmf_presentation_start(presentation);
|
||||
else
|
||||
WLog_ERR(TAG, "unknown presentation id");
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(ifman->output, 16))
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
Stream_Write_UINT32(ifman->output, CLIENT_EVENT_NOTIFICATION); /* FunctionId */
|
||||
Stream_Write_UINT32(ifman->output, 0); /* StreamId */
|
||||
Stream_Write_UINT32(ifman->output, TSMM_CLIENT_EVENT_START_COMPLETED); /* EventId */
|
||||
Stream_Write_UINT32(ifman->output, 0); /* cbData */
|
||||
ifman->output_interface_id = TSMF_INTERFACE_CLIENT_NOTIFICATIONS | STREAM_ID_PROXY;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_on_playback_paused(TSMF_IFMAN* ifman)
|
||||
{
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
DEBUG_TSMF("");
|
||||
ifman->output_pending = TRUE;
|
||||
/* Added pause control so gstreamer pipeline can be paused accordingly */
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
|
||||
if (presentation)
|
||||
{
|
||||
if (!tsmf_presentation_paused(presentation))
|
||||
return ERROR_INVALID_OPERATION;
|
||||
}
|
||||
else
|
||||
WLog_ERR(TAG, "unknown presentation id");
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_on_playback_restarted(TSMF_IFMAN* ifman)
|
||||
{
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
DEBUG_TSMF("");
|
||||
ifman->output_pending = TRUE;
|
||||
/* Added restart control so gstreamer pipeline can be resumed accordingly */
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
|
||||
if (presentation)
|
||||
{
|
||||
if (!tsmf_presentation_restarted(presentation))
|
||||
return ERROR_INVALID_OPERATION;
|
||||
}
|
||||
else
|
||||
WLog_ERR(TAG, "unknown presentation id");
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_on_playback_stopped(TSMF_IFMAN* ifman)
|
||||
{
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
DEBUG_TSMF("");
|
||||
presentation = tsmf_presentation_find_by_id(Stream_Pointer(ifman->input));
|
||||
|
||||
if (presentation)
|
||||
{
|
||||
if (!tsmf_presentation_stop(presentation))
|
||||
return ERROR_INVALID_OPERATION;
|
||||
}
|
||||
else
|
||||
WLog_ERR(TAG, "unknown presentation id");
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(ifman->output, 16))
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
Stream_Write_UINT32(ifman->output, CLIENT_EVENT_NOTIFICATION); /* FunctionId */
|
||||
Stream_Write_UINT32(ifman->output, 0); /* StreamId */
|
||||
Stream_Write_UINT32(ifman->output, TSMM_CLIENT_EVENT_STOP_COMPLETED); /* EventId */
|
||||
Stream_Write_UINT32(ifman->output, 0); /* cbData */
|
||||
ifman->output_interface_id = TSMF_INTERFACE_CLIENT_NOTIFICATIONS | STREAM_ID_PROXY;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT tsmf_ifman_on_playback_rate_changed(TSMF_IFMAN* ifman)
|
||||
{
|
||||
DEBUG_TSMF("");
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(ifman->output, 16))
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
Stream_Write_UINT32(ifman->output, CLIENT_EVENT_NOTIFICATION); /* FunctionId */
|
||||
Stream_Write_UINT32(ifman->output, 0); /* StreamId */
|
||||
Stream_Write_UINT32(ifman->output, TSMM_CLIENT_EVENT_MONITORCHANGED); /* EventId */
|
||||
Stream_Write_UINT32(ifman->output, 0); /* cbData */
|
||||
ifman->output_interface_id = TSMF_INTERFACE_CLIENT_NOTIFICATIONS | STREAM_ID_PROXY;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
92
third_party/FreeRDP/channels/tsmf/client/tsmf_ifman.h
vendored
Normal file
92
third_party/FreeRDP/channels/tsmf/client/tsmf_ifman.h
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - Interface Manipulation
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_TSMF_CLIENT_IFMAN_H
|
||||
#define FREERDP_CHANNEL_TSMF_CLIENT_IFMAN_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IWTSVirtualChannelCallback* channel_callback;
|
||||
const char* decoder_name;
|
||||
const char* audio_name;
|
||||
const char* audio_device;
|
||||
BYTE presentation_id[16];
|
||||
UINT32 stream_id;
|
||||
UINT32 message_id;
|
||||
|
||||
wStream* input;
|
||||
UINT32 input_size;
|
||||
wStream* output;
|
||||
BOOL output_pending;
|
||||
UINT32 output_interface_id;
|
||||
} TSMF_IFMAN;
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT
|
||||
tsmf_ifman_rim_exchange_capability_request(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_exchange_capability_request(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_check_format_support_request(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_on_new_presentation(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_add_stream(TSMF_IFMAN* ifman,
|
||||
rdpContext* rdpcontext);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_set_topology_request(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_remove_stream(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_set_source_video_rect(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_shutdown_presentation(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_on_stream_volume(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_on_channel_volume(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_set_video_window(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_update_geometry_info(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_set_allocator(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_notify_preroll(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_on_sample(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_on_flush(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_on_end_of_stream(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_on_playback_started(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_on_playback_paused(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_on_playback_restarted(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_on_playback_stopped(TSMF_IFMAN* ifman);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_ifman_on_playback_rate_changed(TSMF_IFMAN* ifman);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_TSMF_CLIENT_IFMAN_H */
|
||||
619
third_party/FreeRDP/channels/tsmf/client/tsmf_main.c
vendored
Normal file
619
third_party/FreeRDP/channels/tsmf/client/tsmf_main.c
vendored
Normal file
@@ -0,0 +1,619 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#include <freerdp/client/tsmf.h>
|
||||
|
||||
#include "tsmf_types.h"
|
||||
#include "tsmf_constants.h"
|
||||
#include "tsmf_ifman.h"
|
||||
#include "tsmf_media.h"
|
||||
|
||||
#include "tsmf_main.h"
|
||||
|
||||
BOOL tsmf_send_eos_response(IWTSVirtualChannelCallback* pChannelCallback, UINT32 message_id)
|
||||
{
|
||||
ssize_t status = -1;
|
||||
TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
|
||||
if (!callback)
|
||||
{
|
||||
DEBUG_TSMF("No callback reference - unable to send eos response!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (callback && callback->stream_id && callback->channel && callback->channel->Write)
|
||||
{
|
||||
wStream* s = Stream_New(nullptr, 24);
|
||||
|
||||
if (!s)
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT32(s, TSMF_INTERFACE_CLIENT_NOTIFICATIONS | STREAM_ID_PROXY);
|
||||
Stream_Write_UINT32(s, message_id);
|
||||
Stream_Write_UINT32(s, CLIENT_EVENT_NOTIFICATION); /* FunctionId */
|
||||
Stream_Write_UINT32(s, callback->stream_id); /* StreamId */
|
||||
Stream_Write_UINT32(s, TSMM_CLIENT_EVENT_ENDOFSTREAM); /* EventId */
|
||||
Stream_Write_UINT32(s, 0); /* cbData */
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
DEBUG_TSMF("EOS response size %" PRIuz "", pos);
|
||||
WINPR_ASSERT(pos <= UINT32_MAX);
|
||||
status =
|
||||
callback->channel->Write(callback->channel, (UINT32)pos, Stream_Buffer(s), nullptr);
|
||||
|
||||
if (status)
|
||||
{
|
||||
WLog_ERR(TAG, "response error %" PRId32, WINPR_CXX_COMPAT_CAST(int32_t, status));
|
||||
}
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
}
|
||||
|
||||
return (status == 0);
|
||||
}
|
||||
|
||||
BOOL tsmf_playback_ack(IWTSVirtualChannelCallback* pChannelCallback, UINT32 message_id,
|
||||
UINT64 duration, UINT32 data_size)
|
||||
{
|
||||
ssize_t status = -1;
|
||||
TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
|
||||
if (!callback)
|
||||
return FALSE;
|
||||
|
||||
wStream* s = Stream_New(nullptr, 32);
|
||||
|
||||
if (!s)
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT32(s, TSMF_INTERFACE_CLIENT_NOTIFICATIONS | STREAM_ID_PROXY);
|
||||
Stream_Write_UINT32(s, message_id);
|
||||
Stream_Write_UINT32(s, PLAYBACK_ACK); /* FunctionId */
|
||||
Stream_Write_UINT32(s, callback->stream_id); /* StreamId */
|
||||
Stream_Write_UINT64(s, duration); /* DataDuration */
|
||||
Stream_Write_UINT64(s, data_size); /* cbData */
|
||||
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
DEBUG_TSMF("ACK response size %" PRIuz "", pos);
|
||||
|
||||
if (!callback->channel || !callback->channel->Write)
|
||||
{
|
||||
WLog_ERR(TAG, "channel=%p, write=%p", WINPR_CXX_COMPAT_CAST(const void*, callback->channel),
|
||||
WINPR_CXX_COMPAT_CAST(const void*,
|
||||
callback->channel ? callback->channel->Write : nullptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
status = callback->channel->Write(
|
||||
callback->channel, WINPR_ASSERTING_INT_CAST(uint32_t, pos), Stream_Buffer(s), nullptr);
|
||||
}
|
||||
|
||||
if (status)
|
||||
{
|
||||
WLog_ERR(TAG, "response error %" PRId32, WINPR_CXX_COMPAT_CAST(int32_t, status));
|
||||
}
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return (status == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
||||
{
|
||||
wStream* input = nullptr;
|
||||
wStream* output = nullptr;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
BOOL processed = FALSE;
|
||||
TSMF_IFMAN ifman = WINPR_C_ARRAY_INIT;
|
||||
UINT32 MessageId = 0;
|
||||
UINT32 FunctionId = 0;
|
||||
UINT32 InterfaceId = 0;
|
||||
TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
const size_t cbSize = Stream_GetRemainingLength(data);
|
||||
|
||||
/* 2.2.1 Shared Message Header (SHARED_MSG_HEADER) */
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, data, 12) || (cbSize > UINT32_MAX))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
input = data;
|
||||
output = Stream_New(nullptr, 256);
|
||||
|
||||
if (!output)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
Stream_Seek(output, 8);
|
||||
Stream_Read_UINT32(input, InterfaceId); /* InterfaceId (4 bytes) */
|
||||
Stream_Read_UINT32(input, MessageId); /* MessageId (4 bytes) */
|
||||
Stream_Read_UINT32(input, FunctionId); /* FunctionId (4 bytes) */
|
||||
DEBUG_TSMF("cbSize=%" PRIu32 " InterfaceId=0x%" PRIX32 " MessageId=0x%" PRIX32
|
||||
" FunctionId=0x%" PRIX32 "",
|
||||
cbSize, InterfaceId, MessageId, FunctionId);
|
||||
ifman.channel_callback = pChannelCallback;
|
||||
ifman.decoder_name = ((TSMF_PLUGIN*)callback->plugin)->decoder_name;
|
||||
ifman.audio_name = ((TSMF_PLUGIN*)callback->plugin)->audio_name;
|
||||
ifman.audio_device = ((TSMF_PLUGIN*)callback->plugin)->audio_device;
|
||||
CopyMemory(ifman.presentation_id, callback->presentation_id, GUID_SIZE);
|
||||
ifman.stream_id = callback->stream_id;
|
||||
ifman.message_id = MessageId;
|
||||
ifman.input = input;
|
||||
ifman.input_size = (UINT32)(cbSize - 12U);
|
||||
ifman.output = output;
|
||||
ifman.output_pending = FALSE;
|
||||
ifman.output_interface_id = InterfaceId;
|
||||
|
||||
// (void)fprintf(stderr, "InterfaceId: 0x%08"PRIX32" MessageId: 0x%08"PRIX32" FunctionId:
|
||||
// 0x%08"PRIX32"\n", InterfaceId, MessageId, FunctionId);
|
||||
|
||||
switch (InterfaceId)
|
||||
{
|
||||
case TSMF_INTERFACE_CAPABILITIES | STREAM_ID_NONE:
|
||||
switch (FunctionId)
|
||||
{
|
||||
case RIM_EXCHANGE_CAPABILITY_REQUEST:
|
||||
error = tsmf_ifman_rim_exchange_capability_request(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case RIMCALL_RELEASE:
|
||||
case RIMCALL_QUERYINTERFACE:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TSMF_INTERFACE_DEFAULT | STREAM_ID_PROXY:
|
||||
switch (FunctionId)
|
||||
{
|
||||
case SET_CHANNEL_PARAMS:
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, input, GUID_SIZE + 4))
|
||||
{
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto out;
|
||||
}
|
||||
|
||||
CopyMemory(callback->presentation_id, Stream_Pointer(input), GUID_SIZE);
|
||||
Stream_Seek(input, GUID_SIZE);
|
||||
Stream_Read_UINT32(input, callback->stream_id);
|
||||
DEBUG_TSMF("SET_CHANNEL_PARAMS StreamId=%" PRIu32 "", callback->stream_id);
|
||||
ifman.output_pending = TRUE;
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case EXCHANGE_CAPABILITIES_REQ:
|
||||
error = tsmf_ifman_exchange_capability_request(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case CHECK_FORMAT_SUPPORT_REQ:
|
||||
error = tsmf_ifman_check_format_support_request(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_NEW_PRESENTATION:
|
||||
error = tsmf_ifman_on_new_presentation(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ADD_STREAM:
|
||||
error =
|
||||
tsmf_ifman_add_stream(&ifman, ((TSMF_PLUGIN*)callback->plugin)->rdpcontext);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case SET_TOPOLOGY_REQ:
|
||||
error = tsmf_ifman_set_topology_request(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case REMOVE_STREAM:
|
||||
error = tsmf_ifman_remove_stream(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case SET_SOURCE_VIDEO_RECT:
|
||||
error = tsmf_ifman_set_source_video_rect(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case SHUTDOWN_PRESENTATION_REQ:
|
||||
error = tsmf_ifman_shutdown_presentation(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_STREAM_VOLUME:
|
||||
error = tsmf_ifman_on_stream_volume(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_CHANNEL_VOLUME:
|
||||
error = tsmf_ifman_on_channel_volume(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case SET_VIDEO_WINDOW:
|
||||
error = tsmf_ifman_set_video_window(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case UPDATE_GEOMETRY_INFO:
|
||||
error = tsmf_ifman_update_geometry_info(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case SET_ALLOCATOR:
|
||||
error = tsmf_ifman_set_allocator(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case NOTIFY_PREROLL:
|
||||
error = tsmf_ifman_notify_preroll(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_SAMPLE:
|
||||
error = tsmf_ifman_on_sample(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_FLUSH:
|
||||
error = tsmf_ifman_on_flush(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_END_OF_STREAM:
|
||||
error = tsmf_ifman_on_end_of_stream(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_PLAYBACK_STARTED:
|
||||
error = tsmf_ifman_on_playback_started(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_PLAYBACK_PAUSED:
|
||||
error = tsmf_ifman_on_playback_paused(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_PLAYBACK_RESTARTED:
|
||||
error = tsmf_ifman_on_playback_restarted(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_PLAYBACK_STOPPED:
|
||||
error = tsmf_ifman_on_playback_stopped(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_PLAYBACK_RATE_CHANGED:
|
||||
error = tsmf_ifman_on_playback_rate_changed(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case RIMCALL_RELEASE:
|
||||
case RIMCALL_QUERYINTERFACE:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
input = nullptr;
|
||||
ifman.input = nullptr;
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "ifman data received processing error %" PRIu32 "", error);
|
||||
}
|
||||
|
||||
if (!processed)
|
||||
{
|
||||
switch (FunctionId)
|
||||
{
|
||||
case RIMCALL_RELEASE:
|
||||
/* [MS-RDPEXPS] 2.2.2.2 Interface Release (IFACE_RELEASE)
|
||||
This message does not require a reply. */
|
||||
processed = TRUE;
|
||||
ifman.output_pending = 1;
|
||||
break;
|
||||
|
||||
case RIMCALL_QUERYINTERFACE:
|
||||
/* [MS-RDPEXPS] 2.2.2.1.2 Query Interface Response (QI_RSP)
|
||||
This message is not supported in this channel. */
|
||||
processed = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!processed)
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"Unknown InterfaceId: 0x%08" PRIX32 " MessageId: 0x%08" PRIX32
|
||||
" FunctionId: 0x%08" PRIX32 "\n",
|
||||
InterfaceId, MessageId, FunctionId);
|
||||
/* When a request is not implemented we return empty response indicating error */
|
||||
}
|
||||
|
||||
processed = TRUE;
|
||||
}
|
||||
|
||||
if (processed && !ifman.output_pending)
|
||||
{
|
||||
/* Response packet does not have FunctionId */
|
||||
const size_t length = Stream_GetPosition(output);
|
||||
if (length > UINT32_MAX)
|
||||
goto out;
|
||||
Stream_ResetPosition(output);
|
||||
Stream_Write_UINT32(output, ifman.output_interface_id);
|
||||
Stream_Write_UINT32(output, MessageId);
|
||||
DEBUG_TSMF("response size %d", length);
|
||||
error = callback->channel->Write(callback->channel, (UINT32)length, Stream_Buffer(output),
|
||||
nullptr);
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "response error %" PRIu32 "", error);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
Stream_Free(output, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT tsmf_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
TSMF_STREAM* stream = nullptr;
|
||||
TSMF_PRESENTATION* presentation = nullptr;
|
||||
TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
DEBUG_TSMF("");
|
||||
|
||||
if (callback->stream_id)
|
||||
{
|
||||
presentation = tsmf_presentation_find_by_id(callback->presentation_id);
|
||||
|
||||
if (presentation)
|
||||
{
|
||||
stream = tsmf_stream_find_by_id(presentation, callback->stream_id);
|
||||
|
||||
if (stream)
|
||||
tsmf_stream_free(stream);
|
||||
}
|
||||
}
|
||||
|
||||
free(pChannelCallback);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT tsmf_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel,
|
||||
WINPR_ATTR_UNUSED BYTE* Data,
|
||||
WINPR_ATTR_UNUSED BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
TSMF_CHANNEL_CALLBACK* callback = nullptr;
|
||||
TSMF_LISTENER_CALLBACK* listener_callback = (TSMF_LISTENER_CALLBACK*)pListenerCallback;
|
||||
DEBUG_TSMF("");
|
||||
callback = (TSMF_CHANNEL_CALLBACK*)calloc(1, sizeof(TSMF_CHANNEL_CALLBACK));
|
||||
|
||||
if (!callback)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
callback->iface.OnDataReceived = tsmf_on_data_received;
|
||||
callback->iface.OnClose = tsmf_on_close;
|
||||
callback->iface.OnOpen = nullptr;
|
||||
callback->plugin = listener_callback->plugin;
|
||||
callback->channel_mgr = listener_callback->channel_mgr;
|
||||
callback->channel = pChannel;
|
||||
*ppCallback = (IWTSVirtualChannelCallback*)callback;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT tsmf_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
{
|
||||
UINT status = 0;
|
||||
TSMF_PLUGIN* tsmf = (TSMF_PLUGIN*)pPlugin;
|
||||
DEBUG_TSMF("");
|
||||
tsmf->listener_callback = (TSMF_LISTENER_CALLBACK*)calloc(1, sizeof(TSMF_LISTENER_CALLBACK));
|
||||
|
||||
if (!tsmf->listener_callback)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
tsmf->listener_callback->iface.OnNewChannelConnection = tsmf_on_new_channel_connection;
|
||||
tsmf->listener_callback->plugin = pPlugin;
|
||||
tsmf->listener_callback->channel_mgr = pChannelMgr;
|
||||
status = pChannelMgr->CreateListener(
|
||||
pChannelMgr, "TSMF", 0, (IWTSListenerCallback*)tsmf->listener_callback, &(tsmf->listener));
|
||||
tsmf->listener->pInterface = tsmf->iface.pInterface;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT tsmf_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
TSMF_PLUGIN* tsmf = (TSMF_PLUGIN*)pPlugin;
|
||||
DEBUG_TSMF("");
|
||||
free(tsmf->listener_callback);
|
||||
free(tsmf);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT tsmf_process_addin_args(IWTSPlugin* pPlugin, const ADDIN_ARGV* args)
|
||||
{
|
||||
int status = 0;
|
||||
DWORD flags = 0;
|
||||
const COMMAND_LINE_ARGUMENT_A* arg = nullptr;
|
||||
TSMF_PLUGIN* tsmf = (TSMF_PLUGIN*)pPlugin;
|
||||
COMMAND_LINE_ARGUMENT_A tsmf_args[] = { { "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>",
|
||||
nullptr, nullptr, -1, nullptr, "audio subsystem" },
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
|
||||
nullptr, nullptr, -1, nullptr, "audio device name" },
|
||||
{ "decoder", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>",
|
||||
nullptr, nullptr, -1, nullptr, "decoder subsystem" },
|
||||
{ nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr,
|
||||
nullptr } };
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv, tsmf_args, flags, tsmf, nullptr,
|
||||
nullptr);
|
||||
|
||||
if (status != 0)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
arg = tsmf_args;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "sys")
|
||||
{
|
||||
tsmf->audio_name = _strdup(arg->Value);
|
||||
|
||||
if (!tsmf->audio_name)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
tsmf->audio_device = _strdup(arg->Value);
|
||||
|
||||
if (!tsmf->audio_device)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "decoder")
|
||||
{
|
||||
tsmf->decoder_name = _strdup(arg->Value);
|
||||
|
||||
if (!tsmf->decoder_name)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
CommandLineSwitchDefault(arg)
|
||||
{
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE tsmf_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
UINT status = 0;
|
||||
TSMF_PLUGIN* tsmf = nullptr;
|
||||
TsmfClientContext* context = nullptr;
|
||||
UINT error = CHANNEL_RC_NO_MEMORY;
|
||||
tsmf = (TSMF_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "tsmf");
|
||||
|
||||
if (!tsmf)
|
||||
{
|
||||
tsmf = (TSMF_PLUGIN*)calloc(1, sizeof(TSMF_PLUGIN));
|
||||
|
||||
if (!tsmf)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
tsmf->iface.Initialize = tsmf_plugin_initialize;
|
||||
tsmf->iface.Connected = nullptr;
|
||||
tsmf->iface.Disconnected = nullptr;
|
||||
tsmf->iface.Terminated = tsmf_plugin_terminated;
|
||||
tsmf->rdpcontext = pEntryPoints->GetRdpContext(pEntryPoints);
|
||||
context = (TsmfClientContext*)calloc(1, sizeof(TsmfClientContext));
|
||||
|
||||
if (!context)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
goto error_context;
|
||||
}
|
||||
|
||||
context->handle = (void*)tsmf;
|
||||
tsmf->iface.pInterface = (void*)context;
|
||||
|
||||
if (!tsmf_media_init())
|
||||
{
|
||||
error = ERROR_INVALID_OPERATION;
|
||||
goto error_init;
|
||||
}
|
||||
|
||||
status = pEntryPoints->RegisterPlugin(pEntryPoints, "tsmf", &tsmf->iface);
|
||||
}
|
||||
|
||||
if (status == CHANNEL_RC_OK)
|
||||
{
|
||||
status = tsmf_process_addin_args(&tsmf->iface, pEntryPoints->GetPluginData(pEntryPoints));
|
||||
}
|
||||
|
||||
return status;
|
||||
error_init:
|
||||
free(context);
|
||||
error_context:
|
||||
free(tsmf);
|
||||
return error;
|
||||
}
|
||||
68
third_party/FreeRDP/channels/tsmf/client/tsmf_main.h
vendored
Normal file
68
third_party/FreeRDP/channels/tsmf/client/tsmf_main.h
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_TSMF_CLIENT_MAIN_H
|
||||
#define FREERDP_CHANNEL_TSMF_CLIENT_MAIN_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IWTSListenerCallback iface;
|
||||
|
||||
IWTSPlugin* plugin;
|
||||
IWTSVirtualChannelManager* channel_mgr;
|
||||
} TSMF_LISTENER_CALLBACK;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IWTSVirtualChannelCallback iface;
|
||||
|
||||
IWTSPlugin* plugin;
|
||||
IWTSVirtualChannelManager* channel_mgr;
|
||||
IWTSVirtualChannel* channel;
|
||||
|
||||
BYTE presentation_id[GUID_SIZE];
|
||||
UINT32 stream_id;
|
||||
} TSMF_CHANNEL_CALLBACK;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IWTSPlugin iface;
|
||||
|
||||
IWTSListener* listener;
|
||||
TSMF_LISTENER_CALLBACK* listener_callback;
|
||||
|
||||
const char* decoder_name;
|
||||
const char* audio_name;
|
||||
const char* audio_device;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
} TSMF_PLUGIN;
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL
|
||||
tsmf_send_eos_response(IWTSVirtualChannelCallback* pChannelCallback, UINT32 message_id);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL
|
||||
tsmf_playback_ack(IWTSVirtualChannelCallback* pChannelCallback, UINT32 message_id, UINT64 duration,
|
||||
UINT32 data_size);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_TSMF_CLIENT_MAIN_H */
|
||||
1547
third_party/FreeRDP/channels/tsmf/client/tsmf_media.c
vendored
Normal file
1547
third_party/FreeRDP/channels/tsmf/client/tsmf_media.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
99
third_party/FreeRDP/channels/tsmf/client/tsmf_media.h
vendored
Normal file
99
third_party/FreeRDP/channels/tsmf/client/tsmf_media.h
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - Media Container
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The media container maintains a global list of presentations, and a list of
|
||||
* streams in each presentation.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_TSMF_CLIENT_MEDIA_H
|
||||
#define FREERDP_CHANNEL_TSMF_CLIENT_MEDIA_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
typedef struct S_TSMF_PRESENTATION TSMF_PRESENTATION;
|
||||
|
||||
typedef struct S_TSMF_STREAM TSMF_STREAM;
|
||||
|
||||
typedef struct S_TSMF_SAMPLE TSMF_SAMPLE;
|
||||
|
||||
FREERDP_LOCAL void tsmf_presentation_free(TSMF_PRESENTATION* presentation);
|
||||
|
||||
WINPR_ATTR_MALLOC(tsmf_presentation_free, 1)
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL TSMF_PRESENTATION*
|
||||
tsmf_presentation_new(const BYTE* guid, IWTSVirtualChannelCallback* pChannelCallback);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL TSMF_PRESENTATION*
|
||||
tsmf_presentation_find_by_id(const BYTE* guid);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL tsmf_presentation_start(TSMF_PRESENTATION* presentation);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL tsmf_presentation_stop(TSMF_PRESENTATION* presentation);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL UINT tsmf_presentation_sync(TSMF_PRESENTATION* presentation);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL tsmf_presentation_paused(TSMF_PRESENTATION* presentation);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL
|
||||
tsmf_presentation_restarted(TSMF_PRESENTATION* presentation);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL
|
||||
tsmf_presentation_volume_changed(TSMF_PRESENTATION* presentation, UINT32 newVolume, UINT32 muted);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL tsmf_presentation_set_geometry_info(
|
||||
TSMF_PRESENTATION* presentation, UINT32 x, UINT32 y, UINT32 width, UINT32 height,
|
||||
UINT32 num_rects, const RECTANGLE_32* rects);
|
||||
|
||||
FREERDP_LOCAL
|
||||
void tsmf_presentation_set_audio_device(TSMF_PRESENTATION* presentation, const char* name,
|
||||
const char* device);
|
||||
|
||||
FREERDP_LOCAL
|
||||
void tsmf_stream_free(TSMF_STREAM* stream);
|
||||
|
||||
WINPR_ATTR_MALLOC(tsmf_stream_free, 1)
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL TSMF_STREAM*
|
||||
tsmf_stream_new(TSMF_PRESENTATION* presentation, UINT32 stream_id, rdpContext* rdpcontext);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL TSMF_STREAM*
|
||||
tsmf_stream_find_by_id(TSMF_PRESENTATION* presentation, UINT32 stream_id);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL tsmf_stream_set_format(TSMF_STREAM* stream,
|
||||
const char* name, wStream* s);
|
||||
|
||||
FREERDP_LOCAL
|
||||
void tsmf_stream_end(TSMF_STREAM* stream, UINT32 message_id,
|
||||
IWTSVirtualChannelCallback* pChannelCallback);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL tsmf_stream_flush(TSMF_STREAM* stream);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL 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);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL tsmf_media_init(void);
|
||||
|
||||
FREERDP_LOCAL
|
||||
void tsmf_stream_start_threads(TSMF_STREAM* stream);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_TSMF_CLIENT_MEDIA_H */
|
||||
61
third_party/FreeRDP/channels/tsmf/client/tsmf_types.h
vendored
Normal file
61
third_party/FreeRDP/channels/tsmf/client/tsmf_types.h
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Redirection Virtual Channel - Types
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_TSMF_CLIENT_TYPES_H
|
||||
#define FREERDP_CHANNEL_TSMF_CLIENT_TYPES_H
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("tsmf.client")
|
||||
|
||||
#ifdef WITH_DEBUG_TSMF
|
||||
#define DEBUG_TSMF(...) WLog_DBG(TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_TSMF(...) \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int MajorType;
|
||||
int SubType;
|
||||
int FormatType;
|
||||
|
||||
UINT32 Width;
|
||||
UINT32 Height;
|
||||
UINT32 BitRate;
|
||||
struct
|
||||
{
|
||||
UINT32 Numerator;
|
||||
UINT32 Denominator;
|
||||
} SamplesPerSecond;
|
||||
UINT32 Channels;
|
||||
UINT32 BitsPerSample;
|
||||
UINT32 BlockAlign;
|
||||
const BYTE* ExtraData;
|
||||
UINT32 ExtraDataSize;
|
||||
} TS_AM_MEDIA_TYPE;
|
||||
|
||||
#endif /* FREERDP_CHANNEL_TSMF_CLIENT_TYPES_H */
|
||||
Reference in New Issue
Block a user