FreeRDP
Loading...
Searching...
No Matches
camera_movie.c
1
20/* A pseudo camera playing a movie file in a loop, selected with
21 * /dvc:rdpecam,sys:movie,file:<path>[,name:<camera name>]
22 *
23 * The only media type announced is the movie's own size (cropped to even dimensions) and frame
24 * rate. Frames are decoded with FFmpeg and handed to the channel as I420, the channel encoder
25 * does the rest as for a raw webcam. */
26
27#include <winpr/assert.h>
28#include <winpr/cast.h>
29#include <winpr/cmdline.h>
30#include <winpr/string.h>
31#include <winpr/synch.h>
32#include <winpr/sysinfo.h>
33#include <winpr/thread.h>
34
35#include <libavcodec/avcodec.h>
36#include <libavformat/avformat.h>
37#include <libavutil/imgutils.h>
38#include <libswscale/swscale.h>
39
40#include "camera.h"
41
42#define TAG CHANNELS_TAG("rdpecam-movie.client")
43
44/* the channel name of the device, what a Windows client would use for its first camera */
45#define CAM_MOVIE_DEVICE_ID "RDCamera_Device_0"
46
47#define CAM_MOVIE_FRAMERATE_NUMERATOR_DEFAULT 30
48#define CAM_MOVIE_FRAMERATE_DENOMINATOR_DEFAULT 1
49
50/* longest sleep of the playback thread, so that a stop request is seen quickly */
51#define CAM_MOVIE_MAX_SLEEP_MS 50
52
53/* when decoding fell behind by more than this, restart the playback clock instead of catching up */
54#define CAM_MOVIE_MAX_LATE_NS (1000ull * 1000ull * 1000ull)
55
56typedef struct
57{
58 AVFormatContext* format;
59 AVCodecContext* codec;
60 int videoIndex;
61 AVPacket* packet;
62 AVFrame* frame;
63 struct SwsContext* sws;
64 BOOL draining;
65 size_t framesSinceRewind;
66} CamMovieDecoder;
67
68typedef struct
69{
71 BOOL streaming;
72 HANDLE thread;
73
74 CameraDevice* dev;
75 size_t streamIndex;
76 ICamHalSampleCapturedCallback sampleCallback;
77
78 /* kept open while the capture is released for lack of sample requests, so that playback
79 * resumes where it was */
80 CamMovieDecoder decoder;
81 BYTE* sample;
82 size_t sampleSize;
83} CamMovieStream;
84
85typedef struct
86{
87 ICamHal iHal;
88
89 char* file;
90 char* name;
91
92 /* characteristics of the movie, the only media type announced */
93 UINT32 width;
94 UINT32 height;
95 UINT32 frameRateNumerator;
96 UINT32 frameRateDenominator;
97
98 CamMovieStream stream;
99} CamMovieHal;
100
101static void cam_movie_decoder_close(CamMovieDecoder* decoder)
102{
103 WINPR_ASSERT(decoder);
104
105 sws_freeContext(decoder->sws);
106 av_frame_free(&decoder->frame);
107 av_packet_free(&decoder->packet);
108 avcodec_free_context(&decoder->codec);
109 avformat_close_input(&decoder->format);
110
111 *decoder = (CamMovieDecoder){ .videoIndex = -1 };
112}
113
114static const char* cam_movie_av_error(int error, char* buffer, size_t size)
115{
116 if (av_strerror(error, buffer, size) < 0)
117 (void)_snprintf(buffer, size, "error %d", error);
118 return buffer;
119}
120
121WINPR_ATTR_NODISCARD
122static BOOL cam_movie_decoder_open(CamMovieDecoder* decoder, const char* file)
123{
124 WINPR_ASSERT(decoder);
125 WINPR_ASSERT(file);
126
127 char buffer[128] = WINPR_C_ARRAY_INIT;
128 *decoder = (CamMovieDecoder){ .videoIndex = -1 };
129
130 int rc = avformat_open_input(&decoder->format, file, nullptr, nullptr);
131 if (rc < 0)
132 {
133 WLog_ERR(TAG, "Unable to open movie %s: %s", file,
134 cam_movie_av_error(rc, buffer, sizeof(buffer)));
135 goto fail;
136 }
137
138 rc = avformat_find_stream_info(decoder->format, nullptr);
139 if (rc < 0)
140 {
141 WLog_ERR(TAG, "Unable to read stream information of %s: %s", file,
142 cam_movie_av_error(rc, buffer, sizeof(buffer)));
143 goto fail;
144 }
145
146#if LIBAVFORMAT_VERSION_MAJOR >= 59
147 const AVCodec* codec = nullptr;
148#else
149 AVCodec* codec = nullptr;
150#endif
151 rc = av_find_best_stream(decoder->format, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
152 if (rc < 0)
153 {
154 WLog_ERR(TAG, "No decodable video stream in %s: %s", file,
155 cam_movie_av_error(rc, buffer, sizeof(buffer)));
156 goto fail;
157 }
158 decoder->videoIndex = rc;
159
160 decoder->codec = avcodec_alloc_context3(codec);
161 if (!decoder->codec)
162 goto fail;
163
164 const AVStream* stream = decoder->format->streams[decoder->videoIndex];
165 rc = avcodec_parameters_to_context(decoder->codec, stream->codecpar);
166 if (rc < 0)
167 goto fail;
168
169 rc = avcodec_open2(decoder->codec, codec, nullptr);
170 if (rc < 0)
171 {
172 WLog_ERR(TAG, "Unable to open the %s decoder: %s", codec->name,
173 cam_movie_av_error(rc, buffer, sizeof(buffer)));
174 goto fail;
175 }
176
177 decoder->packet = av_packet_alloc();
178 decoder->frame = av_frame_alloc();
179 if (!decoder->packet || !decoder->frame)
180 goto fail;
181
182 return TRUE;
183
184fail:
185 cam_movie_decoder_close(decoder);
186 return FALSE;
187}
188
189WINPR_ATTR_NODISCARD
190static BOOL cam_movie_decoder_rewind(CamMovieDecoder* decoder)
191{
192 WINPR_ASSERT(decoder);
193
194 if (decoder->framesSinceRewind == 0)
195 {
196 WLog_ERR(TAG, "The movie did not produce any frame");
197 return FALSE;
198 }
199
200 const AVStream* stream = decoder->format->streams[decoder->videoIndex];
201 const int64_t start = (stream->start_time != AV_NOPTS_VALUE) ? stream->start_time : 0;
202 const int rc = av_seek_frame(decoder->format, decoder->videoIndex, start, AVSEEK_FLAG_BACKWARD);
203 if (rc < 0)
204 {
205 char buffer[128] = WINPR_C_ARRAY_INIT;
206 WLog_ERR(TAG, "Unable to rewind the movie: %s",
207 cam_movie_av_error(rc, buffer, sizeof(buffer)));
208 return FALSE;
209 }
210
211 avcodec_flush_buffers(decoder->codec);
212 decoder->draining = FALSE;
213 decoder->framesSinceRewind = 0;
214 WLog_DBG(TAG, "End of movie, playing it again");
215 return TRUE;
216}
217
219WINPR_ATTR_NODISCARD
220static BOOL cam_movie_decoder_next_frame(CamMovieDecoder* decoder)
221{
222 WINPR_ASSERT(decoder);
223
224 char buffer[128] = WINPR_C_ARRAY_INIT;
225 for (;;)
226 {
227 int rc = avcodec_receive_frame(decoder->codec, decoder->frame);
228 if (rc == 0)
229 {
230 decoder->framesSinceRewind++;
231 return TRUE;
232 }
233
234 /* a draining decoder has nothing more to give even if it does not say AVERROR_EOF */
235 if ((rc == AVERROR_EOF) || ((rc == AVERROR(EAGAIN)) && decoder->draining))
236 {
237 if (!cam_movie_decoder_rewind(decoder))
238 return FALSE;
239 continue;
240 }
241
242 if (rc != AVERROR(EAGAIN))
243 {
244 WLog_ERR(TAG, "Unable to decode the movie: %s",
245 cam_movie_av_error(rc, buffer, sizeof(buffer)));
246 return FALSE;
247 }
248
249 rc = av_read_frame(decoder->format, decoder->packet);
250 if (rc == AVERROR_EOF)
251 {
252 /* flush the frames still in the decoder, it then reports AVERROR_EOF */
253 decoder->draining = TRUE;
254 rc = avcodec_send_packet(decoder->codec, nullptr);
255 }
256 else if (rc >= 0)
257 {
258 if (decoder->packet->stream_index == decoder->videoIndex)
259 rc = avcodec_send_packet(decoder->codec, decoder->packet);
260 av_packet_unref(decoder->packet);
261 }
262
263 if ((rc < 0) && (rc != AVERROR(EAGAIN)))
264 {
265 WLog_ERR(TAG, "Unable to read the movie: %s",
266 cam_movie_av_error(rc, buffer, sizeof(buffer)));
267 return FALSE;
268 }
269 }
270}
271
273WINPR_ATTR_NODISCARD
274static BOOL cam_movie_convert_frame(CamMovieStream* stream, UINT32 width, UINT32 height)
275{
276 WINPR_ASSERT(stream);
277
278 CamMovieDecoder* decoder = &stream->decoder;
279 const AVFrame* frame = decoder->frame;
280
281 if ((frame->width < 0) || (frame->height < 0) || ((UINT32)frame->width < width) ||
282 ((UINT32)frame->height < height))
283 {
284 WLog_WARN(TAG, "Skipping a %dx%d frame, smaller than the announced %" PRIu32 "x%" PRIu32,
285 frame->width, frame->height, width, height);
286 return FALSE;
287 }
288
289 const int w = WINPR_ASSERTING_INT_CAST(int, width);
290 const int h = WINPR_ASSERTING_INT_CAST(int, height);
291
292 /* a source of the announced size crops the right and bottom of a larger frame */
293 decoder->sws =
294 sws_getCachedContext(decoder->sws, w, h, (enum AVPixelFormat)frame->format, w, h,
295 AV_PIX_FMT_YUV420P, SWS_BILINEAR, nullptr, nullptr, nullptr);
296 if (!decoder->sws)
297 {
298 WLog_ERR(TAG, "Unable to convert %s frames to I420",
299 av_get_pix_fmt_name((enum AVPixelFormat)frame->format));
300 return FALSE;
301 }
302
303 const size_t lumaSize = 1ull * width * height;
304 BYTE* dst[4] = { stream->sample, stream->sample + lumaSize,
305 stream->sample + lumaSize + lumaSize / 4, nullptr };
306 const int dstStride[4] = { w, w / 2, w / 2, 0 };
307
308 const int rc = sws_scale(decoder->sws, (const uint8_t* const*)frame->data, frame->linesize, 0,
309 h, dst, dstStride);
310 return rc == h;
311}
312
314WINPR_ATTR_NODISCARD
315static BOOL cam_movie_wait_until(CamMovieStream* stream, UINT64 due)
316{
317 WINPR_ASSERT(stream);
318
319 for (;;)
320 {
321 EnterCriticalSection(&stream->lock);
322 const BOOL streaming = stream->streaming;
323 LeaveCriticalSection(&stream->lock);
324 if (!streaming)
325 return FALSE;
326
327 const UINT64 now = winpr_GetTickCount64NS();
328 if (now >= due)
329 return TRUE;
330
331 UINT64 ms = (due - now) / 1000000ull;
332 if (ms > CAM_MOVIE_MAX_SLEEP_MS)
333 ms = CAM_MOVIE_MAX_SLEEP_MS;
334 Sleep((DWORD)ms);
335 }
336}
337
338static DWORD WINAPI cam_movie_playback_thread(LPVOID param)
339{
340 CamMovieHal* hal = (CamMovieHal*)param;
341 WINPR_ASSERT(hal);
342
343 CamMovieStream* stream = &hal->stream;
344 const UINT64 frameDuration =
345 (1000000000ull * hal->frameRateDenominator) / hal->frameRateNumerator;
346 UINT64 start = winpr_GetTickCount64NS();
347 UINT64 frames = 0;
348
349 for (;;)
350 {
351 if (!cam_movie_decoder_next_frame(&stream->decoder))
352 break;
353
354 if (!cam_movie_convert_frame(stream, hal->width, hal->height))
355 continue;
356
357 /* the movie is played at the frame rate announced to the server */
358 UINT64 due = start + frames * frameDuration;
359 const UINT64 now = winpr_GetTickCount64NS();
360 if (now > due + CAM_MOVIE_MAX_LATE_NS)
361 {
362 WLog_DBG(TAG, "Playback late by %" PRIu64 " ms, resetting the clock",
363 (UINT64)((now - due) / 1000000ull));
364 start = due = now;
365 frames = 0;
366 }
367 frames++;
368
369 if (!cam_movie_wait_until(stream, due))
370 break;
371
372 const UINT error = stream->sampleCallback(stream->dev, stream->streamIndex, stream->sample,
373 stream->sampleSize);
374 if (error == ECAM_SAMPLE_CAPTURE_DRAINED)
375 {
376 WLog_DBG(TAG, "No more sample requests, pausing the movie");
377 break;
378 }
379 else if (error != CHANNEL_RC_OK)
380 WLog_ERR(TAG, "Failure in sampleCallback: %" PRIu32, error);
381 }
382
383 EnterCriticalSection(&stream->lock);
384 stream->streaming = FALSE;
385 LeaveCriticalSection(&stream->lock);
386 return CHANNEL_RC_OK;
387}
388
389static void cam_movie_stream_join(CamMovieStream* stream)
390{
391 WINPR_ASSERT(stream);
392
393 EnterCriticalSection(&stream->lock);
394 stream->streaming = FALSE;
395 LeaveCriticalSection(&stream->lock);
396
397 if (stream->thread)
398 {
399 (void)WaitForSingleObject(stream->thread, INFINITE);
400 (void)CloseHandle(stream->thread);
401 stream->thread = nullptr;
402 }
403}
404
405static BOOL cam_movie_activate(WINPR_ATTR_UNUSED ICamHal* ihal,
406 WINPR_ATTR_UNUSED const char* deviceId, CAM_ERROR_CODE* errorCode)
407{
408 *errorCode = CAM_ERROR_CODE_None;
409 return TRUE;
410}
411
412static BOOL cam_movie_deactivate(WINPR_ATTR_UNUSED ICamHal* ihal,
413 WINPR_ATTR_UNUSED const char* deviceId, CAM_ERROR_CODE* errorCode)
414{
415 *errorCode = CAM_ERROR_CODE_None;
416 return TRUE;
417}
418
419static UINT cam_movie_enumerate(ICamHal* ihal, ICamHalEnumCallback callback, CameraPlugin* ecam,
420 GENERIC_CHANNEL_CALLBACK* hchannel)
421{
422 CamMovieHal* hal = (CamMovieHal*)ihal;
423 WINPR_ASSERT(hal);
424
425 IFCALL(callback, ecam, hchannel, CAM_MOVIE_DEVICE_ID, hal->name);
426 return 1;
427}
428
435static INT16 cam_movie_get_media_type_descriptions(
436 ICamHal* ihal, WINPR_ATTR_UNUSED const char* deviceId, WINPR_ATTR_UNUSED size_t streamIndex,
437 const CAM_MEDIA_FORMAT_INFO* supportedFormats, size_t nSupportedFormats,
438 CAM_MEDIA_TYPE_DESCRIPTION* mediaTypes, size_t* nMediaTypes)
439{
440 CamMovieHal* hal = (CamMovieHal*)ihal;
441 WINPR_ASSERT(hal);
442 WINPR_ASSERT(nMediaTypes);
443
444 const size_t maxMediaTypes = *nMediaTypes;
445 *nMediaTypes = 0;
446 if (maxMediaTypes == 0)
447 return -1;
448
449 /* frames are always produced as I420, take the preferred encoding of it */
450 for (size_t formatIndex = 0; formatIndex < nSupportedFormats; formatIndex++)
451 {
452 if (supportedFormats[formatIndex].inputFormat != CAM_MEDIA_FORMAT_I420)
453 continue;
454 if (formatIndex > INT16_MAX)
455 break;
456
457 *mediaTypes = (CAM_MEDIA_TYPE_DESCRIPTION){
458 .Format = CAM_MEDIA_FORMAT_I420,
459 .Width = hal->width,
460 .Height = hal->height,
461 .FrameRateNumerator = hal->frameRateNumerator,
462 .FrameRateDenominator = hal->frameRateDenominator,
463 .PixelAspectRatioNumerator = 1,
464 .PixelAspectRatioDenominator = 1,
465 };
466 *nMediaTypes = 1;
467 return (INT16)formatIndex;
468 }
469
470 WLog_ERR(TAG, "I420 is not a supported input format");
471 return -1;
472}
473
474static CAM_ERROR_CODE cam_movie_stream_start(ICamHal* ihal, CameraDevice* dev, size_t streamIndex,
475 const CAM_MEDIA_TYPE_DESCRIPTION* mediaType,
476 ICamHalSampleCapturedCallback callback)
477{
478 CamMovieHal* hal = (CamMovieHal*)ihal;
479 WINPR_ASSERT(hal);
480 WINPR_ASSERT(dev);
481 WINPR_ASSERT(mediaType);
482
483 CamMovieStream* stream = &hal->stream;
484
485 if ((mediaType->Format != CAM_MEDIA_FORMAT_I420) || (mediaType->Width != hal->width) ||
486 (mediaType->Height != hal->height))
487 {
488 WLog_ERR(TAG, "Unexpected media type %u %" PRIu32 "x%" PRIu32, mediaType->Format,
489 mediaType->Width, mediaType->Height);
490 return CAM_ERROR_CODE_InvalidMediaType;
491 }
492
493 EnterCriticalSection(&stream->lock);
494 const BOOL streaming = stream->streaming;
495 LeaveCriticalSection(&stream->lock);
496
497 if (streaming)
498 {
499 WLog_ERR(TAG, "Streaming already in progress, device %s, streamIndex %" PRIuz,
500 dev->deviceId, streamIndex);
501 return CAM_ERROR_CODE_UnexpectedError;
502 }
503
504 /* the thread of a capture released for lack of sample requests */
505 cam_movie_stream_join(stream);
506
507 if (!stream->decoder.format && !cam_movie_decoder_open(&stream->decoder, hal->file))
508 return CAM_ERROR_CODE_UnexpectedError;
509
510 stream->dev = dev;
511 stream->streamIndex = streamIndex;
512 stream->sampleCallback = callback;
513
514 EnterCriticalSection(&stream->lock);
515 stream->streaming = TRUE;
516 LeaveCriticalSection(&stream->lock);
517
518 stream->thread = CreateThread(nullptr, 0, cam_movie_playback_thread, hal, 0, nullptr);
519 if (!stream->thread)
520 {
521 WLog_ERR(TAG, "CreateThread failure");
522 EnterCriticalSection(&stream->lock);
523 stream->streaming = FALSE;
524 LeaveCriticalSection(&stream->lock);
525 return CAM_ERROR_CODE_OutOfMemory;
526 }
527
528 WLog_INFO(TAG, "Playing %s as camera: %" PRIu32 "x%" PRIu32 ", fps: %" PRIu32 "/%" PRIu32,
529 hal->file, hal->width, hal->height, hal->frameRateNumerator,
530 hal->frameRateDenominator);
531 return CAM_ERROR_CODE_None;
532}
533
534static CAM_ERROR_CODE cam_movie_stream_stop(ICamHal* ihal, WINPR_ATTR_UNUSED const char* deviceId,
535 WINPR_ATTR_UNUSED size_t streamIndex)
536{
537 CamMovieHal* hal = (CamMovieHal*)ihal;
538 WINPR_ASSERT(hal);
539
540 cam_movie_stream_join(&hal->stream);
541
542 /* the next stream starts the movie from the beginning */
543 cam_movie_decoder_close(&hal->stream.decoder);
544 return CAM_ERROR_CODE_None;
545}
546
547static CAM_ERROR_CODE cam_movie_free(ICamHal* ihal)
548{
549 CamMovieHal* hal = (CamMovieHal*)ihal;
550
551 if (!hal)
552 return CAM_ERROR_CODE_NotInitialized;
553
554 cam_movie_stream_join(&hal->stream);
555 cam_movie_decoder_close(&hal->stream.decoder);
556 DeleteCriticalSection(&hal->stream.lock);
557
558 free(hal->stream.sample);
559 free(hal->file);
560 free(hal->name);
561 free(hal);
562 return CAM_ERROR_CODE_None;
563}
564
566WINPR_ATTR_NODISCARD
567static BOOL cam_movie_probe(CamMovieHal* hal)
568{
569 WINPR_ASSERT(hal);
570
571 CamMovieDecoder decoder = WINPR_C_ARRAY_INIT;
572 if (!cam_movie_decoder_open(&decoder, hal->file))
573 return FALSE;
574
575 BOOL rc = FALSE;
576 AVStream* stream = decoder.format->streams[decoder.videoIndex];
577 const int width = decoder.codec->width;
578 const int height = decoder.codec->height;
579
580 /* I420 needs even dimensions: crop the last column or row if needed */
581 if ((width < 2) || (height < 2))
582 {
583 WLog_ERR(TAG, "Invalid movie size %dx%d", width, height);
584 goto out;
585 }
586 hal->width = (UINT32)width & ~1u;
587 hal->height = (UINT32)height & ~1u;
588
589 const AVRational rate = av_guess_frame_rate(decoder.format, stream, nullptr);
590 if ((rate.num > 0) && (rate.den > 0))
591 {
592 hal->frameRateNumerator = (UINT32)rate.num;
593 hal->frameRateDenominator = (UINT32)rate.den;
594 }
595 else
596 {
597 WLog_WARN(TAG, "Unknown frame rate, using %d fps", CAM_MOVIE_FRAMERATE_NUMERATOR_DEFAULT);
598 hal->frameRateNumerator = CAM_MOVIE_FRAMERATE_NUMERATOR_DEFAULT;
599 hal->frameRateDenominator = CAM_MOVIE_FRAMERATE_DENOMINATOR_DEFAULT;
600 }
601
602 hal->stream.sampleSize = 3ull * hal->width * hal->height / 2;
603 hal->stream.sample = calloc(1, hal->stream.sampleSize);
604 if (!hal->stream.sample)
605 goto out;
606
607 WLog_INFO(TAG,
608 "Movie %s: %dx%d %s, announced as %" PRIu32 "x%" PRIu32 " at %" PRIu32 "/%" PRIu32
609 " fps",
610 hal->file, width, height, avcodec_get_name(decoder.codec->codec_id), hal->width,
611 hal->height, hal->frameRateNumerator, hal->frameRateDenominator);
612 rc = TRUE;
613
614out:
615 cam_movie_decoder_close(&decoder);
616 return rc;
617}
618
619static const char* cam_movie_basename(const char* path)
620{
621 const char* name = path;
622 for (const char* cur = path; *cur; cur++)
623 {
624 if ((*cur == '/') || (*cur == '\\'))
625 name = cur + 1;
626 }
627 return name;
628}
629
630WINPR_ATTR_NODISCARD
631static BOOL cam_movie_parse_args(CamMovieHal* hal, const ADDIN_ARGV* args)
632{
633 WINPR_ASSERT(hal);
634
635 COMMAND_LINE_ARGUMENT_A movie_args[] = { { "file", COMMAND_LINE_VALUE_REQUIRED, "<path>",
636 nullptr, nullptr, -1, nullptr, "movie to play" },
637 { "name", COMMAND_LINE_VALUE_REQUIRED, "<name>",
638 nullptr, nullptr, -1, nullptr, "camera name" },
639 { nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr,
640 nullptr } };
641
642 if (args && (args->argc > 1))
643 {
644 const DWORD flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON |
645 COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
646 const int status = CommandLineParseArgumentsA(args->argc, args->argv, movie_args, flags,
647 hal, nullptr, nullptr);
648 if (status != 0)
649 return FALSE;
650
651 const COMMAND_LINE_ARGUMENT_A* arg = movie_args;
652 do
653 {
654 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
655 continue;
656
657 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "file")
658 {
659 free(hal->file);
660 hal->file = _strdup(arg->Value);
661 if (!hal->file)
662 return FALSE;
663 }
664 CommandLineSwitchCase(arg, "name")
665 {
666 free(hal->name);
667 hal->name = _strdup(arg->Value);
668 if (!hal->name)
669 return FALSE;
670 }
671 CommandLineSwitchDefault(arg)
672 {
673 }
674 CommandLineSwitchEnd(arg)
675 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
676 }
677
678 if (!hal->file)
679 {
680 WLog_ERR(TAG, "The movie backend needs a file:<path> argument");
681 return FALSE;
682 }
683
684 if (!hal->name)
685 {
686 size_t len = 0;
687 winpr_asprintf(&hal->name, &len, "Movie: %s", cam_movie_basename(hal->file));
688 if (!hal->name)
689 return FALSE;
690 }
691 return TRUE;
692}
693
699FREERDP_ENTRY_POINT(UINT VCAPITYPE movie_freerdp_rdpecam_client_subsystem_entry(
701{
702 UINT ret = ERROR_INVALID_PARAMETER;
703 WINPR_ASSERT(pEntryPoints);
704
705 CamMovieHal* hal = (CamMovieHal*)calloc(1, sizeof(CamMovieHal));
706 if (!hal)
707 return CHANNEL_RC_NO_MEMORY;
708
709 hal->iHal.Enumerate = cam_movie_enumerate;
710 hal->iHal.RequestDrivenCapture = TRUE;
711 hal->iHal.GetMediaTypeDescriptions = cam_movie_get_media_type_descriptions;
712 hal->iHal.Activate = cam_movie_activate;
713 hal->iHal.Deactivate = cam_movie_deactivate;
714 hal->iHal.StartStream = cam_movie_stream_start;
715 hal->iHal.StopStream = cam_movie_stream_stop;
716 hal->iHal.Free = cam_movie_free;
717 hal->stream.decoder.videoIndex = -1;
718
719 if (!InitializeCriticalSectionEx(&hal->stream.lock, 0, 0))
720 {
721 free(hal);
722 return CHANNEL_RC_NO_MEMORY;
723 }
724
725 if (!cam_movie_parse_args(hal, pEntryPoints->args))
726 goto error;
727
728 /* fail now rather than when the server opens the camera */
729 if (!cam_movie_probe(hal))
730 goto error;
731
732 ret = pEntryPoints->pRegisterCameraHal(pEntryPoints->plugin, &hal->iHal);
733 if (ret != CHANNEL_RC_OK)
734 {
735 WLog_ERR(TAG, "RegisterCameraHal failed with error %" PRIu32 "", ret);
736 goto error;
737 }
738
739 return ret;
740
741error:
742 cam_movie_free(&hal->iHal);
743 return ret;
744}
Definition camera.h:237