1 /*
2 * Copyright 2004-2008, François Revol, <revol@free.fr>.
3 * Distributed under the terms of the MIT License.
4 */
5
6 #include <fcntl.h>
7 #include <malloc.h>
8 #include <math.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <sys/uio.h>
12 #include <unistd.h>
13
14 #include <media/Buffer.h>
15 #include <media/BufferGroup.h>
16 #include <media/ParameterWeb.h>
17 #include <media/TimeSource.h>
18
19 #include <support/Autolock.h>
20 #include <support/Debug.h>
21
22 //XXX: change interface
23 #include <interface/Bitmap.h>
24
25 #include "CamDevice.h"
26 #include "CamSensor.h"
27
28 // don't separate parameters from addon, device and sensor
29 #define SINGLE_PARAMETER_GROUP 1
30
31 // CodyCam and eXposer prefer 320x240
32 #define FORCE_320_240 1
33 //#define FORCE_160_120 1
34 //#define FORCE_MAX_FRAME 1
35
36 #define TOUCH(x) ((void)(x))
37
38 #define PRINTF(a,b) \
39 do { \
40 if (a < 2) { \
41 printf("VideoProducer::"); \
42 printf b; \
43 } \
44 } while (0)
45
46 #include "Producer.h"
47
48 //#define FIELD_RATE 30.f
49 //#define FIELD_RATE 29.97f
50 #define FIELD_RATE 5.f
51
52
53 int32 VideoProducer::fInstances = 0;
54
55
VideoProducer(BMediaAddOn * addon,CamDevice * dev,const char * name,int32 internal_id)56 VideoProducer::VideoProducer(
57 BMediaAddOn *addon, CamDevice *dev, const char *name, int32 internal_id)
58 : BMediaNode(name),
59 BMediaEventLooper(),
60 BBufferProducer(B_MEDIA_RAW_VIDEO),
61 BControllable()
62 {
63 // status_t err;
64
65 fInitStatus = B_NO_INIT;
66
67 /* Only allow one instance of the node to exist at any time */
68 if (atomic_add(&fInstances, 1) != 0)
69 return;
70
71 fInternalID = internal_id;
72 fAddOn = addon;
73 fCamDevice = dev;
74
75 fBufferGroup = NULL;
76
77 fThread = -1;
78 fFrameSync = -1;
79 fProcessingLatency = 0LL;
80
81 fRunning = false;
82 fConnected = false;
83 fEnabled = false;
84
85 fOutput.destination = media_destination::null;
86
87 AddNodeKind(B_PHYSICAL_INPUT);
88
89 fInitStatus = B_OK;
90 return;
91 }
92
93
~VideoProducer()94 VideoProducer::~VideoProducer()
95 {
96 if (fInitStatus == B_OK) {
97 /* Clean up after ourselves, in case the application didn't make us
98 * do so. */
99 if (fConnected)
100 Disconnect(fOutput.source, fOutput.destination);
101 if (fRunning)
102 HandleStop();
103 }
104
105 atomic_add(&fInstances, -1);
106 }
107
108
109 /* BMediaNode */
110 port_id
ControlPort() const111 VideoProducer::ControlPort() const
112 {
113 return BMediaNode::ControlPort();
114 }
115
116
117 BMediaAddOn *
AddOn(int32 * internal_id) const118 VideoProducer::AddOn(int32 *internal_id) const
119 {
120 if (internal_id)
121 *internal_id = fInternalID;
122 return fAddOn;
123 }
124
125
126 status_t
HandleMessage(int32,const void *,size_t)127 VideoProducer::HandleMessage(int32 /*message*/, const void* /*data*/, size_t /*size*/)
128 {
129 return B_ERROR;
130 }
131
132
133 void
Preroll()134 VideoProducer::Preroll()
135 {
136 /* This hook may be called before the node is started to give the hardware
137 * a chance to start. */
138 }
139
140
141 void
SetTimeSource(BTimeSource *)142 VideoProducer::SetTimeSource(BTimeSource* /*time_source*/)
143 {
144 /* Tell frame generation thread to recalculate delay value */
145 release_sem(fFrameSync);
146 }
147
148
149 status_t
RequestCompleted(const media_request_info & info)150 VideoProducer::RequestCompleted(const media_request_info &info)
151 {
152 return BMediaNode::RequestCompleted(info);
153 }
154
155
156 /* BMediaEventLooper */
157
158
159 void
NodeRegistered()160 VideoProducer::NodeRegistered()
161 {
162 if (fInitStatus != B_OK) {
163 ReportError(B_NODE_IN_DISTRESS);
164 return;
165 }
166
167 /* Set up the parameter web */
168
169 //TODO: remove and put sensible stuff there
170 BParameterWeb *web = new BParameterWeb();
171 BParameterGroup *main = web->MakeGroup(Name());
172 BParameterGroup *g;
173
174 /*
175 g = main->MakeGroup("Color");
176 BDiscreteParameter *state = g->MakeDiscreteParameter(
177 P_COLOR, B_MEDIA_RAW_VIDEO, "Color", "Color");
178 state->AddItem(B_HOST_TO_LENDIAN_INT32(0x00ff0000), "Red");
179 state->AddItem(B_HOST_TO_LENDIAN_INT32(0x0000ff00), "Green");
180 state->AddItem(B_HOST_TO_LENDIAN_INT32(0x000000ff), "Blue");
181 */
182
183 BParameter *p;
184 g = main->MakeGroup("Info");
185 p = g->MakeTextParameter(
186 P_INFO, B_MEDIA_RAW_VIDEO, "", "Info", 256);
187
188 int32 id = P_LAST;
189 if (fCamDevice) {
190 #ifndef SINGLE_PARAMETER_GROUP
191 main = web->MakeGroup("Device");
192 #endif
193 fCamDevice->AddParameters(main, id);
194 if (fCamDevice->Sensor()) {
195 #ifndef SINGLE_PARAMETER_GROUP
196 main = web->MakeGroup("Sensor");
197 #endif
198 fCamDevice->Sensor()->AddParameters(main, id);
199 }
200 }
201
202 fColor = B_HOST_TO_LENDIAN_INT32(0x00ff0000);
203 fLastColorChange = system_time();
204
205 /* After this call, the BControllable owns the BParameterWeb object and
206 * will delete it for you */
207 SetParameterWeb(web);
208
209 fOutput.node = Node();
210 fOutput.source.port = ControlPort();
211 fOutput.source.id = 0;
212 fOutput.destination = media_destination::null;
213 strcpy(fOutput.name, Name());
214
215 /* Tailor these for the output of your device */
216 fOutput.format.type = B_MEDIA_RAW_VIDEO;
217 fOutput.format.u.raw_video = media_raw_video_format::wildcard;
218 fOutput.format.u.raw_video.interlace = 1;
219 fOutput.format.u.raw_video.display.format = B_RGB32;
220 fOutput.format.u.raw_video.field_rate = FIELD_RATE; // XXX: mmu
221
222 /* Start the BMediaEventLooper control loop running */
223 Run();
224 }
225
226
227 void
Start(bigtime_t performance_time)228 VideoProducer::Start(bigtime_t performance_time)
229 {
230 BMediaEventLooper::Start(performance_time);
231 }
232
233
234 void
Stop(bigtime_t performance_time,bool immediate)235 VideoProducer::Stop(bigtime_t performance_time, bool immediate)
236 {
237 BMediaEventLooper::Stop(performance_time, immediate);
238 }
239
240
241 void
Seek(bigtime_t media_time,bigtime_t performance_time)242 VideoProducer::Seek(bigtime_t media_time, bigtime_t performance_time)
243 {
244 BMediaEventLooper::Seek(media_time, performance_time);
245 }
246
247
248 void
TimeWarp(bigtime_t at_real_time,bigtime_t to_performance_time)249 VideoProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
250 {
251 BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
252 }
253
254
255 status_t
AddTimer(bigtime_t at_performance_time,int32 cookie)256 VideoProducer::AddTimer(bigtime_t at_performance_time, int32 cookie)
257 {
258 return BMediaEventLooper::AddTimer(at_performance_time, cookie);
259 }
260
261
262 void
SetRunMode(run_mode mode)263 VideoProducer::SetRunMode(run_mode mode)
264 {
265 BMediaEventLooper::SetRunMode(mode);
266 }
267
268
269 void
HandleEvent(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)270 VideoProducer::HandleEvent(const media_timed_event *event,
271 bigtime_t lateness, bool realTimeEvent)
272 {
273 TOUCH(lateness); TOUCH(realTimeEvent);
274
275 switch(event->type) {
276 case BTimedEventQueue::B_START:
277 HandleStart(event->event_time);
278 break;
279 case BTimedEventQueue::B_STOP:
280 HandleStop();
281 break;
282 case BTimedEventQueue::B_WARP:
283 HandleTimeWarp(event->bigdata);
284 break;
285 case BTimedEventQueue::B_SEEK:
286 HandleSeek(event->bigdata);
287 break;
288 case BTimedEventQueue::B_HANDLE_BUFFER:
289 case BTimedEventQueue::B_DATA_STATUS:
290 case BTimedEventQueue::B_PARAMETER:
291 default:
292 PRINTF(-1, ("HandleEvent: Unhandled event -- %" B_PRIx32 "\n",
293 event->type));
294 break;
295 }
296 }
297
298
299 void
CleanUpEvent(const media_timed_event * event)300 VideoProducer::CleanUpEvent(const media_timed_event *event)
301 {
302 BMediaEventLooper::CleanUpEvent(event);
303 }
304
305
306 bigtime_t
OfflineTime()307 VideoProducer::OfflineTime()
308 {
309 return BMediaEventLooper::OfflineTime();
310 }
311
312
313 void
ControlLoop()314 VideoProducer::ControlLoop()
315 {
316 BMediaEventLooper::ControlLoop();
317 }
318
319
320 status_t
DeleteHook(BMediaNode * node)321 VideoProducer::DeleteHook(BMediaNode * node)
322 {
323 return BMediaEventLooper::DeleteHook(node);
324 }
325
326
327 /* BBufferProducer */
328
329
330 status_t
FormatSuggestionRequested(media_type type,int32 quality,media_format * format)331 VideoProducer::FormatSuggestionRequested(
332 media_type type, int32 quality, media_format *format)
333 {
334 if (type != B_MEDIA_ENCODED_VIDEO)
335 return B_MEDIA_BAD_FORMAT;
336
337 TOUCH(quality);
338
339 PRINTF(1, ("FormatSuggestionRequested() %" B_PRIu32 "x%" B_PRIu32 "\n", \
340 format->u.raw_video.display.line_width, \
341 format->u.raw_video.display.line_count));
342
343 *format = fOutput.format;
344 uint32 width, height;
345 if (fCamDevice && fCamDevice->SuggestVideoFrame(width, height) == B_OK) {
346 format->u.raw_video.display.line_width = width;
347 format->u.raw_video.display.line_count = height;
348 }
349 format->u.raw_video.field_rate = FIELD_RATE;
350 return B_OK;
351 }
352
353
354 status_t
FormatProposal(const media_source & output,media_format * format)355 VideoProducer::FormatProposal(const media_source &output, media_format *format)
356 {
357 status_t err;
358
359 if (!format)
360 return B_BAD_VALUE;
361
362 if (output != fOutput.source)
363 return B_MEDIA_BAD_SOURCE;
364
365 PRINTF(1, ("FormatProposal() %" B_PRIu32 "x%" B_PRIu32 "\n", \
366 format->u.raw_video.display.line_width, \
367 format->u.raw_video.display.line_count));
368
369 err = format_is_compatible(*format, fOutput.format) ?
370 B_OK : B_MEDIA_BAD_FORMAT;
371
372 uint32 width = format->u.raw_video.display.line_width;
373 uint32 height = format->u.raw_video.display.line_count;
374
375 *format = fOutput.format;
376
377 if (err == B_OK && fCamDevice) {
378 err = fCamDevice->AcceptVideoFrame(width, height);
379 if (err >= B_OK) {
380 format->u.raw_video.display.line_width = width;
381 format->u.raw_video.display.line_count = height;
382 }
383 }
384
385 PRINTF(1, ("FormatProposal: %" B_PRIu32 "x%" B_PRIu32 "\n", \
386 format->u.raw_video.display.line_width, \
387 format->u.raw_video.display.line_count));
388
389 return err;
390
391 }
392
393
394 status_t
FormatChangeRequested(const media_source & source,const media_destination & destination,media_format * io_format,int32 * _deprecated_)395 VideoProducer::FormatChangeRequested(const media_source &source,
396 const media_destination &destination, media_format *io_format,
397 int32 *_deprecated_)
398 {
399 TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_);
400 if (source != fOutput.source)
401 return B_MEDIA_BAD_SOURCE;
402
403 return B_ERROR;
404 }
405
406
407 status_t
GetNextOutput(int32 * cookie,media_output * out_output)408 VideoProducer::GetNextOutput(int32 *cookie, media_output *out_output)
409 {
410 if (!out_output)
411 return B_BAD_VALUE;
412
413 if ((*cookie) != 0)
414 return B_BAD_INDEX;
415
416 *out_output = fOutput;
417 (*cookie)++;
418 return B_OK;
419 }
420
421
422 status_t
DisposeOutputCookie(int32 cookie)423 VideoProducer::DisposeOutputCookie(int32 cookie)
424 {
425 TOUCH(cookie);
426
427 return B_OK;
428 }
429
430
431 status_t
SetBufferGroup(const media_source & for_source,BBufferGroup * group)432 VideoProducer::SetBufferGroup(const media_source &for_source,
433 BBufferGroup *group)
434 {
435 TOUCH(for_source); TOUCH(group);
436
437 return B_ERROR;
438 }
439
440
441 status_t
VideoClippingChanged(const media_source & for_source,int16 num_shorts,int16 * clip_data,const media_video_display_info & display,int32 * _deprecated_)442 VideoProducer::VideoClippingChanged(const media_source &for_source,
443 int16 num_shorts, int16 *clip_data,
444 const media_video_display_info &display, int32 *_deprecated_)
445 {
446 TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data);
447 TOUCH(display); TOUCH(_deprecated_);
448
449 return B_ERROR;
450 }
451
452
453 status_t
GetLatency(bigtime_t * out_latency)454 VideoProducer::GetLatency(bigtime_t *out_latency)
455 {
456 *out_latency = EventLatency() + SchedulingLatency();
457 return B_OK;
458 }
459
460
461 status_t
PrepareToConnect(const media_source & source,const media_destination & destination,media_format * format,media_source * out_source,char * out_name)462 VideoProducer::PrepareToConnect(const media_source &source,
463 const media_destination &destination, media_format *format,
464 media_source *out_source, char *out_name)
465 {
466 status_t err;
467
468 PRINTF(1, ("PrepareToConnect() %" B_PRIu32 "x%" B_PRIu32 "\n", \
469 format->u.raw_video.display.line_width, \
470 format->u.raw_video.display.line_count));
471
472 if (fConnected) {
473 PRINTF(0, ("PrepareToConnect: Already connected\n"));
474 return EALREADY;
475 }
476
477 if (source != fOutput.source)
478 return B_MEDIA_BAD_SOURCE;
479
480 if (fOutput.destination != media_destination::null)
481 return B_MEDIA_ALREADY_CONNECTED;
482
483 /* The format parameter comes in with the suggested format, and may be
484 * specialized as desired by the node */
485 if (!format_is_compatible(*format, fOutput.format)) {
486 *format = fOutput.format;
487 return B_MEDIA_BAD_FORMAT;
488 }
489
490 //XXX:FIXME
491 #if 0
492 // if (format->u.raw_video.display.line_width == 0)
493 format->u.raw_video.display.line_width = 352;//320;
494 format->u.raw_video.display.line_width = 320;
495 // if (format->u.raw_video.display.line_count == 0)
496 format->u.raw_video.display.line_count = 288;//240;
497 format->u.raw_video.display.line_count = 240;
498 #endif
499
500 #ifdef FORCE_320_240
501 {
502 format->u.raw_video.display.line_width = 320;
503 format->u.raw_video.display.line_count = 240;
504 }
505 #endif
506 #ifdef FORCE_160_120
507 {
508 format->u.raw_video.display.line_width = 160;
509 format->u.raw_video.display.line_count = 120;
510 }
511 #endif
512 #ifdef FORCE_MAX_FRAME
513 {
514 format->u.raw_video.display.line_width = 0;
515 format->u.raw_video.display.line_count = 0;
516 }
517 #endif
518 if (fCamDevice) {
519 err = fCamDevice->AcceptVideoFrame(
520 format->u.raw_video.display.line_width,
521 format->u.raw_video.display.line_count);
522 if (err < B_OK)
523 return err;
524 }
525
526 if (format->u.raw_video.field_rate == 0)
527 format->u.raw_video.field_rate = FIELD_RATE;
528
529 *out_source = fOutput.source;
530 strcpy(out_name, fOutput.name);
531
532 fOutput.destination = destination;
533
534 return B_OK;
535 }
536
537
538 void
Connect(status_t error,const media_source & source,const media_destination & destination,const media_format & format,char * io_name)539 VideoProducer::Connect(status_t error, const media_source &source,
540 const media_destination &destination, const media_format &format,
541 char *io_name)
542 {
543 PRINTF(1, ("Connect() %" B_PRIu32 "x%" B_PRIu32 "\n", \
544 format.u.raw_video.display.line_width, \
545 format.u.raw_video.display.line_count));
546
547 if (fConnected) {
548 PRINTF(0, ("Connect: Already connected\n"));
549 return;
550 }
551
552 BAutolock lock(fCamDevice->Locker());
553 if (!fCamDevice->IsPlugged()) {
554 PRINTF(0, ("Connect: Device unplugged\n"));
555 return;
556 }
557
558 if (source != fOutput.source || error < B_OK
559 || !const_cast<media_format *>(&format)->Matches(&fOutput.format)) {
560 PRINTF(1, ("Connect: Connect error\n"));
561 return;
562 }
563
564 fOutput.destination = destination;
565 strcpy(io_name, fOutput.name);
566
567 if (fOutput.format.u.raw_video.field_rate != 0.0f) {
568 fPerformanceTimeBase = fPerformanceTimeBase +
569 (bigtime_t)
570 ((fFrame - fFrameBase) *
571 (1000000 / fOutput.format.u.raw_video.field_rate));
572 fFrameBase = fFrame;
573 }
574
575 fConnectedFormat = format.u.raw_video;
576
577 /* get the latency */
578 bigtime_t latency = 0;
579 media_node_id tsID = 0;
580 FindLatencyFor(fOutput.destination, &latency, &tsID);
581 #define NODE_LATENCY 1000
582 SetEventLatency(latency + NODE_LATENCY);
583
584 uint32 *buffer, *p, f = 3;
585 p = buffer = (uint32 *)malloc(4 * fConnectedFormat.display.line_count *
586 fConnectedFormat.display.line_width);
587 if (!buffer) {
588 PRINTF(0, ("Connect: Out of memory\n"));
589 return;
590 }
591 bigtime_t now = system_time();
592 for (uint32 y=0;y<fConnectedFormat.display.line_count;y++)
593 for (uint32 x=0;x<fConnectedFormat.display.line_width;x++)
594 *(p++) = ((((x+y)^0^x)+f) & 0xff) * (0x01010101 & fColor);
595 fProcessingLatency = system_time() - now;
596 free(buffer);
597
598 /* Create the buffer group */
599 fBufferGroup = new BBufferGroup(4 * fConnectedFormat.display.line_width *
600 fConnectedFormat.display.line_count, 8);
601 if (fBufferGroup->InitCheck() < B_OK) {
602 delete fBufferGroup;
603 fBufferGroup = NULL;
604 return;
605 }
606
607 fConnected = true;
608 fEnabled = true;
609
610 /* Tell frame generation thread to recalculate delay value */
611 release_sem(fFrameSync);
612 }
613
614 void
Disconnect(const media_source & source,const media_destination & destination)615 VideoProducer::Disconnect(const media_source &source,
616 const media_destination &destination)
617 {
618 PRINTF(1, ("Disconnect()\n"));
619
620 if (!fConnected) {
621 PRINTF(0, ("Disconnect: Not connected\n"));
622 return;
623 }
624
625 if ((source != fOutput.source) || (destination != fOutput.destination)) {
626 PRINTF(0, ("Disconnect: Bad source and/or destination\n"));
627 return;
628 }
629
630 #if 1
631 /* Some dumb apps don't stop nodes before disconnecting... */
632 if (fRunning)
633 HandleStop();
634 #endif
635
636 fEnabled = false;
637 fOutput.destination = media_destination::null;
638
639 fLock.Lock();
640 delete fBufferGroup;
641 fBufferGroup = NULL;
642 fLock.Unlock();
643
644 fConnected = false;
645 }
646
647
648 void
LateNoticeReceived(const media_source & source,bigtime_t how_much,bigtime_t performance_time)649 VideoProducer::LateNoticeReceived(const media_source &source,
650 bigtime_t how_much, bigtime_t performance_time)
651 {
652 TOUCH(source); TOUCH(how_much); TOUCH(performance_time);
653 }
654
655
656 void
EnableOutput(const media_source & source,bool enabled,int32 * _deprecated_)657 VideoProducer::EnableOutput(const media_source &source, bool enabled,
658 int32 *_deprecated_)
659 {
660 TOUCH(_deprecated_);
661
662 if (source != fOutput.source)
663 return;
664
665 fEnabled = enabled;
666 }
667
668
669 status_t
SetPlayRate(int32 numer,int32 denom)670 VideoProducer::SetPlayRate(int32 numer, int32 denom)
671 {
672 TOUCH(numer); TOUCH(denom);
673
674 return B_ERROR;
675 }
676
677
678 void
AdditionalBufferRequested(const media_source & source,media_buffer_id prev_buffer,bigtime_t prev_time,const media_seek_tag * prev_tag)679 VideoProducer::AdditionalBufferRequested(const media_source &source,
680 media_buffer_id prev_buffer, bigtime_t prev_time,
681 const media_seek_tag *prev_tag)
682 {
683 TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag);
684 }
685
686
687 void
LatencyChanged(const media_source & source,const media_destination & destination,bigtime_t new_latency,uint32 flags)688 VideoProducer::LatencyChanged(const media_source &source,
689 const media_destination &destination, bigtime_t new_latency,
690 uint32 flags)
691 {
692 TOUCH(source); TOUCH(destination); TOUCH(new_latency); TOUCH(flags);
693 }
694
695
696 /* BControllable */
697
698
699 status_t
GetParameterValue(int32 id,bigtime_t * last_change,void * value,size_t * size)700 VideoProducer::GetParameterValue(
701 int32 id, bigtime_t *last_change, void *value, size_t *size)
702 {
703 status_t err;
704
705 switch (id) {
706 case P_COLOR:
707 //return B_BAD_VALUE;
708
709 *last_change = fLastColorChange;
710 *size = sizeof(uint32);
711 *((uint32 *)value) = fColor;
712 return B_OK;
713 case P_INFO:
714 if (*size < (size_t)(fInfoString.Length() + 1))
715 return EINVAL;
716 *last_change = fLastColorChange;
717 *size = fInfoString.Length() + 1;
718 memcpy(value, fInfoString.String(), *size);
719 return B_OK;
720 }
721
722 if (fCamDevice) {
723 BAutolock lock(fCamDevice->Locker());
724 err = fCamDevice->GetParameterValue(id, last_change, value, size);
725 if (err >= B_OK)
726 return err;
727 if (fCamDevice->Sensor()) {
728 err = fCamDevice->Sensor()->GetParameterValue(id, last_change, value, size);
729 if (err >= B_OK)
730 return err;
731 }
732 }
733
734 return B_BAD_VALUE;
735 }
736
737
738 void
SetParameterValue(int32 id,bigtime_t when,const void * value,size_t size)739 VideoProducer::SetParameterValue(
740 int32 id, bigtime_t when, const void *value, size_t size)
741 {
742 status_t err = B_OK;
743
744 switch (id) {
745 case P_COLOR:
746 if (!value || (size != sizeof(uint32)))
747 return;
748
749 if (*(uint32 *)value == fColor)
750 return;
751
752 fColor = *(uint32 *)value;
753 fLastColorChange = when;
754 break;
755 case P_INFO:
756 // forbidden
757 return;
758 default:
759 if (fCamDevice == NULL)
760 return;
761
762 BAutolock lock(fCamDevice->Locker());
763 err = fCamDevice->SetParameterValue(id, when, value, size);
764 if ((err < B_OK) && (fCamDevice->Sensor())) {
765 err = fCamDevice->Sensor()->SetParameterValue(id, when, value, size);
766 }
767 }
768
769 if (err >= B_OK)
770 BroadcastNewParameterValue(when, id, (void *)value, size);
771 }
772
773
774 status_t
StartControlPanel(BMessenger * out_messenger)775 VideoProducer::StartControlPanel(BMessenger *out_messenger)
776 {
777 return BControllable::StartControlPanel(out_messenger);
778 }
779
780
781 /* VideoProducer */
782
783
784 void
HandleStart(bigtime_t performance_time)785 VideoProducer::HandleStart(bigtime_t performance_time)
786 {
787 /* Start producing frames, even if the output hasn't been connected yet. */
788
789 PRINTF(1, ("HandleStart(%" B_PRIdBIGTIME ")\n", performance_time));
790
791 if (fRunning) {
792 PRINTF(-1, ("HandleStart: Node already started\n"));
793 return;
794 }
795
796 fFrame = 0;
797 fFrameBase = 0;
798 fPerformanceTimeBase = performance_time;
799
800 fFrameSync = create_sem(0, "frame synchronization");
801 if (fFrameSync < B_OK)
802 goto err1;
803
804 fThread = spawn_thread(_frame_generator_, "frame generator",
805 B_NORMAL_PRIORITY, this);
806 if (fThread < B_OK)
807 goto err2;
808
809 resume_thread(fThread);
810
811 {
812 BAutolock lock(fCamDevice->Locker());
813 fCamDevice->StartTransfer();
814 }
815
816 fRunning = true;
817 return;
818
819 err2:
820 delete_sem(fFrameSync);
821 err1:
822 return;
823 }
824
825
826 void
HandleStop(void)827 VideoProducer::HandleStop(void)
828 {
829 PRINTF(1, ("HandleStop()\n"));
830
831 if (!fRunning) {
832 PRINTF(-1, ("HandleStop: Node isn't running\n"));
833 return;
834 }
835
836 delete_sem(fFrameSync);
837 wait_for_thread(fThread, &fThread);
838
839 BAutolock lock(fCamDevice->Locker());
840 fCamDevice->StopTransfer();
841
842 fRunning = false;
843 }
844
845
846 void
HandleTimeWarp(bigtime_t performance_time)847 VideoProducer::HandleTimeWarp(bigtime_t performance_time)
848 {
849 fPerformanceTimeBase = performance_time;
850 fFrameBase = fFrame;
851
852 /* Tell frame generation thread to recalculate delay value */
853 release_sem(fFrameSync);
854 }
855
856
857 void
HandleSeek(bigtime_t performance_time)858 VideoProducer::HandleSeek(bigtime_t performance_time)
859 {
860 fPerformanceTimeBase = performance_time;
861 fFrameBase = fFrame;
862
863 /* Tell frame generation thread to recalculate delay value */
864 release_sem(fFrameSync);
865 }
866
867
868 void
_UpdateStats()869 VideoProducer::_UpdateStats()
870 {
871 float fps = (fStats[0].frames - fStats[1].frames) * 1000000LL
872 / (double)(fStats[0].stamp - fStats[1].stamp);
873 float rfps = (fStats[0].actual - fStats[1].actual) * 1000000LL
874 / (double)(fStats[0].stamp - fStats[1].stamp);
875 fInfoString = "FPS: ";
876 fInfoString << fps << " virt, "
877 << rfps << " real, missed: " << fStats[0].missed;
878 memcpy(&fStats[1], &fStats[0], sizeof(fStats[0]));
879 fLastColorChange = system_time();
880 BroadcastNewParameterValue(fLastColorChange, P_INFO,
881 (void *)fInfoString.String(), fInfoString.Length()+1);
882 }
883
884
885 /* The following functions form the thread that generates frames. You should
886 * replace this with the code that interfaces to your hardware. */
887 int32
FrameGenerator()888 VideoProducer::FrameGenerator()
889 {
890 bigtime_t wait_until = system_time();
891
892 while (1) {
893 PRINTF(1, ("FrameGenerator: " \
894 "acquire_sem_etc() until %" B_PRIdBIGTIME "µs " \
895 "(in %" B_PRIdBIGTIME "µs)\n", \
896 wait_until, wait_until - system_time()));
897 status_t err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT,
898 wait_until);
899
900 /* The only acceptable responses are B_OK and B_TIMED_OUT. Everything
901 * else means the thread should quit. Deleting the semaphore, as in
902 * VideoProducer::HandleStop(), will trigger this behavior. */
903 if ((err != B_OK) && (err != B_TIMED_OUT))
904 break;
905
906 fFrame++;
907
908 /* Recalculate the time until the thread should wake up to begin
909 * processing the next frame. Subtract fProcessingLatency so that
910 * the frame is sent in time. */
911 wait_until = TimeSource()->RealTimeFor(fPerformanceTimeBase, 0) +
912 (bigtime_t)
913 ((fFrame - fFrameBase) *
914 (1000000 / fConnectedFormat.field_rate)) -
915 fProcessingLatency;
916 PRINT(("PS: %" B_PRIdBIGTIME "\n", fProcessingLatency));
917
918 /* Drop frame if it's at least a frame late */
919 if (wait_until < system_time())
920 continue;
921
922 PRINTF(1, ("FrameGenerator: wait until %" B_PRIdBIGTIME ", "
923 "%ctimed out, %crunning, %cenabled.\n",
924 wait_until,
925 (err == B_OK)?'!':' ',
926 (fRunning)?' ':'!',
927 (fEnabled)?' ':'!'));
928
929 /* If the semaphore was acquired successfully, it means something
930 * changed the timing information (see VideoProducer::Connect()) and
931 * so the thread should go back to sleep until the newly-calculated
932 * wait_until time. */
933 if (err == B_OK)
934 continue;
935
936 /* Send buffers only if the node is running and the output has been
937 * enabled */
938 if (!fRunning || !fEnabled)
939 continue;
940
941 BAutolock _(fLock);
942
943 /* Fetch a buffer from the buffer group */
944 BBuffer *buffer = fBufferGroup->RequestBuffer(
945 4 * fConnectedFormat.display.line_width *
946 fConnectedFormat.display.line_count, 0LL);
947 if (!buffer)
948 continue;
949
950 /* Fill out the details about this buffer. */
951 media_header *h = buffer->Header();
952 h->type = B_MEDIA_RAW_VIDEO;
953 h->time_source = TimeSource()->ID();
954 h->size_used = 4 * fConnectedFormat.display.line_width *
955 fConnectedFormat.display.line_count;
956 /* For a buffer originating from a device, you might want to calculate
957 * this based on the PerformanceTimeFor the time your buffer arrived at
958 * the hardware (plus any applicable adjustments). */
959 /*
960 h->start_time = fPerformanceTimeBase +
961 (bigtime_t)
962 ((fFrame - fFrameBase) *
963 (1000000 / fConnectedFormat.field_rate));
964 */
965 h->file_pos = 0;
966 h->orig_size = 0;
967 h->data_offset = 0;
968 h->u.raw_video.field_gamma = 1.0;
969 h->u.raw_video.field_sequence = fFrame;
970 h->u.raw_video.field_number = 0;
971 h->u.raw_video.pulldown_number = 0;
972 h->u.raw_video.first_active_line = 1;
973 h->u.raw_video.line_count = fConnectedFormat.display.line_count;
974
975 // This is where we fill the video buffer.
976
977 #if 0
978 uint32 *p = (uint32 *)buffer->Data();
979 /* Fill in a pattern */
980 for (uint32 y=0;y<fConnectedFormat.display.line_count;y++)
981 for (uint32 x=0;x<fConnectedFormat.display.line_width;x++)
982 *(p++) = ((((x+y)^0^x)+fFrame) & 0xff) * (0x01010101 & fColor);
983 #endif
984
985 //NO! must be called without lock!
986 //BAutolock lock(fCamDevice->Locker());
987
988 bigtime_t now = system_time();
989 bigtime_t stamp;
990 //#ifdef UseFillFrameBuffer
991 err = fCamDevice->FillFrameBuffer(buffer, &stamp);
992 if (err < B_OK) {
993 ;//XXX handle error
994 fStats[0].missed++;
995 }
996 //#endif
997 #ifdef UseGetFrameBitmap
998 BBitmap *bm;
999 err = fCamDevice->GetFrameBitmap(&bm, &stamp);
1000 if (err >= B_OK) {
1001 ;//XXX handle error
1002 fStats[0].missed++;
1003 }
1004 #endif
1005 fStats[0].frames = fFrame;
1006 fStats[0].actual++;;
1007 fStats[0].stamp = system_time();
1008
1009 //PRINTF(1, ("FrameGenerator: stamp %lld vs %lld\n", stamp, h->start_time));
1010 //XXX: that's what we should be doing, but CodyCam drops all frames as they are late. (maybe add latency ??)
1011 //h->start_time = TimeSource()->PerformanceTimeFor(stamp);
1012 h->start_time = TimeSource()->PerformanceTimeFor(system_time());
1013
1014
1015 // update processing latency
1016 // XXX: should I ??
1017 fProcessingLatency = system_time() - now;
1018 fProcessingLatency /= 10;
1019
1020 PRINTF(1, ("FrameGenerator: SendBuffer...\n"));
1021 /* Send the buffer on down to the consumer */
1022 if (SendBuffer(buffer, fOutput.source, fOutput.destination) < B_OK) {
1023 PRINTF(-1, ("FrameGenerator: Error sending buffer\n"));
1024 /* If there is a problem sending the buffer, return it to its
1025 * buffer group. */
1026 buffer->Recycle();
1027 }
1028
1029 _UpdateStats();
1030 }
1031
1032 PRINTF(1, ("FrameGenerator: thread existed.\n"));
1033 return B_OK;
1034 }
1035
1036
1037 int32
_frame_generator_(void * data)1038 VideoProducer::_frame_generator_(void *data)
1039 {
1040 return ((VideoProducer *)data)->FrameGenerator();
1041 }
1042