vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / media / media-add-ons / firewire_dv / FireWireDVNode.cpp
blob833d9fdcc4686f63d167cb48cf41cbc62be80f92
1 /*
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>
9 */
12 #include "FireWireDVNode.h"
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>
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>
35 #include "FireWireDVNode.h"
36 #include "FireWireCard.h"
37 #include "debug.h"
39 #define REVISION "unknown"
40 #define VERSION "1.0"
41 #define BUILD __DATE__ " " __TIME__
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
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)
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)
72 CALLED();
74 AddNodeKind(B_PHYSICAL_INPUT);
75 // AddNodeKind(B_PHYSICAL_OUTPUT);
77 fInternalID = internal_id;
78 fAddOn = addon;
80 fInitStatus = B_OK;
82 fDefaultFormatEncVideo.type = B_MEDIA_ENCODED_VIDEO;
86 FireWireDVNode::~FireWireDVNode()
88 CALLED();
90 StopCapture();
91 fWeb = NULL;
95 /* BMediaNode */
96 BMediaAddOn*
97 FireWireDVNode::AddOn(int32* internal_id) const
99 if (internal_id)
100 *internal_id = fInternalID;
101 return fAddOn;
105 status_t
106 FireWireDVNode::HandleMessage(int32 message, const void* data, size_t size)
108 return B_ERROR;
112 void
113 FireWireDVNode::Preroll()
115 /* This hook may be called before the node is started to give the hardware
116 * a chance to start. */
120 void
121 FireWireDVNode::SetTimeSource(BTimeSource* time_source)
123 CALLED();
127 void
128 FireWireDVNode::SetRunMode(run_mode mode)
130 CALLED();
131 TRACE("FireWireDVNode::SetRunMode(%d)\n", mode);
135 /* BMediaEventLooper */
136 void
137 FireWireDVNode::NodeRegistered()
139 CALLED();
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);
151 Run();
155 void
156 FireWireDVNode::HandleEvent(const media_timed_event* event,
157 bigtime_t lateness, bool realTimeEvent)
160 switch(event->type)
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;
187 /* BBufferProducer */
188 status_t
189 FireWireDVNode::FormatChangeRequested(const media_source& source,
190 const media_destination& destination, media_format* io_format,
191 int32* _deprecated_)
193 CALLED();
195 // we don't support any other formats, so we just reject any format changes.
196 return B_ERROR;
200 status_t
201 FireWireDVNode::GetNextOutput(int32* cookie, media_output* out_output)
203 CALLED();
205 if (*cookie == 0) {
206 *out_output = fOutputEncVideo;
207 *cookie += 1;
208 return B_OK;
209 } else {
210 return B_BAD_INDEX;
215 status_t
216 FireWireDVNode::DisposeOutputCookie(int32 cookie)
218 CALLED();
219 // do nothing because we don't use the cookie for anything special
220 return B_OK;
224 status_t
225 FireWireDVNode::SetBufferGroup(const media_source& source, BBufferGroup* group)
227 CALLED();
228 return B_ERROR;
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_)
237 CALLED();
238 return B_ERROR;
242 status_t
243 FireWireDVNode::GetLatency(bigtime_t* out_latency)
245 CALLED();
247 *out_latency = EventLatency() + SchedulingLatency();
248 return B_OK;
252 status_t
253 FireWireDVNode::FormatSuggestionRequested(
254 media_type type, int32 quality, media_format* format)
256 CALLED();
258 if (format == NULL) {
259 fprintf(stderr, "\tERROR - NULL format pointer passed in!\n");
260 return B_BAD_VALUE;
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;
273 return B_OK;
277 status_t
278 FireWireDVNode::FormatProposal(const media_source& source,
279 media_format* format)
281 CALLED();
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
314 return B_OK;
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)
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!
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;
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");
366 return B_OK;
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)
375 /* The connection process:
376 * BBufferProducer::FormatProposal
377 * BBufferConsumer::AcceptFormat
378 * BBufferProducer::PrepareToConnect
379 * BBufferConsumer::Connected
380 * we are here => BBufferProducer::Connect
383 CALLED();
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;
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");
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 %Ld\n", latency);
410 #endif
414 void
415 FireWireDVNode::Disconnect(const media_source &source,
416 const media_destination& destination)
418 CALLED();
420 // unreserve the connection
421 fOutputEncVideo.destination = media_destination::null;
422 fOutputEncVideo.format = fDefaultFormatEncVideo;
426 void
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);
434 void
435 FireWireDVNode::EnableOutput(const media_source& source, bool enabled,
436 int32* _deprecated_)
438 CALLED();
439 fOutputEnabledEncVideo = enabled;
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)
448 CALLED();
449 // we don't support offline mode
450 return;
454 /* FireWireDVNode */
455 void
456 FireWireDVNode::HandleTimeWarp(bigtime_t performance_time)
458 TRACE("FireWireDVNode::HandleTimeWarp at %Ld\n", performance_time);
462 void
463 FireWireDVNode::HandleSeek(bigtime_t performance_time)
465 TRACE("FireWireDVNode::HandleSeek at %Ld\n", performance_time);
469 void
470 FireWireDVNode::HandleStart(bigtime_t performance_time)
472 CALLED();
473 StartCapture();
477 void
478 FireWireDVNode::HandleStop(void)
480 CALLED();
481 StopCapture();
485 status_t
486 FireWireDVNode::StartCapture()
488 CALLED();
490 if (fCaptureActive)
491 return B_OK;
493 RETURN_IF_ERROR(StopCaptureThreads());
495 RETURN_IF_ERROR(StartCaptureThreads());
497 fCaptureActive = true;
499 RefreshParameterWeb();
501 return B_OK;
505 status_t
506 FireWireDVNode::StopCapture()
508 CALLED();
509 if (!fCaptureActive)
510 return B_OK;
512 StopCaptureThreads();
514 fCaptureActive = false;
515 return B_OK;
519 status_t
520 FireWireDVNode::StartCaptureThreads()
522 CALLED();
524 if (fCaptureThreadsActive)
525 return B_OK;
527 fTerminateThreads = false;
529 fThreadIdCardReader = spawn_thread(_card_reader_thread_, "FireWire DV reader", 120, this);
530 resume_thread(fThreadIdCardReader);
532 fCaptureThreadsActive = true;
533 return B_OK;
537 status_t
538 FireWireDVNode::StopCaptureThreads()
540 CALLED();
542 if (!fCaptureThreadsActive)
543 return B_OK;
545 fTerminateThreads = true;
547 status_t dummy; // NULL as parameter does not work
548 wait_for_thread(fThreadIdCardReader, &dummy);
550 fCaptureThreadsActive = false;
551 return B_OK;
555 int32
556 FireWireDVNode::_card_reader_thread_(void* arg)
558 static_cast<FireWireDVNode *>(arg)->card_reader_thread();
559 return 0;
563 void
564 FireWireDVNode::card_reader_thread()
566 status_t err;
567 size_t rbufsize;
568 int rcount;
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;
582 end = (char*)data + sizeUsed;
584 while (data < end) {
585 BBuffer* buf = fBufferGroupEncVideo->RequestBuffer(rbufsize, 10000);
586 if (!buf) {
587 TRACE("OutVideo: request buffer timout\n");
588 continue;
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;
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());
605 fLock.Lock();
606 if (SendBuffer(buf, fOutputEncVideo.source,
607 fOutputEncVideo.destination) != B_OK) {
608 TRACE("OutVideo: sending buffer failed\n");
609 buf->Recycle();
611 fLock.Unlock();
618 void
619 FireWireDVNode::RefreshParameterWeb()
621 TRACE("FireWireDVNode::RefreshParameterWeb enter\n");
622 fWeb = CreateParameterWeb();
623 SetParameterWeb(fWeb);
624 TRACE("FireWireDVNode::RefreshParameterWeb finished\n");
628 void
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);
642 BParameterWeb *
643 FireWireDVNode::CreateParameterWeb()
645 /* Set up the parameter web */
646 BParameterWeb* web = new BParameterWeb();
648 BString name;
649 name << Name();
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);
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;
664 BParameterGroup* about = main->MakeGroup("About");
665 about->MakeNullParameter(0, B_MEDIA_NO_TYPE, about->Name(), B_GENERIC);
666 SetAboutInfo(about);
668 return web;
672 status_t
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?
678 return B_OK;
682 void
683 FireWireDVNode::SetParameterValue(int32 id, bigtime_t when, const void* value,
684 size_t size)
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");