commit
44664c0a48
@ -91,6 +91,7 @@ if(NOT WIN32)
|
||||
find_suggested_package(ALSA)
|
||||
find_optional_package(PulseAudio)
|
||||
find_suggested_package(Cups)
|
||||
find_suggested_package(FFmpeg)
|
||||
endif()
|
||||
|
||||
# Endian
|
||||
|
@ -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)
|
||||
|
||||
|
@ -71,7 +71,7 @@ static int drdynvc_write_variable_uint(STREAM* stream, uint32 val)
|
||||
return cb;
|
||||
}
|
||||
|
||||
int drdynvc_write_data(drdynvcPlugin* drdynvc, uint32 ChannelId, char* data, uint32 data_size)
|
||||
int drdynvc_write_data(drdynvcPlugin* drdynvc, uint32 ChannelId, uint8* data, uint32 data_size)
|
||||
{
|
||||
STREAM* data_out;
|
||||
uint32 pos = 0;
|
||||
@ -150,20 +150,20 @@ int drdynvc_push_event(drdynvcPlugin* drdynvc, RDP_EVENT* event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* data_in)
|
||||
static int drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
|
||||
{
|
||||
STREAM* data_out;
|
||||
int error;
|
||||
|
||||
DEBUG_DVC("Sp=%d cbChId=%d", Sp, cbChId);
|
||||
stream_seek(data_in, 1); /* pad */
|
||||
stream_read_uint16(data_in, drdynvc->version);
|
||||
stream_seek(s, 1); /* pad */
|
||||
stream_read_uint16(s, drdynvc->version);
|
||||
if (drdynvc->version == 2)
|
||||
{
|
||||
stream_read_uint16(data_in, drdynvc->PriorityCharge0);
|
||||
stream_read_uint16(data_in, drdynvc->PriorityCharge1);
|
||||
stream_read_uint16(data_in, drdynvc->PriorityCharge2);
|
||||
stream_read_uint16(data_in, drdynvc->PriorityCharge3);
|
||||
stream_read_uint16(s, drdynvc->PriorityCharge0);
|
||||
stream_read_uint16(s, drdynvc->PriorityCharge1);
|
||||
stream_read_uint16(s, drdynvc->PriorityCharge2);
|
||||
stream_read_uint16(s, drdynvc->PriorityCharge3);
|
||||
}
|
||||
data_out = stream_new(4);
|
||||
stream_write_uint16(data_out, 0x0050); /* Cmd+Sp+cbChId+Pad. Note: MSTSC sends 0x005c */
|
||||
@ -196,23 +196,23 @@ static uint32 drdynvc_read_variable_uint(STREAM* stream, int cbLen)
|
||||
return val;
|
||||
}
|
||||
|
||||
static int drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* data_in)
|
||||
static int drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
|
||||
{
|
||||
STREAM* data_out;
|
||||
int pos;
|
||||
int error;
|
||||
uint32 ChannelId;
|
||||
|
||||
ChannelId = drdynvc_read_variable_uint(data_in, cbChId);
|
||||
pos = stream_get_pos(data_in);
|
||||
DEBUG_DVC("ChannelId=%d ChannelName=%s", ChannelId, stream_get_tail(data_in));
|
||||
ChannelId = drdynvc_read_variable_uint(s, cbChId);
|
||||
pos = stream_get_pos(s);
|
||||
DEBUG_DVC("ChannelId=%d ChannelName=%s", ChannelId, stream_get_tail(s));
|
||||
|
||||
error = dvcman_create_channel(drdynvc->channel_mgr, ChannelId, (char*)stream_get_tail(data_in));
|
||||
error = dvcman_create_channel(drdynvc->channel_mgr, ChannelId, (char*)stream_get_tail(s));
|
||||
|
||||
data_out = stream_new(pos + 4);
|
||||
stream_write_uint8(data_out, 0x10 | cbChId);
|
||||
stream_set_pos(data_in, 1);
|
||||
stream_copy(data_out, data_in, pos - 1);
|
||||
stream_set_pos(s, 1);
|
||||
stream_copy(data_out, s, pos - 1);
|
||||
|
||||
if (error == 0)
|
||||
{
|
||||
@ -234,16 +234,14 @@ static int drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int cb
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* data_in, int in_length)
|
||||
static int drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
|
||||
{
|
||||
int pos;
|
||||
uint32 ChannelId;
|
||||
uint32 Length;
|
||||
int error;
|
||||
|
||||
ChannelId = drdynvc_read_variable_uint(data_in, cbChId);
|
||||
Length = drdynvc_read_variable_uint(data_in, Sp);
|
||||
pos = stream_get_pos(data_in);
|
||||
ChannelId = drdynvc_read_variable_uint(s, cbChId);
|
||||
Length = drdynvc_read_variable_uint(s, Sp);
|
||||
DEBUG_DVC("ChannelId=%d Length=%d", ChannelId, Length);
|
||||
|
||||
error = dvcman_receive_channel_data_first(drdynvc->channel_mgr, ChannelId, Length);
|
||||
@ -251,74 +249,69 @@ static int drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId
|
||||
return error;
|
||||
|
||||
return dvcman_receive_channel_data(drdynvc->channel_mgr, ChannelId,
|
||||
stream_get_tail(data_in), in_length - pos);
|
||||
stream_get_tail(s), stream_get_left(s));
|
||||
}
|
||||
|
||||
static int drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* data_in, int in_length)
|
||||
static int drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
|
||||
{
|
||||
int pos;
|
||||
uint32 ChannelId;
|
||||
|
||||
ChannelId = drdynvc_read_variable_uint(data_in, cbChId);
|
||||
pos = stream_get_pos(data_in);
|
||||
ChannelId = drdynvc_read_variable_uint(s, cbChId);
|
||||
DEBUG_DVC("ChannelId=%d", ChannelId);
|
||||
|
||||
return dvcman_receive_channel_data(drdynvc->channel_mgr, ChannelId,
|
||||
stream_get_tail(data_in), in_length - pos);
|
||||
stream_get_tail(s), stream_get_left(s));
|
||||
}
|
||||
|
||||
static int drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* data_in)
|
||||
static int drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
|
||||
{
|
||||
uint32 ChannelId;
|
||||
|
||||
ChannelId = drdynvc_read_variable_uint(data_in, cbChId);
|
||||
ChannelId = drdynvc_read_variable_uint(s, cbChId);
|
||||
DEBUG_DVC("ChannelId=%d", ChannelId);
|
||||
dvcman_close_channel(drdynvc->channel_mgr, ChannelId);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drdynvc_process_receive(rdpSvcPlugin* plugin, STREAM* data_in)
|
||||
static void drdynvc_process_receive(rdpSvcPlugin* plugin, STREAM* s)
|
||||
{
|
||||
drdynvcPlugin* drdynvc = (drdynvcPlugin*)plugin;
|
||||
int in_length;
|
||||
int value;
|
||||
int Cmd;
|
||||
int Sp;
|
||||
int cbChId;
|
||||
|
||||
in_length = stream_get_length(data_in);
|
||||
stream_set_pos(data_in, 0);
|
||||
|
||||
stream_read_uint8(data_in, value);
|
||||
stream_read_uint8(s, value);
|
||||
Cmd = (value & 0xf0) >> 4;
|
||||
Sp = (value & 0x0c) >> 2;
|
||||
cbChId = (value & 0x03) >> 0;
|
||||
DEBUG_DVC("in_length=%d Cmd=0x%x", in_length, Cmd);
|
||||
|
||||
DEBUG_DVC("Cmd=0x%x", Cmd);
|
||||
|
||||
switch (Cmd)
|
||||
{
|
||||
case CAPABILITY_REQUEST_PDU:
|
||||
drdynvc_process_capability_request(drdynvc, Sp, cbChId, data_in);
|
||||
drdynvc_process_capability_request(drdynvc, Sp, cbChId, s);
|
||||
break;
|
||||
case CREATE_REQUEST_PDU:
|
||||
drdynvc_process_create_request(drdynvc, Sp, cbChId, data_in);
|
||||
drdynvc_process_create_request(drdynvc, Sp, cbChId, s);
|
||||
break;
|
||||
case DATA_FIRST_PDU:
|
||||
drdynvc_process_data_first(drdynvc, Sp, cbChId, data_in, in_length);
|
||||
drdynvc_process_data_first(drdynvc, Sp, cbChId, s);
|
||||
break;
|
||||
case DATA_PDU:
|
||||
drdynvc_process_data(drdynvc, Sp, cbChId, data_in, in_length);
|
||||
drdynvc_process_data(drdynvc, Sp, cbChId, s);
|
||||
break;
|
||||
case CLOSE_REQUEST_PDU:
|
||||
drdynvc_process_close_request(drdynvc, Sp, cbChId, data_in);
|
||||
drdynvc_process_close_request(drdynvc, Sp, cbChId, s);
|
||||
break;
|
||||
default:
|
||||
DEBUG_WARN("unknown drdynvc cmd 0x%x", Cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
stream_free(data_in);
|
||||
stream_free(s);
|
||||
}
|
||||
|
||||
static void drdynvc_process_connect(rdpSvcPlugin* plugin)
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
typedef struct drdynvc_plugin drdynvcPlugin;
|
||||
|
||||
int drdynvc_write_data(drdynvcPlugin* plugin, uint32 ChannelId, char* data, uint32 data_size);
|
||||
int drdynvc_write_data(drdynvcPlugin* plugin, uint32 ChannelId, uint8* data, uint32 data_size);
|
||||
int drdynvc_push_event(drdynvcPlugin* plugin, RDP_EVENT* event);
|
||||
|
||||
#endif
|
||||
|
@ -21,6 +21,8 @@
|
||||
#define __DRDYNVC_TYPES_H
|
||||
|
||||
#include "config.h"
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/utils/debug.h>
|
||||
|
||||
#ifdef WITH_DEBUG_DVC
|
||||
|
@ -263,7 +263,7 @@ int dvcman_init(IWTSVirtualChannelManager* pChannelMgr)
|
||||
|
||||
static int dvcman_write_channel(IWTSVirtualChannel* pChannel,
|
||||
uint32 cbSize,
|
||||
char* pBuffer,
|
||||
uint8* pBuffer,
|
||||
void* pReserved)
|
||||
{
|
||||
DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
|
||||
|
57
channels/drdynvc/tsmf/CMakeLists.txt
Normal file
57
channels/drdynvc/tsmf/CMakeLists.txt
Normal file
@ -0,0 +1,57 @@
|
||||
# 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})
|
||||
|
||||
if(FFMPEG_FOUND)
|
||||
add_subdirectory(ffmpeg)
|
||||
endif()
|
||||
|
||||
if(ALSA_FOUND)
|
||||
add_subdirectory(alsa)
|
||||
endif()
|
||||
|
||||
if(PULSE_FOUND)
|
||||
add_subdirectory(pulse)
|
||||
endif()
|
||||
|
34
channels/drdynvc/tsmf/alsa/CMakeLists.txt
Normal file
34
channels/drdynvc/tsmf/alsa/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
||||
# 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_ALSA_SRCS
|
||||
tsmf_alsa.c
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${ALSA_INCLUDE_DIRS})
|
||||
|
||||
add_library(tsmf_alsa ${TSMF_ALSA_SRCS})
|
||||
set_target_properties(tsmf_alsa PROPERTIES PREFIX "")
|
||||
|
||||
target_link_libraries(tsmf_alsa freerdp-utils)
|
||||
target_link_libraries(tsmf_alsa ${ALSA_LIBRARIES})
|
||||
|
||||
install(TARGETS tsmf_alsa DESTINATION ${FREERDP_PLUGIN_PATH})
|
||||
|
264
channels/drdynvc/tsmf/alsa/tsmf_alsa.c
Normal file
264
channels/drdynvc/tsmf/alsa/tsmf_alsa.c
Normal file
@ -0,0 +1,264 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol client.
|
||||
* Video Redirection Virtual Channel - ALSA Audio Device
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/utils/memory.h>
|
||||
#include <freerdp/utils/dsp.h>
|
||||
|
||||
#include "tsmf_audio.h"
|
||||
|
||||
typedef struct _TSMFALSAAudioDevice
|
||||
{
|
||||
ITSMFAudioDevice iface;
|
||||
|
||||
char device[32];
|
||||
snd_pcm_t* out_handle;
|
||||
uint32 source_rate;
|
||||
uint32 actual_rate;
|
||||
uint32 source_channels;
|
||||
uint32 actual_channels;
|
||||
uint32 bytes_per_sample;
|
||||
} TSMFALSAAudioDevice;
|
||||
|
||||
static boolean tsmf_alsa_open_device(TSMFALSAAudioDevice* alsa)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = snd_pcm_open(&alsa->out_handle, alsa->device, SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if (error < 0)
|
||||
{
|
||||
DEBUG_WARN("failed to open device %s", alsa->device);
|
||||
return False;
|
||||
}
|
||||
|
||||
DEBUG_DVC("open device %s", alsa->device);
|
||||
return True;
|
||||
}
|
||||
|
||||
static boolean tsmf_alsa_open(ITSMFAudioDevice* audio, const char* device)
|
||||
{
|
||||
TSMFALSAAudioDevice* alsa = (TSMFALSAAudioDevice*) audio;
|
||||
|
||||
if (!device)
|
||||
{
|
||||
if (!alsa->device[0])
|
||||
strcpy(alsa->device, "default");
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(alsa->device, device);
|
||||
}
|
||||
|
||||
return tsmf_alsa_open_device(alsa);
|
||||
}
|
||||
|
||||
static boolean tsmf_alsa_set_format(ITSMFAudioDevice* audio,
|
||||
uint32 sample_rate, uint32 channels, uint32 bits_per_sample)
|
||||
{
|
||||
int error;
|
||||
snd_pcm_uframes_t frames;
|
||||
snd_pcm_hw_params_t* hw_params;
|
||||
snd_pcm_sw_params_t* sw_params;
|
||||
TSMFALSAAudioDevice* alsa = (TSMFALSAAudioDevice*) audio;
|
||||
|
||||
if (!alsa->out_handle)
|
||||
return False;
|
||||
|
||||
snd_pcm_drop(alsa->out_handle);
|
||||
|
||||
alsa->actual_rate = alsa->source_rate = sample_rate;
|
||||
alsa->actual_channels = alsa->source_channels = channels;
|
||||
alsa->bytes_per_sample = bits_per_sample / 8;
|
||||
|
||||
error = snd_pcm_hw_params_malloc(&hw_params);
|
||||
if (error < 0)
|
||||
{
|
||||
DEBUG_WARN("snd_pcm_hw_params_malloc failed");
|
||||
return False;
|
||||
}
|
||||
snd_pcm_hw_params_any(alsa->out_handle, hw_params);
|
||||
snd_pcm_hw_params_set_access(alsa->out_handle, hw_params,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
snd_pcm_hw_params_set_format(alsa->out_handle, hw_params,
|
||||
SND_PCM_FORMAT_S16_LE);
|
||||
snd_pcm_hw_params_set_rate_near(alsa->out_handle, hw_params,
|
||||
&alsa->actual_rate, NULL);
|
||||
snd_pcm_hw_params_set_channels_near(alsa->out_handle, hw_params,
|
||||
&alsa->actual_channels);
|
||||
frames = sample_rate;
|
||||
snd_pcm_hw_params_set_buffer_size_near(alsa->out_handle, hw_params,
|
||||
&frames);
|
||||
snd_pcm_hw_params(alsa->out_handle, hw_params);
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
|
||||
error = snd_pcm_sw_params_malloc(&sw_params);
|
||||
if (error < 0)
|
||||
{
|
||||
DEBUG_WARN("snd_pcm_sw_params_malloc");
|
||||
return False;
|
||||
}
|
||||
snd_pcm_sw_params_current(alsa->out_handle, sw_params);
|
||||
snd_pcm_sw_params_set_start_threshold(alsa->out_handle, sw_params,
|
||||
frames / 2);
|
||||
snd_pcm_sw_params(alsa->out_handle, sw_params);
|
||||
snd_pcm_sw_params_free(sw_params);
|
||||
|
||||
snd_pcm_prepare(alsa->out_handle);
|
||||
|
||||
DEBUG_DVC("sample_rate %d channels %d bits_per_sample %d",
|
||||
sample_rate, channels, bits_per_sample);
|
||||
DEBUG_DVC("hardware buffer %d frames", (int)frames);
|
||||
if ((alsa->actual_rate != alsa->source_rate) ||
|
||||
(alsa->actual_channels != alsa->source_channels))
|
||||
{
|
||||
DEBUG_DVC("actual rate %d / channel %d is different "
|
||||
"from source rate %d / channel %d, resampling required.",
|
||||
alsa->actual_rate, alsa->actual_channels,
|
||||
alsa->source_rate, alsa->source_channels);
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
static boolean tsmf_alsa_play(ITSMFAudioDevice* audio, uint8* data, uint32 data_size)
|
||||
{
|
||||
int len;
|
||||
int error;
|
||||
int frames;
|
||||
uint8* end;
|
||||
uint8* src;
|
||||
uint8* pindex;
|
||||
int rbytes_per_frame;
|
||||
int sbytes_per_frame;
|
||||
uint8* resampled_data;
|
||||
TSMFALSAAudioDevice* alsa = (TSMFALSAAudioDevice*) audio;
|
||||
|
||||
DEBUG_DVC("data_size %d", data_size);
|
||||
|
||||
if (alsa->out_handle)
|
||||
{
|
||||
sbytes_per_frame = alsa->source_channels * alsa->bytes_per_sample;
|
||||
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_sample;
|
||||
|
||||
if ((alsa->source_rate == alsa->actual_rate) &&
|
||||
(alsa->source_channels == alsa->actual_channels))
|
||||
{
|
||||
resampled_data = NULL;
|
||||
src = data;
|
||||
data_size = data_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
resampled_data = dsp_resample(data, alsa->bytes_per_sample,
|
||||
alsa->source_channels, alsa->source_rate, data_size / sbytes_per_frame,
|
||||
alsa->actual_channels, alsa->actual_rate, &frames);
|
||||
DEBUG_DVC("resampled %d frames at %d to %d frames at %d",
|
||||
data_size / sbytes_per_frame, alsa->source_rate, frames, alsa->actual_rate);
|
||||
data_size = frames * rbytes_per_frame;
|
||||
src = resampled_data;
|
||||
}
|
||||
|
||||
pindex = src;
|
||||
end = pindex + data_size;
|
||||
while (pindex < end)
|
||||
{
|
||||
len = end - pindex;
|
||||
frames = len / rbytes_per_frame;
|
||||
error = snd_pcm_writei(alsa->out_handle, pindex, frames);
|
||||
if (error == -EPIPE)
|
||||
{
|
||||
snd_pcm_recover(alsa->out_handle, error, 0);
|
||||
error = 0;
|
||||
}
|
||||
else if (error < 0)
|
||||
{
|
||||
DEBUG_DVC("error len %d", error);
|
||||
snd_pcm_close(alsa->out_handle);
|
||||
alsa->out_handle = 0;
|
||||
tsmf_alsa_open_device(alsa);
|
||||
break;
|
||||
}
|
||||
DEBUG_DVC("%d frames played.", error);
|
||||
if (error == 0)
|
||||
break;
|
||||
pindex += error * rbytes_per_frame;
|
||||
}
|
||||
|
||||
if (resampled_data)
|
||||
xfree(resampled_data);
|
||||
}
|
||||
xfree(data);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static uint64 tsmf_alsa_get_latency(ITSMFAudioDevice* audio)
|
||||
{
|
||||
uint64 latency = 0;
|
||||
snd_pcm_sframes_t frames = 0;
|
||||
TSMFALSAAudioDevice* alsa = (TSMFALSAAudioDevice*) audio;
|
||||
|
||||
if (alsa->out_handle && alsa->actual_rate > 0 &&
|
||||
snd_pcm_delay(alsa->out_handle, &frames) == 0 &&
|
||||
frames > 0)
|
||||
{
|
||||
latency = ((uint64)frames) * 10000000LL / (uint64)alsa->actual_rate;
|
||||
}
|
||||
return latency;
|
||||
}
|
||||
|
||||
static void tsmf_alsa_flush(ITSMFAudioDevice* audio)
|
||||
{
|
||||
}
|
||||
|
||||
static void tsmf_alsa_free(ITSMFAudioDevice* audio)
|
||||
{
|
||||
TSMFALSAAudioDevice* alsa = (TSMFALSAAudioDevice*) audio;
|
||||
|
||||
DEBUG_DVC("");
|
||||
|
||||
if (alsa->out_handle)
|
||||
{
|
||||
snd_pcm_drain(alsa->out_handle);
|
||||
snd_pcm_close(alsa->out_handle);
|
||||
}
|
||||
xfree(alsa);
|
||||
}
|
||||
|
||||
ITSMFAudioDevice* TSMFAudioDeviceEntry(void)
|
||||
{
|
||||
TSMFALSAAudioDevice* alsa;
|
||||
|
||||
alsa = xnew(TSMFALSAAudioDevice);
|
||||
|
||||
alsa->iface.Open = tsmf_alsa_open;
|
||||
alsa->iface.SetFormat = tsmf_alsa_set_format;
|
||||
alsa->iface.Play = tsmf_alsa_play;
|
||||
alsa->iface.GetLatency = tsmf_alsa_get_latency;
|
||||
alsa->iface.Flush = tsmf_alsa_flush;
|
||||
alsa->iface.Free = tsmf_alsa_free;
|
||||
|
||||
return (ITSMFAudioDevice*) alsa;
|
||||
}
|
||||
|
34
channels/drdynvc/tsmf/ffmpeg/CMakeLists.txt
Normal file
34
channels/drdynvc/tsmf/ffmpeg/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
||||
# 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_FFMPEG_SRCS
|
||||
tsmf_ffmpeg.c
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${FFMPEG_INCLUDE_DIRS})
|
||||
|
||||
add_library(tsmf_ffmpeg ${TSMF_FFMPEG_SRCS})
|
||||
set_target_properties(tsmf_ffmpeg PROPERTIES PREFIX "")
|
||||
|
||||
target_link_libraries(tsmf_ffmpeg freerdp-utils)
|
||||
target_link_libraries(tsmf_ffmpeg ${FFMPEG_LIBRARIES})
|
||||
|
||||
install(TARGETS tsmf_ffmpeg DESTINATION ${FREERDP_PLUGIN_PATH})
|
||||
|
514
channels/drdynvc/tsmf/ffmpeg/tsmf_ffmpeg.c
Normal file
514
channels/drdynvc/tsmf/ffmpeg/tsmf_ffmpeg.c
Normal file
@ -0,0 +1,514 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol client.
|
||||
* Video Redirection Virtual Channel - FFmpeg Decoder
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <freerdp/utils/memory.h>
|
||||
#include <freerdp/utils/event.h>
|
||||
#include <freerdp/plugins/tsmf.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
#include "tsmf_constants.h"
|
||||
#include "tsmf_decoder.h"
|
||||
|
||||
/* Compatibility with older FFmpeg */
|
||||
#if LIBAVUTIL_VERSION_MAJOR < 50
|
||||
#define AVMEDIA_TYPE_VIDEO 0
|
||||
#define AVMEDIA_TYPE_AUDIO 1
|
||||
#endif
|
||||
|
||||
typedef struct _TSMFFFmpegDecoder
|
||||
{
|
||||
ITSMFDecoder iface;
|
||||
|
||||
int media_type;
|
||||
enum CodecID codec_id;
|
||||
AVCodecContext* codec_context;
|
||||
AVCodec* codec;
|
||||
AVFrame* frame;
|
||||
int prepared;
|
||||
|
||||
uint8* decoded_data;
|
||||
uint32 decoded_size;
|
||||
uint32 decoded_size_max;
|
||||
} TSMFFFmpegDecoder;
|
||||
|
||||
static boolean tsmf_ffmpeg_init_context(ITSMFDecoder* decoder)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
|
||||
mdecoder->codec_context = avcodec_alloc_context();
|
||||
if (!mdecoder->codec_context)
|
||||
{
|
||||
DEBUG_WARN("avcodec_alloc_context failed.");
|
||||
return False;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static boolean tsmf_ffmpeg_init_video_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYPE* media_type)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
|
||||
mdecoder->codec_context->width = media_type->Width;
|
||||
mdecoder->codec_context->height = media_type->Height;
|
||||
mdecoder->codec_context->bit_rate = media_type->BitRate;
|
||||
mdecoder->codec_context->time_base.den = media_type->SamplesPerSecond.Numerator;
|
||||
mdecoder->codec_context->time_base.num = media_type->SamplesPerSecond.Denominator;
|
||||
|
||||
mdecoder->frame = avcodec_alloc_frame();
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static boolean tsmf_ffmpeg_init_audio_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYPE* media_type)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
|
||||
mdecoder->codec_context->sample_rate = media_type->SamplesPerSecond.Numerator;
|
||||
mdecoder->codec_context->bit_rate = media_type->BitRate;
|
||||
mdecoder->codec_context->channels = media_type->Channels;
|
||||
mdecoder->codec_context->block_align = media_type->BlockAlign;
|
||||
|
||||
#ifdef AV_CPU_FLAG_SSE2
|
||||
mdecoder->codec_context->dsp_mask = AV_CPU_FLAG_SSE2 | AV_CPU_FLAG_MMX2;
|
||||
#else
|
||||
mdecoder->codec_context->dsp_mask = FF_MM_SSE2 | FF_MM_MMX2;
|
||||
#endif
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static boolean tsmf_ffmpeg_init_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYPE* media_type)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
uint32 size;
|
||||
const uint8* s;
|
||||
uint8* p;
|
||||
|
||||
mdecoder->codec = avcodec_find_decoder(mdecoder->codec_id);
|
||||
if (!mdecoder->codec)
|
||||
{
|
||||
DEBUG_WARN("avcodec_find_decoder failed.");
|
||||
return False;
|
||||
}
|
||||
|
||||
mdecoder->codec_context->codec_id = mdecoder->codec_id;
|
||||
mdecoder->codec_context->codec_type = mdecoder->media_type;
|
||||
|
||||
if (mdecoder->media_type == AVMEDIA_TYPE_VIDEO)
|
||||
{
|
||||
if (!tsmf_ffmpeg_init_video_stream(decoder, media_type))
|
||||
return False;
|
||||
}
|
||||
else if (mdecoder->media_type == AVMEDIA_TYPE_AUDIO)
|
||||
{
|
||||
if (!tsmf_ffmpeg_init_audio_stream(decoder, media_type))
|
||||
return False;
|
||||
}
|
||||
|
||||
if (media_type->ExtraData)
|
||||
{
|
||||
if (media_type->SubType == TSMF_SUB_TYPE_AVC1 &&
|
||||
media_type->FormatType == TSMF_FORMAT_TYPE_MPEG2VIDEOINFO)
|
||||
{
|
||||
/* The extradata format that FFmpeg uses is following CodecPrivate in Matroska.
|
||||
See http://haali.su/mkv/codecs.pdf */
|
||||
mdecoder->codec_context->extradata_size = media_type->ExtraDataSize + 8;
|
||||
mdecoder->codec_context->extradata = xzalloc(mdecoder->codec_context->extradata_size);
|
||||
p = mdecoder->codec_context->extradata;
|
||||
*p++ = 1; /* Reserved? */
|
||||
*p++ = media_type->ExtraData[8]; /* Profile */
|
||||
*p++ = 0; /* Profile */
|
||||
*p++ = media_type->ExtraData[12]; /* Level */
|
||||
*p++ = 0xff; /* Flag? */
|
||||
*p++ = 0xe0 | 0x01; /* Reserved | #sps */
|
||||
s = media_type->ExtraData + 20;
|
||||
size = ((uint32)(*s)) * 256 + ((uint32)(*(s + 1)));
|
||||
memcpy(p, s, size + 2);
|
||||
s += size + 2;
|
||||
p += size + 2;
|
||||
*p++ = 1; /* #pps */
|
||||
size = ((uint32)(*s)) * 256 + ((uint32)(*(s + 1)));
|
||||
memcpy(p, s, size + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Add a padding to avoid invalid memory read in some codec */
|
||||
mdecoder->codec_context->extradata_size = media_type->ExtraDataSize + 8;
|
||||
mdecoder->codec_context->extradata = xzalloc(mdecoder->codec_context->extradata_size);
|
||||
memcpy(mdecoder->codec_context->extradata, media_type->ExtraData, media_type->ExtraDataSize);
|
||||
memset(mdecoder->codec_context->extradata + media_type->ExtraDataSize, 0, 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (mdecoder->codec->capabilities & CODEC_CAP_TRUNCATED)
|
||||
mdecoder->codec_context->flags |= CODEC_FLAG_TRUNCATED;
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static boolean tsmf_ffmpeg_prepare(ITSMFDecoder* decoder)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
|
||||
if (avcodec_open(mdecoder->codec_context, mdecoder->codec) < 0)
|
||||
{
|
||||
DEBUG_WARN("avcodec_open failed.");
|
||||
return False;
|
||||
}
|
||||
|
||||
mdecoder->prepared = 1;
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static boolean tsmf_ffmpeg_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* media_type)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
|
||||
switch (media_type->MajorType)
|
||||
{
|
||||
case TSMF_MAJOR_TYPE_VIDEO:
|
||||
mdecoder->media_type = AVMEDIA_TYPE_VIDEO;
|
||||
break;
|
||||
case TSMF_MAJOR_TYPE_AUDIO:
|
||||
mdecoder->media_type = AVMEDIA_TYPE_AUDIO;
|
||||
break;
|
||||
default:
|
||||
return False;
|
||||
}
|
||||
switch (media_type->SubType)
|
||||
{
|
||||
case TSMF_SUB_TYPE_WVC1:
|
||||
mdecoder->codec_id = CODEC_ID_VC1;
|
||||
break;
|
||||
case TSMF_SUB_TYPE_WMA2:
|
||||
mdecoder->codec_id = CODEC_ID_WMAV2;
|
||||
break;
|
||||
case TSMF_SUB_TYPE_WMA9:
|
||||
mdecoder->codec_id = CODEC_ID_WMAPRO;
|
||||
break;
|
||||
case TSMF_SUB_TYPE_MP3:
|
||||
mdecoder->codec_id = CODEC_ID_MP3;
|
||||
break;
|
||||
case TSMF_SUB_TYPE_MP2A:
|
||||
mdecoder->codec_id = CODEC_ID_MP2;
|
||||
break;
|
||||
case TSMF_SUB_TYPE_MP2V:
|
||||
mdecoder->codec_id = CODEC_ID_MPEG2VIDEO;
|
||||
break;
|
||||
case TSMF_SUB_TYPE_WMV3:
|
||||
mdecoder->codec_id = CODEC_ID_WMV3;
|
||||
break;
|
||||
case TSMF_SUB_TYPE_AAC:
|
||||
mdecoder->codec_id = CODEC_ID_AAC;
|
||||
/* For AAC the pFormat is a HEAACWAVEINFO struct, and the codec data
|
||||
is at the end of it. See
|
||||
http://msdn.microsoft.com/en-us/library/dd757806.aspx */
|
||||
if (media_type->ExtraData)
|
||||
{
|
||||
media_type->ExtraData += 12;
|
||||
media_type->ExtraDataSize -= 12;
|
||||
}
|
||||
break;
|
||||
case TSMF_SUB_TYPE_H264:
|
||||
case TSMF_SUB_TYPE_AVC1:
|
||||
mdecoder->codec_id = CODEC_ID_H264;
|
||||
break;
|
||||
case TSMF_SUB_TYPE_AC3:
|
||||
mdecoder->codec_id = CODEC_ID_AC3;
|
||||
break;
|
||||
default:
|
||||
return False;
|
||||
}
|
||||
|
||||
if (!tsmf_ffmpeg_init_context(decoder))
|
||||
return False;
|
||||
if (!tsmf_ffmpeg_init_stream(decoder, media_type))
|
||||
return False;
|
||||
if (!tsmf_ffmpeg_prepare(decoder))
|
||||
return False;
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static boolean tsmf_ffmpeg_decode_video(ITSMFDecoder* decoder, const uint8* data, uint32 data_size, uint32 extensions)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
int decoded;
|
||||
int len;
|
||||
AVFrame* frame;
|
||||
boolean ret = True;
|
||||
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 52 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR <= 20)
|
||||
len = avcodec_decode_video(mdecoder->codec_context, mdecoder->frame, &decoded, data, data_size);
|
||||
#else
|
||||
{
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
pkt.data = (uint8*) data;
|
||||
pkt.size = data_size;
|
||||
if (extensions & TSMM_SAMPLE_EXT_CLEANPOINT)
|
||||
pkt.flags |= AV_PKT_FLAG_KEY;
|
||||
len = avcodec_decode_video2(mdecoder->codec_context, mdecoder->frame, &decoded, &pkt);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
DEBUG_WARN("data_size %d, avcodec_decode_video failed (%d)", data_size, len);
|
||||
ret = False;
|
||||
}
|
||||
else if (!decoded)
|
||||
{
|
||||
DEBUG_WARN("data_size %d, no frame is decoded.", data_size);
|
||||
ret = False;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_DVC("linesize[0] %d linesize[1] %d linesize[2] %d linesize[3] %d "
|
||||
"pix_fmt %d width %d height %d",
|
||||
mdecoder->frame->linesize[0], mdecoder->frame->linesize[1],
|
||||
mdecoder->frame->linesize[2], mdecoder->frame->linesize[3],
|
||||
mdecoder->codec_context->pix_fmt,
|
||||
mdecoder->codec_context->width, mdecoder->codec_context->height);
|
||||
|
||||
mdecoder->decoded_size = avpicture_get_size(mdecoder->codec_context->pix_fmt,
|
||||
mdecoder->codec_context->width, mdecoder->codec_context->height);
|
||||
mdecoder->decoded_data = xzalloc(mdecoder->decoded_size);
|
||||
frame = avcodec_alloc_frame();
|
||||
avpicture_fill((AVPicture *) frame, mdecoder->decoded_data,
|
||||
mdecoder->codec_context->pix_fmt,
|
||||
mdecoder->codec_context->width, mdecoder->codec_context->height);
|
||||
|
||||
av_picture_copy((AVPicture *) frame, (AVPicture *) mdecoder->frame,
|
||||
mdecoder->codec_context->pix_fmt,
|
||||
mdecoder->codec_context->width, mdecoder->codec_context->height);
|
||||
|
||||
av_free(frame);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static boolean tsmf_ffmpeg_decode_audio(ITSMFDecoder* decoder, const uint8* data, uint32 data_size, uint32 extensions)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
int len;
|
||||
int frame_size;
|
||||
uint32 src_size;
|
||||
const uint8* src;
|
||||
uint8* dst;
|
||||
int dst_offset;
|
||||
|
||||
#if 0
|
||||
LLOGLN(0, ("tsmf_ffmpeg_decode_audio: data_size %d", data_size));
|
||||
int i;
|
||||
for (i = 0; i < data_size; i++)
|
||||
{
|
||||
LLOG(0, ("%02X ", data[i]));
|
||||
if (i % 16 == 15)
|
||||
LLOG(0, ("\n"));
|
||||
}
|
||||
LLOG(0, ("\n"));
|
||||
#endif
|
||||
|
||||
if (mdecoder->decoded_size_max == 0)
|
||||
mdecoder->decoded_size_max = AVCODEC_MAX_AUDIO_FRAME_SIZE + 16;
|
||||
mdecoder->decoded_data = xzalloc(mdecoder->decoded_size_max);
|
||||
/* align the memory for SSE2 needs */
|
||||
dst = (uint8*) (((uintptr_t)mdecoder->decoded_data + 15) & ~ 0x0F);
|
||||
dst_offset = dst - mdecoder->decoded_data;
|
||||
src = data;
|
||||
src_size = data_size;
|
||||
|
||||
while (src_size > 0)
|
||||
{
|
||||
/* Ensure enough space for decoding */
|
||||
if (mdecoder->decoded_size_max - mdecoder->decoded_size < AVCODEC_MAX_AUDIO_FRAME_SIZE)
|
||||
{
|
||||
mdecoder->decoded_size_max = mdecoder->decoded_size_max * 2 + 16;
|
||||
mdecoder->decoded_data = xrealloc(mdecoder->decoded_data, mdecoder->decoded_size_max);
|
||||
dst = (uint8*) (((uintptr_t)mdecoder->decoded_data + 15) & ~ 0x0F);
|
||||
if (dst - mdecoder->decoded_data != dst_offset)
|
||||
{
|
||||
/* re-align the memory if the alignment has changed after realloc */
|
||||
memmove(dst, mdecoder->decoded_data + dst_offset, mdecoder->decoded_size);
|
||||
dst_offset = dst - mdecoder->decoded_data;
|
||||
}
|
||||
dst += mdecoder->decoded_size;
|
||||
}
|
||||
frame_size = mdecoder->decoded_size_max - mdecoder->decoded_size;
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 52 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR <= 20)
|
||||
len = avcodec_decode_audio2(mdecoder->codec_context,
|
||||
(int16_t*) dst, &frame_size,
|
||||
src, src_size);
|
||||
#else
|
||||
{
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
pkt.data = (uint8*) src;
|
||||
pkt.size = src_size;
|
||||
len = avcodec_decode_audio3(mdecoder->codec_context,
|
||||
(int16_t*) dst, &frame_size, &pkt);
|
||||
}
|
||||
#endif
|
||||
if (len <= 0 || frame_size <= 0)
|
||||
{
|
||||
DEBUG_WARN("error decoding");
|
||||
break;
|
||||
}
|
||||
src += len;
|
||||
src_size -= len;
|
||||
mdecoder->decoded_size += frame_size;
|
||||
dst += frame_size;
|
||||
}
|
||||
|
||||
if (mdecoder->decoded_size == 0)
|
||||
{
|
||||
xfree(mdecoder->decoded_data);
|
||||
mdecoder->decoded_data = NULL;
|
||||
}
|
||||
else if (dst_offset)
|
||||
{
|
||||
/* move the aligned decoded data to original place */
|
||||
memmove(mdecoder->decoded_data, mdecoder->decoded_data + dst_offset, mdecoder->decoded_size);
|
||||
}
|
||||
|
||||
DEBUG_DVC("data_size %d decoded_size %d",
|
||||
data_size, mdecoder->decoded_size);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static boolean tsmf_ffmpeg_decode(ITSMFDecoder* decoder, const uint8* data, uint32 data_size, uint32 extensions)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
|
||||
if (mdecoder->decoded_data)
|
||||
{
|
||||
xfree(mdecoder->decoded_data);
|
||||
mdecoder->decoded_data = NULL;
|
||||
}
|
||||
mdecoder->decoded_size = 0;
|
||||
|
||||
switch (mdecoder->media_type)
|
||||
{
|
||||
case AVMEDIA_TYPE_VIDEO:
|
||||
return tsmf_ffmpeg_decode_video(decoder, data, data_size, extensions);
|
||||
case AVMEDIA_TYPE_AUDIO:
|
||||
return tsmf_ffmpeg_decode_audio(decoder, data, data_size, extensions);
|
||||
default:
|
||||
DEBUG_WARN("unknown media type.");
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8* tsmf_ffmpeg_get_decoded_data(ITSMFDecoder* decoder, uint32* size)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
uint8* buf;
|
||||
|
||||
*size = mdecoder->decoded_size;
|
||||
buf = mdecoder->decoded_data;
|
||||
mdecoder->decoded_data = NULL;
|
||||
mdecoder->decoded_size = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static uint32 tsmf_ffmpeg_get_decoded_format(ITSMFDecoder* decoder)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
|
||||
switch (mdecoder->codec_context->pix_fmt)
|
||||
{
|
||||
case PIX_FMT_YUV420P:
|
||||
return RDP_PIXFMT_I420;
|
||||
|
||||
default:
|
||||
DEBUG_WARN("unsupported pixel format %u",
|
||||
mdecoder->codec_context->pix_fmt);
|
||||
return (uint32) -1;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean tsmf_ffmpeg_get_decoded_dimension(ITSMFDecoder* decoder, uint32* width, uint32* height)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
|
||||
if (mdecoder->codec_context->width > 0 && mdecoder->codec_context->height > 0)
|
||||
{
|
||||
*width = mdecoder->codec_context->width;
|
||||
*height = mdecoder->codec_context->height;
|
||||
return True;
|
||||
}
|
||||
else
|
||||
{
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
static void tsmf_ffmpeg_free(ITSMFDecoder* decoder)
|
||||
{
|
||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
|
||||
|
||||
if (mdecoder->frame)
|
||||
av_free(mdecoder->frame);
|
||||
if (mdecoder->decoded_data)
|
||||
xfree(mdecoder->decoded_data);
|
||||
if (mdecoder->codec_context)
|
||||
{
|
||||
if (mdecoder->prepared)
|
||||
avcodec_close(mdecoder->codec_context);
|
||||
if (mdecoder->codec_context->extradata)
|
||||
xfree(mdecoder->codec_context->extradata);
|
||||
av_free(mdecoder->codec_context);
|
||||
}
|
||||
xfree(decoder);
|
||||
}
|
||||
|
||||
static boolean initialized = False;
|
||||
|
||||
ITSMFDecoder*
|
||||
TSMFDecoderEntry(void)
|
||||
{
|
||||
TSMFFFmpegDecoder * decoder;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
avcodec_init();
|
||||
avcodec_register_all();
|
||||
initialized = True;
|
||||
}
|
||||
|
||||
decoder = xnew(TSMFFFmpegDecoder);
|
||||
|
||||
decoder->iface.SetFormat = tsmf_ffmpeg_set_format;
|
||||
decoder->iface.Decode = tsmf_ffmpeg_decode;
|
||||
decoder->iface.GetDecodedData = tsmf_ffmpeg_get_decoded_data;
|
||||
decoder->iface.GetDecodedFormat = tsmf_ffmpeg_get_decoded_format;
|
||||
decoder->iface.GetDecodedDimension = tsmf_ffmpeg_get_decoded_dimension;
|
||||
decoder->iface.Free = tsmf_ffmpeg_free;
|
||||
|
||||
return (ITSMFDecoder*) decoder;
|
||||
}
|
||||
|
34
channels/drdynvc/tsmf/pulse/CMakeLists.txt
Normal file
34
channels/drdynvc/tsmf/pulse/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
||||
# 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_PULSE_SRCS
|
||||
tsmf_pulse.c
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${PULSE_INCLUDE_DIRS})
|
||||
|
||||
add_library(tsmf_pulse ${TSMF_PULSE_SRCS})
|
||||
set_target_properties(tsmf_pulse PROPERTIES PREFIX "")
|
||||
|
||||
target_link_libraries(tsmf_pulse freerdp-utils)
|
||||
target_link_libraries(tsmf_pulse ${PULSE_LIBRARIES})
|
||||
|
||||
install(TARGETS tsmf_pulse DESTINATION ${FREERDP_PLUGIN_PATH})
|
||||
|
402
channels/drdynvc/tsmf/pulse/tsmf_pulse.c
Normal file
402
channels/drdynvc/tsmf/pulse/tsmf_pulse.c
Normal file
@ -0,0 +1,402 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol client.
|
||||
* Video Redirection Virtual Channel - PulseAudio Device
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <freerdp/utils/memory.h>
|
||||
|
||||
#include "tsmf_audio.h"
|
||||
|
||||
typedef struct _TSMFPulseAudioDevice
|
||||
{
|
||||
ITSMFAudioDevice iface;
|
||||
|
||||
char device[32];
|
||||
pa_threaded_mainloop* mainloop;
|
||||
pa_context* context;
|
||||
pa_sample_spec sample_spec;
|
||||
pa_stream* stream;
|
||||
} TSMFPulseAudioDevice;
|
||||
|
||||
static void tsmf_pulse_context_state_callback(pa_context* context, void* userdata)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
|
||||
pa_context_state_t state;
|
||||
|
||||
state = pa_context_get_state(context);
|
||||
switch (state)
|
||||
{
|
||||
case PA_CONTEXT_READY:
|
||||
DEBUG_DVC("PA_CONTEXT_READY");
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_FAILED:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
DEBUG_DVC("state %d", (int)state);
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_DVC("state %d", (int)state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean tsmf_pulse_connect(TSMFPulseAudioDevice* pulse)
|
||||
{
|
||||
pa_context_state_t state;
|
||||
|
||||
if (!pulse->context)
|
||||
return False;
|
||||
|
||||
if (pa_context_connect(pulse->context, NULL, 0, NULL))
|
||||
{
|
||||
DEBUG_WARN("pa_context_connect failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return False;
|
||||
}
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
DEBUG_WARN("pa_threaded_mainloop_start failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return False;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
state = pa_context_get_state(pulse->context);
|
||||
if (state == PA_CONTEXT_READY)
|
||||
break;
|
||||
if (!PA_CONTEXT_IS_GOOD(state))
|
||||
{
|
||||
DEBUG_DVC("bad context state (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
break;
|
||||
}
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
if (state == PA_CONTEXT_READY)
|
||||
{
|
||||
DEBUG_DVC("connected");
|
||||
return True;
|
||||
}
|
||||
else
|
||||
{
|
||||
pa_context_disconnect(pulse->context);
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean tsmf_pulse_open(ITSMFAudioDevice* audio, const char* device)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
|
||||
|
||||
if (device)
|
||||
{
|
||||
strcpy(pulse->device, device);
|
||||
}
|
||||
|
||||
pulse->mainloop = pa_threaded_mainloop_new();
|
||||
if (!pulse->mainloop)
|
||||
{
|
||||
DEBUG_WARN("pa_threaded_mainloop_new failed");
|
||||
return False;
|
||||
}
|
||||
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
|
||||
if (!pulse->context)
|
||||
{
|
||||
DEBUG_WARN("pa_context_new failed");
|
||||
return False;
|
||||
}
|
||||
pa_context_set_state_callback(pulse->context, tsmf_pulse_context_state_callback, pulse);
|
||||
if (tsmf_pulse_connect(pulse))
|
||||
{
|
||||
DEBUG_WARN("tsmf_pulse_connect failed");
|
||||
return False;
|
||||
}
|
||||
|
||||
DEBUG_DVC("open device %s", pulse->device);
|
||||
return True;
|
||||
}
|
||||
|
||||
static void tsmf_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
|
||||
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
}
|
||||
|
||||
static void tsmf_pulse_wait_for_operation(TSMFPulseAudioDevice* pulse, pa_operation* operation)
|
||||
{
|
||||
if (operation == NULL)
|
||||
return;
|
||||
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
|
||||
{
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
pa_operation_unref(operation);
|
||||
}
|
||||
|
||||
static void tsmf_pulse_stream_state_callback(pa_stream* stream, void* userdata)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
|
||||
pa_stream_state_t state;
|
||||
|
||||
state = pa_stream_get_state(stream);
|
||||
switch (state)
|
||||
{
|
||||
case PA_STREAM_READY:
|
||||
DEBUG_DVC("PA_STREAM_READY");
|
||||
pa_threaded_mainloop_signal (pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_STREAM_FAILED:
|
||||
case PA_STREAM_TERMINATED:
|
||||
DEBUG_DVC("state %d", (int)state);
|
||||
pa_threaded_mainloop_signal (pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_DVC("state %d", (int)state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void tsmf_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
|
||||
|
||||
DEBUG_DVC("%d", (int) length);
|
||||
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
}
|
||||
|
||||
static boolean tsmf_pulse_close_stream(TSMFPulseAudioDevice* pulse)
|
||||
{
|
||||
if (!pulse->context || !pulse->stream)
|
||||
return False;
|
||||
|
||||
DEBUG_DVC("");
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
pa_stream_set_write_callback(pulse->stream, NULL, NULL);
|
||||
tsmf_pulse_wait_for_operation(pulse,
|
||||
pa_stream_drain(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
|
||||
pa_stream_disconnect(pulse->stream);
|
||||
pa_stream_unref(pulse->stream);
|
||||
pulse->stream = NULL;
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static boolean tsmf_pulse_open_stream(TSMFPulseAudioDevice* pulse)
|
||||
{
|
||||
pa_stream_state_t state;
|
||||
pa_buffer_attr buffer_attr = { 0 };
|
||||
|
||||
if (!pulse->context)
|
||||
return False;
|
||||
|
||||
DEBUG_DVC("");
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
pulse->stream = pa_stream_new(pulse->context, "freerdp",
|
||||
&pulse->sample_spec, NULL);
|
||||
if (!pulse->stream)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
DEBUG_WARN("pa_stream_new failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return False;
|
||||
}
|
||||
pa_stream_set_state_callback(pulse->stream,
|
||||
tsmf_pulse_stream_state_callback, pulse);
|
||||
pa_stream_set_write_callback(pulse->stream,
|
||||
tsmf_pulse_stream_request_callback, pulse);
|
||||
buffer_attr.maxlength = pa_usec_to_bytes(500000, &pulse->sample_spec);
|
||||
buffer_attr.tlength = pa_usec_to_bytes(250000, &pulse->sample_spec);
|
||||
buffer_attr.prebuf = (uint32_t) -1;
|
||||
buffer_attr.minreq = (uint32_t) -1;
|
||||
buffer_attr.fragsize = (uint32_t) -1;
|
||||
if (pa_stream_connect_playback(pulse->stream,
|
||||
pulse->device[0] ? pulse->device : NULL, &buffer_attr,
|
||||
PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
|
||||
NULL, NULL) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
DEBUG_WARN("pa_stream_connect_playback failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return False;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
state = pa_stream_get_state(pulse->stream);
|
||||
if (state == PA_STREAM_READY)
|
||||
break;
|
||||
if (!PA_STREAM_IS_GOOD(state))
|
||||
{
|
||||
DEBUG_WARN("bad stream state (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
break;
|
||||
}
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
if (state == PA_STREAM_READY)
|
||||
{
|
||||
DEBUG_DVC("connected");
|
||||
return True;
|
||||
}
|
||||
else
|
||||
{
|
||||
tsmf_pulse_close_stream(pulse);
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean tsmf_pulse_set_format(ITSMFAudioDevice* audio,
|
||||
uint32 sample_rate, uint32 channels, uint32 bits_per_sample)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
|
||||
|
||||
DEBUG_DVC("sample_rate %d channels %d bits_per_sample %d",
|
||||
sample_rate, channels, bits_per_sample);
|
||||
|
||||
pulse->sample_spec.rate = sample_rate;
|
||||
pulse->sample_spec.channels = channels;
|
||||
pulse->sample_spec.format = PA_SAMPLE_S16LE;
|
||||
|
||||
return tsmf_pulse_open_stream(pulse);
|
||||
}
|
||||
|
||||
static boolean tsmf_pulse_play(ITSMFAudioDevice* audio, uint8* data, uint32 data_size)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
|
||||
uint8* src;
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
DEBUG_DVC("data_size %d", data_size);
|
||||
|
||||
if (pulse->stream)
|
||||
{
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
|
||||
src = data;
|
||||
while (data_size > 0)
|
||||
{
|
||||
while ((len = pa_stream_writable_size(pulse->stream)) == 0)
|
||||
{
|
||||
DEBUG_DVC("waiting");
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
if (len < 0)
|
||||
break;
|
||||
if (len > data_size)
|
||||
len = data_size;
|
||||
ret = pa_stream_write(pulse->stream, src, len, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
if (ret < 0)
|
||||
{
|
||||
DEBUG_DVC("pa_stream_write failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
break;
|
||||
}
|
||||
src += len;
|
||||
data_size -= len;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
}
|
||||
xfree(data);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
static uint64 tsmf_pulse_get_latency(ITSMFAudioDevice* audio)
|
||||
{
|
||||
pa_usec_t usec;
|
||||
uint64 latency = 0;
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
|
||||
|
||||
if (pulse->stream && pa_stream_get_latency(pulse->stream, &usec, NULL) == 0)
|
||||
{
|
||||
latency = ((uint64)usec) * 10LL;
|
||||
}
|
||||
return latency;
|
||||
}
|
||||
|
||||
static void tsmf_pulse_flush(ITSMFAudioDevice* audio)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
tsmf_pulse_wait_for_operation(pulse,
|
||||
pa_stream_flush(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
}
|
||||
|
||||
static void tsmf_pulse_free(ITSMFAudioDevice* audio)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
|
||||
|
||||
DEBUG_DVC("");
|
||||
|
||||
tsmf_pulse_close_stream(pulse);
|
||||
if (pulse->mainloop)
|
||||
{
|
||||
pa_threaded_mainloop_stop(pulse->mainloop);
|
||||
}
|
||||
if (pulse->context)
|
||||
{
|
||||
pa_context_disconnect(pulse->context);
|
||||
pa_context_unref(pulse->context);
|
||||
pulse->context = NULL;
|
||||
}
|
||||
if (pulse->mainloop)
|
||||
{
|
||||
pa_threaded_mainloop_free(pulse->mainloop);
|
||||
pulse->mainloop = NULL;
|
||||
}
|
||||
xfree(pulse);
|
||||
}
|
||||
|
||||
ITSMFAudioDevice* TSMFAudioDeviceEntry(void)
|
||||
{
|
||||
TSMFPulseAudioDevice* pulse;
|
||||
|
||||
pulse = xnew(TSMFPulseAudioDevice);
|
||||
|
||||
pulse->iface.Open = tsmf_pulse_open;
|
||||
pulse->iface.SetFormat = tsmf_pulse_set_format;
|
||||
pulse->iface.Play = tsmf_pulse_play;
|
||||
pulse->iface.GetLatency = tsmf_pulse_get_latency;
|
||||
pulse->iface.Flush = tsmf_pulse_flush;
|
||||
pulse->iface.Free = tsmf_pulse_free;
|
||||
|
||||
return (ITSMFAudioDevice*) pulse;
|
||||
}
|
||||
|
80
channels/drdynvc/tsmf/tsmf_audio.c
Normal file
80
channels/drdynvc/tsmf/tsmf_audio.c
Normal 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;
|
||||
}
|
||||
|
49
channels/drdynvc/tsmf/tsmf_audio.h
Normal file
49
channels/drdynvc/tsmf/tsmf_audio.h
Normal 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
|
||||
|
407
channels/drdynvc/tsmf/tsmf_codec.c
Normal file
407
channels/drdynvc/tsmf/tsmf_codec.c
Normal 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;
|
||||
}
|
||||
|
29
channels/drdynvc/tsmf/tsmf_codec.h
Normal file
29
channels/drdynvc/tsmf/tsmf_codec.h
Normal 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
|
||||
|
120
channels/drdynvc/tsmf/tsmf_constants.h
Normal file
120
channels/drdynvc/tsmf/tsmf_constants.h
Normal 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
|
||||
|
81
channels/drdynvc/tsmf/tsmf_decoder.c
Normal file
81
channels/drdynvc/tsmf/tsmf_decoder.c
Normal 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;
|
||||
}
|
||||
|
50
channels/drdynvc/tsmf/tsmf_decoder.h
Normal file
50
channels/drdynvc/tsmf/tsmf_decoder.h
Normal 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
|
||||
|
478
channels/drdynvc/tsmf/tsmf_ifman.c
Normal file
478
channels/drdynvc/tsmf/tsmf_ifman.c
Normal 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;
|
||||
}
|
||||
|
65
channels/drdynvc/tsmf/tsmf_ifman.h
Normal file
65
channels/drdynvc/tsmf/tsmf_ifman.h
Normal 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
|
||||
|
443
channels/drdynvc/tsmf/tsmf_main.c
Normal file
443
channels/drdynvc/tsmf/tsmf_main.c
Normal 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;
|
||||
}
|
||||
|
29
channels/drdynvc/tsmf/tsmf_main.h
Normal file
29
channels/drdynvc/tsmf/tsmf_main.h
Normal 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
|
||||
|
795
channels/drdynvc/tsmf/tsmf_media.c
Normal file
795
channels/drdynvc/tsmf/tsmf_media.c
Normal 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();
|
||||
}
|
||||
|
59
channels/drdynvc/tsmf/tsmf_media.h
Normal file
59
channels/drdynvc/tsmf/tsmf_media.h
Normal 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
|
||||
|
47
channels/drdynvc/tsmf/tsmf_types.h
Normal file
47
channels/drdynvc/tsmf/tsmf_types.h
Normal 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
|
||||
|
@ -555,6 +555,7 @@ static void disk_free(DEVICE* device)
|
||||
DISK_FILE* file;
|
||||
|
||||
freerdp_thread_stop(disk->thread);
|
||||
freerdp_thread_free(disk->thread);
|
||||
|
||||
while ((irp = (IRP*)list_dequeue(disk->irp_list)) != NULL)
|
||||
irp->Discard(irp);
|
||||
|
@ -210,6 +210,7 @@ static void printer_free(DEVICE* device)
|
||||
IRP* irp;
|
||||
|
||||
freerdp_thread_stop(printer_dev->thread);
|
||||
freerdp_thread_free(printer_dev->thread);
|
||||
|
||||
while ((irp = (IRP*)list_dequeue(printer_dev->irp_list)) != NULL)
|
||||
irp->Discard(irp);
|
||||
|
@ -25,6 +25,8 @@ add_executable(xfreerdp
|
||||
xf_gdi.h
|
||||
xf_rail.c
|
||||
xf_rail.h
|
||||
xf_tsmf.c
|
||||
xf_tsmf.h
|
||||
xf_event.c
|
||||
xf_event.h
|
||||
xf_keyboard.c
|
||||
@ -72,6 +74,13 @@ if(Xcursor_FOUND)
|
||||
target_link_libraries(xfreerdp ${Xcursor_LIBRARIES})
|
||||
endif()
|
||||
|
||||
find_suggested_package(Xv)
|
||||
if(XV_FOUND)
|
||||
add_definitions(-DWITH_XV)
|
||||
include_directories(${XV_INCLUDE_DIRS})
|
||||
target_link_libraries(xfreerdp ${XV_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(xfreerdp freerdp-core)
|
||||
target_link_libraries(xfreerdp freerdp-gdi)
|
||||
target_link_libraries(xfreerdp freerdp-kbd)
|
||||
|
370
client/X11/xf_tsmf.c
Normal file
370
client/X11/xf_tsmf.c
Normal file
@ -0,0 +1,370 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Client
|
||||
* X11 Video Redirection
|
||||
*
|
||||
* 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/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
#include <freerdp/utils/memory.h>
|
||||
#include <freerdp/utils/event.h>
|
||||
#include <freerdp/plugins/tsmf.h>
|
||||
|
||||
#include "xf_tsmf.h"
|
||||
|
||||
#ifdef WITH_XV
|
||||
|
||||
#include <X11/extensions/Xv.h>
|
||||
#include <X11/extensions/Xvlib.h>
|
||||
|
||||
typedef struct xf_xv_context xfXvContext;
|
||||
|
||||
struct xf_xv_context
|
||||
{
|
||||
long xv_port;
|
||||
Atom xv_colorkey_atom;
|
||||
int xv_image_size;
|
||||
int xv_shmid;
|
||||
char* xv_shmaddr;
|
||||
uint32* xv_pixfmts;
|
||||
};
|
||||
|
||||
#ifdef WITH_DEBUG_XV
|
||||
#define DEBUG_XV(fmt, ...) DEBUG_CLASS(XV, fmt, ## __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_XV(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
void xf_tsmf_init(xfInfo* xfi, long xv_port)
|
||||
{
|
||||
int ret;
|
||||
unsigned int i;
|
||||
unsigned int version;
|
||||
unsigned int release;
|
||||
unsigned int event_base;
|
||||
unsigned int error_base;
|
||||
unsigned int request_base;
|
||||
unsigned int num_adaptors;
|
||||
xfXvContext* xv;
|
||||
XvAdaptorInfo* ai;
|
||||
XvAttribute* attr;
|
||||
XvImageFormatValues* fo;
|
||||
|
||||
xv = xnew(xfXvContext);
|
||||
xfi->xv_context = xv;
|
||||
|
||||
xv->xv_colorkey_atom = None;
|
||||
xv->xv_image_size = 0;
|
||||
xv->xv_port = xv_port;
|
||||
|
||||
if (!XShmQueryExtension(xfi->display))
|
||||
{
|
||||
DEBUG_XV("no shmem available.");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = XvQueryExtension(xfi->display, &version, &release, &request_base, &event_base, &error_base);
|
||||
if (ret != Success)
|
||||
{
|
||||
DEBUG_XV("XvQueryExtension failed %d.", ret);
|
||||
return;
|
||||
}
|
||||
DEBUG_XV("version %u release %u", version, release);
|
||||
|
||||
ret = XvQueryAdaptors(xfi->display, DefaultRootWindow(xfi->display),
|
||||
&num_adaptors, &ai);
|
||||
if (ret != Success)
|
||||
{
|
||||
DEBUG_XV("XvQueryAdaptors failed %d.", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_adaptors; i++)
|
||||
{
|
||||
DEBUG_XV("adapter port %ld-%ld (%s)", ai[i].base_id,
|
||||
ai[i].base_id + ai[i].num_ports - 1, ai[i].name);
|
||||
if (xv->xv_port == 0 && i == num_adaptors - 1)
|
||||
xv->xv_port = ai[i].base_id;
|
||||
}
|
||||
|
||||
if (num_adaptors > 0)
|
||||
XvFreeAdaptorInfo(ai);
|
||||
|
||||
if (xv->xv_port == 0)
|
||||
{
|
||||
DEBUG_XV("no adapter selected, video frames will not be processed.");
|
||||
return;
|
||||
}
|
||||
DEBUG_XV("selected %ld", xv->xv_port);
|
||||
|
||||
attr = XvQueryPortAttributes(xfi->display, xv->xv_port, &ret);
|
||||
for (i = 0; i < (unsigned int)ret; i++)
|
||||
{
|
||||
if (strcmp(attr[i].name, "XV_COLORKEY") == 0)
|
||||
{
|
||||
xv->xv_colorkey_atom = XInternAtom(xfi->display, "XV_COLORKEY", False);
|
||||
XvSetPortAttribute(xfi->display, xv->xv_port, xv->xv_colorkey_atom, attr[i].min_value + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
XFree(attr);
|
||||
|
||||
#ifdef WITH_DEBUG_XV
|
||||
printf("xf_tsmf_init: pixel format ");
|
||||
#endif
|
||||
fo = XvListImageFormats(xfi->display, xv->xv_port, &ret);
|
||||
if (ret > 0)
|
||||
{
|
||||
xv->xv_pixfmts = (uint32*) xzalloc((ret + 1) * sizeof(uint32));
|
||||
for (i = 0; i < ret; i++)
|
||||
{
|
||||
xv->xv_pixfmts[i] = fo[i].id;
|
||||
#ifdef WITH_DEBUG_XV
|
||||
printf("%c%c%c%c ", ((char*)(xv->xv_pixfmts + i))[0], ((char*)(xv->xv_pixfmts + i))[1],
|
||||
((char*)(xv->xv_pixfmts + i))[2], ((char*)(xv->xv_pixfmts + i))[3]);
|
||||
#endif
|
||||
}
|
||||
xv->xv_pixfmts[i] = 0;
|
||||
}
|
||||
XFree(fo);
|
||||
#ifdef WITH_DEBUG_XV
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void xf_tsmf_uninit(xfInfo* xfi)
|
||||
{
|
||||
xfXvContext* xv = (xfXvContext*) xfi->xv_context;
|
||||
|
||||
if (xv)
|
||||
{
|
||||
if (xv->xv_image_size > 0)
|
||||
{
|
||||
shmdt(xv->xv_shmaddr);
|
||||
shmctl(xv->xv_shmid, IPC_RMID, NULL);
|
||||
}
|
||||
if (xv->xv_pixfmts)
|
||||
{
|
||||
xfree(xv->xv_pixfmts);
|
||||
xv->xv_pixfmts = NULL;
|
||||
}
|
||||
xfree(xv);
|
||||
xfi->xv_context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean
|
||||
xf_tsmf_is_format_supported(xfXvContext* xv, uint32 pixfmt)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!xv->xv_pixfmts)
|
||||
return False;
|
||||
|
||||
for (i = 0; xv->xv_pixfmts[i]; i++)
|
||||
{
|
||||
if (xv->xv_pixfmts[i] == pixfmt)
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
static void xf_process_tsmf_video_frame_event(xfInfo* xfi, RDP_VIDEO_FRAME_EVENT* vevent)
|
||||
{
|
||||
int i;
|
||||
uint8* data1;
|
||||
uint8* data2;
|
||||
uint32 pixfmt;
|
||||
XvImage * image;
|
||||
int colorkey = 0;
|
||||
XShmSegmentInfo shminfo;
|
||||
xfXvContext* xv = (xfXvContext*) xfi->xv_context;
|
||||
|
||||
if (xv->xv_port == 0)
|
||||
return;
|
||||
|
||||
/* In case the player is minimized */
|
||||
if (vevent->x < -2048 || vevent->y < -2048 || vevent->num_visible_rects <= 0)
|
||||
return;
|
||||
|
||||
if (xv->xv_colorkey_atom != None)
|
||||
{
|
||||
XvGetPortAttribute(xfi->display, xv->xv_port, xv->xv_colorkey_atom, &colorkey);
|
||||
XSetFunction(xfi->display, xfi->gc, GXcopy);
|
||||
XSetFillStyle(xfi->display, xfi->gc, FillSolid);
|
||||
XSetForeground(xfi->display, xfi->gc, colorkey);
|
||||
for (i = 0; i < vevent->num_visible_rects; i++)
|
||||
{
|
||||
XFillRectangle(xfi->display, xfi->window->handle, xfi->gc,
|
||||
vevent->x + vevent->visible_rects[i].x,
|
||||
vevent->y + vevent->visible_rects[i].y,
|
||||
vevent->visible_rects[i].width,
|
||||
vevent->visible_rects[i].height);
|
||||
}
|
||||
}
|
||||
|
||||
pixfmt = vevent->frame_pixfmt;
|
||||
image = XvShmCreateImage(xfi->display, xv->xv_port,
|
||||
pixfmt, 0, vevent->frame_width, vevent->frame_height, &shminfo);
|
||||
if (xv->xv_image_size != image->data_size)
|
||||
{
|
||||
if (xv->xv_image_size > 0)
|
||||
{
|
||||
shmdt(xv->xv_shmaddr);
|
||||
shmctl(xv->xv_shmid, IPC_RMID, NULL);
|
||||
}
|
||||
xv->xv_image_size = image->data_size;
|
||||
xv->xv_shmid = shmget(IPC_PRIVATE, image->data_size, IPC_CREAT | 0777);
|
||||
xv->xv_shmaddr = shmat(xv->xv_shmid, 0, 0);
|
||||
}
|
||||
shminfo.shmid = xv->xv_shmid;
|
||||
shminfo.shmaddr = image->data = xv->xv_shmaddr;
|
||||
shminfo.readOnly = False;
|
||||
|
||||
if (!XShmAttach(xfi->display, &shminfo))
|
||||
{
|
||||
XFree(image);
|
||||
DEBUG_XV("XShmAttach failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* The video driver may align each line to a different size
|
||||
and we need to convert our original image data. */
|
||||
switch (pixfmt)
|
||||
{
|
||||
case RDP_PIXFMT_I420:
|
||||
case RDP_PIXFMT_YV12:
|
||||
if (!xf_tsmf_is_format_supported(xv, RDP_PIXFMT_I420) &&
|
||||
!xf_tsmf_is_format_supported(xv, RDP_PIXFMT_YV12))
|
||||
{
|
||||
DEBUG_XV("pixel format 0x%X not supported by hardware.", pixfmt);
|
||||
break;
|
||||
}
|
||||
/* Y */
|
||||
if (image->pitches[0] == vevent->frame_width)
|
||||
{
|
||||
memcpy(image->data + image->offsets[0],
|
||||
vevent->frame_data,
|
||||
vevent->frame_width * vevent->frame_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < vevent->frame_height; i++)
|
||||
{
|
||||
memcpy(image->data + image->offsets[0] + i * image->pitches[0],
|
||||
vevent->frame_data + i * vevent->frame_width,
|
||||
vevent->frame_width);
|
||||
}
|
||||
}
|
||||
/* UV */
|
||||
/* Conversion between I420 and YV12 is to simply swap U and V */
|
||||
if (xf_tsmf_is_format_supported(xv, pixfmt))
|
||||
{
|
||||
data1 = vevent->frame_data + vevent->frame_width * vevent->frame_height;
|
||||
data2 = vevent->frame_data + vevent->frame_width * vevent->frame_height +
|
||||
vevent->frame_width * vevent->frame_height / 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
data2 = vevent->frame_data + vevent->frame_width * vevent->frame_height;
|
||||
data1 = vevent->frame_data + vevent->frame_width * vevent->frame_height +
|
||||
vevent->frame_width * vevent->frame_height / 4;
|
||||
image->id = pixfmt == RDP_PIXFMT_I420 ? RDP_PIXFMT_YV12 : RDP_PIXFMT_I420;
|
||||
}
|
||||
if (image->pitches[1] * 2 == vevent->frame_width)
|
||||
{
|
||||
memcpy(image->data + image->offsets[1],
|
||||
data1,
|
||||
vevent->frame_width * vevent->frame_height / 4);
|
||||
memcpy(image->data + image->offsets[2],
|
||||
data2,
|
||||
vevent->frame_width * vevent->frame_height / 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < vevent->frame_height / 2; i++)
|
||||
{
|
||||
memcpy(image->data + image->offsets[1] + i * image->pitches[1],
|
||||
data1 + i * vevent->frame_width / 2,
|
||||
vevent->frame_width / 2);
|
||||
memcpy(image->data + image->offsets[2] + i * image->pitches[2],
|
||||
data2 + i * vevent->frame_width / 2,
|
||||
vevent->frame_width / 2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
memcpy(image->data, vevent->frame_data, image->data_size <= vevent->frame_size ?
|
||||
image->data_size : vevent->frame_size);
|
||||
break;
|
||||
}
|
||||
|
||||
XvShmPutImage(xfi->display, xv->xv_port, xfi->window->handle, xfi->gc, image,
|
||||
0, 0, image->width, image->height,
|
||||
vevent->x, vevent->y, vevent->width, vevent->height, False);
|
||||
XSync(xfi->display, False);
|
||||
|
||||
XShmDetach(xfi->display, &shminfo);
|
||||
XFree(image);
|
||||
}
|
||||
|
||||
static void xf_process_tsmf_redraw_event(xfInfo* xfi, RDP_REDRAW_EVENT* revent)
|
||||
{
|
||||
XSetFunction(xfi->display, xfi->gc, GXcopy);
|
||||
XSetFillStyle(xfi->display, xfi->gc, FillSolid);
|
||||
XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc,
|
||||
revent->x, revent->y, revent->width, revent->height, revent->x, revent->y);
|
||||
}
|
||||
|
||||
void xf_process_tsmf_event(xfInfo* xfi, RDP_EVENT* event)
|
||||
{
|
||||
switch (event->event_type)
|
||||
{
|
||||
case RDP_EVENT_TYPE_TSMF_VIDEO_FRAME:
|
||||
xf_process_tsmf_video_frame_event(xfi, (RDP_VIDEO_FRAME_EVENT*) event);
|
||||
break;
|
||||
|
||||
case RDP_EVENT_TYPE_TSMF_REDRAW:
|
||||
xf_process_tsmf_redraw_event(xfi, (RDP_REDRAW_EVENT*) event);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#else /* WITH_XV */
|
||||
|
||||
void xf_tsmf_init(xfInfo* xfi, long xv_port)
|
||||
{
|
||||
}
|
||||
|
||||
void xf_tsmf_uninit(xfInfo* xfi)
|
||||
{
|
||||
}
|
||||
|
||||
void xf_process_tsmf_event(xfInfo* xfi, RDP_EVENT* event)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* WITH_XV */
|
29
client/X11/xf_tsmf.h
Normal file
29
client/X11/xf_tsmf.h
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Client
|
||||
* X11 Video Redirection
|
||||
*
|
||||
* 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 __XF_TSMF_H
|
||||
#define __XF_TSMF_H
|
||||
|
||||
#include "xfreerdp.h"
|
||||
|
||||
void xf_tsmf_init(xfInfo* xfi, long xv_port);
|
||||
void xf_tsmf_uninit(xfInfo* xfi);
|
||||
void xf_process_tsmf_event(xfInfo* xfi, RDP_EVENT* event);
|
||||
|
||||
#endif /* __XF_TSMF_H */
|
@ -46,6 +46,7 @@
|
||||
|
||||
#include "xf_gdi.h"
|
||||
#include "xf_rail.h"
|
||||
#include "xf_tsmf.h"
|
||||
#include "xf_event.h"
|
||||
#include "xf_monitor.h"
|
||||
#include "xf_keyboard.h"
|
||||
@ -55,6 +56,8 @@
|
||||
freerdp_sem g_sem;
|
||||
static int g_thread_count = 0;
|
||||
|
||||
static long xv_port = 0;
|
||||
|
||||
struct thread_data
|
||||
{
|
||||
freerdp* instance;
|
||||
@ -463,11 +466,15 @@ boolean xf_post_connect(freerdp* instance)
|
||||
|
||||
freerdp_chanman_post_connect(GET_CHANMAN(instance), instance);
|
||||
|
||||
xf_tsmf_init(xfi, xv_port);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
int xf_process_ui_args(rdpSettings* settings, const char* opt, const char* val, void* user_data)
|
||||
{
|
||||
int argc = 0;
|
||||
|
||||
if (strcmp("--kbd-list", opt) == 0)
|
||||
{
|
||||
int i;
|
||||
@ -493,8 +500,13 @@ int xf_process_ui_args(rdpSettings* settings, const char* opt, const char* val,
|
||||
|
||||
exit(0);
|
||||
}
|
||||
else if (strcmp("--xv-port", opt) == 0)
|
||||
{
|
||||
xv_port = atoi(val);
|
||||
argc = 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return argc;
|
||||
}
|
||||
|
||||
int xf_process_plugin_args(rdpSettings* settings, const char* name, RDP_PLUGIN_DATA* plugin_data, void* user_data)
|
||||
@ -512,7 +524,7 @@ int xf_receive_channel_data(freerdp* instance, int channelId, uint8* data, int s
|
||||
return freerdp_chanman_data(instance, channelId, data, size, flags, total_size);
|
||||
}
|
||||
|
||||
void xf_process_cb_sync_event(rdpChanMan* chanman, freerdp* instance)
|
||||
void xf_process_cb_sync_event(xfInfo* xfi, rdpChanMan* chanman)
|
||||
{
|
||||
RDP_EVENT* event;
|
||||
RDP_CB_FORMAT_LIST_EVENT* format_list_event;
|
||||
@ -525,6 +537,19 @@ void xf_process_cb_sync_event(rdpChanMan* chanman, freerdp* instance)
|
||||
freerdp_chanman_send_event(chanman, event);
|
||||
}
|
||||
|
||||
void xf_process_cliprdr_event(xfInfo* xfi, rdpChanMan* chanman, RDP_EVENT* event)
|
||||
{
|
||||
switch (event->event_type)
|
||||
{
|
||||
case RDP_EVENT_TYPE_CB_SYNC:
|
||||
xf_process_cb_sync_event(xfi, chanman);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void xf_process_channel_event(rdpChanMan* chanman, freerdp* instance)
|
||||
{
|
||||
xfInfo* xfi;
|
||||
@ -542,18 +567,18 @@ void xf_process_channel_event(rdpChanMan* chanman, freerdp* instance)
|
||||
xf_process_rail_event(xfi, chanman, event);
|
||||
break;
|
||||
|
||||
case RDP_EVENT_CLASS_TSMF:
|
||||
xf_process_tsmf_event(xfi, event);
|
||||
break;
|
||||
|
||||
case RDP_EVENT_CLASS_CLIPRDR:
|
||||
xf_process_cliprdr_event(xfi, chanman, event);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (event->event_type)
|
||||
{
|
||||
case RDP_EVENT_TYPE_CB_SYNC:
|
||||
xf_process_cb_sync_event(chanman, instance);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
freerdp_event_free(event);
|
||||
}
|
||||
}
|
||||
@ -590,6 +615,8 @@ void xf_window_free(xfInfo* xfi)
|
||||
|
||||
xfree(xfi->clrconv);
|
||||
rail_free(xfi->rail);
|
||||
|
||||
xf_tsmf_uninit(xfi);
|
||||
}
|
||||
|
||||
void xf_free(xfInfo* xfi)
|
||||
|
@ -93,6 +93,7 @@ struct xf_info
|
||||
VIRTUAL_SCREEN vscreen;
|
||||
uint8* bmp_codec_none;
|
||||
void* rfx_context;
|
||||
void* xv_context;
|
||||
|
||||
Atom _NET_WM_ICON;
|
||||
Atom _MOTIF_WM_HINTS;
|
||||
|
@ -11,6 +11,7 @@ option(WITH_DEBUG_GDI "Print graphics debug messages." OFF)
|
||||
option(WITH_DEBUG_RFX "Print RemoteFX debug messages." OFF)
|
||||
option(WITH_DEBUG_X11 "Print X11 Client debug messages" OFF)
|
||||
option(WITH_DEBUG_RAIL "Print RemoteApp debug messages" OFF)
|
||||
option(WITH_DEBUG_XV "Print XVideo debug messages" OFF)
|
||||
option(WITH_MANPAGES "Generate manpages." ON)
|
||||
option(WITH_PROFILER "Compile profiler." OFF)
|
||||
option(WITH_SSE2 "Use SSE2 optimization." OFF)
|
||||
|
1
cmake/FindFFmpeg.cmake
Normal file
1
cmake/FindFFmpeg.cmake
Normal file
@ -0,0 +1 @@
|
||||
pkg_check_modules(FFMPEG libavcodec)
|
49
cmake/FindXv.cmake
Normal file
49
cmake/FindXv.cmake
Normal file
@ -0,0 +1,49 @@
|
||||
# - Find Xv
|
||||
# Find the Xv libraries
|
||||
#
|
||||
# This module defines the following variables:
|
||||
# XV_FOUND - True if XV_INCLUDE_DIR & XV_LIBRARY are found
|
||||
# XV_LIBRARIES - Set when XV_LIBRARY is found
|
||||
# XV_INCLUDE_DIRS - Set when XV_INCLUDE_DIR is found
|
||||
#
|
||||
# XV_INCLUDE_DIR - where to find Xv.h, etc.
|
||||
# XV_LIBRARY - the Xv library
|
||||
#
|
||||
|
||||
#=============================================================================
|
||||
# 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.
|
||||
#=============================================================================
|
||||
|
||||
find_path(XV_INCLUDE_DIR NAMES Xv.h
|
||||
PATH_SUFFIXES X11/extensions
|
||||
DOC "The Xv include directory"
|
||||
)
|
||||
|
||||
find_library(XV_LIBRARY NAMES Xv
|
||||
DOC "The Xv library"
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(XV DEFAULT_MSG XV_LIBRARY XV_INCLUDE_DIR)
|
||||
|
||||
if(XV_FOUND)
|
||||
set( XV_LIBRARIES ${XV_LIBRARY} )
|
||||
set( XV_INCLUDE_DIRS ${XV_INCLUDE_DIR} )
|
||||
endif()
|
||||
|
||||
mark_as_advanced(XV_INCLUDE_DIR XV_LIBRARY)
|
||||
|
@ -33,5 +33,6 @@
|
||||
#cmakedefine WITH_SSE2
|
||||
#cmakedefine WITH_DEBUG_X11
|
||||
#cmakedefine WITH_DEBUG_RAIL
|
||||
#cmakedefine WITH_DEBUG_XV
|
||||
|
||||
#endif
|
||||
|
@ -73,7 +73,7 @@ struct _IWTSVirtualChannel
|
||||
/* Starts a write request on the channel. */
|
||||
int (*Write) (IWTSVirtualChannel* pChannel,
|
||||
uint32 cbSize,
|
||||
char* pBuffer,
|
||||
uint8* pBuffer,
|
||||
void* pReserved);
|
||||
/* Closes the channel. */
|
||||
int (*Close) (IWTSVirtualChannel* pChannel);
|
||||
@ -118,7 +118,7 @@ struct _IWTSListenerCallback
|
||||
the associated listener. */
|
||||
int (*OnNewChannelConnection) (IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel,
|
||||
char* Data,
|
||||
uint8* Data,
|
||||
int* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback);
|
||||
};
|
||||
|
@ -56,4 +56,9 @@ struct _RDP_REDRAW_EVENT
|
||||
};
|
||||
typedef struct _RDP_REDRAW_EVENT RDP_REDRAW_EVENT;
|
||||
|
||||
/* RDP_VIDEO_FRAME_EVENT.frame_pixfmt */
|
||||
/* http://www.fourcc.org/yuv.php */
|
||||
#define RDP_PIXFMT_I420 0x30323449
|
||||
#define RDP_PIXFMT_YV12 0x32315659
|
||||
|
||||
#endif /* __TSMF_PLUGIN */
|
||||
|
@ -43,6 +43,7 @@ FREERDP_API LIST* list_new(void);
|
||||
FREERDP_API void list_free(LIST* list);
|
||||
FREERDP_API void list_enqueue(LIST* list, void* data);
|
||||
FREERDP_API void* list_dequeue(LIST* list);
|
||||
FREERDP_API void* list_peek(LIST* list);
|
||||
#define list_add(_l, _d) list_enqueue(_l, _d)
|
||||
FREERDP_API void* list_remove(LIST* list, void* data);
|
||||
|
||||
|
@ -43,11 +43,15 @@ struct _freerdp_thread
|
||||
FREERDP_API freerdp_thread* freerdp_thread_new(void);
|
||||
FREERDP_API void freerdp_thread_start(freerdp_thread* thread, void* func, void* arg);
|
||||
FREERDP_API void freerdp_thread_stop(freerdp_thread* thread);
|
||||
FREERDP_API void freerdp_thread_free(freerdp_thread* thread);
|
||||
|
||||
#define freerdp_thread_wait(_t) wait_obj_select(_t->signals, _t->num_signals, -1)
|
||||
#define freerdp_thread_wait_timeout(_t, _timeout) wait_obj_select(_t->signals, _t->num_signals, _timeout)
|
||||
#define freerdp_thread_is_stopped(_t) wait_obj_is_set(_t->signals[0])
|
||||
#define freerdp_thread_quit(_t) _t->status = -1
|
||||
#define freerdp_thread_is_running(_t) (_t->status == 1)
|
||||
#define freerdp_thread_quit(_t) do { \
|
||||
_t->status = -1; \
|
||||
wait_obj_clear(_t->signals[0]); } while (0)
|
||||
#define freerdp_thread_signal(_t) wait_obj_set(_t->signals[1])
|
||||
#define freerdp_thread_reset(_t) wait_obj_clear(_t->signals[1])
|
||||
#define freerdp_thread_lock(_t) freerdp_mutex_lock(_t->mutex)
|
||||
|
@ -443,8 +443,8 @@ INLINE uint16 update_read_glyph_fragments(STREAM* s, GLYPH_FRAGMENT** fragments,
|
||||
array_mark = NULL;
|
||||
stream_end = s->p + size;
|
||||
stream_get_mark(s, stream_start);
|
||||
offsets = (uint8**) xmalloc(size / 2);
|
||||
lengths = (uint16*) xmalloc(size / 2);
|
||||
offsets = (uint8**) xmalloc(sizeof(uint8*) * size / 2);
|
||||
lengths = (uint16*) xmalloc(sizeof(uint16) * size / 2);
|
||||
operations = (uint8*) xmalloc(size / 2);
|
||||
|
||||
while (s->p < stream_end)
|
||||
|
@ -724,6 +724,9 @@ void gdi_fast_index(rdpUpdate* update, FAST_INDEX_ORDER* fast_index)
|
||||
bmp = bmps[j];
|
||||
glyph = glyphs[j];
|
||||
|
||||
if (bmp == NULL || glyph == NULL)
|
||||
continue;
|
||||
|
||||
gdi_BitBlt(gdi->drawing->hdc, glyph->x + x, glyph->y + y, bmp->bitmap->width,
|
||||
bmp->bitmap->height, bmp->hdc, 0, 0, GDI_DSPDxax);
|
||||
|
||||
|
@ -97,6 +97,14 @@ void* list_dequeue(LIST* list)
|
||||
return data;
|
||||
}
|
||||
|
||||
void* list_peek(LIST* list)
|
||||
{
|
||||
LIST_ITEM* item;
|
||||
|
||||
item = list->head;
|
||||
return item ? item->data : NULL;
|
||||
}
|
||||
|
||||
void* list_remove(LIST* list, void* data)
|
||||
{
|
||||
LIST_ITEM* item;
|
||||
|
@ -303,6 +303,7 @@ static void svc_plugin_process_terminated(rdpSvcPlugin* plugin)
|
||||
svc_data_in_item* item;
|
||||
|
||||
freerdp_thread_stop(plugin->priv->thread);
|
||||
freerdp_thread_free(plugin->priv->thread);
|
||||
|
||||
plugin->channel_entry_points.pVirtualChannelClose(plugin->priv->open_handle);
|
||||
xfree(plugin->channel_entry_points.pExtendedData);
|
||||
|
@ -72,6 +72,11 @@ void freerdp_thread_stop(freerdp_thread* thread)
|
||||
i++;
|
||||
freerdp_usleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
void freerdp_thread_free(freerdp_thread* thread)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < thread->num_signals; i++)
|
||||
wait_obj_free(thread->signals[i]);
|
||||
|
Loading…
Reference in New Issue
Block a user