20#include <freerdp/config.h>
27#include <winpr/assert.h>
28#include <winpr/cast.h>
29#include <winpr/synch.h>
30#include <winpr/print.h>
31#include <winpr/stream.h>
32#include <winpr/cmdline.h>
33#include <winpr/collections.h>
34#include <winpr/interlocked.h>
35#include <winpr/sysinfo.h>
37#include <freerdp/freerdp.h>
38#include <freerdp/addin.h>
39#include <freerdp/primitives.h>
40#include <freerdp/client/channels.h>
41#include <freerdp/client/geometry.h>
42#include <freerdp/client/video.h>
43#include <freerdp/channels/log.h>
44#include <freerdp/codec/h264.h>
45#include <freerdp/codec/yuv.h>
46#include <freerdp/timer.h>
48#define TAG CHANNELS_TAG("video.client")
50#include "video_main.h"
56 IWTSListener* controlListener;
57 IWTSListener* dataListener;
61 VideoClientContext* context;
63 rdpContext* rdpcontext;
68#define XF_VIDEO_UNLIMITED_RATE 31
70static const BYTE MFVideoFormat_H264[] = {
'H',
'2',
'6',
'4', 0x00, 0x00, 0x10, 0x00,
71 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 };
79 UINT64 startTimeStamp;
82 UINT64 lastPublishTime;
83 UINT64 nextPublishTime;
84 volatile LONG refCounter;
87 MAPPED_GEOMETRY* geometry;
88 VideoClientContext* video;
96 MAPPED_GEOMETRY* geometry;
103struct s_VideoClientContextPriv
105 VideoClientContext* video;
106 GeometryClientContext* geometry;
109 wBufferPool* surfacePool;
110 UINT32 publishedFrames;
111 UINT32 droppedFrames;
113 UINT64 nextFeedbackTime;
114 PresentationContext* currentPresentation;
115 FreeRDP_TimerID timerID;
118static void PresentationContext_unref(PresentationContext** presentation);
119static void VideoClientContextPriv_free(VideoClientContextPriv* priv);
122static const char* video_command_name(BYTE cmd)
126 case TSMM_START_PRESENTATION:
128 case TSMM_STOP_PRESENTATION:
135static void video_client_context_set_geometry(VideoClientContext* video,
136 GeometryClientContext* geometry)
139 WINPR_ASSERT(video->priv);
141 video->priv->geometry = geometry;
144WINPR_ATTR_MALLOC(VideoClientContextPriv_free, 1)
145static VideoClientContextPriv* VideoClientContextPriv_new(VideoClientContext* video)
148 VideoClientContextPriv* ret = calloc(1,
sizeof(*ret));
152 ret->frames = Queue_New(TRUE, 10, 2);
155 WLog_ERR(TAG,
"unable to allocate frames queue");
159 ret->surfacePool = BufferPool_New(FALSE, 0, 16);
160 if (!ret->surfacePool)
162 WLog_ERR(TAG,
"unable to create surface pool");
166 if (!InitializeCriticalSectionAndSpinCount(&ret->framesLock, 4 * 1000))
168 WLog_ERR(TAG,
"unable to initialize frames lock");
177 ret->lastSentRate = 30;
181 VideoClientContextPriv_free(ret);
186static BOOL PresentationContext_ref(PresentationContext* presentation)
188 WINPR_ASSERT(presentation);
190 const LONG val = InterlockedIncrement(&presentation->refCounter);
194static void PresentationContext_free(PresentationContext* presentation)
199 MAPPED_GEOMETRY* geometry = presentation->geometry;
202 geometry->MappedGeometryUpdate =
nullptr;
203 geometry->MappedGeometryClear =
nullptr;
204 geometry->custom =
nullptr;
205 mappedGeometryUnref(geometry);
208 h264_context_free(presentation->h264);
209 Stream_Free(presentation->currentSample, TRUE);
210 presentation->video->deleteSurface(presentation->video, presentation->surface);
214WINPR_ATTR_MALLOC(PresentationContext_free, 1)
215static PresentationContext* PresentationContext_new(VideoClientContext* video, BYTE PresentationId,
216 UINT32 x, UINT32 y, UINT32 width, UINT32 height)
218 if ((width == 0) || (height == 0))
220 WLog_ERR(TAG,
"width==%" PRIu32
", height=%" PRIu32, width, height);
223 const size_t s = 4ULL * width * height;
230 PresentationContext* ret = calloc(1,
sizeof(*ret));
235 ret->PresentationId = PresentationId;
237 ret->h264 = h264_context_new(FALSE);
240 WLog_ERR(TAG,
"unable to create a h264 context");
244 VIDEO_PLUGIN* plugin = (VIDEO_PLUGIN*)video->handle;
245 WINPR_ASSERT(plugin);
246 WINPR_ASSERT(plugin->rdpcontext);
247 if (!h264_context_set_option(ret->h264, H264_CONTEXT_OPTION_HW_ACCEL,
249 FreeRDP_SoftwareGdi) == 0))
251 if (!h264_context_reset(ret->h264, width, height))
254 ret->currentSample = Stream_New(
nullptr, 4096);
255 if (!ret->currentSample)
257 WLog_ERR(TAG,
"unable to create current packet stream");
261 ret->surface = video->createSurface(video, x, y, width, height);
264 WLog_ERR(TAG,
"unable to create surface");
268 if (!PresentationContext_ref(ret))
274 PresentationContext_free(ret);
278static void PresentationContext_unref(PresentationContext** ppresentation)
280 WINPR_ASSERT(ppresentation);
282 PresentationContext* presentation = *ppresentation;
286 if (InterlockedDecrement(&presentation->refCounter) > 0)
288 *ppresentation =
nullptr;
290 PresentationContext_free(presentation);
293static void VideoFrame_free(VideoClientContextPriv* priv, VideoFrame* frame)
299 mappedGeometryUnref(frame->geometry);
301 BufferPool_Return(priv->surfacePool, frame->surfaceData);
305WINPR_ATTR_MALLOC(VideoFrame_free, 1)
306static VideoFrame* VideoFrame_new(VideoClientContextPriv* priv, PresentationContext* presentation,
307 MAPPED_GEOMETRY* geom)
310 WINPR_ASSERT(presentation);
314 WINPR_ASSERT(surface);
316 VideoFrame* frame = calloc(1,
sizeof(VideoFrame));
319 frame->PresentationId = presentation->PresentationId;
321 mappedGeometryRef(geom);
323 frame->publishTime = presentation->lastPublishTime;
324 frame->geometry = geom;
325 frame->w = surface->alignedWidth;
326 frame->h = surface->alignedHeight;
327 frame->scanline = surface->scanline;
329 frame->surfaceData = BufferPool_Take(priv->surfacePool, 1ll * frame->scanline * frame->h);
330 if (!frame->surfaceData)
336 VideoFrame_free(priv, frame);
340void VideoClientContextPriv_free(VideoClientContextPriv* priv)
345 EnterCriticalSection(&priv->framesLock);
349 while (Queue_Count(priv->frames))
351 VideoFrame* frame = Queue_Dequeue(priv->frames);
353 VideoFrame_free(priv, frame);
357 Queue_Free(priv->frames);
358 LeaveCriticalSection(&priv->framesLock);
360 DeleteCriticalSection(&priv->framesLock);
362 if (priv->currentPresentation)
363 PresentationContext_unref(&priv->currentPresentation);
365 BufferPool_Free(priv->surfacePool);
370static UINT video_channel_write(VIDEO_PLUGIN* video,
const BYTE* data, UINT32 length)
374 if (!video->control_callback || !video->control_callback->channel_callback)
375 return ERROR_BAD_CONFIGURATION;
376 IWTSVirtualChannel* channel = video->control_callback->channel_callback->channel;
377 if (!channel || !channel->Write)
378 return ERROR_BAD_CONFIGURATION;
379 return channel->Write(channel, length, data,
nullptr);
383static UINT video_control_send_presentation_response(VideoClientContext* context,
386 BYTE buf[12] = WINPR_C_ARRAY_INIT;
388 WINPR_ASSERT(context);
391 VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)context->handle;
394 wStream* s = Stream_New(buf, 12);
396 return CHANNEL_RC_NO_MEMORY;
398 Stream_Write_UINT32(s, 12);
399 Stream_Write_UINT32(s, TSMM_PACKET_TYPE_PRESENTATION_RESPONSE);
400 Stream_Write_UINT8(s, resp->PresentationId);
402 Stream_SealLength(s);
403 Stream_Free(s, FALSE);
405 return video_channel_write(video, buf,
sizeof(buf));
409static BOOL video_onMappedGeometryUpdate(MAPPED_GEOMETRY* geometry)
411 WINPR_ASSERT(geometry);
413 PresentationContext* presentation = (PresentationContext*)geometry->custom;
414 WINPR_ASSERT(presentation);
416 RDP_RECT* r = &geometry->geometry.boundingRect;
418 "geometry updated topGeom=(%" PRId32
",%" PRId32
"-%" PRId32
"x%" PRId32
419 ") geom=(%" PRId32
",%" PRId32
"-%" PRId32
"x%" PRId32
") rects=(%" PRId16
",%" PRId16
420 "-%" PRId16
"x%" PRId16
")",
421 geometry->topLevelLeft, geometry->topLevelTop,
422 geometry->topLevelRight - geometry->topLevelLeft,
423 geometry->topLevelBottom - geometry->topLevelTop,
425 geometry->left, geometry->top, geometry->right - geometry->left,
426 geometry->bottom - geometry->top,
428 r->x, r->y, r->width, r->height);
430 WINPR_ASSERT(presentation->surface);
431 if (geometry->topLevelLeft < 0)
433 WLog_ERR(TAG,
"geometry->topLevelLeft=%d < 0", geometry->topLevelLeft);
436 if (geometry->left < 0)
438 WLog_ERR(TAG,
"geometry->left=%d < 0", geometry->left);
441 presentation->surface->x = WINPR_ASSERTING_INT_CAST(uint32_t, geometry->topLevelLeft) +
442 WINPR_ASSERTING_INT_CAST(uint32_t, geometry->left);
444 if (geometry->topLevelTop < 0)
446 WLog_ERR(TAG,
"geometry->topLevelTop=%d < 0", geometry->topLevelTop);
449 if (geometry->top < 0)
451 WLog_ERR(TAG,
"geometry->top=%d < 0", geometry->top);
454 presentation->surface->y = WINPR_ASSERTING_INT_CAST(uint32_t, geometry->topLevelTop) +
455 WINPR_ASSERTING_INT_CAST(uint32_t, geometry->top);
461static BOOL video_onMappedGeometryClear(MAPPED_GEOMETRY* geometry)
463 WINPR_ASSERT(geometry);
465 PresentationContext* presentation = (PresentationContext*)geometry->custom;
466 WINPR_ASSERT(presentation);
468 mappedGeometryUnref(presentation->geometry);
469 presentation->geometry =
nullptr;
474static UINT video_PresentationRequest(VideoClientContext* video,
477 UINT ret = CHANNEL_RC_OK;
482 VideoClientContextPriv* priv = video->priv;
485 EnterCriticalSection(&priv->framesLock);
486 if (req->Command == TSMM_START_PRESENTATION)
488 MAPPED_GEOMETRY* geom =
nullptr;
491 if (memcmp(req->VideoSubtypeId, MFVideoFormat_H264, 16) != 0)
493 WLog_ERR(TAG,
"not a H264 video, ignoring request");
497 if (priv->currentPresentation)
499 if (priv->currentPresentation->PresentationId == req->PresentationId)
501 WLog_ERR(TAG,
"ignoring start request for existing presentation %" PRIu8,
502 req->PresentationId);
506 WLog_ERR(TAG,
"releasing current presentation %" PRIu8, req->PresentationId);
507 PresentationContext_unref(&priv->currentPresentation);
512 WLog_ERR(TAG,
"geometry channel not ready, ignoring request");
516 geom = HashTable_GetItemValue(priv->geometry->geometries, &(req->GeometryMappingId));
519 WLog_ERR(TAG,
"geometry mapping 0x%" PRIx64
" not registered", req->GeometryMappingId);
523 WLog_DBG(TAG,
"creating presentation 0x%x", req->PresentationId);
524 if ((geom->topLevelLeft < 0) || (geom->left < 0) || (geom->topLevelTop < 0) ||
528 "geometry: topLevelLeft=%" PRId32
" < 0, left=%" PRId32
529 " < 0, topLevelTop=%" PRId32
" < 0, top=%" PRId32
" < 0",
530 geom->topLevelLeft, geom->left, geom->topLevelTop, geom->top);
534 priv->currentPresentation = PresentationContext_new(
535 video, req->PresentationId,
536 WINPR_ASSERTING_INT_CAST(uint32_t, geom->topLevelLeft + geom->left),
537 WINPR_ASSERTING_INT_CAST(uint32_t, geom->topLevelTop + geom->top), req->SourceWidth,
539 if (!priv->currentPresentation)
541 WLog_ERR(TAG,
"unable to create presentation video");
542 ret = CHANNEL_RC_NO_MEMORY;
546 mappedGeometryRef(geom);
547 priv->currentPresentation->geometry = geom;
549 priv->currentPresentation->video = video;
550 priv->currentPresentation->ScaledWidth = req->ScaledWidth;
551 priv->currentPresentation->ScaledHeight = req->ScaledHeight;
553 geom->custom = priv->currentPresentation;
554 geom->MappedGeometryUpdate = video_onMappedGeometryUpdate;
555 geom->MappedGeometryClear = video_onMappedGeometryClear;
558 resp.PresentationId = req->PresentationId;
559 ret = video_control_send_presentation_response(video, &resp);
561 else if (req->Command == TSMM_STOP_PRESENTATION)
563 WLog_DBG(TAG,
"stopping presentation 0x%x", req->PresentationId);
564 if (!priv->currentPresentation)
566 WLog_ERR(TAG,
"unknown presentation to stop %" PRIu8, req->PresentationId);
570 priv->droppedFrames = 0;
571 priv->publishedFrames = 0;
572 PresentationContext_unref(&priv->currentPresentation);
576 LeaveCriticalSection(&priv->framesLock);
581static UINT video_read_tsmm_presentation_req(VideoClientContext* context,
wStream* s)
585 WINPR_ASSERT(context);
588 if (!Stream_CheckAndLogRequiredLength(TAG, s, 60))
589 return ERROR_INVALID_DATA;
591 Stream_Read_UINT8(s, req.PresentationId);
592 Stream_Read_UINT8(s, req.Version);
593 Stream_Read_UINT8(s, req.Command);
594 Stream_Read_UINT8(s, req.FrameRate);
596 Stream_Seek_UINT16(s);
597 Stream_Seek_UINT16(s);
599 Stream_Read_UINT32(s, req.SourceWidth);
600 Stream_Read_UINT32(s, req.SourceHeight);
601 Stream_Read_UINT32(s, req.ScaledWidth);
602 Stream_Read_UINT32(s, req.ScaledHeight);
603 if ((req.ScaledWidth == 0) || (req.SourceHeight == 0) || (req.ScaledWidth == 0) ||
604 (req.ScaledHeight == 0))
607 "SourceWidth=%" PRIu32
", SourceHeight=%" PRIu32
", ScaledWidth=%" PRIu32
608 ", ScaledHeight=%" PRIu32,
609 req.SourceWidth, req.SourceHeight, req.ScaledWidth, req.ScaledHeight);
610 return ERROR_INVALID_DATA;
612 Stream_Read_UINT64(s, req.hnsTimestampOffset);
613 Stream_Read_UINT64(s, req.GeometryMappingId);
614 Stream_Read(s, req.VideoSubtypeId, 16);
616 Stream_Read_UINT32(s, req.cbExtra);
618 if (!Stream_CheckAndLogRequiredLength(TAG, s, req.cbExtra))
619 return ERROR_INVALID_DATA;
621 req.pExtraData = Stream_Pointer(s);
624 "presentationReq: id:%" PRIu8
" version:%" PRIu8
625 " command:%s srcWidth/srcHeight=%" PRIu32
"x%" PRIu32
" scaled Width/Height=%" PRIu32
626 "x%" PRIu32
" timestamp=%" PRIu64
" mappingId=%" PRIx64
"",
627 req.PresentationId, req.Version, video_command_name(req.Command), req.SourceWidth,
628 req.SourceHeight, req.ScaledWidth, req.ScaledHeight, req.hnsTimestampOffset,
629 req.GeometryMappingId);
631 return video_PresentationRequest(context, &req);
640static UINT video_control_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* s)
643 UINT ret = CHANNEL_RC_OK;
645 UINT32 packetType = 0;
647 WINPR_ASSERT(callback);
650 VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)callback->plugin;
653 VideoClientContext* context = (VideoClientContext*)video->wtsPlugin.pInterface;
654 WINPR_ASSERT(context);
656 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
657 return ERROR_INVALID_DATA;
659 Stream_Read_UINT32(s, cbSize);
662 WLog_ERR(TAG,
"invalid cbSize %" PRIu32
", expected 8", cbSize);
663 return ERROR_INVALID_DATA;
665 if (!Stream_CheckAndLogRequiredLength(TAG, s, cbSize - 4))
666 return ERROR_INVALID_DATA;
668 Stream_Read_UINT32(s, packetType);
671 case TSMM_PACKET_TYPE_PRESENTATION_REQUEST:
672 ret = video_read_tsmm_presentation_req(context, s);
675 WLog_ERR(TAG,
"not expecting packet type %" PRIu32
"", packetType);
676 ret = ERROR_UNSUPPORTED_TYPE;
683static UINT video_control_send_client_notification(VideoClientContext* context,
686 BYTE buf[100] = WINPR_C_ARRAY_INIT;
688 WINPR_ASSERT(context);
691 VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)context->handle;
694 wStream* s = Stream_New(buf, 32);
696 return CHANNEL_RC_NO_MEMORY;
699 Stream_Seek_UINT32(s);
700 Stream_Write_UINT32(s, TSMM_PACKET_TYPE_CLIENT_NOTIFICATION);
701 Stream_Write_UINT8(s, notif->PresentationId);
702 Stream_Write_UINT8(s, notif->NotificationType);
704 if (notif->NotificationType == TSMM_CLIENT_NOTIFICATION_TYPE_FRAMERATE_OVERRIDE)
706 Stream_Write_UINT32(s, 16);
709 Stream_Write_UINT32(s, notif->FramerateOverride.Flags);
710 Stream_Write_UINT32(s, notif->FramerateOverride.DesiredFrameRate);
711 Stream_Zero(s, 4ULL * 2ULL);
717 Stream_Write_UINT32(s, 0);
720 Stream_SealLength(s);
721 Stream_ResetPosition(s);
722 Stream_Write_UINT32(s, cbSize);
723 Stream_Free(s, FALSE);
725 return video_channel_write(video, buf, cbSize);
728static void video_timer(VideoClientContext* video, UINT64 now)
730 VideoFrame* frame =
nullptr;
734 VideoClientContextPriv* priv = video->priv;
737 EnterCriticalSection(&priv->framesLock);
738 PresentationContext* presentation = video->priv->currentPresentation;
741 const VideoFrame* peekFrame = (VideoFrame*)Queue_Peek(priv->frames);
745 if (peekFrame->publishTime > now)
750 WLog_DBG(TAG,
"dropping frame @%" PRIu64, frame->publishTime);
751 priv->droppedFrames++;
752 VideoFrame_free(priv, frame);
754 frame = Queue_Dequeue(priv->frames);
759 if (presentation && (presentation->PresentationId == frame->PresentationId))
762 const size_t frameSize = 1ull * frame->scanline * frame->h;
763 const size_t surfaceSize = 1ull * surface->scanline * surface->alignedHeight;
768 if (frameSize > surfaceSize)
769 WLog_WARN(TAG,
"dropping stale frame of %" PRIuz
" bytes, surface holds %" PRIuz,
770 frameSize, surfaceSize);
773 priv->publishedFrames++;
774 memcpy(surface->data, frame->surfaceData, frameSize);
776 WINPR_ASSERT(video->showSurface);
777 if (!video->showSurface(video, surface, presentation->ScaledWidth,
778 presentation->ScaledHeight))
779 WLog_WARN(TAG,
"showSurface failed");
782 VideoFrame_free(priv, frame);
785 if (priv->nextFeedbackTime < now)
790 if (priv->publishedFrames && priv->currentPresentation)
792 UINT32 computedRate = 0;
794 if (!PresentationContext_ref(priv->currentPresentation))
795 WLog_WARN(TAG,
"PresentationContext_ref(priv->currentPresentation) failed");
797 if (priv->droppedFrames)
804 if (priv->lastSentRate == XF_VIDEO_UNLIMITED_RATE)
808 computedRate = priv->lastSentRate - 2;
819 if (priv->lastSentRate == XF_VIDEO_UNLIMITED_RATE)
820 computedRate = XF_VIDEO_UNLIMITED_RATE;
823 computedRate = priv->lastSentRate + 2;
824 if (computedRate > XF_VIDEO_UNLIMITED_RATE)
825 computedRate = XF_VIDEO_UNLIMITED_RATE;
829 if (computedRate != priv->lastSentRate)
833 WINPR_ASSERT(priv->currentPresentation);
834 notif.PresentationId = priv->currentPresentation->PresentationId;
835 notif.NotificationType = TSMM_CLIENT_NOTIFICATION_TYPE_FRAMERATE_OVERRIDE;
836 if (computedRate == XF_VIDEO_UNLIMITED_RATE)
838 notif.FramerateOverride.Flags = 0x01;
839 notif.FramerateOverride.DesiredFrameRate = 0x00;
843 notif.FramerateOverride.Flags = 0x02;
844 notif.FramerateOverride.DesiredFrameRate = computedRate;
847 video_control_send_client_notification(video, ¬if);
848 priv->lastSentRate = computedRate;
851 "server notified with rate %" PRIu32
" published=%" PRIu32
853 priv->lastSentRate, priv->publishedFrames, priv->droppedFrames);
856 PresentationContext_unref(&priv->currentPresentation);
859 priv->droppedFrames = 0;
860 priv->publishedFrames = 0;
861 priv->nextFeedbackTime = now + 1000;
863 LeaveCriticalSection(&priv->framesLock);
867static UINT video_VideoData(VideoClientContext* context,
const TSMM_VIDEO_DATA* data)
870 UINT res = CHANNEL_RC_OK;
872 WINPR_ASSERT(context);
875 VideoClientContextPriv* priv = context->priv;
878 EnterCriticalSection(&priv->framesLock);
879 PresentationContext* presentation = priv->currentPresentation;
882 WLog_ERR(TAG,
"no current presentation");
883 LeaveCriticalSection(&priv->framesLock);
884 return CHANNEL_RC_OK;
887 if (!PresentationContext_ref(presentation))
889 LeaveCriticalSection(&priv->framesLock);
890 return ERROR_INTERNAL_ERROR;
893 if (presentation->PresentationId != data->PresentationId)
895 WLog_ERR(TAG,
"current presentation id=%" PRIu8
" doesn't match data id=%" PRIu8,
896 presentation->PresentationId, data->PresentationId);
900 if (!Stream_EnsureRemainingCapacity(presentation->currentSample, data->cbSample))
902 WLog_ERR(TAG,
"unable to expand the current packet");
903 res = CHANNEL_RC_NO_MEMORY;
907 Stream_Write(presentation->currentSample, data->pSample, data->cbSample);
909 if (data->CurrentPacketIndex == data->PacketsInSample)
912 H264_CONTEXT* h264 = presentation->h264;
913 const UINT64 startTime = winpr_GetTickCount64NS();
914 MAPPED_GEOMETRY* geom = presentation->geometry;
918 const RECTANGLE_16 rect = { 0, 0, WINPR_ASSERTING_INT_CAST(UINT16, surface->alignedWidth),
919 WINPR_ASSERTING_INT_CAST(UINT16, surface->alignedHeight) };
920 Stream_SealLength(presentation->currentSample);
921 Stream_ResetPosition(presentation->currentSample);
923 if (data->SampleNumber == 1)
925 presentation->lastPublishTime = startTime;
928 presentation->lastPublishTime += 100ull * data->hnsDuration;
929 const size_t len = Stream_Length(presentation->currentSample);
930 if (len > UINT32_MAX)
933 BOOL enqueueResult = 0;
934 VideoFrame* frame = VideoFrame_new(priv, presentation, geom);
937 WLog_ERR(TAG,
"unable to create frame");
938 res = CHANNEL_RC_NO_MEMORY;
942 status = avc420_decompress(h264, Stream_Pointer(presentation->currentSample), (UINT32)len,
943 frame->surfaceData, surface->format, surface->scanline,
944 surface->alignedWidth, surface->alignedHeight, &rect, 1);
947 VideoFrame_free(priv, frame);
951 enqueueResult = Queue_Enqueue(priv->frames, frame);
955 WLog_ERR(TAG,
"unable to enqueue frame");
956 VideoFrame_free(priv, frame);
957 res = CHANNEL_RC_NO_MEMORY;
962 WLog_DBG(TAG,
"scheduling frame in %" PRIu64
" ns", (frame->publishTime - startTime));
966 PresentationContext_unref(&priv->currentPresentation);
967 LeaveCriticalSection(&priv->framesLock);
973static UINT video_data_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* s)
977 UINT32 packetType = 0;
980 VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)callback->plugin;
983 VideoClientContext* context = (VideoClientContext*)video->wtsPlugin.pInterface;
985 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
986 return ERROR_INVALID_DATA;
988 Stream_Read_UINT32(s, cbSize);
991 WLog_ERR(TAG,
"invalid cbSize %" PRIu32
", expected >= 8", cbSize);
992 return ERROR_INVALID_DATA;
995 if (!Stream_CheckAndLogRequiredLength(TAG, s, cbSize - 4))
996 return ERROR_INVALID_DATA;
998 Stream_Read_UINT32(s, packetType);
999 if (packetType != TSMM_PACKET_TYPE_VIDEO_DATA)
1001 WLog_ERR(TAG,
"only expecting VIDEO_DATA on the data channel");
1002 return ERROR_INVALID_DATA;
1005 if (!Stream_CheckAndLogRequiredLength(TAG, s, 32))
1006 return ERROR_INVALID_DATA;
1008 Stream_Read_UINT8(s, data.PresentationId);
1009 Stream_Read_UINT8(s, data.Version);
1010 Stream_Read_UINT8(s, data.Flags);
1011 Stream_Seek_UINT8(s);
1012 Stream_Read_UINT64(s, data.hnsTimestamp);
1013 Stream_Read_UINT64(s, data.hnsDuration);
1014 Stream_Read_UINT16(s, data.CurrentPacketIndex);
1015 Stream_Read_UINT16(s, data.PacketsInSample);
1016 Stream_Read_UINT32(s, data.SampleNumber);
1017 Stream_Read_UINT32(s, data.cbSample);
1018 if (!Stream_CheckAndLogRequiredLength(TAG, s, data.cbSample))
1019 return ERROR_INVALID_DATA;
1020 data.pSample = Stream_Pointer(s);
1030 return video_VideoData(context, &data);
1039static UINT video_control_on_close(IWTSVirtualChannelCallback* pChannelCallback)
1041 if (pChannelCallback)
1044 VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)listener_callback->plugin;
1045 if (video && video->control_callback)
1047 video->control_callback->channel_callback =
nullptr;
1050 video->connected = FALSE;
1052 free(pChannelCallback);
1053 return CHANNEL_RC_OK;
1057static UINT video_data_on_close(IWTSVirtualChannelCallback* pChannelCallback)
1059 if (pChannelCallback)
1062 VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)listener_callback->plugin;
1063 if (video && video->data_callback)
1065 video->data_callback->channel_callback =
nullptr;
1069 video->dataConnected = FALSE;
1071 free(pChannelCallback);
1072 return CHANNEL_RC_OK;
1082static UINT video_control_on_new_channel_connection(IWTSListenerCallback* listenerCallback,
1083 IWTSVirtualChannel* channel,
1084 WINPR_ATTR_UNUSED BYTE* Data,
1085 WINPR_ATTR_UNUSED BOOL* pbAccept,
1086 IWTSVirtualChannelCallback** ppCallback)
1090 WINPR_ASSERT(listener_callback);
1092 VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)listener_callback->plugin;
1093 WINPR_ASSERT(video);
1095 if (video->connected)
1097 WLog_ERR(TAG,
"Channel already connected, terminating.");
1098 return ERROR_DEVICE_ALREADY_ATTACHED;
1105 WLog_ERR(TAG,
"calloc failed!");
1106 return CHANNEL_RC_NO_MEMORY;
1109 callback->iface.OnDataReceived = video_control_on_data_received;
1110 callback->iface.OnClose = video_control_on_close;
1111 callback->plugin = listener_callback->plugin;
1112 callback->channel_mgr = listener_callback->channel_mgr;
1113 callback->channel = channel;
1114 listener_callback->channel_callback = callback;
1116 *ppCallback = &callback->iface;
1117 video->connected = TRUE;
1119 return CHANNEL_RC_OK;
1124static UINT video_data_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
1125 IWTSVirtualChannel* pChannel,
1126 WINPR_ATTR_UNUSED BYTE* Data,
1127 WINPR_ATTR_UNUSED BOOL* pbAccept,
1128 IWTSVirtualChannelCallback** ppCallback)
1132 WINPR_ASSERT(listener_callback);
1134 VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)listener_callback->plugin;
1135 WINPR_ASSERT(video);
1137 if (video->dataConnected)
1139 WLog_ERR(TAG,
"Channel already connected, terminating.");
1140 return ERROR_DEVICE_ALREADY_ATTACHED;
1147 WLog_ERR(TAG,
"calloc failed!");
1148 return CHANNEL_RC_NO_MEMORY;
1151 callback->iface.OnDataReceived = video_data_on_data_received;
1152 callback->iface.OnClose = video_data_on_close;
1153 callback->plugin = listener_callback->plugin;
1154 callback->channel_mgr = listener_callback->channel_mgr;
1155 callback->channel = pChannel;
1156 listener_callback->channel_callback = callback;
1158 *ppCallback = &callback->iface;
1159 video->dataConnected = TRUE;
1161 return CHANNEL_RC_OK;
1165static uint64_t timer_cb(WINPR_ATTR_UNUSED rdpContext* context,
void* userdata,
1166 WINPR_ATTR_UNUSED FreeRDP_TimerID timerID, uint64_t timestamp,
1169 VideoClientContext* video = userdata;
1175 video->timer(video, timestamp);
1186static UINT video_plugin_initialize(IWTSPlugin* plugin, IWTSVirtualChannelManager* channelMgr)
1189 VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)plugin;
1191 if (video->initialized)
1193 WLog_ERR(TAG,
"[%s] channel initialized twice, aborting", VIDEO_CONTROL_DVC_CHANNEL_NAME);
1194 return ERROR_INVALID_DATA;
1202 WLog_ERR(TAG,
"calloc for control callback failed!");
1203 return CHANNEL_RC_NO_MEMORY;
1206 callback->iface.OnNewChannelConnection = video_control_on_new_channel_connection;
1207 callback->plugin = plugin;
1208 callback->channel_mgr = channelMgr;
1210 status = channelMgr->CreateListener(channelMgr, VIDEO_CONTROL_DVC_CHANNEL_NAME, 0,
1211 &callback->iface, &(video->controlListener));
1212 video->control_callback = callback;
1213 if (status != CHANNEL_RC_OK)
1216 video->controlListener->pInterface = video->wtsPlugin.pInterface;
1223 WLog_ERR(TAG,
"calloc for data callback failed!");
1224 return CHANNEL_RC_NO_MEMORY;
1227 callback->iface.OnNewChannelConnection = video_data_on_new_channel_connection;
1228 callback->plugin = plugin;
1229 callback->channel_mgr = channelMgr;
1231 status = channelMgr->CreateListener(channelMgr, VIDEO_DATA_DVC_CHANNEL_NAME, 0,
1232 &callback->iface, &(video->dataListener));
1233 video->data_callback = callback;
1234 if (status == CHANNEL_RC_OK)
1235 video->dataListener->pInterface = video->wtsPlugin.pInterface;
1238 if (status == CHANNEL_RC_OK)
1239 video->context->priv->timerID =
1240 freerdp_timer_add(video->rdpcontext, 20000000, timer_cb, video->context,
true);
1241 video->initialized = video->context->priv->timerID != 0;
1242 if (!video->initialized)
1243 status = ERROR_INTERNAL_ERROR;
1253static UINT video_plugin_terminated(IWTSPlugin* pPlugin)
1255 VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)pPlugin;
1257 return CHANNEL_RC_INVALID_INSTANCE;
1259 if (video->context && video->context->priv)
1260 freerdp_timer_remove(video->rdpcontext, video->context->priv->timerID);
1262 if (video->control_callback)
1264 IWTSVirtualChannelManager* mgr = video->control_callback->channel_mgr;
1266 IFCALL(mgr->DestroyListener, mgr, video->controlListener);
1268 if (video->data_callback)
1270 IWTSVirtualChannelManager* mgr = video->data_callback->channel_mgr;
1272 IFCALL(mgr->DestroyListener, mgr, video->dataListener);
1276 VideoClientContextPriv_free(video->context->priv);
1278 free(video->control_callback);
1279 free(video->data_callback);
1280 free(video->wtsPlugin.pInterface);
1282 return CHANNEL_RC_OK;
1294FREERDP_ENTRY_POINT(UINT VCAPITYPE video_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
1296 UINT error = ERROR_INTERNAL_ERROR;
1298 VIDEO_PLUGIN* videoPlugin = (VIDEO_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints,
"video");
1301 videoPlugin = (VIDEO_PLUGIN*)calloc(1,
sizeof(VIDEO_PLUGIN));
1304 WLog_ERR(TAG,
"calloc failed!");
1305 return CHANNEL_RC_NO_MEMORY;
1308 videoPlugin->wtsPlugin.Initialize = video_plugin_initialize;
1309 videoPlugin->wtsPlugin.Connected =
nullptr;
1310 videoPlugin->wtsPlugin.Disconnected =
nullptr;
1311 videoPlugin->wtsPlugin.Terminated = video_plugin_terminated;
1313 VideoClientContext* videoContext =
1314 (VideoClientContext*)calloc(1,
sizeof(VideoClientContext));
1317 WLog_ERR(TAG,
"calloc failed!");
1319 return CHANNEL_RC_NO_MEMORY;
1322 VideoClientContextPriv* priv = VideoClientContextPriv_new(videoContext);
1325 WLog_ERR(TAG,
"VideoClientContextPriv_new failed!");
1328 return CHANNEL_RC_NO_MEMORY;
1331 videoContext->handle = (
void*)videoPlugin;
1332 videoContext->priv = priv;
1333 videoContext->timer = video_timer;
1334 videoContext->setGeometry = video_client_context_set_geometry;
1336 videoPlugin->wtsPlugin.pInterface = (
void*)videoContext;
1337 videoPlugin->context = videoContext;
1338 videoPlugin->rdpcontext = pEntryPoints->GetRdpContext(pEntryPoints);
1339 if (videoPlugin->rdpcontext)
1340 error = pEntryPoints->RegisterPlugin(pEntryPoints,
"video", &videoPlugin->wtsPlugin);
1344 WLog_ERR(TAG,
"could not get video Plugin.");
1345 return CHANNEL_RC_BAD_CHANNEL;
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
a client to server notification struct
presentation request struct
response to a TSMM_PRESENTATION_REQUEST
an implementation of surface used by the video channel