2 * FireWire DV media addon for Haiku
4 * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn)
5 * Distributed under the terms of the MIT License.
7 * Based on DVB media addon
8 * Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
12 #include "FireWireDVNode.h"
25 #include <BufferGroup.h>
27 #include <Directory.h>
29 #include <MediaRoster.h>
30 #include <ParameterWeb.h>
32 #include <TimeSource.h>
35 #include "FireWireDVNode.h"
36 #include "FireWireCard.h"
39 #define REVISION "unknown"
41 #define BUILD __DATE__ " " __TIME__
47 //#define TRACE_FIREWIRE_NODE
48 #ifdef TRACE_FIREWIRE_NODE
49 # define TRACE(x...) printf(x)
54 #define RETURN_IF_ERROR(expr) { status_t e = (expr); if (e != B_OK) return e; }
56 #define M_REFRESH_PARAMETER_WEB (BTimedEventQueue::B_USER_EVENT + 1)
58 FireWireDVNode::FireWireDVNode(BMediaAddOn
* addon
, const char* name
,
59 int32 internal_id
, FireWireCard
* card
)
61 BBufferProducer(B_MEDIA_ENCODED_VIDEO
),
64 fOutputEnabledEncVideo(false),
66 fCaptureThreadsActive(false),
67 fThreadIdCardReader(-1),
68 fTerminateThreads(false),
69 fBufferGroupEncVideo(0),
74 AddNodeKind(B_PHYSICAL_INPUT
);
75 // AddNodeKind(B_PHYSICAL_OUTPUT);
77 fInternalID
= internal_id
;
82 fDefaultFormatEncVideo
.type
= B_MEDIA_ENCODED_VIDEO
;
86 FireWireDVNode::~FireWireDVNode()
97 FireWireDVNode::AddOn(int32
* internal_id
) const
100 *internal_id
= fInternalID
;
106 FireWireDVNode::HandleMessage(int32 message
, const void* data
, size_t size
)
113 FireWireDVNode::Preroll()
115 /* This hook may be called before the node is started to give the hardware
116 * a chance to start. */
121 FireWireDVNode::SetTimeSource(BTimeSource
* time_source
)
128 FireWireDVNode::SetRunMode(run_mode mode
)
131 TRACE("FireWireDVNode::SetRunMode(%d)\n", mode
);
135 /* BMediaEventLooper */
137 FireWireDVNode::NodeRegistered()
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");
148 RefreshParameterWeb();
150 SetPriority(B_REAL_TIME_PRIORITY
);
156 FireWireDVNode::HandleEvent(const media_timed_event
* event
,
157 bigtime_t lateness
, bool realTimeEvent
)
162 case M_REFRESH_PARAMETER_WEB
:
163 RefreshParameterWeb();
165 case BTimedEventQueue::B_START
:
166 HandleStart(event
->event_time
);
168 case BTimedEventQueue::B_STOP
:
171 case BTimedEventQueue::B_WARP
:
172 HandleTimeWarp(event
->bigdata
);
174 case BTimedEventQueue::B_SEEK
:
175 HandleSeek(event
->bigdata
);
177 case BTimedEventQueue::B_HANDLE_BUFFER
:
178 case BTimedEventQueue::B_DATA_STATUS
:
179 case BTimedEventQueue::B_PARAMETER
:
181 TRACE("FireWireDVNode::HandleEvent: Unhandled event -- %lx\n", event
->type
);
187 /* BBufferProducer */
189 FireWireDVNode::FormatChangeRequested(const media_source
& source
,
190 const media_destination
& destination
, media_format
* io_format
,
195 // we don't support any other formats, so we just reject any format changes.
201 FireWireDVNode::GetNextOutput(int32
* cookie
, media_output
* out_output
)
206 *out_output
= fOutputEncVideo
;
216 FireWireDVNode::DisposeOutputCookie(int32 cookie
)
219 // do nothing because we don't use the cookie for anything special
225 FireWireDVNode::SetBufferGroup(const media_source
& source
, BBufferGroup
* group
)
233 FireWireDVNode::VideoClippingChanged(const media_source
& for_source
,
234 int16 num_shorts
, int16
* clip_data
,
235 const media_video_display_info
& display
, int32
* _deprecated_
)
243 FireWireDVNode::GetLatency(bigtime_t
* out_latency
)
247 *out_latency
= EventLatency() + SchedulingLatency();
253 FireWireDVNode::FormatSuggestionRequested(
254 media_type type
, int32 quality
, media_format
* format
)
258 if (format
== NULL
) {
259 fprintf(stderr
, "\tERROR - NULL format pointer passed in!\n");
263 // this is the format we'll be returning (our preferred format)
264 *format
= fDefaultFormatEncVideo
;
266 // a wildcard type is okay; we can specialize it
267 if (type
== B_MEDIA_UNKNOWN_TYPE
)
268 type
= B_MEDIA_ENCODED_VIDEO
;
270 if (type
!= B_MEDIA_ENCODED_VIDEO
)
271 return B_MEDIA_BAD_FORMAT
;
278 FireWireDVNode::FormatProposal(const media_source
& source
,
279 media_format
* format
)
282 /* The connection process:
283 * we are here => BBufferProducer::FormatProposal
284 * BBufferConsumer::AcceptFormat
285 * BBufferProducer::PrepareToConnect
286 * BBufferConsumer::Connected
287 * BBufferProducer::Connect
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.
297 if (source
.port
!= ControlPort()) {
298 fprintf(stderr
, "FireWireDVNode::FormatProposal returning "
299 "B_MEDIA_BAD_SOURCE\n");
300 return B_MEDIA_BAD_SOURCE
;
303 media_type requestedType
= format
->type
;
304 *format
= fDefaultFormatEncVideo
;
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
;
313 // encoded video or wildcard type, either is okay by us
319 FireWireDVNode::PrepareToConnect(const media_source
& source
,
320 const media_destination
& destination
, media_format
* format
,
321 media_source
* out_source
, char* out_name
)
323 /* The connection process:
324 * BBufferProducer::FormatProposal
325 * BBufferConsumer::AcceptFormat
326 * we are here => BBufferProducer::PrepareToConnect
327 * BBufferConsumer::Connected
328 * BBufferProducer::Connect
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!
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
;
345 // are we already connected?
346 if (fOutputEncVideo
.destination
!= media_destination::null
)
347 return B_MEDIA_ALREADY_CONNECTED
;
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
;
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
;
362 // set source and suggest a name
363 *out_source
= source
;
364 strcpy(out_name
, "encoded video");
371 FireWireDVNode::Connect(status_t error
, const media_source
& source
,
372 const media_destination
& destination
, const media_format
& format
,
375 /* The connection process:
376 * BBufferProducer::FormatProposal
377 * BBufferConsumer::AcceptFormat
378 * BBufferProducer::PrepareToConnect
379 * BBufferConsumer::Connected
380 * we are here => BBufferProducer::Connect
386 TRACE("Error during connecting\n");
387 // if an error occured, unreserve the connection
388 fOutputEncVideo
.destination
= media_destination::null
;
389 fOutputEncVideo
.format
= fDefaultFormatEncVideo
;
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
;
399 // if the connection has no name, we set it now
400 if (strlen(io_name
) == 0)
401 strcpy(io_name
, "encoded video");
406 if (B_OK
!= FindLatencyFor(destination
, &latency
, &ts
))
407 TRACE("FindLatencyFor failed\n");
409 TRACE("downstream latency %Ld\n", latency
);
415 FireWireDVNode::Disconnect(const media_source
&source
,
416 const media_destination
& destination
)
420 // unreserve the connection
421 fOutputEncVideo
.destination
= media_destination::null
;
422 fOutputEncVideo
.format
= fDefaultFormatEncVideo
;
427 FireWireDVNode::LateNoticeReceived(const media_source
& source
,
428 bigtime_t how_much
, bigtime_t performance_time
)
430 TRACE("FireWireDVNode::LateNoticeReceived %Ld late at %Ld\n", how_much
, performance_time
);
435 FireWireDVNode::EnableOutput(const media_source
& source
, bool enabled
,
439 fOutputEnabledEncVideo
= enabled
;
444 FireWireDVNode::AdditionalBufferRequested(const media_source
& source
,
445 media_buffer_id prev_buffer
, bigtime_t prev_time
,
446 const media_seek_tag
* prev_tag
)
449 // we don't support offline mode
456 FireWireDVNode::HandleTimeWarp(bigtime_t performance_time
)
458 TRACE("FireWireDVNode::HandleTimeWarp at %Ld\n", performance_time
);
463 FireWireDVNode::HandleSeek(bigtime_t performance_time
)
465 TRACE("FireWireDVNode::HandleSeek at %Ld\n", performance_time
);
470 FireWireDVNode::HandleStart(bigtime_t performance_time
)
478 FireWireDVNode::HandleStop(void)
486 FireWireDVNode::StartCapture()
493 RETURN_IF_ERROR(StopCaptureThreads());
495 RETURN_IF_ERROR(StartCaptureThreads());
497 fCaptureActive
= true;
499 RefreshParameterWeb();
506 FireWireDVNode::StopCapture()
512 StopCaptureThreads();
514 fCaptureActive
= false;
520 FireWireDVNode::StartCaptureThreads()
524 if (fCaptureThreadsActive
)
527 fTerminateThreads
= false;
529 fThreadIdCardReader
= spawn_thread(_card_reader_thread_
, "FireWire DV reader", 120, this);
530 resume_thread(fThreadIdCardReader
);
532 fCaptureThreadsActive
= true;
538 FireWireDVNode::StopCaptureThreads()
542 if (!fCaptureThreadsActive
)
545 fTerminateThreads
= true;
547 status_t dummy
; // NULL as parameter does not work
548 wait_for_thread(fThreadIdCardReader
, &dummy
);
550 fCaptureThreadsActive
= false;
556 FireWireDVNode::_card_reader_thread_(void* arg
)
558 static_cast<FireWireDVNode
*>(arg
)->card_reader_thread();
564 FireWireDVNode::card_reader_thread()
570 fCard
->GetBufInfo(&rbufsize
, &rcount
);
571 delete fBufferGroupEncVideo
;
572 fBufferGroupEncVideo
= new BBufferGroup(rbufsize
, rcount
);
573 while (!fTerminateThreads
) {
575 ssize_t sizeUsed
= fCard
->Read(&data
);
577 TRACE("FireWireDVNode::%s: %s\n", __FUNCTION__
,
582 end
= (char*)data
+ sizeUsed
;
585 BBuffer
* buf
= fBufferGroupEncVideo
->RequestBuffer(rbufsize
, 10000);
587 TRACE("OutVideo: request buffer timout\n");
591 err
= fCard
->Extract(buf
->Data(), &data
, &sizeUsed
);
594 printf("OutVideo Extract error %s\n", strerror(err
));
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());
606 if (SendBuffer(buf
, fOutputEncVideo
.source
,
607 fOutputEncVideo
.destination
) != B_OK
) {
608 TRACE("OutVideo: sending buffer failed\n");
619 FireWireDVNode::RefreshParameterWeb()
621 TRACE("FireWireDVNode::RefreshParameterWeb enter\n");
622 fWeb
= CreateParameterWeb();
623 SetParameterWeb(fWeb
);
624 TRACE("FireWireDVNode::RefreshParameterWeb finished\n");
629 FireWireDVNode::SetAboutInfo(BParameterGroup
* about
)
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
);
637 about
->MakeNullParameter(0, B_MEDIA_NO_TYPE
, "", B_GENERIC
);
638 about
->MakeNullParameter(0, B_MEDIA_NO_TYPE
, "Driver info:", B_GENERIC
);
643 FireWireDVNode::CreateParameterWeb()
645 /* Set up the parameter web */
646 BParameterWeb
* web
= new BParameterWeb();
651 BParameterGroup
* main
= web
->MakeGroup(name
.String());
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
);
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
);
664 BParameterGroup
* about
= main
->MakeGroup("About");
665 about
->MakeNullParameter(0, B_MEDIA_NO_TYPE
, about
->Name(), B_GENERIC
);
673 FireWireDVNode::GetParameterValue(int32 id
, bigtime_t
* last_change
,
674 void* value
, size_t* size
)
676 TRACE("FireWireDVNode::GetParameterValue, id 0x%lx\n", id
);
677 //do we need Parameter for firewire dv?
683 FireWireDVNode::SetParameterValue(int32 id
, bigtime_t when
, const void* value
,
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");