1 /* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved. 2 * Distributed under the terms of the Be Sample Code license. 3 * 4 * Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>, 5 * Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>, 6 * All Rights Reserved. Distributed under the terms of the MIT license. 7 */ 8 #include "VideoConsumer.h" 9 10 #include <fcntl.h> 11 #include <stdio.h> 12 #include <string.h> 13 #include <unistd.h> 14 15 #include <Buffer.h> 16 #include <BufferGroup.h> 17 #include <NodeInfo.h> 18 #include <Bitmap.h> 19 #include <View.h> 20 #include <scheduler.h> 21 #include <TimeSource.h> 22 #include <MediaRoster.h> 23 24 #include "ColorSpaceToString.h" 25 #include "NodeManager.h" 26 #include "VideoTarget.h" 27 28 29 // debugging 30 //#define TRACE_VIDEO_CONSUMER 31 #ifdef TRACE_VIDEO_CONSUMER 32 # define TRACE(x...) printf(x) 33 # define PROGRESS(x...) printf(x) 34 # define FUNCTION(x...) printf(x) 35 # define LOOP(x...) printf(x) 36 # define ERROR(x...) fprintf(stderr, x) 37 #else 38 # define TRACE(x...) 39 # define PROGRESS(x...) 40 # define FUNCTION(x...) 41 # define LOOP(x...) 42 # define ERROR(x...) fprintf(stderr, x) 43 #endif 44 45 #define M1 ((double)1000000.0) 46 #define JITTER 20000 47 48 49 VideoConsumer::VideoConsumer(const char* name, BMediaAddOn* addon, 50 const uint32 internal_id, NodeManager* manager, 51 VideoTarget* target) 52 : BMediaNode(name), 53 BMediaEventLooper(), 54 BBufferConsumer(B_MEDIA_RAW_VIDEO), 55 fInternalID(internal_id), 56 fAddOn(addon), 57 fConnectionActive(false), 58 fMyLatency(20000), 59 fOurBuffers(false), 60 fBuffers(NULL), 61 fManager(manager), 62 fTargetLock(), 63 fTarget(target), 64 fTargetBufferIndex(-1), 65 fTryOverlay(true) 66 { 67 FUNCTION("VideoConsumer::VideoConsumer\n"); 68 69 AddNodeKind(B_PHYSICAL_OUTPUT); 70 SetEventLatency(0); 71 72 for (uint32 i = 0; i < kBufferCount; i++) { 73 fBitmap[i] = NULL; 74 fBufferMap[i] = 0; 75 } 76 77 SetPriority(B_DISPLAY_PRIORITY); 78 } 79 80 81 VideoConsumer::~VideoConsumer() 82 { 83 Quit(); 84 DeleteBuffers(); 85 } 86 87 88 BMediaAddOn* 89 VideoConsumer::AddOn(long* cookie) const 90 { 91 FUNCTION("VideoConsumer::AddOn\n"); 92 // do the right thing if we're ever used with an add-on 93 *cookie = fInternalID; 94 return fAddOn; 95 } 96 97 98 // This implementation is required to get around a bug in 99 // the ppc compiler. 100 void 101 VideoConsumer::Start(bigtime_t performance_time) 102 { 103 BMediaEventLooper::Start(performance_time); 104 } 105 106 107 void 108 VideoConsumer::Stop(bigtime_t performance_time, bool immediate) 109 { 110 BMediaEventLooper::Stop(performance_time, immediate); 111 } 112 113 114 void 115 VideoConsumer::Seek(bigtime_t media_time, bigtime_t performance_time) 116 { 117 BMediaEventLooper::Seek(media_time, performance_time); 118 } 119 120 121 void 122 VideoConsumer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time) 123 { 124 BMediaEventLooper::TimeWarp(at_real_time, to_performance_time); 125 } 126 127 128 void 129 VideoConsumer::NodeRegistered() 130 { 131 FUNCTION("VideoConsumer::NodeRegistered\n"); 132 fIn.destination.port = ControlPort(); 133 fIn.destination.id = 0; 134 fIn.source = media_source::null; 135 fIn.format.type = B_MEDIA_RAW_VIDEO; 136 // wild cards yet 137 fIn.format.u.raw_video = media_raw_video_format::wildcard; 138 fIn.format.u.raw_video.interlace = 1; 139 fIn.format.u.raw_video.display.format = B_NO_COLOR_SPACE; 140 fIn.format.u.raw_video.display.bytes_per_row = 0; 141 fIn.format.u.raw_video.display.line_width = 0; 142 fIn.format.u.raw_video.display.line_count = 0; 143 144 Run(); 145 } 146 147 148 status_t 149 VideoConsumer::RequestCompleted(const media_request_info& info) 150 { 151 FUNCTION("VideoConsumer::RequestCompleted\n"); 152 switch(info.what) { 153 case media_request_info::B_SET_OUTPUT_BUFFERS_FOR: 154 if (info.status != B_OK) 155 ERROR("VideoConsumer::RequestCompleted: Not using our " 156 "buffers!\n"); 157 break; 158 159 default: 160 break; 161 } 162 return B_OK; 163 } 164 165 166 status_t 167 VideoConsumer::HandleMessage(int32 message, const void* data, size_t size) 168 { 169 return B_OK; 170 } 171 172 173 void 174 VideoConsumer::BufferReceived(BBuffer* buffer) 175 { 176 LOOP("VideoConsumer::Buffer #%ld received\n", buffer->ID()); 177 178 if (RunState() == B_STOPPED) { 179 buffer->Recycle(); 180 return; 181 } 182 183 media_timed_event event(buffer->Header()->start_time, 184 BTimedEventQueue::B_HANDLE_BUFFER, buffer, 185 BTimedEventQueue::B_RECYCLE_BUFFER); 186 EventQueue()->AddEvent(event); 187 } 188 189 190 void 191 VideoConsumer::ProducerDataStatus(const media_destination& forWhom, 192 int32 status, bigtime_t atMediaTime) 193 { 194 FUNCTION("VideoConsumer::ProducerDataStatus\n"); 195 196 if (forWhom != fIn.destination) 197 return; 198 } 199 200 201 status_t 202 VideoConsumer::CreateBuffers(const media_format& format) 203 { 204 FUNCTION("VideoConsumer::CreateBuffers\n"); 205 206 // delete any old buffers 207 DeleteBuffers(); 208 209 status_t status = B_OK; 210 211 // create a buffer group 212 uint32 width = format.u.raw_video.display.line_width; 213 uint32 height = format.u.raw_video.display.line_count; 214 color_space colorSpace = format.u.raw_video.display.format; 215 PROGRESS("VideoConsumer::CreateBuffers - Width = %ld - Height = %ld - " 216 "Colorspace = %d\n", width, height, colorSpace); 217 218 fBuffers = new BBufferGroup(); 219 status = fBuffers->InitCheck(); 220 if (B_OK != status) { 221 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n"); 222 return status; 223 } 224 225 // and attach the bitmaps to the buffer group 226 BRect bounds(0, 0, width - 1, height - 1); 227 for (uint32 i = 0; i < kBufferCount; i++) { 228 // figure out the bitmap creation flags 229 uint32 bitmapFlags = 0; 230 if (fTryOverlay) { 231 // try to use hardware overlay 232 bitmapFlags |= B_BITMAP_WILL_OVERLAY; 233 if (i == 0) 234 bitmapFlags |= B_BITMAP_RESERVE_OVERLAY_CHANNEL; 235 } else 236 bitmapFlags = B_BITMAP_IS_LOCKED; 237 238 fBitmap[i] = new BBitmap(bounds, bitmapFlags, colorSpace); 239 status = fBitmap[i]->InitCheck(); 240 if (status >= B_OK) { 241 buffer_clone_info info; 242 243 uint8* bits = (uint8*)fBitmap[i]->Bits(); 244 info.area = area_for(bits); 245 area_info bitmapAreaInfo; 246 status = get_area_info(info.area, &bitmapAreaInfo); 247 if (status != B_OK) { 248 fprintf(stderr, "VideoConsumer::CreateBuffers() - " 249 "get_area_info(): %s\n", strerror(status)); 250 return status; 251 } 252 253 //printf("area info for bitmap %ld (%p):\n", i, fBitmap[i]->Bits()); 254 //printf(" area: %ld\n", bitmapAreaInfo.area); 255 //printf(" size: %ld\n", bitmapAreaInfo.size); 256 //printf(" lock: %ld\n", bitmapAreaInfo.lock); 257 //printf(" protection: %ld\n", bitmapAreaInfo.protection); 258 //printf(" ram size: %ld\n", bitmapAreaInfo.ram_size); 259 //printf(" copy_count: %ld\n", bitmapAreaInfo.copy_count); 260 //printf(" out_count: %ld\n", bitmapAreaInfo.out_count); 261 //printf(" address: %p\n", bitmapAreaInfo.address); 262 263 info.offset = bits - (uint8*)bitmapAreaInfo.address; 264 info.size = (size_t)fBitmap[i]->BitsLength(); 265 info.flags = 0; 266 info.buffer = 0; 267 // the media buffer id 268 269 BBuffer *buffer = NULL; 270 if ((status = fBuffers->AddBuffer(info, &buffer)) != B_OK) { 271 ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER " 272 "TO GROUP (%ld): %s\n", i, strerror(status)); 273 return status; 274 } else { 275 PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD " 276 "BUFFER TO GROUP\n"); 277 } 278 } else { 279 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING " 280 "BUFFER (%ld): %s\n", i, strerror(status)); 281 return status; 282 } 283 } 284 285 BBuffer** buffList = new BBuffer*[kBufferCount]; 286 for (uint32 i = 0; i < kBufferCount; i++) 287 buffList[i] = 0; 288 289 if ((status = fBuffers->GetBufferList(kBufferCount, buffList)) == B_OK) { 290 for (uint32 i = 0; i < kBufferCount; i++) { 291 if (buffList[i] != NULL) { 292 fBufferMap[i] = (uint32)buffList[i]; 293 PROGRESS(" i = %lu buffer = %08lx\n", i, fBufferMap[i]); 294 } else { 295 ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n"); 296 return B_ERROR; 297 } 298 } 299 } else 300 ERROR("VideoConsumer::CreateBuffers ERROR IN GET BUFFER LIST\n"); 301 302 delete[] buffList; 303 304 FUNCTION("VideoConsumer::CreateBuffers - EXIT\n"); 305 return status; 306 } 307 308 309 void 310 VideoConsumer::DeleteBuffers() 311 { 312 FUNCTION("VideoConsumer::DeleteBuffers\n"); 313 if (fBuffers) { 314 fTargetLock.Lock(); 315 if (fTargetBufferIndex >= 0) { 316 if (fTarget) 317 fTarget->SetBitmap(NULL); 318 fTargetBufferIndex = -1; 319 } 320 fTargetLock.Unlock(); 321 322 delete fBuffers; 323 fBuffers = NULL; 324 325 for (uint32 i = 0; i < kBufferCount; i++) { 326 snooze(20000); 327 delete fBitmap[i]; 328 fBitmap[i] = NULL; 329 } 330 } 331 FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n"); 332 } 333 334 335 void 336 VideoConsumer::SetTarget(VideoTarget* target) 337 { 338 fTargetLock.Lock(); 339 if (fTarget) 340 fTarget->SetBitmap(NULL); 341 fTarget = target; 342 if (fTarget && fTargetBufferIndex >= 0) 343 fTarget->SetBitmap(fBitmap[fTargetBufferIndex]); 344 fTargetLock.Unlock(); 345 } 346 347 348 void 349 VideoConsumer::SetTryOverlay(bool tryOverlay) 350 { 351 fTryOverlay = tryOverlay; 352 } 353 354 355 status_t 356 VideoConsumer::Connected(const media_source& producer, 357 const media_destination& where, const media_format& format, 358 media_input* outInput) 359 { 360 FUNCTION("VideoConsumer::Connected\n"); 361 362 fIn.source = producer; 363 fIn.format = format; 364 fIn.node = Node(); 365 sprintf(fIn.name, "Video Consumer"); 366 *outInput = fIn; 367 368 uint32 user_data = 0; 369 int32 change_tag = 1; 370 if (CreateBuffers(format) == B_OK) { 371 BBufferConsumer::SetOutputBuffersFor(producer, fDestination, 372 fBuffers, (void *)&user_data, 373 &change_tag, true); 374 fIn.format.u.raw_video.display.bytes_per_row = fBitmap[0]->BytesPerRow(); 375 } else { 376 ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n"); 377 return B_ERROR; 378 } 379 380 *outInput = fIn; 381 // bytes per row might have changed 382 fConnectionActive = true; 383 384 FUNCTION("VideoConsumer::Connected - EXIT\n"); 385 return B_OK; 386 } 387 388 389 void 390 VideoConsumer::Disconnected(const media_source& producer, 391 const media_destination& where) 392 { 393 FUNCTION("VideoConsumer::Disconnected\n"); 394 395 if (where != fIn.destination || producer != fIn.source) 396 return; 397 398 // reclaim our buffers 399 int32 changeTag = 0; 400 BBufferConsumer::SetOutputBuffersFor(producer, fDestination, NULL, 401 NULL, &changeTag, false); 402 if (fOurBuffers) { 403 status_t reclaimError = fBuffers->ReclaimAllBuffers(); 404 if (reclaimError != B_OK) { 405 fprintf(stderr, "VideoConsumer::Disconnected() - Failed to " 406 "reclaim our buffers: %s\n", strerror(reclaimError)); 407 } 408 } 409 // disconnect the connection 410 fIn.source = media_source::null; 411 fConnectionActive = false; 412 413 // Unset the target's bitmap. Just to be safe -- since it is usually 414 // done when the stop event arrives, but someone may disonnect 415 // without stopping us before. 416 fTargetLock.Lock(); 417 if (fTargetBufferIndex >= 0) { 418 if (fTarget) 419 fTarget->SetBitmap(NULL); 420 if (fOurBuffers) 421 ((BBuffer*)fBufferMap[fTargetBufferIndex])->Recycle(); 422 fTargetBufferIndex = -1; 423 } 424 fTargetLock.Unlock(); 425 } 426 427 428 status_t 429 VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format) 430 { 431 FUNCTION("VideoConsumer::AcceptFormat\n"); 432 433 if (dest != fIn.destination) { 434 ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n"); 435 return B_MEDIA_BAD_DESTINATION; 436 } 437 438 if (format->type == B_MEDIA_NO_TYPE) 439 format->type = B_MEDIA_RAW_VIDEO; 440 441 if (format->type != B_MEDIA_RAW_VIDEO) { 442 ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n"); 443 return B_MEDIA_BAD_FORMAT; 444 } 445 446 if (format->u.raw_video.display.format 447 != media_raw_video_format::wildcard.display.format) { 448 uint32 flags = 0; 449 bool supported = bitmaps_support_space( 450 format->u.raw_video.display.format, &flags); 451 #ifndef HAIKU_TARGET_PLATFORM_HAIKU 452 // GRRR! BeOS implementation claims not 453 // to support these formats, while they work just fine. 454 switch (format->u.raw_video.display.format) { 455 case B_YCbCr422: 456 case B_YCbCr411: 457 case B_YCbCr444: 458 case B_YCbCr420: 459 supported = true; 460 break; 461 default: 462 break; 463 } 464 #endif 465 if (!supported) { 466 // cannot create bitmaps with such a color space 467 ERROR("AcceptFormat - unsupported color space for BBitmaps " 468 "(%s)!\n", 469 color_space_to_string(format->u.raw_video.display.format)); 470 return B_MEDIA_BAD_FORMAT; 471 } 472 if (!fTryOverlay && (flags & B_VIEWS_SUPPORT_DRAW_BITMAP) == 0) { 473 // BViews do not support drawing such a bitmap 474 ERROR("AcceptFormat - BViews cannot draw bitmaps in given " 475 "colorspace (%s)!\n", 476 color_space_to_string(format->u.raw_video.display.format)); 477 return B_MEDIA_BAD_FORMAT; 478 } 479 } 480 481 #ifdef TRACE_VIDEO_CONSUMER 482 char string[256]; 483 string[0] = 0; 484 string_for_format(*format, string, 256); 485 FUNCTION("VideoConsumer::AcceptFormat: %s\n", string); 486 #endif 487 488 return B_OK; 489 } 490 491 492 status_t 493 VideoConsumer::GetNextInput(int32* cookie, media_input* outInput) 494 { 495 FUNCTION("VideoConsumer::GetNextInput\n"); 496 497 // custom build a destination for this connection 498 // put connection number in id 499 500 if (*cookie < 1) { 501 fIn.node = Node(); 502 fIn.destination.id = *cookie; 503 sprintf(fIn.name, "Video Consumer"); 504 *outInput = fIn; 505 (*cookie)++; 506 return B_OK; 507 } else { 508 return B_MEDIA_BAD_DESTINATION; 509 } 510 } 511 512 513 void 514 VideoConsumer::DisposeInputCookie(int32 /*cookie*/) 515 { 516 } 517 518 519 status_t 520 VideoConsumer::GetLatencyFor( 521 const media_destination &for_whom, 522 bigtime_t * out_latency, 523 media_node_id * out_timesource) 524 { 525 FUNCTION("VideoConsumer::GetLatencyFor\n"); 526 527 if (for_whom != fIn.destination) 528 return B_MEDIA_BAD_DESTINATION; 529 530 *out_latency = fMyLatency; 531 *out_timesource = TimeSource()->ID(); 532 return B_OK; 533 } 534 535 536 status_t 537 VideoConsumer::FormatChanged(const media_source& producer, 538 const media_destination& consumer, int32 fromChangeCount, 539 const media_format& format) 540 { 541 FUNCTION("VideoConsumer::FormatChanged\n"); 542 543 if (consumer != fIn.destination) 544 return B_MEDIA_BAD_DESTINATION; 545 546 if (producer != fIn.source) 547 return B_MEDIA_BAD_SOURCE; 548 549 fIn.format = format; 550 551 return CreateBuffers(format); 552 } 553 554 555 void 556 VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness, 557 bool realTimeEvent) 558 { 559 LOOP("VideoConsumer::HandleEvent\n"); 560 561 BBuffer* buffer; 562 563 switch (event->type) { 564 case BTimedEventQueue::B_START: 565 PROGRESS("VideoConsumer::HandleEvent - START\n"); 566 break; 567 568 case BTimedEventQueue::B_STOP: 569 PROGRESS("VideoConsumer::HandleEvent - STOP\n"); 570 EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); 571 // unset the target's bitmap 572 fTargetLock.Lock(); 573 if (fTargetBufferIndex >= 0) { 574 if (fTarget) 575 fTarget->SetBitmap(NULL); 576 if (fOurBuffers) 577 ((BBuffer*)fBufferMap[fTargetBufferIndex])->Recycle(); 578 fTargetBufferIndex = -1; 579 } 580 fTargetLock.Unlock(); 581 break; 582 583 case BTimedEventQueue::B_HANDLE_BUFFER: 584 LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n"); 585 buffer = (BBuffer *) event->pointer; 586 587 if (RunState() == B_STARTED && fConnectionActive) { 588 // see if this is one of our buffers 589 uint32 index = 0; 590 fOurBuffers = true; 591 while (index < kBufferCount) { 592 if ((uint32)buffer == fBufferMap[index]) 593 break; 594 else 595 index++; 596 } 597 if (index == kBufferCount) { 598 // no, buffers belong to consumer 599 fOurBuffers = false; 600 index = (fTargetBufferIndex + 1) % kBufferCount; 601 } 602 603 bool dropped = false; 604 bool recycle = true; 605 if ((RunMode() == B_OFFLINE) 606 // TODO: Fix the runmode stuff! Setting the consumer to B_OFFLINE does 607 // not do the trick. I made the VideoConsumer check the performance 608 // time of the buffer and if it is 0, it plays it regardless. 609 || (buffer->Header()->start_time == 2 * fMyLatency + SchedulingLatency()) 610 || (/*(TimeSource()->Now() 611 > (buffer->Header()->start_time - JITTER)) &&*/ 612 (TimeSource()->Now() < (buffer->Header()->start_time 613 + JITTER)))) { 614 if (!fOurBuffers) { 615 memcpy(fBitmap[index]->Bits(), buffer->Data(), 616 fBitmap[index]->BitsLength()); 617 } 618 619 fTargetLock.Lock(); 620 if (fTarget) { 621 fTarget->SetBitmap(fBitmap[index]); 622 if (fOurBuffers) { 623 // recycle the previous but not the current buffer 624 if (fTargetBufferIndex >= 0) { 625 ((BBuffer*)fBufferMap[fTargetBufferIndex]) 626 ->Recycle(); 627 } 628 recycle = false; 629 } 630 fTargetBufferIndex = index; 631 } 632 fTargetLock.Unlock(); 633 } else { 634 dropped = true; 635 PROGRESS("VideoConsumer::HandleEvent - DROPPED FRAME\n" 636 " start_time: %lld, current: %lld, latency: %lld\n", 637 buffer->Header()->start_time, TimeSource()->Now(), 638 SchedulingLatency()); 639 } 640 if (dropped) { 641 if (fManager->LockWithTimeout(10000) == B_OK) { 642 fManager->FrameDropped(); 643 fManager->Unlock(); 644 } 645 } 646 if (recycle) 647 buffer->Recycle(); 648 } else { 649 TRACE("RunState() != B_STARTED\n"); 650 buffer->Recycle(); 651 } 652 break; 653 default: 654 ERROR("VideoConsumer::HandleEvent - BAD EVENT\n"); 655 break; 656 } 657 } 658