xref: /haiku/src/add-ons/media/media-add-ons/firewire_dv/FireWireDVNode.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
1 /*
2  * FireWire DV media addon for Haiku
3  *
4  * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn)
5  * Distributed under the terms of the MIT License.
6  *
7  * Based on DVB media addon
8  * Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
9  */
10 
11 
12 #include "FireWireDVNode.h"
13 
14 #include <fcntl.h>
15 #include <malloc.h>
16 #include <math.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/uio.h>
21 #include <unistd.h>
22 
23 #include <Autolock.h>
24 #include <Buffer.h>
25 #include <BufferGroup.h>
26 #include <Debug.h>
27 #include <Directory.h>
28 #include <Entry.h>
29 #include <MediaRoster.h>
30 #include <ParameterWeb.h>
31 #include <Path.h>
32 #include <TimeSource.h>
33 #include <String.h>
34 
35 #include "FireWireDVNode.h"
36 #include "FireWireCard.h"
37 #include "debug.h"
38 
39 #define REVISION		"unknown"
40 #define VERSION			"1.0"
41 #define BUILD	 		__DATE__ " " __TIME__
42 
43 // debugging
44 #ifdef TRACE
45 #	undef TRACE
46 #endif
47 //#define TRACE_FIREWIRE_NODE
48 #ifdef TRACE_FIREWIRE_NODE
49 #	define TRACE(x...)		printf(x)
50 #else
51 #	define TRACE(x...)
52 #endif
53 
54 #define RETURN_IF_ERROR(expr) { status_t e = (expr); if (e != B_OK) return e; }
55 
56 #define M_REFRESH_PARAMETER_WEB 		(BTimedEventQueue::B_USER_EVENT + 1)
57 
58 FireWireDVNode::FireWireDVNode(BMediaAddOn* addon, const char* name,
59 	int32 internal_id, FireWireCard* card)
60 	: BMediaNode(name),
61 	BBufferProducer(B_MEDIA_ENCODED_VIDEO),
62 	BControllable(),
63 	BMediaEventLooper(),
64 	fOutputEnabledEncVideo(false),
65 	fCard(card),
66 	fCaptureThreadsActive(false),
67 	fThreadIdCardReader(-1),
68 	fTerminateThreads(false),
69 	fBufferGroupEncVideo(0),
70 	fCaptureActive(false)
71 {
72 	CALLED();
73 
74 	AddNodeKind(B_PHYSICAL_INPUT);
75 //	AddNodeKind(B_PHYSICAL_OUTPUT);
76 
77 	fInternalID = internal_id;
78 	fAddOn = addon;
79 
80 	fInitStatus = B_OK;
81 
82 	fDefaultFormatEncVideo.type = B_MEDIA_ENCODED_VIDEO;
83 }
84 
85 
86 FireWireDVNode::~FireWireDVNode()
87 {
88 	CALLED();
89 
90 	StopCapture();
91 	fWeb = NULL;
92 }
93 
94 
95 /* BMediaNode */
96 BMediaAddOn*
97 FireWireDVNode::AddOn(int32* internal_id) const
98 {
99 	if (internal_id)
100 		*internal_id = fInternalID;
101 	return fAddOn;
102 }
103 
104 
105 status_t
106 FireWireDVNode::HandleMessage(int32 message, const void* data, size_t size)
107 {
108 	return B_ERROR;
109 }
110 
111 
112 void
113 FireWireDVNode::Preroll()
114 {
115 	/* This hook may be called before the node is started to give the hardware
116 	 * a chance to start. */
117 }
118 
119 
120 void
121 FireWireDVNode::SetTimeSource(BTimeSource* time_source)
122 {
123 	CALLED();
124 }
125 
126 
127 void
128 FireWireDVNode::SetRunMode(run_mode mode)
129 {
130 	CALLED();
131 	TRACE("FireWireDVNode::SetRunMode(%d)\n", mode);
132 }
133 
134 
135 /* BMediaEventLooper */
136 void
137 FireWireDVNode::NodeRegistered()
138 {
139 	CALLED();
140 
141 	fOutputEncVideo.node = Node();
142 	fOutputEncVideo.source.port = ControlPort();
143 	fOutputEncVideo.source.id = 0;
144 	fOutputEncVideo.destination = media_destination::null;
145 	fOutputEncVideo.format = fDefaultFormatEncVideo;
146 	strcpy(fOutputEncVideo.name, "encoded video");
147 
148 	RefreshParameterWeb();
149 
150 	SetPriority(B_REAL_TIME_PRIORITY);
151 	Run();
152 }
153 
154 
155 void
156 FireWireDVNode::HandleEvent(const media_timed_event* event,
157 	bigtime_t lateness, bool realTimeEvent)
158 {
159 
160 	switch(event->type)
161 	{
162 		case M_REFRESH_PARAMETER_WEB:
163 			RefreshParameterWeb();
164 			break;
165 		case BTimedEventQueue::B_START:
166 			HandleStart(event->event_time);
167 			break;
168 		case BTimedEventQueue::B_STOP:
169 			HandleStop();
170 			break;
171 		case BTimedEventQueue::B_WARP:
172 			HandleTimeWarp(event->bigdata);
173 			break;
174 		case BTimedEventQueue::B_SEEK:
175 			HandleSeek(event->bigdata);
176 			break;
177 		case BTimedEventQueue::B_HANDLE_BUFFER:
178 		case BTimedEventQueue::B_DATA_STATUS:
179 		case BTimedEventQueue::B_PARAMETER:
180 		default:
181 			TRACE("FireWireDVNode::HandleEvent: Unhandled event -- %lx\n", event->type);
182 			break;
183 	}
184 }
185 
186 
187 /* BBufferProducer */
188 status_t
189 FireWireDVNode::FormatChangeRequested(const media_source& source,
190 	const media_destination& destination, media_format* io_format,
191 	int32* _deprecated_)
192 {
193 	CALLED();
194 
195 	// we don't support any other formats, so we just reject any format changes.
196 	return B_ERROR;
197 }
198 
199 
200 status_t
201 FireWireDVNode::GetNextOutput(int32* cookie, media_output* out_output)
202 {
203 	CALLED();
204 
205 	if (*cookie == 0) {
206 		*out_output = fOutputEncVideo;
207 		*cookie += 1;
208 		return B_OK;
209 	} else {
210 		return B_BAD_INDEX;
211 	}
212 }
213 
214 
215 status_t
216 FireWireDVNode::DisposeOutputCookie(int32 cookie)
217 {
218 	CALLED();
219 	// do nothing because we don't use the cookie for anything special
220 	return B_OK;
221 }
222 
223 
224 status_t
225 FireWireDVNode::SetBufferGroup(const media_source& source, BBufferGroup* group)
226 {
227 	CALLED();
228 	return B_ERROR;
229 }
230 
231 
232 status_t
233 FireWireDVNode::VideoClippingChanged(const media_source& for_source,
234 	int16 num_shorts, int16* clip_data,
235 	const media_video_display_info& display, int32* _deprecated_)
236 {
237 	CALLED();
238 	return B_ERROR;
239 }
240 
241 
242 status_t
243 FireWireDVNode::GetLatency(bigtime_t* out_latency)
244 {
245 	CALLED();
246 
247 	*out_latency = EventLatency() + SchedulingLatency();
248 	return B_OK;
249 }
250 
251 
252 status_t
253 FireWireDVNode::FormatSuggestionRequested(
254 	media_type type, int32 quality, media_format* format)
255 {
256 	CALLED();
257 
258 	if (format == NULL) {
259 		fprintf(stderr, "\tERROR - NULL format pointer passed in!\n");
260 		return B_BAD_VALUE;
261 	}
262 
263 	// this is the format we'll be returning (our preferred format)
264 	*format = fDefaultFormatEncVideo;
265 
266 	// a wildcard type is okay; we can specialize it
267 	if (type == B_MEDIA_UNKNOWN_TYPE)
268 		type = B_MEDIA_ENCODED_VIDEO;
269 
270 	if (type != B_MEDIA_ENCODED_VIDEO)
271 		return B_MEDIA_BAD_FORMAT;
272 
273 	return B_OK;
274 }
275 
276 
277 status_t
278 FireWireDVNode::FormatProposal(const media_source& source,
279 	media_format* format)
280 {
281 	CALLED();
282 	/* The connection process:
283 	 * we are here => BBufferProducer::FormatProposal
284 	 *                BBufferConsumer::AcceptFormat
285 	 *                BBufferProducer::PrepareToConnect
286 	 *                BBufferConsumer::Connected
287 	 *                BBufferProducer::Connect
288 	 *
289 	 * What we need to do:
290 	 * - if the format contains a wildcard AND we have a requirement for that
291 	 *   field, set it to the value we need.
292 	 * - if a field has a value that is not wildcard and not supported by us,
293 	 *   we don't change it, and return B_MEDIA_BAD_FORMAT
294 	 * - after we are done, the format may still contain wildcards.
295 	 */
296 
297 	if (source.port != ControlPort()) {
298 		fprintf(stderr, "FireWireDVNode::FormatProposal returning "
299 			"B_MEDIA_BAD_SOURCE\n");
300 		return B_MEDIA_BAD_SOURCE;
301 	}
302 
303 	media_type requestedType = format->type;
304 	*format = fDefaultFormatEncVideo;
305 
306 	if (requestedType != B_MEDIA_UNKNOWN_TYPE
307 		&& requestedType != B_MEDIA_ENCODED_VIDEO) {
308 		fprintf(stderr, "FireWireDVNode::FormatProposal returning "
309 			"B_MEDIA_BAD_FORMAT\n");
310 		return B_MEDIA_BAD_FORMAT;
311 	}
312 
313 	// encoded video or wildcard type, either is okay by us
314 	return B_OK;
315 }
316 
317 
318 status_t
319 FireWireDVNode::PrepareToConnect(const media_source& source,
320 	const media_destination& destination, media_format* format,
321 	media_source* out_source, char* out_name)
322 {
323 	/* The connection process:
324 	 *                BBufferProducer::FormatProposal
325 	 *                BBufferConsumer::AcceptFormat
326 	 * we are here => BBufferProducer::PrepareToConnect
327 	 *                BBufferConsumer::Connected
328 	 *                BBufferProducer::Connect
329 	 *
330 	 * At this point, the consumer's AcceptFormat() method has been called,
331 	 * and that node has potentially changed the proposed format. It may
332 	 * also have left wildcards in the format. PrepareToConnect()
333 	 * *must* fully specialize the format before returning!
334 	 */
335 
336 	CALLED();
337 	// is the source valid?
338 	if (source.port != ControlPort() &&
339 			fCard->DetectRecvFn() != B_OK) {
340 		fprintf(stderr, "FireWireDVNode::PrepareToConnect returning "
341 			"B_MEDIA_BAD_SOURCE\n");
342 		return B_MEDIA_BAD_SOURCE;
343 	}
344 
345 	// are we already connected?
346 	if (fOutputEncVideo.destination != media_destination::null)
347 		return B_MEDIA_ALREADY_CONNECTED;
348 
349 	// the format may not yet be fully specialized (the consumer might have
350 	// passed back some wildcards).  Finish specializing it now, and return an
351 	// error if we don't support the requested format.
352 	if (format->type != B_MEDIA_RAW_AUDIO) {
353 		fprintf(stderr, "\tnon-raw-audio format?!\n");
354 		return B_MEDIA_BAD_FORMAT;
355 	}
356 
357 	// reserve the connection by setting destination
358 	// set the output's format to the new format
359 	fOutputEncVideo.destination = destination;
360 	fOutputEncVideo.format = *format;
361 
362 	// set source and suggest a name
363 	*out_source = source;
364 	strcpy(out_name, "encoded video");
365 
366 	return B_OK;
367 }
368 
369 
370 void
371 FireWireDVNode::Connect(status_t error, const media_source& source,
372 	const media_destination& destination, const media_format& format,
373 	char* io_name)
374 {
375 	/* The connection process:
376 	 *                BBufferProducer::FormatProposal
377 	 *                BBufferConsumer::AcceptFormat
378 	 *                BBufferProducer::PrepareToConnect
379 	 *                BBufferConsumer::Connected
380 	 * we are here => BBufferProducer::Connect
381 	 */
382 
383 	CALLED();
384 
385 	if (error != B_OK) {
386 		TRACE("Error during connecting\n");
387 		// if an error occured, unreserve the connection
388 		fOutputEncVideo.destination = media_destination::null;
389 		fOutputEncVideo.format = fDefaultFormatEncVideo;
390 		return;
391 	}
392 
393 	// Since the destination is allowed to be changed by the
394 	// consumer, the one we got in PrepareToConnect() is no
395 	// longer correct, and must be updated here.
396 	fOutputEncVideo.destination = destination;
397 	fOutputEncVideo.format = format;
398 
399 	// if the connection has no name, we set it now
400 	if (strlen(io_name) == 0)
401 		strcpy(io_name, "encoded video");
402 
403 	#ifdef DEBUG
404 		bigtime_t latency;
405 		media_node_id ts;
406 		if (B_OK != FindLatencyFor(destination, &latency, &ts))
407 			TRACE("FindLatencyFor failed\n");
408 		else
409 			TRACE("downstream latency %lld\n", latency);
410 	#endif
411 }
412 
413 
414 void
415 FireWireDVNode::Disconnect(const media_source &source,
416 	const media_destination& destination)
417 {
418 	CALLED();
419 
420 	// unreserve the connection
421 	fOutputEncVideo.destination = media_destination::null;
422 	fOutputEncVideo.format = fDefaultFormatEncVideo;
423 }
424 
425 
426 void
427 FireWireDVNode::LateNoticeReceived(const media_source& source,
428 	bigtime_t how_much, bigtime_t performance_time)
429 {
430 	TRACE("FireWireDVNode::LateNoticeReceived %lld late at %lld\n", how_much, performance_time);
431 }
432 
433 
434 void
435 FireWireDVNode::EnableOutput(const media_source& source, bool enabled,
436 	int32* _deprecated_)
437 {
438 	CALLED();
439 	fOutputEnabledEncVideo = enabled;
440 }
441 
442 
443 void
444 FireWireDVNode::AdditionalBufferRequested(const media_source& source,
445 	media_buffer_id prev_buffer, bigtime_t prev_time,
446 	const media_seek_tag* prev_tag)
447 {
448 	CALLED();
449 	// we don't support offline mode
450 	return;
451 }
452 
453 
454 /* FireWireDVNode */
455 void
456 FireWireDVNode::HandleTimeWarp(bigtime_t performance_time)
457 {
458 	TRACE("FireWireDVNode::HandleTimeWarp at %lld\n", performance_time);
459 }
460 
461 
462 void
463 FireWireDVNode::HandleSeek(bigtime_t performance_time)
464 {
465 	TRACE("FireWireDVNode::HandleSeek at %lld\n", performance_time);
466 }
467 
468 
469 void
470 FireWireDVNode::HandleStart(bigtime_t performance_time)
471 {
472 	CALLED();
473 	StartCapture();
474 }
475 
476 
477 void
478 FireWireDVNode::HandleStop(void)
479 {
480 	CALLED();
481 	StopCapture();
482 }
483 
484 
485 status_t
486 FireWireDVNode::StartCapture()
487 {
488 	CALLED();
489 
490 	if (fCaptureActive)
491 		return B_OK;
492 
493 	RETURN_IF_ERROR(StopCaptureThreads());
494 
495 	RETURN_IF_ERROR(StartCaptureThreads());
496 
497 	fCaptureActive = true;
498 
499 	RefreshParameterWeb();
500 
501 	return B_OK;
502 }
503 
504 
505 status_t
506 FireWireDVNode::StopCapture()
507 {
508 	CALLED();
509 	if (!fCaptureActive)
510 		return B_OK;
511 
512 	StopCaptureThreads();
513 
514 	fCaptureActive = false;
515 	return B_OK;
516 }
517 
518 
519 status_t
520 FireWireDVNode::StartCaptureThreads()
521 {
522 	CALLED();
523 
524 	if (fCaptureThreadsActive)
525 		return B_OK;
526 
527 	fTerminateThreads = false;
528 
529 	fThreadIdCardReader = spawn_thread(_card_reader_thread_, "FireWire DV reader", 120, this);
530 	resume_thread(fThreadIdCardReader);
531 
532 	fCaptureThreadsActive = true;
533 	return B_OK;
534 }
535 
536 
537 status_t
538 FireWireDVNode::StopCaptureThreads()
539 {
540 	CALLED();
541 
542 	if (!fCaptureThreadsActive)
543 		return B_OK;
544 
545 	fTerminateThreads = true;
546 
547 	status_t dummy; // NULL as parameter does not work
548 	wait_for_thread(fThreadIdCardReader, &dummy);
549 
550 	fCaptureThreadsActive = false;
551 	return B_OK;
552 }
553 
554 
555 int32
556 FireWireDVNode::_card_reader_thread_(void* arg)
557 {
558 	static_cast<FireWireDVNode *>(arg)->card_reader_thread();
559 	return 0;
560 }
561 
562 
563 void
564 FireWireDVNode::card_reader_thread()
565 {
566 	status_t err;
567 	size_t rbufsize;
568 	int rcount;
569 
570 	fCard->GetBufInfo(&rbufsize, &rcount);
571 	delete fBufferGroupEncVideo;
572 	fBufferGroupEncVideo = new BBufferGroup(rbufsize, rcount);
573 	while (!fTerminateThreads) {
574 		void *data, *end;
575 		ssize_t sizeUsed = fCard->Read(&data);
576 		if (sizeUsed < 0) {
577 			TRACE("FireWireDVNode::%s: %s\n", __FUNCTION__,
578 				strerror(sizeUsed));
579 			continue;
580 		}
581 
582 		end = (char*)data + sizeUsed;
583 
584 		while (data < end) {
585 			BBuffer* buf = fBufferGroupEncVideo->RequestBuffer(rbufsize, 10000);
586 			if (!buf) {
587 				TRACE("OutVideo: request buffer timout\n");
588 				continue;
589 			}
590 
591 			err = fCard->Extract(buf->Data(), &data, &sizeUsed);
592 			if (err) {
593 				buf->Recycle();
594 				printf("OutVideo Extract error %s\n", strerror(err));
595 				continue;
596 			}
597 
598 			media_header* hdr = buf->Header();
599 			hdr->type = B_MEDIA_ENCODED_VIDEO;
600 			hdr->size_used = sizeUsed;
601 			hdr->time_source = TimeSource()->ID();	// set time source id
602 			//what should the start_time be?
603 			hdr->start_time = TimeSource()->PerformanceTimeFor(system_time());
604 
605 			fLock.Lock();
606 			if (SendBuffer(buf, fOutputEncVideo.source,
607 					fOutputEncVideo.destination) != B_OK) {
608 				TRACE("OutVideo: sending buffer failed\n");
609 				buf->Recycle();
610 			}
611 			fLock.Unlock();
612 		}
613 
614 	}
615 }
616 
617 
618 void
619 FireWireDVNode::RefreshParameterWeb()
620 {
621 	TRACE("FireWireDVNode::RefreshParameterWeb enter\n");
622 	fWeb = CreateParameterWeb();
623 	SetParameterWeb(fWeb);
624 	TRACE("FireWireDVNode::RefreshParameterWeb finished\n");
625 }
626 
627 
628 void
629 FireWireDVNode::SetAboutInfo(BParameterGroup* about)
630 {
631 	//May need more useful infomation?
632 	about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "FireWireDV media_addon info:", B_GENERIC);
633 	about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Version " VERSION, B_GENERIC);
634 	about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Revision " REVISION, B_GENERIC);
635 	about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Build " BUILD, B_GENERIC);
636 
637 	about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "", B_GENERIC);
638 	about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Driver info:", B_GENERIC);
639 }
640 
641 
642 BParameterWeb *
643 FireWireDVNode::CreateParameterWeb()
644 {
645 	/* Set up the parameter web */
646 	BParameterWeb* web = new BParameterWeb();
647 
648 	BString name;
649 	name << Name();
650 
651 	BParameterGroup* main = web->MakeGroup(name.String());
652 
653 	if (!fCaptureActive) {
654 		BParameterGroup* info = main->MakeGroup("Info");
655 		info->MakeNullParameter(0, B_MEDIA_NO_TYPE, info->Name(), B_GENERIC);
656 		BParameterGroup* about = main->MakeGroup("About");
657 		about->MakeNullParameter(0, B_MEDIA_NO_TYPE, about->Name(), B_GENERIC);
658 		SetAboutInfo(about);
659 		info->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Node is stopped", B_GENERIC);
660 		info->MakeNullParameter(0, B_MEDIA_NO_TYPE, "or tuning failed.", B_GENERIC);
661 		return web;
662 	}
663 
664 	BParameterGroup* about = main->MakeGroup("About");
665 	about->MakeNullParameter(0, B_MEDIA_NO_TYPE, about->Name(), B_GENERIC);
666 	SetAboutInfo(about);
667 
668 	return web;
669 }
670 
671 
672 status_t
673 FireWireDVNode::GetParameterValue(int32 id, bigtime_t* last_change,
674 	void* value, size_t* size)
675 {
676 	TRACE("FireWireDVNode::GetParameterValue, id 0x%lx\n", id);
677 	//do we need Parameter for firewire dv?
678 	return B_OK;
679 }
680 
681 
682 void
683 FireWireDVNode::SetParameterValue(int32 id, bigtime_t when, const void* value,
684 	size_t size)
685 {
686 	TRACE("FireWireDVNode::SetParameterValue, id 0x%lx, size %ld, "
687 		"value 0x%lx\n", id, size, *(const int32*)value);
688 	//do we need parameter for firewire dv?
689 	TRACE("FireWireDVNode::SetParameterValue finished\n");
690 }
691 
692