tsmf: add tsmf main module.

This commit is contained in:
Vic Lee 2011-09-19 22:54:09 +08:00
parent fee5b3fab8
commit dadb94a1e3
16 changed files with 2780 additions and 0 deletions

View File

@ -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)

View File

@ -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 <otavio@ossystems.com.br>
# Copyright 2011 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.
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})

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <freerdp/utils/memory.h>
#include <freerdp/utils/load_plugin.h>
#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;
}

View File

@ -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

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <freerdp/utils/stream.h>
#include <freerdp/utils/hexdump.h>
#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;
}

View File

@ -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

View File

@ -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

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <freerdp/utils/memory.h>
#include <freerdp/utils/load_plugin.h>
#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;
}

View File

@ -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

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <freerdp/utils/memory.h>
#include <freerdp/utils/stream.h>
#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;
}

View File

@ -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

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <freerdp/utils/memory.h>
#include <freerdp/utils/stream.h>
#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;
}

View File

@ -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

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <freerdp/utils/memory.h>
#include <freerdp/utils/stream.h>
#include <freerdp/utils/list.h>
#include <freerdp/utils/thread.h>
#include <freerdp/utils/mutex.h>
#include <freerdp/utils/event.h>
#include <freerdp/utils/sleep.h>
#include <freerdp/plugins/tsmf.h>
#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();
}

View File

@ -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

View File

@ -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 <freerdp/types.h>
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