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 - Colorspace = %d\n", colorSpace); 216 217 fBuffers = new BBufferGroup(); 218 status = fBuffers->InitCheck(); 219 if (B_OK != status) { 220 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n"); 221 return status; 222 } 223 224 // and attach the bitmaps to the buffer group 225 BRect bounds(0, 0, width - 1, height - 1); 226 for (uint32 i = 0; i < kBufferCount; i++) { 227 // figure out the bitmap creation flags 228 uint32 bitmapFlags = 0; 229 if (fTryOverlay) { 230 // try to use hardware overlay 231 bitmapFlags |= B_BITMAP_WILL_OVERLAY; 232 if (i == 0) 233 bitmapFlags |= B_BITMAP_RESERVE_OVERLAY_CHANNEL; 234 } else 235 bitmapFlags = B_BITMAP_IS_LOCKED; 236 237 fBitmap[i] = new BBitmap(bounds, bitmapFlags, colorSpace); 238 status = fBitmap[i]->InitCheck(); 239 if (status >= B_OK) { 240 buffer_clone_info info; 241 242 uint8* bits = (uint8*)fBitmap[i]->Bits(); 243 info.area = area_for(bits); 244 area_info bitmapAreaInfo; 245 status = get_area_info(info.area, &bitmapAreaInfo); 246 if (status != B_OK) { 247 fprintf(stderr, "VideoConsumer::CreateBuffers() - " 248 "get_area_info(): %s\n", strerror(status)); 249 return status; 250 } 251 252 //printf("area info for bitmap %ld (%p):\n", i, fBitmap[i]->Bits()); 253 //printf(" area: %ld\n", bitmapAreaInfo.area); 254 //printf(" size: %ld\n", bitmapAreaInfo.size); 255 //printf(" lock: %ld\n", bitmapAreaInfo.lock); 256 //printf(" protection: %ld\n", bitmapAreaInfo.protection); 257 //printf(" ram size: %ld\n", bitmapAreaInfo.ram_size); 258 //printf(" copy_count: %ld\n", bitmapAreaInfo.copy_count); 259 //printf(" out_count: %ld\n", bitmapAreaInfo.out_count); 260 //printf(" address: %p\n", bitmapAreaInfo.address); 261 262 info.offset = bits - (uint8*)bitmapAreaInfo.address; 263 info.size = (size_t)fBitmap[i]->BitsLength(); 264 info.flags = 0; 265 info.buffer = 0; 266 // the media buffer id 267 268 BBuffer *buffer = NULL; 269 if ((status = fBuffers->AddBuffer(info, &buffer)) != B_OK) { 270 ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER TO GROUP\n"); 271 return status; 272 } else 273 PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD BUFFER TO GROUP\n"); 274 } else { 275 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING BUFFER: %s\n", strerror(status)); 276 return status; 277 } 278 } 279 280 BBuffer** buffList = new BBuffer*[kBufferCount]; 281 for (uint32 i = 0; i < kBufferCount; i++) 282 buffList[i] = 0; 283 284 if ((status = fBuffers->GetBufferList(kBufferCount, buffList)) == B_OK) { 285 for (uint32 i = 0; i < kBufferCount; i++) { 286 if (buffList[i] != NULL) { 287 fBufferMap[i] = (uint32)buffList[i]; 288 PROGRESS(" i = %lu buffer = %08lx\n", i, fBufferMap[i]); 289 } else { 290 ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n"); 291 return B_ERROR; 292 } 293 } 294 } else 295 ERROR("VideoConsumer::CreateBuffers ERROR IN GET BUFFER LIST\n"); 296 297 delete[] buffList; 298 299 FUNCTION("VideoConsumer::CreateBuffers - EXIT\n"); 300 return status; 301 } 302 303 304 void 305 VideoConsumer::DeleteBuffers() 306 { 307 FUNCTION("VideoConsumer::DeleteBuffers\n"); 308 if (fBuffers) { 309 fTargetLock.Lock(); 310 if (fTargetBufferIndex >= 0) { 311 if (fTarget) 312 fTarget->SetBitmap(NULL); 313 fTargetBufferIndex = -1; 314 } 315 fTargetLock.Unlock(); 316 317 delete fBuffers; 318 fBuffers = NULL; 319 320 for (uint32 i = 0; i < kBufferCount; i++) { 321 snooze(20000); 322 delete fBitmap[i]; 323 fBitmap[i] = NULL; 324 } 325 } 326 FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n"); 327 } 328 329 330 void 331 VideoConsumer::SetTarget(VideoTarget* target) 332 { 333 fTargetLock.Lock(); 334 if (fTarget) 335 fTarget->SetBitmap(NULL); 336 fTarget = target; 337 if (fTarget && fTargetBufferIndex >= 0) 338 fTarget->SetBitmap(fBitmap[fTargetBufferIndex]); 339 fTargetLock.Unlock(); 340 } 341 342 343 void 344 VideoConsumer::SetTryOverlay(bool tryOverlay) 345 { 346 fTryOverlay = tryOverlay; 347 } 348 349 350 status_t 351 VideoConsumer::Connected(const media_source& producer, 352 const media_destination& where, const media_format& format, 353 media_input* outInput) 354 { 355 FUNCTION("VideoConsumer::Connected\n"); 356 357 fIn.source = producer; 358 fIn.format = format; 359 fIn.node = Node(); 360 sprintf(fIn.name, "Video Consumer"); 361 *outInput = fIn; 362 363 uint32 user_data = 0; 364 int32 change_tag = 1; 365 if (CreateBuffers(format) == B_OK) { 366 BBufferConsumer::SetOutputBuffersFor(producer, fDestination, 367 fBuffers, (void *)&user_data, 368 &change_tag, true); 369 fIn.format.u.raw_video.display.bytes_per_row = fBitmap[0]->BytesPerRow(); 370 } else { 371 ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n"); 372 return B_ERROR; 373 } 374 375 *outInput = fIn; 376 // bytes per row might have changed 377 fConnectionActive = true; 378 379 FUNCTION("VideoConsumer::Connected - EXIT\n"); 380 return B_OK; 381 } 382 383 384 void 385 VideoConsumer::Disconnected(const media_source& producer, 386 const media_destination& where) 387 { 388 FUNCTION("VideoConsumer::Disconnected\n"); 389 390 if (where != fIn.destination || producer != fIn.source) 391 return; 392 393 // reclaim our buffers 394 int32 changeTag = 0; 395 BBufferConsumer::SetOutputBuffersFor(producer, fDestination, NULL, 396 NULL, &changeTag, false); 397 if (fOurBuffers) { 398 status_t reclaimError = fBuffers->ReclaimAllBuffers(); 399 if (reclaimError != B_OK) { 400 fprintf(stderr, "VideoConsumer::Disconnected() - Failed to " 401 "reclaim our buffers: %s\n", strerror(reclaimError)); 402 } 403 } 404 // disconnect the connection 405 fIn.source = media_source::null; 406 fConnectionActive = false; 407 408 // Unset the target's bitmap. Just to be safe -- since it is usually 409 // done when the stop event arrives, but someone may disonnect 410 // without stopping us before. 411 fTargetLock.Lock(); 412 if (fTargetBufferIndex >= 0) { 413 if (fTarget) 414 fTarget->SetBitmap(NULL); 415 if (fOurBuffers) 416 ((BBuffer*)fBufferMap[fTargetBufferIndex])->Recycle(); 417 fTargetBufferIndex = -1; 418 } 419 fTargetLock.Unlock(); 420 } 421 422 423 status_t 424 VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format) 425 { 426 FUNCTION("VideoConsumer::AcceptFormat\n"); 427 428 if (dest != fIn.destination) { 429 ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n"); 430 return B_MEDIA_BAD_DESTINATION; 431 } 432 433 if (format->type == B_MEDIA_NO_TYPE) 434 format->type = B_MEDIA_RAW_VIDEO; 435 436 if (format->type != B_MEDIA_RAW_VIDEO) { 437 ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n"); 438 return B_MEDIA_BAD_FORMAT; 439 } 440 441 if (format->u.raw_video.display.format 442 != media_raw_video_format::wildcard.display.format) { 443 uint32 flags = 0; 444 bool supported = bitmaps_support_space( 445 format->u.raw_video.display.format, &flags); 446 #ifndef HAIKU_TARGET_PLATFORM_HAIKU 447 // GRRR! BeOS implementation claims not 448 // to support these formats, while they work just fine. 449 switch (format->u.raw_video.display.format) { 450 case B_YCbCr422: 451 case B_YCbCr411: 452 case B_YCbCr444: 453 case B_YCbCr420: 454 supported = true; 455 break; 456 default: 457 break; 458 } 459 #endif 460 if (!supported) { 461 // cannot create bitmaps with such a color space 462 ERROR("AcceptFormat - unsupported color space for BBitmaps " 463 "(%s)!\n", 464 color_space_to_string(format->u.raw_video.display.format)); 465 return B_MEDIA_BAD_FORMAT; 466 } 467 if (!fTryOverlay && (flags & B_VIEWS_SUPPORT_DRAW_BITMAP) == 0) { 468 // BViews do not support drawing such a bitmap 469 ERROR("AcceptFormat - BViews cannot draw bitmaps in given " 470 "colorspace (%s)!\n", 471 color_space_to_string(format->u.raw_video.display.format)); 472 return B_MEDIA_BAD_FORMAT; 473 } 474 } 475 476 #ifdef TRACE_VIDEO_CONSUMER 477 char string[256]; 478 string[0] = 0; 479 string_for_format(*format, string, 256); 480 FUNCTION("VideoConsumer::AcceptFormat: %s\n", string); 481 #endif 482 483 return B_OK; 484 } 485 486 487 status_t 488 VideoConsumer::GetNextInput(int32* cookie, media_input* outInput) 489 { 490 FUNCTION("VideoConsumer::GetNextInput\n"); 491 492 // custom build a destination for this connection 493 // put connection number in id 494 495 if (*cookie < 1) { 496 fIn.node = Node(); 497 fIn.destination.id = *cookie; 498 sprintf(fIn.name, "Video Consumer"); 499 *outInput = fIn; 500 (*cookie)++; 501 return B_OK; 502 } else { 503 return B_MEDIA_BAD_DESTINATION; 504 } 505 } 506 507 508 void 509 VideoConsumer::DisposeInputCookie(int32 /*cookie*/) 510 { 511 } 512 513 514 status_t 515 VideoConsumer::GetLatencyFor( 516 const media_destination &for_whom, 517 bigtime_t * out_latency, 518 media_node_id * out_timesource) 519 { 520 FUNCTION("VideoConsumer::GetLatencyFor\n"); 521 522 if (for_whom != fIn.destination) 523 return B_MEDIA_BAD_DESTINATION; 524 525 *out_latency = fMyLatency; 526 *out_timesource = TimeSource()->ID(); 527 return B_OK; 528 } 529 530 531 status_t 532 VideoConsumer::FormatChanged(const media_source& producer, 533 const media_destination& consumer, int32 fromChangeCount, 534 const media_format& format) 535 { 536 FUNCTION("VideoConsumer::FormatChanged\n"); 537 538 if (consumer != fIn.destination) 539 return B_MEDIA_BAD_DESTINATION; 540 541 if (producer != fIn.source) 542 return B_MEDIA_BAD_SOURCE; 543 544 fIn.format = format; 545 546 return CreateBuffers(format); 547 } 548 549 550 void 551 VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness, 552 bool realTimeEvent) 553 { 554 LOOP("VideoConsumer::HandleEvent\n"); 555 556 BBuffer* buffer; 557 558 switch (event->type) { 559 case BTimedEventQueue::B_START: 560 PROGRESS("VideoConsumer::HandleEvent - START\n"); 561 break; 562 563 case BTimedEventQueue::B_STOP: 564 PROGRESS("VideoConsumer::HandleEvent - STOP\n"); 565 EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); 566 // unset the target's bitmap 567 fTargetLock.Lock(); 568 if (fTargetBufferIndex >= 0) { 569 if (fTarget) 570 fTarget->SetBitmap(NULL); 571 if (fOurBuffers) 572 ((BBuffer*)fBufferMap[fTargetBufferIndex])->Recycle(); 573 fTargetBufferIndex = -1; 574 } 575 fTargetLock.Unlock(); 576 break; 577 578 case BTimedEventQueue::B_HANDLE_BUFFER: 579 LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n"); 580 buffer = (BBuffer *) event->pointer; 581 582 if (RunState() == B_STARTED && fConnectionActive) { 583 // see if this is one of our buffers 584 uint32 index = 0; 585 fOurBuffers = true; 586 while (index < kBufferCount) { 587 if ((uint32)buffer == fBufferMap[index]) 588 break; 589 else 590 index++; 591 } 592 if (index == kBufferCount) { 593 // no, buffers belong to consumer 594 fOurBuffers = false; 595 index = (fTargetBufferIndex + 1) % kBufferCount; 596 } 597 598 bool dropped = false; 599 bool recycle = true; 600 if ((RunMode() == B_OFFLINE) 601 // TODO: Fix the runmode stuff! Setting the consumer to B_OFFLINE does 602 // not do the trick. I made the VideoConsumer check the performance 603 // time of the buffer and if it is 0, it plays it regardless. 604 || (buffer->Header()->start_time == 2 * fMyLatency + SchedulingLatency()) 605 || (/*(TimeSource()->Now() 606 > (buffer->Header()->start_time - JITTER)) &&*/ 607 (TimeSource()->Now() < (buffer->Header()->start_time 608 + JITTER)))) { 609 if (!fOurBuffers) { 610 memcpy(fBitmap[index]->Bits(), buffer->Data(), 611 fBitmap[index]->BitsLength()); 612 } 613 614 fTargetLock.Lock(); 615 if (fTarget) { 616 fTarget->SetBitmap(fBitmap[index]); 617 if (fOurBuffers) { 618 // recycle the previous but not the current buffer 619 if (fTargetBufferIndex >= 0) { 620 ((BBuffer*)fBufferMap[fTargetBufferIndex]) 621 ->Recycle(); 622 } 623 recycle = false; 624 } 625 fTargetBufferIndex = index; 626 } 627 fTargetLock.Unlock(); 628 } else { 629 dropped = true; 630 PROGRESS("VideoConsumer::HandleEvent - DROPPED FRAME\n" 631 " start_time: %lld, current: %lld, latency: %lld\n", 632 buffer->Header()->start_time, TimeSource()->Now(), 633 SchedulingLatency()); 634 } 635 if (dropped) { 636 if (fManager->LockWithTimeout(10000) == B_OK) { 637 fManager->FrameDropped(); 638 fManager->Unlock(); 639 } 640 } 641 if (recycle) 642 buffer->Recycle(); 643 } else { 644 TRACE("RunState() != B_STARTED\n"); 645 buffer->Recycle(); 646 } 647 break; 648 default: 649 ERROR("VideoConsumer::HandleEvent - BAD EVENT\n"); 650 break; 651 } 652 } 653