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 58FireWireDVNode::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 86FireWireDVNode::~FireWireDVNode() 87{ 88 CALLED(); 89 90 StopCapture(); 91 fWeb = NULL; 92} 93 94 95/* BMediaNode */ 96BMediaAddOn* 97FireWireDVNode::AddOn(int32* internal_id) const 98{ 99 if (internal_id) 100 *internal_id = fInternalID; 101 return fAddOn; 102} 103 104 105status_t 106FireWireDVNode::HandleMessage(int32 message, const void* data, size_t size) 107{ 108 return B_ERROR; 109} 110 111 112void 113FireWireDVNode::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 120void 121FireWireDVNode::SetTimeSource(BTimeSource* time_source) 122{ 123 CALLED(); 124} 125 126 127void 128FireWireDVNode::SetRunMode(run_mode mode) 129{ 130 CALLED(); 131 TRACE("FireWireDVNode::SetRunMode(%d)\n", mode); 132} 133 134 135/* BMediaEventLooper */ 136void 137FireWireDVNode::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 155void 156FireWireDVNode::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 */ 188status_t 189FireWireDVNode::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 200status_t 201FireWireDVNode::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 215status_t 216FireWireDVNode::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 224status_t 225FireWireDVNode::SetBufferGroup(const media_source& source, BBufferGroup* group) 226{ 227 CALLED(); 228 return B_ERROR; 229} 230 231 232status_t 233FireWireDVNode::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 242status_t 243FireWireDVNode::GetLatency(bigtime_t* out_latency) 244{ 245 CALLED(); 246 247 *out_latency = EventLatency() + SchedulingLatency(); 248 return B_OK; 249} 250 251 252status_t 253FireWireDVNode::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 277status_t 278FireWireDVNode::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 318status_t 319FireWireDVNode::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 370void 371FireWireDVNode::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 414void 415FireWireDVNode::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 426void 427FireWireDVNode::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 434void 435FireWireDVNode::EnableOutput(const media_source& source, bool enabled, 436 int32* _deprecated_) 437{ 438 CALLED(); 439 fOutputEnabledEncVideo = enabled; 440} 441 442 443void 444FireWireDVNode::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 */ 455void 456FireWireDVNode::HandleTimeWarp(bigtime_t performance_time) 457{ 458 TRACE("FireWireDVNode::HandleTimeWarp at %lld\n", performance_time); 459} 460 461 462void 463FireWireDVNode::HandleSeek(bigtime_t performance_time) 464{ 465 TRACE("FireWireDVNode::HandleSeek at %lld\n", performance_time); 466} 467 468 469void 470FireWireDVNode::HandleStart(bigtime_t performance_time) 471{ 472 CALLED(); 473 StartCapture(); 474} 475 476 477void 478FireWireDVNode::HandleStop(void) 479{ 480 CALLED(); 481 StopCapture(); 482} 483 484 485status_t 486FireWireDVNode::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 505status_t 506FireWireDVNode::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 519status_t 520FireWireDVNode::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 537status_t 538FireWireDVNode::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 555int32 556FireWireDVNode::_card_reader_thread_(void* arg) 557{ 558 static_cast<FireWireDVNode *>(arg)->card_reader_thread(); 559 return 0; 560} 561 562 563void 564FireWireDVNode::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 618void 619FireWireDVNode::RefreshParameterWeb() 620{ 621 TRACE("FireWireDVNode::RefreshParameterWeb enter\n"); 622 fWeb = CreateParameterWeb(); 623 SetParameterWeb(fWeb); 624 TRACE("FireWireDVNode::RefreshParameterWeb finished\n"); 625} 626 627 628void 629FireWireDVNode::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 642BParameterWeb * 643FireWireDVNode::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 672status_t 673FireWireDVNode::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 682void 683FireWireDVNode::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