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