From dadb94a1e343648503949094a50053d81212a153 Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Mon, 19 Sep 2011 22:54:09 +0800 Subject: [PATCH] tsmf: add tsmf main module. --- channels/drdynvc/CMakeLists.txt | 3 + channels/drdynvc/tsmf/CMakeLists.txt | 45 ++ channels/drdynvc/tsmf/tsmf_audio.c | 80 +++ channels/drdynvc/tsmf/tsmf_audio.h | 49 ++ channels/drdynvc/tsmf/tsmf_codec.c | 407 +++++++++++++ channels/drdynvc/tsmf/tsmf_codec.h | 29 + channels/drdynvc/tsmf/tsmf_constants.h | 120 ++++ channels/drdynvc/tsmf/tsmf_decoder.c | 81 +++ channels/drdynvc/tsmf/tsmf_decoder.h | 50 ++ channels/drdynvc/tsmf/tsmf_ifman.c | 478 +++++++++++++++ channels/drdynvc/tsmf/tsmf_ifman.h | 65 ++ channels/drdynvc/tsmf/tsmf_main.c | 443 ++++++++++++++ channels/drdynvc/tsmf/tsmf_main.h | 29 + channels/drdynvc/tsmf/tsmf_media.c | 795 +++++++++++++++++++++++++ channels/drdynvc/tsmf/tsmf_media.h | 59 ++ channels/drdynvc/tsmf/tsmf_types.h | 47 ++ 16 files changed, 2780 insertions(+) create mode 100644 channels/drdynvc/tsmf/CMakeLists.txt create mode 100644 channels/drdynvc/tsmf/tsmf_audio.c create mode 100644 channels/drdynvc/tsmf/tsmf_audio.h create mode 100644 channels/drdynvc/tsmf/tsmf_codec.c create mode 100644 channels/drdynvc/tsmf/tsmf_codec.h create mode 100644 channels/drdynvc/tsmf/tsmf_constants.h create mode 100644 channels/drdynvc/tsmf/tsmf_decoder.c create mode 100644 channels/drdynvc/tsmf/tsmf_decoder.h create mode 100644 channels/drdynvc/tsmf/tsmf_ifman.c create mode 100644 channels/drdynvc/tsmf/tsmf_ifman.h create mode 100644 channels/drdynvc/tsmf/tsmf_main.c create mode 100644 channels/drdynvc/tsmf/tsmf_main.h create mode 100644 channels/drdynvc/tsmf/tsmf_media.c create mode 100644 channels/drdynvc/tsmf/tsmf_media.h create mode 100644 channels/drdynvc/tsmf/tsmf_types.h diff --git a/channels/drdynvc/CMakeLists.txt b/channels/drdynvc/CMakeLists.txt index c3bdbb0b4..ce32efd1b 100644 --- a/channels/drdynvc/CMakeLists.txt +++ b/channels/drdynvc/CMakeLists.txt @@ -31,3 +31,6 @@ set_target_properties(drdynvc PROPERTIES PREFIX "") target_link_libraries(drdynvc freerdp-utils) install(TARGETS drdynvc DESTINATION ${FREERDP_PLUGIN_PATH}) + +add_subdirectory(tsmf) + diff --git a/channels/drdynvc/tsmf/CMakeLists.txt b/channels/drdynvc/tsmf/CMakeLists.txt new file mode 100644 index 000000000..98e25058d --- /dev/null +++ b/channels/drdynvc/tsmf/CMakeLists.txt @@ -0,0 +1,45 @@ +# FreeRDP: A Remote Desktop Protocol Client +# FreeRDP cmake build script +# +# Copyright 2011 O.S. Systems Software Ltda. +# Copyright 2011 Otavio Salvador +# Copyright 2011 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(TSMF_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 +) + +include_directories(..) + +add_library(tsmf ${TSMF_SRCS}) +set_target_properties(tsmf PROPERTIES PREFIX "") + +target_link_libraries(tsmf freerdp-utils) + +install(TARGETS tsmf DESTINATION ${FREERDP_PLUGIN_PATH}) + diff --git a/channels/drdynvc/tsmf/tsmf_audio.c b/channels/drdynvc/tsmf/tsmf_audio.c new file mode 100644 index 000000000..5a1a14512 --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_audio.c @@ -0,0 +1,80 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * 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 +#include +#include +#include +#include + +#include "tsmf_audio.h" + +static ITSMFAudioDevice* tsmf_load_audio_device_by_name(const char* name, const char* device) +{ + ITSMFAudioDevice* audio; + TSMF_AUDIO_DEVICE_ENTRY entry; + char* fullname; + + if (strrchr(name, '.') != NULL) + entry = (TSMF_AUDIO_DEVICE_ENTRY) freerdp_load_plugin(name, TSMF_AUDIO_DEVICE_EXPORT_FUNC_NAME); + else + { + fullname = xzalloc(strlen(name) + 6); + strcpy(fullname, "tsmf_"); + strcat(fullname, name); + entry = (TSMF_AUDIO_DEVICE_ENTRY) freerdp_load_plugin(fullname, TSMF_AUDIO_DEVICE_EXPORT_FUNC_NAME); + xfree(fullname); + } + if (entry == NULL) + { + return NULL; + } + + audio = entry(); + if (audio == NULL) + { + DEBUG_WARN("failed to call export function in %s", name); + return NULL; + } + if (!audio->Open(audio, device)) + { + audio->Free(audio); + audio = NULL; + } + return audio; +} + +ITSMFAudioDevice* tsmf_load_audio_device(const char* name, const char* device) +{ + ITSMFAudioDevice* audio; + + if (name) + { + audio = tsmf_load_audio_device_by_name(name, device); + } + else + { + audio = tsmf_load_audio_device_by_name("pulse", device); + if (!audio) + audio = tsmf_load_audio_device_by_name("alsa", device); + } + + return audio; +} + diff --git a/channels/drdynvc/tsmf/tsmf_audio.h b/channels/drdynvc/tsmf/tsmf_audio.h new file mode 100644 index 000000000..af0075927 --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_audio.h @@ -0,0 +1,49 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * 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 __TSMF_AUDIO_H +#define __TSMF_AUDIO_H + +#include "drdynvc_types.h" + +typedef struct _ITSMFAudioDevice ITSMFAudioDevice; + +struct _ITSMFAudioDevice +{ + /* Open the audio device. */ + boolean (*Open) (ITSMFAudioDevice* audio, const char* device); + /* Set the audio data format. */ + boolean (*SetFormat) (ITSMFAudioDevice* audio, uint32 sample_rate, uint32 channels, uint32 bits_per_sample); + /* Play audio data. */ + boolean (*Play) (ITSMFAudioDevice* audio, uint8* data, uint32 data_size); + /* Get the latency of the last written sample, in 100ns */ + uint64 (*GetLatency) (ITSMFAudioDevice* audio); + /* Flush queued audio data */ + void (*Flush) (ITSMFAudioDevice* audio); + /* Free the audio device */ + void (*Free) (ITSMFAudioDevice* audio); +}; + +#define TSMF_AUDIO_DEVICE_EXPORT_FUNC_NAME "TSMFAudioDeviceEntry" +typedef ITSMFAudioDevice* (*TSMF_AUDIO_DEVICE_ENTRY) (void); + +ITSMFAudioDevice* tsmf_load_audio_device(const char* name, const char* device); + +#endif + diff --git a/channels/drdynvc/tsmf/tsmf_codec.c b/channels/drdynvc/tsmf/tsmf_codec.c new file mode 100644 index 000000000..5a5134c6b --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_codec.c @@ -0,0 +1,407 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * 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. + */ + +#include +#include +#include +#include +#include + +#include "drdynvc_types.h" +#include "tsmf_constants.h" +#include "tsmf_types.h" + +#include "tsmf_codec.h" + +typedef struct _TSMFMediaTypeMap +{ + uint8 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 + }, + + /* 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 + }, + + /* 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 + }, + + /* 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 + }, + + { + { 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 + }, + + { + { 0 }, + "Unknown", + TSMF_FORMAT_TYPE_UNKNOWN + } +}; + +static void tsmf_print_guid(const uint8* guid) +{ +#ifdef WITH_DEBUG_DVC + int i; + + for (i = 3; i >= 0; i--) + printf("%02X", guid[i]); + printf("-"); + for (i = 5; i >= 4; i--) + printf("%02X", guid[i]); + printf("-"); + for (i = 7; i >= 6; i--) + printf("%02X", guid[i]); + printf("-"); + for (i = 8; i < 16; i++) + { + printf("%02X", guid[i]); + if (i == 9) + printf("-"); + } + printf("\n"); +#endif +} + +/* http://msdn.microsoft.com/en-us/library/dd318229.aspx */ +static uint32 tsmf_codec_parse_BITMAPINFOHEADER(TS_AM_MEDIA_TYPE* mediatype, STREAM* s, boolean bypass) +{ + uint32 biSize; + uint32 biWidth; + uint32 biHeight; + + 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 (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, STREAM* s) +{ + uint64 AvgTimePerFrame; + + /* VIDEOINFOHEADER2.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); + /* 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 = (int)(AvgTimePerFrame / 10LL); + /* Remaining fields before bmiHeader */ + stream_seek(s, 24); + + return 72; +} + +boolean tsmf_codec_parse_media_type(TS_AM_MEDIA_TYPE* mediatype, STREAM* s) +{ + int i; + uint32 cbFormat; + boolean ret = True; + + memset(mediatype, 0, sizeof(TS_AM_MEDIA_TYPE)); + + /* MajorType */ + DEBUG_DVC("MajorType:"); + tsmf_print_guid(stream_get_tail(s)); + for (i = 0; tsmf_major_type_map[i].type != TSMF_MAJOR_TYPE_UNKNOWN; i++) + { + if (memcmp(tsmf_major_type_map[i].guid, stream_get_tail(s), 16) == 0) + break; + } + mediatype->MajorType = tsmf_major_type_map[i].type; + if (mediatype->MajorType == TSMF_MAJOR_TYPE_UNKNOWN) + ret = False; + DEBUG_DVC("MajorType %s", tsmf_major_type_map[i].name); + stream_seek(s, 16); + + /* SubType */ + DEBUG_DVC("SubType:"); + tsmf_print_guid(stream_get_tail(s)); + for (i = 0; tsmf_sub_type_map[i].type != TSMF_SUB_TYPE_UNKNOWN; i++) + { + if (memcmp(tsmf_sub_type_map[i].guid, stream_get_tail(s), 16) == 0) + break; + } + mediatype->SubType = tsmf_sub_type_map[i].type; + if (mediatype->SubType == TSMF_SUB_TYPE_UNKNOWN) + ret = False; + DEBUG_DVC("SubType %s", tsmf_sub_type_map[i].name); + stream_seek(s, 16); + + /* bFixedSizeSamples, bTemporalCompression, SampleSize */ + stream_seek(s, 12); + + /* FormatType */ + DEBUG_DVC("FormatType:"); + tsmf_print_guid(stream_get_tail(s)); + for (i = 0; tsmf_format_type_map[i].type != TSMF_FORMAT_TYPE_UNKNOWN; i++) + { + if (memcmp(tsmf_format_type_map[i].guid, stream_get_tail(s), 16) == 0) + break; + } + mediatype->FormatType = tsmf_format_type_map[i].type; + if (mediatype->FormatType == TSMF_FORMAT_TYPE_UNKNOWN) + ret = False; + DEBUG_DVC("FormatType %s", tsmf_format_type_map[i].name); + stream_seek(s, 16); + + /* cbFormat */ + stream_read_uint32(s, cbFormat); + DEBUG_DVC("cbFormat %d", cbFormat); + +#ifdef WITH_DEBUG_DVC + freerdp_hexdump(stream_get_tail(s), cbFormat); +#endif + + switch (mediatype->FormatType) + { + case TSMF_FORMAT_TYPE_MFVIDEOFORMAT: + /* http://msdn.microsoft.com/en-us/library/aa473808.aspx */ + + 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) + { + mediatype->ExtraDataSize = cbFormat - 176; + mediatype->ExtraData = stream_get_tail(s); + } + break; + + case TSMF_FORMAT_TYPE_WAVEFORMATEX: + /* http://msdn.microsoft.com/en-us/library/dd757720.aspx */ + + 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) + mediatype->ExtraData = stream_get_tail(s); + + break; + + case TSMF_FORMAT_TYPE_MPEG2VIDEOINFO: + /* http://msdn.microsoft.com/en-us/library/dd390707.aspx */ + + i = tsmf_codec_parse_VIDEOINFOHEADER2(mediatype, s); + i += tsmf_codec_parse_BITMAPINFOHEADER(mediatype, s, True); + if (cbFormat > i) + { + mediatype->ExtraDataSize = cbFormat - i; + mediatype->ExtraData = stream_get_tail(s); + } + break; + + case TSMF_FORMAT_TYPE_VIDEOINFO2: + i = tsmf_codec_parse_VIDEOINFOHEADER2(mediatype, s); + i += tsmf_codec_parse_BITMAPINFOHEADER(mediatype, s, False); + if (cbFormat > i) + { + mediatype->ExtraDataSize = cbFormat - i; + mediatype->ExtraData = stream_get_tail(s); + } + break; + + default: + break; + } + + if (mediatype->SamplesPerSecond.Numerator == 0) + mediatype->SamplesPerSecond.Numerator = 1; + if (mediatype->SamplesPerSecond.Denominator == 0) + mediatype->SamplesPerSecond.Denominator = 1; + + return ret; +} + +boolean tsmf_codec_check_media_type(STREAM* s) +{ + uint8* m; + boolean ret; + TS_AM_MEDIA_TYPE mediatype; + + stream_get_mark(s, m); + ret = tsmf_codec_parse_media_type(&mediatype, s); + stream_set_mark(s, m); + + return ret; +} + diff --git a/channels/drdynvc/tsmf/tsmf_codec.h b/channels/drdynvc/tsmf/tsmf_codec.h new file mode 100644 index 000000000..eb6bbeaf5 --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_codec.h @@ -0,0 +1,29 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * 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 __TSMF_CODEC +#define __TSMF_CODEC + +#include "tsmf_types.h" + +boolean tsmf_codec_parse_media_type(TS_AM_MEDIA_TYPE* mediatype, STREAM* s); +boolean tsmf_codec_check_media_type(STREAM* s); + +#endif + diff --git a/channels/drdynvc/tsmf/tsmf_constants.h b/channels/drdynvc/tsmf/tsmf_constants.h new file mode 100644 index 000000000..e836dd72f --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_constants.h @@ -0,0 +1,120 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Video Redirection Virtual Channel - Constants + * + * 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 __TSMF_CONSTANTS_H +#define __TSMF_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 + +/* Functon 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 + +/* 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 + +/* 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 + +#endif + diff --git a/channels/drdynvc/tsmf/tsmf_decoder.c b/channels/drdynvc/tsmf/tsmf_decoder.c new file mode 100644 index 000000000..db31c027b --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_decoder.c @@ -0,0 +1,81 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * 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 +#include +#include +#include +#include + +#include "drdynvc_types.h" +#include "tsmf_types.h" +#include "tsmf_constants.h" +#include "tsmf_decoder.h" + +static ITSMFDecoder* tsmf_load_decoder_by_name(const char* name, TS_AM_MEDIA_TYPE* media_type) +{ + ITSMFDecoder* decoder; + TSMF_DECODER_ENTRY entry; + char* fullname; + + if (strrchr(name, '.') != NULL) + entry = (TSMF_DECODER_ENTRY) freerdp_load_plugin(name, TSMF_DECODER_EXPORT_FUNC_NAME); + else + { + fullname = xzalloc(strlen(name) + 6); + strcpy(fullname, "tsmf_"); + strcat(fullname, name); + entry = (TSMF_DECODER_ENTRY) freerdp_load_plugin(fullname, TSMF_DECODER_EXPORT_FUNC_NAME); + xfree(fullname); + } + if (entry == NULL) + { + return NULL; + } + + decoder = entry(); + if (decoder == NULL) + { + DEBUG_WARN("failed to call export function in %s", name); + return NULL; + } + if (!decoder->SetFormat(decoder, media_type)) + { + decoder->Free(decoder); + decoder = NULL; + } + return decoder; +} + +ITSMFDecoder* tsmf_load_decoder(const char* name, TS_AM_MEDIA_TYPE* media_type) +{ + ITSMFDecoder* decoder; + + if (name) + { + decoder = tsmf_load_decoder_by_name(name, media_type); + } + else + { + decoder = tsmf_load_decoder_by_name("ffmpeg", media_type); + } + + return decoder; +} + diff --git a/channels/drdynvc/tsmf/tsmf_decoder.h b/channels/drdynvc/tsmf/tsmf_decoder.h new file mode 100644 index 000000000..b79c263aa --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_decoder.h @@ -0,0 +1,50 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * 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. + */ + +#ifndef __TSMF_DECODER_H +#define __TSMF_DECODER_H + +#include "drdynvc_types.h" +#include "tsmf_types.h" + +typedef struct _ITSMFDecoder ITSMFDecoder; + +struct _ITSMFDecoder +{ + /* Set the decoder format. Return True if supported. */ + boolean (*SetFormat) (ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* media_type); + /* Decode a sample. */ + boolean (*Decode) (ITSMFDecoder* decoder, const uint8* data, uint32 data_size, uint32 extensions); + /* Get the decoded data */ + uint8* (*GetDecodedData) (ITSMFDecoder* decoder, uint32* size); + /* Get the pixel format of decoded video frame */ + uint32 (*GetDecodedFormat) (ITSMFDecoder* decoder); + /* Get the width and height of decoded video frame */ + boolean (*GetDecodedDimension) (ITSMFDecoder* decoder, uint32* width, uint32* height); + /* Free the decoder */ + void (*Free) (ITSMFDecoder* decoder); +}; + +#define TSMF_DECODER_EXPORT_FUNC_NAME "TSMFDecoderEntry" +typedef ITSMFDecoder* (*TSMF_DECODER_ENTRY) (void); + +ITSMFDecoder* tsmf_load_decoder(const char* name, TS_AM_MEDIA_TYPE* media_type); + +#endif + diff --git a/channels/drdynvc/tsmf/tsmf_ifman.c b/channels/drdynvc/tsmf/tsmf_ifman.c new file mode 100644 index 000000000..fb2ea203c --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_ifman.c @@ -0,0 +1,478 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Video Redirection Virtual Channel - Interface Manipulation + * + * 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 +#include +#include +#include +#include + +#include "drdynvc_types.h" +#include "tsmf_constants.h" +#include "tsmf_media.h" +#include "tsmf_codec.h" + +#include "tsmf_ifman.h" + +int tsmf_ifman_rim_exchange_capability_request(TSMF_IFMAN* ifman) +{ + uint32 CapabilityValue; + + stream_read_uint32(ifman->input, CapabilityValue); + DEBUG_DVC("server CapabilityValue %d", CapabilityValue); + + stream_check_size(ifman->output, 8); + stream_write_uint32(ifman->output, 1); /* CapabilityValue */ + stream_write_uint32(ifman->output, 0); /* Result */ + + return 0; +} + +int tsmf_ifman_exchange_capability_request(TSMF_IFMAN* ifman) +{ + uint32 i; + uint32 v; + uint32 pos; + uint32 CapabilityType; + uint32 cbCapabilityLength; + uint32 numHostCapabilities; + + pos = stream_get_pos(ifman->output); + stream_check_size(ifman->output, ifman->input_size + 4); + stream_copy(ifman->output, ifman->input, ifman->input_size); + + stream_set_pos(ifman->output, pos); + stream_read_uint32(ifman->output, numHostCapabilities); + for (i = 0; i < numHostCapabilities; i++) + { + stream_read_uint32(ifman->output, CapabilityType); + stream_read_uint32(ifman->output, cbCapabilityLength); + pos = stream_get_pos(ifman->output); + switch (CapabilityType) + { + case 1: /* Protocol version request */ + stream_read_uint32(ifman->output, v); + DEBUG_DVC("server protocol version %d", v); + break; + case 2: /* Supported platform */ + stream_peek_uint32(ifman->output, v); + DEBUG_DVC("server supported platform %d", 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: + DEBUG_WARN("unknown capability type %d", CapabilityType); + break; + } + stream_set_pos(ifman->output, pos + cbCapabilityLength); + } + stream_write_uint32(ifman->output, 0); /* Result */ + + ifman->output_interface_id = TSMF_INTERFACE_DEFAULT | STREAM_ID_STUB; + + return 0; +} + +int tsmf_ifman_check_format_support_request(TSMF_IFMAN* ifman) +{ + uint32 numMediaType; + uint32 PlatformCookie; + uint32 FormatSupported = 1; + + stream_read_uint32(ifman->input, PlatformCookie); + stream_seek_uint32(ifman->input); /* NoRolloverFlags (4 bytes) */ + stream_read_uint32(ifman->input, numMediaType); + + DEBUG_DVC("PlatformCookie %d numMediaType %d", PlatformCookie, numMediaType); + + if (!tsmf_codec_check_media_type(ifman->input)) + FormatSupported = 0; + + if (FormatSupported) + DEBUG_DVC("format ok."); + + stream_check_size(ifman->output, 12); + 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 0; +} + +int tsmf_ifman_on_new_presentation(TSMF_IFMAN* ifman) +{ + int error = 0; + TSMF_PRESENTATION* presentation; + + DEBUG_DVC(""); + + presentation = tsmf_presentation_new(stream_get_tail(ifman->input), ifman->channel_callback); + if (presentation == NULL) + error = 1; + tsmf_presentation_set_audio_device(presentation, ifman->audio_name, ifman->audio_device); + ifman->output_pending = True; + return error; +} + +int tsmf_ifman_add_stream(TSMF_IFMAN* ifman) +{ + uint32 StreamId; + int error = 0; + TSMF_STREAM* stream; + TSMF_PRESENTATION* presentation; + + DEBUG_DVC(""); + + presentation = tsmf_presentation_find_by_id(stream_get_tail(ifman->input)); + stream_seek(ifman->input, 16); + + if (presentation == NULL) + error = 1; + else + { + stream_read_uint32(ifman->input, StreamId); + stream_seek_uint32(ifman->input); /* numMediaType */ + stream = tsmf_stream_new(presentation, StreamId); + if (stream) + tsmf_stream_set_format(stream, ifman->decoder_name, ifman->input); + } + ifman->output_pending = True; + return error; +} + +int tsmf_ifman_set_topology_request(TSMF_IFMAN* ifman) +{ + DEBUG_DVC(""); + + stream_check_size(ifman->output, 8); + 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 0; +} + +int tsmf_ifman_remove_stream(TSMF_IFMAN* ifman) +{ + int error = 0; + uint32 StreamId; + TSMF_STREAM* stream; + TSMF_PRESENTATION* presentation; + + DEBUG_DVC(""); + + presentation = tsmf_presentation_find_by_id(stream_get_tail(ifman->input)); + stream_seek(ifman->input, 16); + + if (presentation == NULL) + error = 1; + else + { + stream_read_uint32(ifman->input, StreamId); + stream = tsmf_stream_find_by_id(presentation, StreamId); + if (stream) + tsmf_stream_free(stream); + else + error = 1; + } + ifman->output_pending = True; + return error; +} + +int tsmf_ifman_shutdown_presentation(TSMF_IFMAN* ifman) +{ + TSMF_PRESENTATION* presentation; + + DEBUG_DVC(""); + + presentation = tsmf_presentation_find_by_id(stream_get_tail(ifman->input)); + if (presentation) + tsmf_presentation_free(presentation); + + stream_check_size(ifman->output, 4); + stream_write_uint32(ifman->output, 0); /* Result */ + ifman->output_interface_id = TSMF_INTERFACE_DEFAULT | STREAM_ID_STUB; + return 0; +} + +int tsmf_ifman_on_stream_volume(TSMF_IFMAN* ifman) +{ + DEBUG_DVC(""); + ifman->output_pending = True; + return 0; +} + +int tsmf_ifman_on_channel_volume(TSMF_IFMAN* ifman) +{ + DEBUG_DVC(""); + ifman->output_pending = True; + return 0; +} + +int tsmf_ifman_set_video_window(TSMF_IFMAN* ifman) +{ + DEBUG_DVC(""); + ifman->output_pending = True; + return 0; +} + +int tsmf_ifman_update_geometry_info(TSMF_IFMAN* ifman) +{ + TSMF_PRESENTATION* presentation; + uint32 numGeometryInfo; + uint32 Left; + uint32 Top; + uint32 Width; + uint32 Height; + uint32 cbVisibleRect; + RDP_RECT* rects = NULL; + int num_rects = 0; + int error = 0; + int i; + int pos; + + presentation = tsmf_presentation_find_by_id(stream_get_tail(ifman->input)); + stream_seek(ifman->input, 16); + + stream_read_uint32(ifman->input, numGeometryInfo); + pos = stream_get_pos(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_set_pos(ifman->input, pos + numGeometryInfo); + stream_read_uint32(ifman->input, cbVisibleRect); + num_rects = cbVisibleRect / 16; + + DEBUG_DVC("numGeometryInfo %d Width %d Height %d Left %d Top %d cbVisibleRect %d num_rects %d", + numGeometryInfo, Width, Height, Left, Top, cbVisibleRect, num_rects); + + if (presentation == NULL) + error = 1; + else + { + if (num_rects > 0) + { + rects = (RDP_RECT*) xzalloc(sizeof(RDP_RECT) * num_rects); + for (i = 0; i < num_rects; i++) + { + stream_read_uint16(ifman->input, rects[i].y); /* Top */ + stream_seek_uint16(ifman->input); + stream_read_uint16(ifman->input, rects[i].x); /* Left */ + stream_seek_uint16(ifman->input); + stream_read_uint16(ifman->input, rects[i].height); /* Bottom */ + stream_seek_uint16(ifman->input); + stream_read_uint16(ifman->input, rects[i].width); /* Right */ + stream_seek_uint16(ifman->input); + rects[i].width -= rects[i].x; + rects[i].height -= rects[i].y; + + DEBUG_DVC("rect %d: %d %d %d %d", i, + rects[i].x, rects[i].y, rects[i].width, rects[i].height); + } + } + tsmf_presentation_set_geometry_info(presentation, Left, Top, Width, Height, num_rects, rects); + } + ifman->output_pending = True; + return error; +} + +int tsmf_ifman_set_allocator(TSMF_IFMAN* ifman) +{ + DEBUG_DVC(""); + ifman->output_pending = True; + return 0; +} + +int tsmf_ifman_notify_preroll(TSMF_IFMAN* ifman) +{ + DEBUG_DVC(""); + ifman->output_pending = True; + return 0; +} + +int tsmf_ifman_on_sample(TSMF_IFMAN* ifman) +{ + TSMF_PRESENTATION* presentation; + TSMF_STREAM* stream; + uint32 StreamId; + uint64 SampleStartTime; + uint64 SampleEndTime; + uint64 ThrottleDuration; + uint32 SampleExtensions; + uint32 cbData; + + 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); + + DEBUG_DVC("MessageId %d StreamId %d SampleStartTime %d SampleEndTime %d " + "ThrottleDuration %d SampleExtensions %d cbData %d", + ifman->message_id, StreamId, (int)SampleStartTime, (int)SampleEndTime, + (int)ThrottleDuration, SampleExtensions, cbData); + + presentation = tsmf_presentation_find_by_id(ifman->presentation_id); + if (presentation == NULL) + { + DEBUG_WARN("unknown presentation id"); + return 1; + } + stream = tsmf_stream_find_by_id(presentation, StreamId); + if (stream == NULL) + { + DEBUG_WARN("unknown stream id"); + return 1; + } + tsmf_stream_push_sample(stream, ifman->channel_callback, + ifman->message_id, SampleStartTime, SampleEndTime, ThrottleDuration, SampleExtensions, + cbData, stream_get_tail(ifman->input)); + + ifman->output_pending = True; + return 0; +} + +int tsmf_ifman_on_flush(TSMF_IFMAN* ifman) +{ + TSMF_PRESENTATION* presentation; + uint32 StreamId; + + stream_seek(ifman->input, 16); + stream_read_uint32(ifman->input, StreamId); + DEBUG_DVC("StreamId %d", StreamId); + + presentation = tsmf_presentation_find_by_id(ifman->presentation_id); + if (presentation == NULL) + { + DEBUG_WARN("unknown presentation id"); + return 1; + } + + tsmf_presentation_flush(presentation); + + ifman->output_pending = True; + return 0; +} + +int tsmf_ifman_on_end_of_stream(TSMF_IFMAN* ifman) +{ + TSMF_PRESENTATION* presentation; + TSMF_STREAM* stream; + uint32 StreamId; + + presentation = tsmf_presentation_find_by_id(stream_get_tail(ifman->input)); + stream_seek(ifman->input, 16); + stream_read_uint32(ifman->input, StreamId); + stream = tsmf_stream_find_by_id(presentation, StreamId); + tsmf_stream_end(stream); + + DEBUG_DVC("StreamId %d", StreamId); + + stream_check_size(ifman->output, 16); + stream_write_uint32(ifman->output, CLIENT_EVENT_NOTIFICATION); /* FunctionId */ + stream_write_uint32(ifman->output, StreamId); /* StreamId */ + stream_write_uint32(ifman->output, TSMM_CLIENT_EVENT_ENDOFSTREAM); /* EventId */ + stream_write_uint32(ifman->output, 0); /* cbData */ + ifman->output_interface_id = TSMF_INTERFACE_CLIENT_NOTIFICATIONS | STREAM_ID_PROXY; + + return 0; +} + +int tsmf_ifman_on_playback_started(TSMF_IFMAN* ifman) +{ + TSMF_PRESENTATION* presentation; + + DEBUG_DVC(""); + + presentation = tsmf_presentation_find_by_id(stream_get_tail(ifman->input)); + if (presentation) + tsmf_presentation_start(presentation); + else + DEBUG_WARN("unknown presentation id"); + + stream_check_size(ifman->output, 16); + 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 0; +} + +int tsmf_ifman_on_playback_paused(TSMF_IFMAN* ifman) +{ + DEBUG_DVC(""); + ifman->output_pending = True; + return 0; +} + +int tsmf_ifman_on_playback_restarted(TSMF_IFMAN* ifman) +{ + DEBUG_DVC(""); + ifman->output_pending = True; + return 0; +} + +int tsmf_ifman_on_playback_stopped(TSMF_IFMAN* ifman) +{ + TSMF_PRESENTATION* presentation; + + DEBUG_DVC(""); + + presentation = tsmf_presentation_find_by_id(stream_get_tail(ifman->input)); + if (presentation) + tsmf_presentation_stop(presentation); + else + DEBUG_WARN("unknown presentation id"); + + stream_check_size(ifman->output, 16); + 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 0; +} + +int tsmf_ifman_on_playback_rate_changed(TSMF_IFMAN * ifman) +{ + DEBUG_DVC(""); + + stream_check_size(ifman->output, 16); + 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 0; +} + diff --git a/channels/drdynvc/tsmf/tsmf_ifman.h b/channels/drdynvc/tsmf/tsmf_ifman.h new file mode 100644 index 000000000..7a68c06b7 --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_ifman.h @@ -0,0 +1,65 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Video Redirection Virtual Channel - Interface Manipulation + * + * 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 __TSMF_IFMAN_H +#define __TSMF_IFMAN_H + +typedef struct _TSMF_IFMAN TSMF_IFMAN; +struct _TSMF_IFMAN +{ + IWTSVirtualChannelCallback* channel_callback; + const char* decoder_name; + const char* audio_name; + const char* audio_device; + uint8 presentation_id[16]; + uint32 stream_id; + uint32 message_id; + + STREAM* input; + uint32 input_size; + STREAM* output; + boolean output_pending; + uint32 output_interface_id; +}; + +int tsmf_ifman_rim_exchange_capability_request(TSMF_IFMAN* ifman); +int tsmf_ifman_exchange_capability_request(TSMF_IFMAN* ifman); +int tsmf_ifman_check_format_support_request(TSMF_IFMAN* ifman); +int tsmf_ifman_on_new_presentation(TSMF_IFMAN* ifman); +int tsmf_ifman_add_stream(TSMF_IFMAN* ifman); +int tsmf_ifman_set_topology_request(TSMF_IFMAN* ifman); +int tsmf_ifman_remove_stream(TSMF_IFMAN* ifman); +int tsmf_ifman_shutdown_presentation(TSMF_IFMAN* ifman); +int tsmf_ifman_on_stream_volume(TSMF_IFMAN* ifman); +int tsmf_ifman_on_channel_volume(TSMF_IFMAN* ifman); +int tsmf_ifman_set_video_window(TSMF_IFMAN* ifman); +int tsmf_ifman_update_geometry_info(TSMF_IFMAN* ifman); +int tsmf_ifman_set_allocator(TSMF_IFMAN* ifman); +int tsmf_ifman_notify_preroll(TSMF_IFMAN* ifman); +int tsmf_ifman_on_sample(TSMF_IFMAN* ifman); +int tsmf_ifman_on_flush(TSMF_IFMAN* ifman); +int tsmf_ifman_on_end_of_stream(TSMF_IFMAN* ifman); +int tsmf_ifman_on_playback_started(TSMF_IFMAN* ifman); +int tsmf_ifman_on_playback_paused(TSMF_IFMAN* ifman); +int tsmf_ifman_on_playback_restarted(TSMF_IFMAN* ifman); +int tsmf_ifman_on_playback_stopped(TSMF_IFMAN* ifman); +int tsmf_ifman_on_playback_rate_changed(TSMF_IFMAN* ifman); + +#endif + diff --git a/channels/drdynvc/tsmf/tsmf_main.c b/channels/drdynvc/tsmf/tsmf_main.c new file mode 100644 index 000000000..7156dd212 --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_main.c @@ -0,0 +1,443 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Video Redirection Virtual Channel + * + * 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 +#include +#include +#include +#include + +#include "drdynvc_types.h" +#include "tsmf_constants.h" +#include "tsmf_ifman.h" +#include "tsmf_media.h" + +#include "tsmf_main.h" + +typedef struct _TSMF_LISTENER_CALLBACK TSMF_LISTENER_CALLBACK; + +typedef struct _TSMF_CHANNEL_CALLBACK TSMF_CHANNEL_CALLBACK; + +typedef struct _TSMF_PLUGIN TSMF_PLUGIN; + +struct _TSMF_LISTENER_CALLBACK +{ + IWTSListenerCallback iface; + + IWTSPlugin* plugin; + IWTSVirtualChannelManager* channel_mgr; +}; + +struct _TSMF_CHANNEL_CALLBACK +{ + IWTSVirtualChannelCallback iface; + + IWTSPlugin* plugin; + IWTSVirtualChannelManager* channel_mgr; + IWTSVirtualChannel* channel; + + uint8 presentation_id[16]; + uint32 stream_id; +}; + +struct _TSMF_PLUGIN +{ + IWTSPlugin iface; + + TSMF_LISTENER_CALLBACK* listener_callback; + + const char* decoder_name; + const char* audio_name; + const char* audio_device; +}; + +void tsmf_playback_ack(IWTSVirtualChannelCallback* pChannelCallback, + uint32 message_id, uint64 duration, uint32 data_size) +{ + STREAM* s; + int error; + TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*) pChannelCallback; + + s = stream_new(32); + 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 */ + + DEBUG_DVC("response size %d", stream_get_length(s)); + error = callback->channel->Write(callback->channel, stream_get_length(s), stream_get_head(s), NULL); + if (error) + { + DEBUG_WARN("response error %d", error); + } + stream_free(s); +} + +boolean tsmf_push_event(IWTSVirtualChannelCallback* pChannelCallback, + RDP_EVENT* event) +{ + int error; + TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*) pChannelCallback; + + error = callback->channel_mgr->PushEvent(callback->channel_mgr, event); + if (error) + { + DEBUG_WARN("response error %d", error); + return False; + } + return True; +} + +static int tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, + uint32 cbSize, + uint8* pBuffer) +{ + int length; + STREAM* input; + STREAM* output; + int error = -1; + TSMF_IFMAN ifman; + uint32 MessageId; + uint32 FunctionId; + uint32 InterfaceId; + TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*) pChannelCallback; + + /* 2.2.1 Shared Message Header (SHARED_MSG_HEADER) */ + if (cbSize < 12) + { + DEBUG_WARN("invalid size. cbSize=%d", cbSize); + return 1; + } + input = stream_new(0); + stream_attach(input, (uint8*) pBuffer, cbSize); + output = stream_new(256); + stream_seek(output, 8); + + stream_read_uint32(input, InterfaceId); + stream_read_uint32(input, MessageId); + stream_read_uint32(input, FunctionId); + DEBUG_DVC("cbSize=%d InterfaceId=0x%X MessageId=0x%X FunctionId=0x%X", + cbSize, InterfaceId, MessageId, FunctionId); + + memset(&ifman, 0, sizeof(TSMF_IFMAN)); + 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; + memcpy(ifman.presentation_id, callback->presentation_id, 16); + ifman.stream_id = callback->stream_id; + ifman.message_id = MessageId; + ifman.input = input; + ifman.input_size = cbSize - 12; + ifman.output = output; + ifman.output_pending = False; + ifman.output_interface_id = InterfaceId; + + switch (InterfaceId) + { + case TSMF_INTERFACE_CAPABILITIES | STREAM_ID_NONE: + + switch (FunctionId) + { + case RIM_EXCHANGE_CAPABILITY_REQUEST: + error = tsmf_ifman_rim_exchange_capability_request(&ifman); + break; + + default: + break; + } + break; + + case TSMF_INTERFACE_DEFAULT | STREAM_ID_PROXY: + + switch (FunctionId) + { + case SET_CHANNEL_PARAMS: + memcpy(callback->presentation_id, stream_get_tail(input), 16); + stream_seek(input, 16); + stream_read_uint32(input, callback->stream_id); + DEBUG_DVC("SET_CHANNEL_PARAMS StreamId=%d", callback->stream_id); + ifman.output_pending = True; + error = 0; + break; + + case EXCHANGE_CAPABILITIES_REQ: + error = tsmf_ifman_exchange_capability_request(&ifman); + break; + + case CHECK_FORMAT_SUPPORT_REQ: + error = tsmf_ifman_check_format_support_request(&ifman); + break; + + case ON_NEW_PRESENTATION: + error = tsmf_ifman_on_new_presentation(&ifman); + break; + + case ADD_STREAM: + error = tsmf_ifman_add_stream(&ifman); + break; + + case SET_TOPOLOGY_REQ: + error = tsmf_ifman_set_topology_request(&ifman); + break; + + case REMOVE_STREAM: + error = tsmf_ifman_remove_stream(&ifman); + break; + + case SHUTDOWN_PRESENTATION_REQ: + error = tsmf_ifman_shutdown_presentation(&ifman); + break; + + case ON_STREAM_VOLUME: + error = tsmf_ifman_on_stream_volume(&ifman); + break; + + case ON_CHANNEL_VOLUME: + error = tsmf_ifman_on_channel_volume(&ifman); + break; + + case SET_VIDEO_WINDOW: + error = tsmf_ifman_set_video_window(&ifman); + break; + + case UPDATE_GEOMETRY_INFO: + error = tsmf_ifman_update_geometry_info(&ifman); + break; + + case SET_ALLOCATOR: + error = tsmf_ifman_set_allocator(&ifman); + break; + + case NOTIFY_PREROLL: + error = tsmf_ifman_notify_preroll(&ifman); + break; + + case ON_SAMPLE: + error = tsmf_ifman_on_sample(&ifman); + break; + + case ON_FLUSH: + error = tsmf_ifman_on_flush(&ifman); + break; + + case ON_END_OF_STREAM: + error = tsmf_ifman_on_end_of_stream(&ifman); + break; + + case ON_PLAYBACK_STARTED: + error = tsmf_ifman_on_playback_started(&ifman); + break; + + case ON_PLAYBACK_PAUSED: + error = tsmf_ifman_on_playback_paused(&ifman); + break; + + case ON_PLAYBACK_RESTARTED: + error = tsmf_ifman_on_playback_restarted(&ifman); + break; + + case ON_PLAYBACK_STOPPED: + error = tsmf_ifman_on_playback_stopped(&ifman); + break; + + case ON_PLAYBACK_RATE_CHANGED: + error = tsmf_ifman_on_playback_rate_changed(&ifman); + break; + + default: + break; + } + break; + + default: + break; + } + + stream_detach(input); + stream_free(input); + input = NULL; + ifman.input = NULL; + + if (error == -1) + { + switch (FunctionId) + { + case RIMCALL_RELEASE: + /* [MS-RDPEXPS] 2.2.2.2 Interface Release (IFACE_RELEASE) + This message does not require a reply. */ + error = 0; + 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. */ + error = 0; + break; + } + + if (error == -1) + { + DEBUG_WARN("InterfaceId 0x%X FunctionId 0x%X not processed.", + InterfaceId, FunctionId); + /* When a request is not implemented we return empty response indicating error */ + } + error = 0; + } + + if (error == 0 && !ifman.output_pending) + { + /* Response packet does not have FunctionId */ + length = stream_get_length(output); + stream_set_pos(output, 0); + stream_write_uint32(output, ifman.output_interface_id); + stream_write_uint32(output, MessageId); + + DEBUG_DVC("response size %d", length); + error = callback->channel->Write(callback->channel, length, stream_get_head(output), NULL); + if (error) + { + DEBUG_WARN("response error %d", error); + } + } + + stream_free(output); + + return error; +} + +static int tsmf_on_close(IWTSVirtualChannelCallback* pChannelCallback) +{ + TSMF_STREAM* stream; + TSMF_PRESENTATION* presentation; + TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*) pChannelCallback; + + DEBUG_DVC(""); + + 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); + } + } + xfree(pChannelCallback); + + return 0; +} + +static int tsmf_on_new_channel_connection(IWTSListenerCallback* pListenerCallback, + IWTSVirtualChannel* pChannel, + uint8* Data, + int* pbAccept, + IWTSVirtualChannelCallback** ppCallback) +{ + TSMF_CHANNEL_CALLBACK* callback; + TSMF_LISTENER_CALLBACK* listener_callback = (TSMF_LISTENER_CALLBACK*) pListenerCallback; + + DEBUG_DVC(""); + + callback = xnew(TSMF_CHANNEL_CALLBACK); + callback->iface.OnDataReceived = tsmf_on_data_received; + callback->iface.OnClose = tsmf_on_close; + callback->plugin = listener_callback->plugin; + callback->channel_mgr = listener_callback->channel_mgr; + callback->channel = pChannel; + *ppCallback = (IWTSVirtualChannelCallback*) callback; + + return 0; +} + +static int tsmf_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr) +{ + TSMF_PLUGIN* tsmf = (TSMF_PLUGIN*) pPlugin; + + DEBUG_DVC(""); + + tsmf->listener_callback = xnew(TSMF_LISTENER_CALLBACK); + tsmf->listener_callback->iface.OnNewChannelConnection = tsmf_on_new_channel_connection; + tsmf->listener_callback->plugin = pPlugin; + tsmf->listener_callback->channel_mgr = pChannelMgr; + return pChannelMgr->CreateListener(pChannelMgr, "TSMF", 0, + (IWTSListenerCallback*) tsmf->listener_callback, NULL); +} + +static int tsmf_plugin_terminated(IWTSPlugin* pPlugin) +{ + TSMF_PLUGIN* tsmf = (TSMF_PLUGIN*) pPlugin; + + DEBUG_DVC(""); + + if (tsmf->listener_callback) + xfree(tsmf->listener_callback); + xfree(tsmf); + + return 0; +} + +static void tsmf_process_plugin_data(IWTSPlugin* pPlugin, RDP_PLUGIN_DATA* data) +{ + TSMF_PLUGIN* tsmf = (TSMF_PLUGIN*) pPlugin; + + if (data->data[0] && strcmp((char*)data->data[0], "tsmf") == 0) + { + if (data->data[1] && strcmp((char*)data->data[1], "decoder") == 0) + { + tsmf->decoder_name = data->data[2]; + } + else if (data->data[1] && strcmp((char*)data->data[1], "audio") == 0) + { + tsmf->audio_name = data->data[2]; + tsmf->audio_device = data->data[3]; + } + } +} + +int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) +{ + TSMF_PLUGIN * tsmf; + int error = 0; + + tsmf = (TSMF_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "tsmf"); + if (tsmf == NULL) + { + tsmf = xnew(TSMF_PLUGIN); + + tsmf->iface.Initialize = tsmf_plugin_initialize; + tsmf->iface.Connected = NULL; + tsmf->iface.Disconnected = NULL; + tsmf->iface.Terminated = tsmf_plugin_terminated; + error = pEntryPoints->RegisterPlugin(pEntryPoints, "tsmf", (IWTSPlugin*) tsmf); + + tsmf_media_init(); + } + if (error == 0) + { + tsmf_process_plugin_data((IWTSPlugin*) tsmf, + pEntryPoints->GetPluginData(pEntryPoints)); + } + return error; +} + diff --git a/channels/drdynvc/tsmf/tsmf_main.h b/channels/drdynvc/tsmf/tsmf_main.h new file mode 100644 index 000000000..deda1ceb6 --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_main.h @@ -0,0 +1,29 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Video Redirection Virtual Channel + * + * 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 __TSMF_MAIN_H +#define __TSMF_MAIN_H + +void tsmf_playback_ack(IWTSVirtualChannelCallback* pChannelCallback, + uint32 message_id, uint64 duration, uint32 data_size); +boolean tsmf_push_event(IWTSVirtualChannelCallback* pChannelCallback, + RDP_EVENT* event); + +#endif + diff --git a/channels/drdynvc/tsmf/tsmf_media.c b/channels/drdynvc/tsmf/tsmf_media.c new file mode 100644 index 000000000..7670bb6ec --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_media.c @@ -0,0 +1,795 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Video Redirection Virtual Channel - Media Container + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drdynvc_types.h" +#include "tsmf_constants.h" +#include "tsmf_types.h" +#include "tsmf_decoder.h" +#include "tsmf_audio.h" +#include "tsmf_main.h" +#include "tsmf_codec.h" +#include "tsmf_media.h" + +#define AUDIO_TOLERANCE 10000000LL + +struct _TSMF_PRESENTATION +{ + uint8 presentation_id[GUID_SIZE]; + + const char* audio_name; + const char* audio_device; + int eos; + + uint32 last_x; + uint32 last_y; + uint32 last_width; + uint32 last_height; + uint16 last_num_rects; + RDP_RECT* last_rects; + + uint32 output_x; + uint32 output_y; + uint32 output_width; + uint32 output_height; + uint16 output_num_rects; + RDP_RECT* output_rects; + + IWTSVirtualChannelCallback* channel_callback; + + uint64 audio_start_time; + uint64 audio_end_time; + + /* The stream list could be accessed by differnt threads and need to be protected. */ + freerdp_mutex mutex; + + LIST* stream_list; +}; + +struct _TSMF_STREAM +{ + uint32 stream_id; + + TSMF_PRESENTATION* presentation; + + ITSMFDecoder* decoder; + + int major_type; + int eos; + uint32 width; + uint32 height; + + ITSMFAudioDevice* audio; + uint32 sample_rate; + uint32 channels; + uint32 bits_per_sample; + + /* The end_time of last played sample */ + uint64 last_end_time; + /* Next sample should not start before this system time. */ + uint64 next_start_time; + + freerdp_thread* thread; + + LIST* sample_list; + + /* The sample ack response queue will be accessed only by the stream thread. */ + LIST* sample_ack_list; +}; + +struct _TSMF_SAMPLE +{ + uint32 sample_id; + uint64 start_time; + uint64 end_time; + uint64 duration; + uint32 extensions; + uint32 data_size; + uint8* data; + uint32 decoded_size; + uint32 pixfmt; + + TSMF_STREAM* stream; + IWTSVirtualChannelCallback* channel_callback; + uint64 ack_time; +}; + +static LIST* presentation_list = NULL; + +static uint64 get_current_time(void) +{ + struct timeval tp; + + gettimeofday(&tp, 0); + return ((uint64)tp.tv_sec) * 10000000LL + ((uint64)tp.tv_usec) * 10LL; +} + +static TSMF_SAMPLE* tsmf_stream_pop_sample(TSMF_STREAM* stream, int sync) +{ + TSMF_STREAM* s; + LIST_ITEM* item; + TSMF_SAMPLE* sample; + boolean pending = False; + TSMF_PRESENTATION* presentation = stream->presentation; + + if (!stream->sample_list->head) + return NULL; + + if (sync) + { + if (stream->major_type == TSMF_MAJOR_TYPE_AUDIO) + { + /* Check if some other stream has earlier sample that needs to be played first */ + if (stream->last_end_time > AUDIO_TOLERANCE) + { + freerdp_mutex_lock(presentation->mutex); + for (item = presentation->stream_list->head; item; item = item->next) + { + s = (TSMF_STREAM*) item->data; + if (s != stream && !s->eos && s->last_end_time && + s->last_end_time < stream->last_end_time - AUDIO_TOLERANCE) + { + pending = True; + break; + } + } + freerdp_mutex_unlock(presentation->mutex); + } + } + else + { + if (stream->last_end_time > presentation->audio_end_time) + { + pending = True; + } + } + } + if (pending) + return NULL; + + freerdp_thread_lock(stream->thread); + sample = (TSMF_SAMPLE*) list_dequeue(stream->sample_list); + freerdp_thread_unlock(stream->thread); + + if (sample && sample->end_time > stream->last_end_time) + stream->last_end_time = sample->end_time; + + return sample; +} + +static void tsmf_sample_free(TSMF_SAMPLE* sample) +{ + if (sample->data) + xfree(sample->data); + xfree(sample); +} + +static void tsmf_sample_ack(TSMF_SAMPLE* sample) +{ + tsmf_playback_ack(sample->channel_callback, sample->sample_id, sample->duration, sample->data_size); +} + +static void tsmf_sample_queue_ack(TSMF_SAMPLE* sample) +{ + TSMF_STREAM* stream = sample->stream; + + list_enqueue(stream->sample_ack_list, sample); +} + +static void tsmf_stream_process_ack(TSMF_STREAM* stream) +{ + TSMF_SAMPLE* sample; + uint64 ack_time; + + ack_time = get_current_time(); + while (stream->sample_ack_list->head && !freerdp_thread_is_stopped(stream->thread)) + { + sample = (TSMF_SAMPLE*) list_peek(stream->sample_ack_list); + if (sample->ack_time > ack_time) + break; + + sample = list_dequeue(stream->sample_ack_list); + tsmf_sample_ack(sample); + tsmf_sample_free(sample); + } +} + +TSMF_PRESENTATION* tsmf_presentation_new(const uint8* guid, IWTSVirtualChannelCallback* pChannelCallback) +{ + TSMF_PRESENTATION* presentation; + + presentation = tsmf_presentation_find_by_id(guid); + if (presentation) + { + DEBUG_WARN("duplicated presentation id!"); + return NULL; + } + + presentation = xnew(TSMF_PRESENTATION); + + memcpy(presentation->presentation_id, guid, GUID_SIZE); + presentation->channel_callback = pChannelCallback; + + presentation->mutex = freerdp_mutex_new(); + presentation->stream_list = list_new(); + + list_enqueue(presentation_list, presentation); + + return presentation; +} + +TSMF_PRESENTATION* tsmf_presentation_find_by_id(const uint8* guid) +{ + LIST_ITEM* item; + TSMF_PRESENTATION* presentation; + + for (item = presentation_list->head; item; item = item->next) + { + presentation = (TSMF_PRESENTATION*) item->data; + if (memcmp(presentation->presentation_id, guid, GUID_SIZE) == 0) + return presentation; + } + return NULL; +} + +static void tsmf_presentation_restore_last_video_frame(TSMF_PRESENTATION* presentation) +{ + RDP_REDRAW_EVENT* revent; + + if (presentation->last_width && presentation->last_height) + { + revent = (RDP_REDRAW_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_TSMF, RDP_EVENT_TYPE_TSMF_REDRAW, + NULL, NULL); + revent->x = presentation->last_x; + revent->y = presentation->last_y; + revent->width = presentation->last_width; + revent->height = presentation->last_height; + if (!tsmf_push_event(presentation->channel_callback, (RDP_EVENT*) revent)) + { + freerdp_event_free((RDP_EVENT*) revent); + } + presentation->last_x = 0; + presentation->last_y = 0; + presentation->last_width = 0; + presentation->last_height = 0; + } +} + +static void tsmf_sample_playback_video(TSMF_SAMPLE* sample) +{ + uint64 t; + RDP_VIDEO_FRAME_EVENT* vevent; + TSMF_STREAM* stream = sample->stream; + TSMF_PRESENTATION* presentation = stream->presentation; + + DEBUG_DVC("MessageId %d EndTime %d data_size %d consumed.", + sample->sample_id, (int)sample->end_time, sample->data_size); + + if (sample->data) + { + t = get_current_time(); + if (stream->next_start_time > t && + (sample->end_time >= presentation->audio_start_time || + sample->end_time < stream->last_end_time)) + { + freerdp_usleep((stream->next_start_time - t) / 10); + } + stream->next_start_time = t + sample->duration - 50000; + + if (presentation->last_x != presentation->output_x || + presentation->last_y != presentation->output_y || + presentation->last_width != presentation->output_width || + presentation->last_height != presentation->output_height || + presentation->last_num_rects != presentation->output_num_rects || + (presentation->last_rects && presentation->output_rects && + memcmp(presentation->last_rects, presentation->output_rects, + presentation->last_num_rects * sizeof(RDP_RECT)) != 0)) + { + tsmf_presentation_restore_last_video_frame(presentation); + + presentation->last_x = presentation->output_x; + presentation->last_y = presentation->output_y; + presentation->last_width = presentation->output_width; + presentation->last_height = presentation->output_height; + + if (presentation->last_rects) + { + xfree(presentation->last_rects); + presentation->last_rects = NULL; + } + presentation->last_num_rects = presentation->output_num_rects; + if (presentation->last_num_rects > 0) + { + presentation->last_rects = xzalloc(presentation->last_num_rects * sizeof(RDP_RECT)); + memcpy(presentation->last_rects, presentation->output_rects, + presentation->last_num_rects * sizeof(RDP_RECT)); + } + } + + vevent = (RDP_VIDEO_FRAME_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_TSMF, RDP_EVENT_TYPE_TSMF_VIDEO_FRAME, + NULL, NULL); + vevent->frame_data = sample->data; + vevent->frame_size = sample->decoded_size; + vevent->frame_pixfmt = sample->pixfmt; + vevent->frame_width = sample->stream->width; + vevent->frame_height = sample->stream->height; + vevent->x = presentation->output_x; + vevent->y = presentation->output_y; + vevent->width = presentation->output_width; + vevent->height = presentation->output_height; + if (presentation->output_num_rects > 0) + { + vevent->num_visible_rects = presentation->output_num_rects; + vevent->visible_rects = (RDP_RECT*) xzalloc(presentation->output_num_rects * sizeof(RDP_RECT)); + memcpy(vevent->visible_rects, presentation->output_rects, + presentation->output_num_rects * sizeof(RDP_RECT)); + } + + /* The frame data ownership is passed to the event object, and is freed after the event is processed. */ + sample->data = NULL; + sample->decoded_size = 0; + + if (!tsmf_push_event(sample->channel_callback, (RDP_EVENT*) vevent)) + { + freerdp_event_free((RDP_EVENT*) vevent); + } + +#if 0 + /* Dump a .ppm image for every 30 frames. Assuming the frame is in YUV format, we + extract the Y values to create a grayscale image. */ + static int frame_id = 0; + char buf[100]; + FILE * fp; + if ((frame_id % 30) == 0) + { + snprintf(buf, sizeof(buf), "/tmp/FreeRDP_Frame_%d.ppm", frame_id); + fp = fopen(buf, "wb"); + fwrite("P5\n", 1, 3, fp); + snprintf(buf, sizeof(buf), "%d %d\n", sample->stream->width, sample->stream->height); + fwrite(buf, 1, strlen(buf), fp); + fwrite("255\n", 1, 4, fp); + fwrite(sample->data, 1, sample->stream->width * sample->stream->height, fp); + fflush(fp); + fclose(fp); + } + frame_id++; +#endif + } +} + +static void tsmf_sample_playback_audio(TSMF_SAMPLE* sample) +{ + uint64 latency = 0; + TSMF_STREAM* stream = sample->stream; + + DEBUG_DVC("MessageId %d EndTime %d consumed.", + sample->sample_id, (int)sample->end_time); + + if (sample->stream->audio && sample->data) + { + sample->stream->audio->Play(sample->stream->audio, + sample->data, sample->decoded_size); + sample->data = NULL; + sample->decoded_size = 0; + + if (stream->audio && stream->audio->GetLatency) + latency = stream->audio->GetLatency(stream->audio); + } + else + { + latency = 0; + } + + sample->ack_time = latency + get_current_time(); + stream->last_end_time = sample->end_time + latency; + stream->presentation->audio_start_time = sample->start_time + latency; + stream->presentation->audio_end_time = sample->end_time + latency; +} + +static void tsmf_sample_playback(TSMF_SAMPLE* sample) +{ + boolean ret = False; + uint32 width; + uint32 height; + uint32 pixfmt = 0; + TSMF_STREAM* stream = sample->stream; + + if (stream->decoder) + ret = stream->decoder->Decode(stream->decoder, sample->data, sample->data_size, sample->extensions); + if (!ret) + { + tsmf_sample_ack(sample); + tsmf_sample_free(sample); + return; + } + + xfree(sample->data); + sample->data = NULL; + + if (stream->major_type == TSMF_MAJOR_TYPE_VIDEO) + { + if (stream->decoder->GetDecodedFormat) + { + pixfmt = stream->decoder->GetDecodedFormat(stream->decoder); + if (pixfmt == ((uint32) -1)) + { + tsmf_sample_ack(sample); + tsmf_sample_free(sample); + return; + } + sample->pixfmt = pixfmt; + } + + if (stream->decoder->GetDecodedDimension) + ret = stream->decoder->GetDecodedDimension(stream->decoder, &width, &height); + if (ret && (width != stream->width || height != stream->height)) + { + DEBUG_DVC("video dimension changed to %d x %d", width, height); + stream->width = width; + stream->height = height; + } + } + + if (stream->decoder->GetDecodedData) + { + sample->data = stream->decoder->GetDecodedData(stream->decoder, &sample->decoded_size); + } + + switch (sample->stream->major_type) + { + case TSMF_MAJOR_TYPE_VIDEO: + tsmf_sample_playback_video(sample); + tsmf_sample_ack(sample); + tsmf_sample_free(sample); + break; + case TSMF_MAJOR_TYPE_AUDIO: + tsmf_sample_playback_audio(sample); + tsmf_sample_queue_ack(sample); + break; + } +} + +static void* tsmf_stream_playback_func(void* arg) +{ + TSMF_SAMPLE* sample; + TSMF_STREAM* stream = (TSMF_STREAM*) arg; + TSMF_PRESENTATION* presentation = stream->presentation; + + DEBUG_DVC("in %d", stream->stream_id); + + if (stream->major_type == TSMF_MAJOR_TYPE_AUDIO && + stream->sample_rate && stream->channels && stream->bits_per_sample) + { + stream->audio = tsmf_load_audio_device( + presentation->audio_name && presentation->audio_name[0] ? presentation->audio_name : NULL, + presentation->audio_device && presentation->audio_device[0] ? presentation->audio_device : NULL); + if (stream->audio) + { + stream->audio->SetFormat(stream->audio, + stream->sample_rate, stream->channels, stream->bits_per_sample); + } + } + while (!freerdp_thread_is_stopped(stream->thread)) + { + tsmf_stream_process_ack(stream); + sample = tsmf_stream_pop_sample(stream, 1); + if (sample) + tsmf_sample_playback(sample); + else + freerdp_usleep(5000); + } + if (stream->eos || presentation->eos) + { + while ((sample = tsmf_stream_pop_sample(stream, 1)) != NULL) + tsmf_sample_playback(sample); + } + if (stream->audio) + { + stream->audio->Free(stream->audio); + stream->audio = NULL; + } + + freerdp_thread_quit(stream->thread); + + DEBUG_DVC("out %d", stream->stream_id); + + return NULL; +} + +static void tsmf_stream_start(TSMF_STREAM* stream) +{ + if (!freerdp_thread_is_running(stream->thread)) + { + freerdp_thread_start(stream->thread, tsmf_stream_playback_func, stream); + } +} + +static void tsmf_stream_stop(TSMF_STREAM* stream) +{ + if (freerdp_thread_is_running(stream->thread)) + { + freerdp_thread_stop(stream->thread); + } +} + +void tsmf_presentation_start(TSMF_PRESENTATION* presentation) +{ + LIST_ITEM* item; + TSMF_STREAM* stream; + + for (item = presentation->stream_list->head; item; item = item->next) + { + stream = (TSMF_STREAM*) item->data; + tsmf_stream_start(stream); + } +} + +void tsmf_presentation_stop(TSMF_PRESENTATION* presentation) +{ + LIST_ITEM* item; + TSMF_STREAM* stream; + + tsmf_presentation_flush(presentation); + + for (item = presentation->stream_list->head; item; item = item->next) + { + stream = (TSMF_STREAM*) item->data; + tsmf_stream_stop(stream); + } + + tsmf_presentation_restore_last_video_frame(presentation); + if (presentation->last_rects) + { + xfree(presentation->last_rects); + presentation->last_rects = NULL; + } + presentation->last_num_rects = 0; + if (presentation->output_rects) + { + xfree(presentation->output_rects); + presentation->output_rects = NULL; + } + presentation->output_num_rects = 0; +} + +void tsmf_presentation_set_geometry_info(TSMF_PRESENTATION* presentation, + uint32 x, uint32 y, uint32 width, uint32 height, + int num_rects, RDP_RECT* rects) +{ + presentation->output_x = x; + presentation->output_y = y; + presentation->output_width = width; + presentation->output_height = height; + if (presentation->output_rects) + xfree(presentation->output_rects); + presentation->output_rects = rects; + presentation->output_num_rects = num_rects; +} + +void tsmf_presentation_set_audio_device(TSMF_PRESENTATION* presentation, const char* name, const char* device) +{ + presentation->audio_name = name; + presentation->audio_device = device; +} + +static void tsmf_stream_flush(TSMF_STREAM* stream) +{ + TSMF_SAMPLE* sample; + + while ((sample = tsmf_stream_pop_sample(stream, 0)) != NULL) + tsmf_sample_free(sample); + + while ((sample = list_dequeue(stream->sample_ack_list)) != NULL) + tsmf_sample_free(sample); + + if (stream->audio) + stream->audio->Flush(stream->audio); + + stream->eos = 0; + stream->last_end_time = 0; + stream->next_start_time = 0; + if (stream->major_type == TSMF_MAJOR_TYPE_AUDIO) + { + stream->presentation->audio_start_time = 0; + stream->presentation->audio_end_time = 0; + } +} + +void tsmf_presentation_flush(TSMF_PRESENTATION* presentation) +{ + LIST_ITEM* item; + TSMF_STREAM * stream; + + for (item = presentation->stream_list->head; item; item = item->next) + { + stream = (TSMF_STREAM*) item->data; + tsmf_stream_flush(stream); + } + + presentation->eos = 0; + presentation->audio_start_time = 0; + presentation->audio_end_time = 0; +} + +void tsmf_presentation_free(TSMF_PRESENTATION* presentation) +{ + TSMF_STREAM* stream; + + tsmf_presentation_stop(presentation); + list_remove(presentation_list, presentation); + + while (presentation->stream_list->head) + { + stream = (TSMF_STREAM*) list_peek(presentation->stream_list); + tsmf_stream_free(stream); + } + list_free(presentation->stream_list); + + freerdp_mutex_free(presentation->mutex); + + xfree(presentation); +} + +TSMF_STREAM* tsmf_stream_new(TSMF_PRESENTATION* presentation, uint32 stream_id) +{ + TSMF_STREAM* stream; + + stream = tsmf_stream_find_by_id(presentation, stream_id); + if (stream) + { + DEBUG_WARN("duplicated stream id %d!", stream_id); + return NULL; + } + + stream = xnew(TSMF_STREAM); + + stream->stream_id = stream_id; + stream->presentation = presentation; + stream->thread = freerdp_thread_new(); + stream->sample_list = list_new(); + stream->sample_ack_list = list_new(); + + freerdp_mutex_lock(presentation->mutex); + list_enqueue(presentation->stream_list, stream); + freerdp_mutex_unlock(presentation->mutex); + + return stream; +} + +TSMF_STREAM* tsmf_stream_find_by_id(TSMF_PRESENTATION* presentation, uint32 stream_id) +{ + LIST_ITEM* item; + TSMF_STREAM* stream; + + for (item = presentation->stream_list->head; item; item = item->next) + { + stream = (TSMF_STREAM*) item->data; + if (stream->stream_id == stream_id) + return stream; + } + return NULL; +} + +void tsmf_stream_set_format(TSMF_STREAM* stream, const char* name, STREAM* s) +{ + TS_AM_MEDIA_TYPE mediatype; + + if (stream->decoder) + { + DEBUG_WARN("duplicated call"); + return; + } + + tsmf_codec_parse_media_type(&mediatype, s); + + if (mediatype.MajorType == TSMF_MAJOR_TYPE_VIDEO) + { + DEBUG_DVC("video width %d height %d bit_rate %d frame_rate %f codec_data %d", + mediatype.Width, mediatype.Height, mediatype.BitRate, + (double)mediatype.SamplesPerSecond.Numerator / (double)mediatype.SamplesPerSecond.Denominator, + mediatype.ExtraDataSize); + } + else if (mediatype.MajorType == TSMF_MAJOR_TYPE_AUDIO) + { + DEBUG_DVC("audio channel %d sample_rate %d bits_per_sample %d codec_data %d", + mediatype.Channels, mediatype.SamplesPerSecond.Numerator, mediatype.BitsPerSample, + mediatype.ExtraDataSize); + stream->sample_rate = mediatype.SamplesPerSecond.Numerator; + stream->channels = mediatype.Channels; + stream->bits_per_sample = mediatype.BitsPerSample; + if (stream->bits_per_sample == 0) + stream->bits_per_sample = 16; + } + + stream->major_type = mediatype.MajorType; + stream->width = mediatype.Width; + stream->height = mediatype.Height; + stream->decoder = tsmf_load_decoder(name, &mediatype); +} + +void tsmf_stream_end(TSMF_STREAM* stream) +{ + stream->eos = 1; + stream->presentation->eos = 1; +} + +void tsmf_stream_free(TSMF_STREAM* stream) +{ + TSMF_PRESENTATION* presentation = stream->presentation; + + tsmf_stream_stop(stream); + tsmf_stream_flush(stream); + + freerdp_mutex_lock(presentation->mutex); + list_remove(presentation->stream_list, stream); + freerdp_mutex_unlock(presentation->mutex); + + list_free(stream->sample_list); + list_free(stream->sample_ack_list); + + if (stream->decoder) + stream->decoder->Free(stream->decoder); + + freerdp_thread_free(stream->thread); + + xfree(stream); +} + +void tsmf_stream_push_sample(TSMF_STREAM* stream, IWTSVirtualChannelCallback* pChannelCallback, + uint32 sample_id, uint64 start_time, uint64 end_time, uint64 duration, uint32 extensions, + uint32 data_size, uint8* data) +{ + TSMF_SAMPLE* sample; + + sample = xnew(TSMF_SAMPLE); + + sample->sample_id = sample_id; + sample->start_time = start_time; + sample->end_time = end_time; + sample->duration = duration; + sample->extensions = extensions; + sample->stream = stream; + sample->channel_callback = pChannelCallback; + sample->data_size = data_size; + sample->data = xzalloc(data_size + TSMF_BUFFER_PADDING_SIZE); + memcpy(sample->data, data, data_size); + + freerdp_thread_lock(stream->thread); + list_enqueue(stream->sample_list, sample); + freerdp_thread_unlock(stream->thread); +} + +void tsmf_media_init(void) +{ + if (presentation_list == NULL) + presentation_list = list_new(); +} + diff --git a/channels/drdynvc/tsmf/tsmf_media.h b/channels/drdynvc/tsmf/tsmf_media.h new file mode 100644 index 000000000..eac3e0035 --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_media.h @@ -0,0 +1,59 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Video Redirection Virtual Channel - Media Container + * + * 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. + */ + +/** + * The media container maintains a global list of presentations, and a list of + * streams in each presentation. + */ + +#ifndef __TSMF_MEDIA_H +#define __TSMF_MEDIA_H + +typedef struct _TSMF_PRESENTATION TSMF_PRESENTATION; + +typedef struct _TSMF_STREAM TSMF_STREAM; + +typedef struct _TSMF_SAMPLE TSMF_SAMPLE; + +TSMF_PRESENTATION* tsmf_presentation_new(const uint8* guid, IWTSVirtualChannelCallback* pChannelCallback); +TSMF_PRESENTATION* tsmf_presentation_find_by_id(const uint8* guid); +void tsmf_presentation_start(TSMF_PRESENTATION* presentation); +void tsmf_presentation_stop(TSMF_PRESENTATION* presentation); +void tsmf_presentation_set_geometry_info(TSMF_PRESENTATION* presentation, + uint32 x, uint32 y, uint32 width, uint32 height, + int num_rects, RDP_RECT* rects); +void tsmf_presentation_set_audio_device(TSMF_PRESENTATION* presentation, + const char* name, const char* device); +void tsmf_presentation_flush(TSMF_PRESENTATION* presentation); +void tsmf_presentation_free(TSMF_PRESENTATION* presentation); + +TSMF_STREAM* tsmf_stream_new(TSMF_PRESENTATION* presentation, uint32 stream_id); +TSMF_STREAM* tsmf_stream_find_by_id(TSMF_PRESENTATION* presentation, uint32 stream_id); +void tsmf_stream_set_format(TSMF_STREAM* stream, const char* name, STREAM* s); +void tsmf_stream_end(TSMF_STREAM* stream); +void tsmf_stream_free(TSMF_STREAM* stream); + +void tsmf_stream_push_sample(TSMF_STREAM* stream, IWTSVirtualChannelCallback* pChannelCallback, + uint32 sample_id, uint64 start_time, uint64 end_time, uint64 duration, uint32 extensions, + uint32 data_size, uint8* data); + +void tsmf_media_init(void); + +#endif + diff --git a/channels/drdynvc/tsmf/tsmf_types.h b/channels/drdynvc/tsmf/tsmf_types.h new file mode 100644 index 000000000..20745cb92 --- /dev/null +++ b/channels/drdynvc/tsmf/tsmf_types.h @@ -0,0 +1,47 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * 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 __TSMF_TYPES_H +#define __TSMF_TYPES_H + +#include + +typedef struct _TS_AM_MEDIA_TYPE +{ + 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 uint8* ExtraData; + uint32 ExtraDataSize; +} TS_AM_MEDIA_TYPE; + +#endif +