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 static const bigtime_t kMaxBufferLateness = 20000LL; 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(3000), 59 fOurBuffers(false), 60 fBuffers(NULL), 61 fManager(manager), 62 fTargetLock(), 63 fTarget(target), 64 fLastBufferIndex(-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] = NULL; 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 void 99 VideoConsumer::NodeRegistered() 100 { 101 FUNCTION("VideoConsumer::NodeRegistered\n"); 102 fIn.destination.port = ControlPort(); 103 fIn.destination.id = 0; 104 fIn.source = media_source::null; 105 fIn.format.type = B_MEDIA_RAW_VIDEO; 106 // wild cards yet 107 fIn.format.u.raw_video = media_raw_video_format::wildcard; 108 fIn.format.u.raw_video.interlace = 1; 109 fIn.format.u.raw_video.display.format = B_NO_COLOR_SPACE; 110 fIn.format.u.raw_video.display.bytes_per_row = 0; 111 fIn.format.u.raw_video.display.line_width = 0; 112 fIn.format.u.raw_video.display.line_count = 0; 113 114 Run(); 115 } 116 117 118 status_t 119 VideoConsumer::RequestCompleted(const media_request_info& info) 120 { 121 FUNCTION("VideoConsumer::RequestCompleted\n"); 122 switch(info.what) { 123 case media_request_info::B_SET_OUTPUT_BUFFERS_FOR: 124 if (info.status != B_OK) 125 ERROR("VideoConsumer::RequestCompleted: Not using our " 126 "buffers!\n"); 127 break; 128 129 default: 130 break; 131 } 132 return B_OK; 133 } 134 135 136 status_t 137 VideoConsumer::HandleMessage(int32 message, const void* data, size_t size) 138 { 139 return B_OK; 140 } 141 142 143 void 144 VideoConsumer::BufferReceived(BBuffer* buffer) 145 { 146 LOOP("VideoConsumer::Buffer #%ld received\n", buffer->ID()); 147 148 if (RunState() == B_STOPPED) { 149 buffer->Recycle(); 150 return; 151 } 152 153 media_timed_event event(buffer->Header()->start_time, 154 BTimedEventQueue::B_HANDLE_BUFFER, buffer, 155 BTimedEventQueue::B_RECYCLE_BUFFER); 156 EventQueue()->AddEvent(event); 157 } 158 159 160 void 161 VideoConsumer::ProducerDataStatus(const media_destination& forWhom, 162 int32 status, bigtime_t atMediaTime) 163 { 164 FUNCTION("VideoConsumer::ProducerDataStatus\n"); 165 166 if (forWhom != fIn.destination) 167 return; 168 } 169 170 171 status_t 172 VideoConsumer::CreateBuffers(const media_format& format) 173 { 174 FUNCTION("VideoConsumer::CreateBuffers\n"); 175 176 // delete any old buffers 177 DeleteBuffers(); 178 179 status_t status = B_OK; 180 181 // create a buffer group 182 uint32 width = format.u.raw_video.display.line_width; 183 uint32 height = format.u.raw_video.display.line_count; 184 color_space colorSpace = format.u.raw_video.display.format; 185 PROGRESS("VideoConsumer::CreateBuffers - Width = %ld - Height = %ld - " 186 "Colorspace = %d\n", width, height, colorSpace); 187 188 fBuffers = new BBufferGroup(); 189 status = fBuffers->InitCheck(); 190 if (B_OK != status) { 191 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n"); 192 return status; 193 } 194 195 // and attach the bitmaps to the buffer group 196 BRect bounds(0, 0, width - 1, height - 1); 197 for (uint32 i = 0; i < kBufferCount; i++) { 198 // figure out the bitmap creation flags 199 uint32 bitmapFlags = 0; 200 if (fTryOverlay) { 201 // try to use hardware overlay 202 bitmapFlags |= B_BITMAP_WILL_OVERLAY; 203 if (i == 0) 204 bitmapFlags |= B_BITMAP_RESERVE_OVERLAY_CHANNEL; 205 } else 206 bitmapFlags = B_BITMAP_IS_LOCKED; 207 208 fBitmap[i] = new BBitmap(bounds, bitmapFlags, colorSpace); 209 status = fBitmap[i]->InitCheck(); 210 if (status >= B_OK) { 211 buffer_clone_info info; 212 213 uint8* bits = (uint8*)fBitmap[i]->Bits(); 214 info.area = area_for(bits); 215 area_info bitmapAreaInfo; 216 status = get_area_info(info.area, &bitmapAreaInfo); 217 if (status != B_OK) { 218 fprintf(stderr, "VideoConsumer::CreateBuffers() - " 219 "get_area_info(): %s\n", strerror(status)); 220 return status; 221 } 222 223 //printf("area info for bitmap %ld (%p):\n", i, fBitmap[i]->Bits()); 224 //printf(" area: %ld\n", bitmapAreaInfo.area); 225 //printf(" size: %ld\n", bitmapAreaInfo.size); 226 //printf(" lock: %ld\n", bitmapAreaInfo.lock); 227 //printf(" protection: %ld\n", bitmapAreaInfo.protection); 228 //printf(" ram size: %ld\n", bitmapAreaInfo.ram_size); 229 //printf(" copy_count: %ld\n", bitmapAreaInfo.copy_count); 230 //printf(" out_count: %ld\n", bitmapAreaInfo.out_count); 231 //printf(" address: %p\n", bitmapAreaInfo.address); 232 233 info.offset = bits - (uint8*)bitmapAreaInfo.address; 234 info.size = (size_t)fBitmap[i]->BitsLength(); 235 info.flags = 0; 236 info.buffer = 0; 237 // the media buffer id 238 239 BBuffer* buffer = NULL; 240 if ((status = fBuffers->AddBuffer(info, &buffer)) != B_OK) { 241 ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER " 242 "TO GROUP (%ld): %s\n", i, strerror(status)); 243 return status; 244 } else { 245 PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD " 246 "BUFFER TO GROUP\n"); 247 } 248 fBufferMap[i] = buffer; 249 } else { 250 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING " 251 "BUFFER (%ld): %s\n", i, strerror(status)); 252 return status; 253 } 254 } 255 256 FUNCTION("VideoConsumer::CreateBuffers - EXIT\n"); 257 return status; 258 } 259 260 261 void 262 VideoConsumer::DeleteBuffers() 263 { 264 FUNCTION("VideoConsumer::DeleteBuffers\n"); 265 if (fBuffers) { 266 fTargetLock.Lock(); 267 if (fLastBufferIndex >= 0) { 268 if (fTarget) 269 fTarget->SetBitmap(NULL); 270 fLastBufferIndex = -1; 271 } 272 fTargetLock.Unlock(); 273 274 delete fBuffers; 275 fBuffers = NULL; 276 277 for (uint32 i = 0; i < kBufferCount; i++) { 278 snooze(20000); 279 delete fBitmap[i]; 280 fBitmap[i] = NULL; 281 } 282 } 283 FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n"); 284 } 285 286 287 void 288 VideoConsumer::SetTarget(VideoTarget* target) 289 { 290 fTargetLock.Lock(); 291 if (fTarget) 292 fTarget->SetBitmap(NULL); 293 fTarget = target; 294 if (fTarget && fLastBufferIndex >= 0) 295 fTarget->SetBitmap(fBitmap[fLastBufferIndex]); 296 fTargetLock.Unlock(); 297 } 298 299 300 void 301 VideoConsumer::SetTryOverlay(bool tryOverlay) 302 { 303 fTryOverlay = tryOverlay; 304 } 305 306 307 status_t 308 VideoConsumer::Connected(const media_source& producer, 309 const media_destination& where, const media_format& format, 310 media_input* outInput) 311 { 312 FUNCTION("VideoConsumer::Connected\n"); 313 314 fIn.source = producer; 315 fIn.format = format; 316 fIn.node = Node(); 317 sprintf(fIn.name, "Video Consumer"); 318 *outInput = fIn; 319 320 uint32 userData = 0; 321 int32 changeTag = 1; 322 status_t ret = CreateBuffers(format); 323 if (ret == B_OK) { 324 // TODO: With overlay bitmaps, there seems to be a problem with 325 // mapping the BBitmap areas into the BBuffers. Until that is fixed, 326 // don't enable a shared BBufferGroup. 327 if (!fTryOverlay) { 328 ret = SetOutputBuffersFor(producer, fIn.destination, 329 fBuffers, &userData, &changeTag, true); 330 if (ret != B_OK) 331 ERROR("SetOutputBuffersFor() failed: %s\n", strerror(ret)); 332 } 333 fIn.format.u.raw_video.display.bytes_per_row 334 = fBitmap[0]->BytesPerRow(); 335 } else { 336 ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n"); 337 return ret; 338 } 339 340 *outInput = fIn; 341 // bytes per row might have changed 342 fConnectionActive = true; 343 344 FUNCTION("VideoConsumer::Connected - EXIT\n"); 345 return B_OK; 346 } 347 348 349 void 350 VideoConsumer::Disconnected(const media_source& producer, 351 const media_destination& where) 352 { 353 FUNCTION("VideoConsumer::Disconnected\n"); 354 355 if (where != fIn.destination || producer != fIn.source) 356 return; 357 358 // reclaim our buffers 359 int32 changeTag = 0; 360 SetOutputBuffersFor(producer, fIn.destination, NULL, NULL, &changeTag, 361 false); 362 if (fOurBuffers) { 363 status_t reclaimError = fBuffers->ReclaimAllBuffers(); 364 if (reclaimError != B_OK) { 365 fprintf(stderr, "VideoConsumer::Disconnected() - Failed to " 366 "reclaim our buffers: %s\n", strerror(reclaimError)); 367 } 368 } 369 // disconnect the connection 370 fIn.source = media_source::null; 371 fConnectionActive = false; 372 373 // Unset the target's bitmap. Just to be safe -- since it is usually 374 // done when the stop event arrives, but someone may disonnect 375 // without stopping us before. 376 _UnsetTargetBuffer(); 377 } 378 379 380 status_t 381 VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format) 382 { 383 FUNCTION("VideoConsumer::AcceptFormat\n"); 384 385 if (dest != fIn.destination) { 386 ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n"); 387 return B_MEDIA_BAD_DESTINATION; 388 } 389 390 if (format->type == B_MEDIA_NO_TYPE) 391 format->type = B_MEDIA_RAW_VIDEO; 392 393 if (format->type != B_MEDIA_RAW_VIDEO) { 394 ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n"); 395 return B_MEDIA_BAD_FORMAT; 396 } 397 398 if (format->u.raw_video.display.format 399 != media_raw_video_format::wildcard.display.format) { 400 uint32 flags = 0; 401 bool supported = bitmaps_support_space( 402 format->u.raw_video.display.format, &flags); 403 #ifndef HAIKU_TARGET_PLATFORM_HAIKU 404 // GRRR! BeOS implementation claims not 405 // to support these formats, while they work just fine. 406 switch (format->u.raw_video.display.format) { 407 case B_YCbCr422: 408 case B_YCbCr411: 409 case B_YCbCr444: 410 case B_YCbCr420: 411 supported = true; 412 break; 413 default: 414 break; 415 } 416 #endif 417 if (!supported) { 418 // cannot create bitmaps with such a color space 419 ERROR("AcceptFormat - unsupported color space for BBitmaps " 420 "(%s)!\n", 421 color_space_to_string(format->u.raw_video.display.format)); 422 return B_MEDIA_BAD_FORMAT; 423 } 424 if (!fTryOverlay && (flags & B_VIEWS_SUPPORT_DRAW_BITMAP) == 0) { 425 // BViews do not support drawing such a bitmap 426 ERROR("AcceptFormat - BViews cannot draw bitmaps in given " 427 "colorspace (%s)!\n", 428 color_space_to_string(format->u.raw_video.display.format)); 429 return B_MEDIA_BAD_FORMAT; 430 } 431 } 432 433 #ifdef TRACE_VIDEO_CONSUMER 434 char string[256]; 435 string[0] = 0; 436 string_for_format(*format, string, 256); 437 FUNCTION("VideoConsumer::AcceptFormat: %s\n", string); 438 #endif 439 440 return B_OK; 441 } 442 443 444 status_t 445 VideoConsumer::GetNextInput(int32* cookie, media_input* outInput) 446 { 447 FUNCTION("VideoConsumer::GetNextInput\n"); 448 449 // custom build a destination for this connection 450 // put connection number in id 451 452 if (*cookie < 1) { 453 fIn.node = Node(); 454 fIn.destination.id = *cookie; 455 sprintf(fIn.name, "Video Consumer"); 456 *outInput = fIn; 457 (*cookie)++; 458 return B_OK; 459 } else { 460 return B_MEDIA_BAD_DESTINATION; 461 } 462 } 463 464 465 void 466 VideoConsumer::DisposeInputCookie(int32 /*cookie*/) 467 { 468 } 469 470 471 status_t 472 VideoConsumer::GetLatencyFor(const media_destination& whom, 473 bigtime_t* _latency, media_node_id* _timeSource) 474 { 475 FUNCTION("VideoConsumer::GetLatencyFor\n"); 476 477 if (whom != fIn.destination) 478 return B_MEDIA_BAD_DESTINATION; 479 480 *_latency = fMyLatency; 481 *_timeSource = TimeSource()->ID(); 482 return B_OK; 483 } 484 485 486 status_t 487 VideoConsumer::FormatChanged(const media_source& producer, 488 const media_destination& consumer, int32 fromChangeCount, 489 const media_format& format) 490 { 491 FUNCTION("VideoConsumer::FormatChanged\n"); 492 493 if (consumer != fIn.destination) 494 return B_MEDIA_BAD_DESTINATION; 495 496 if (producer != fIn.source) 497 return B_MEDIA_BAD_SOURCE; 498 499 fIn.format = format; 500 501 return CreateBuffers(format); 502 } 503 504 505 void 506 VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness, 507 bool realTimeEvent) 508 { 509 LOOP("VideoConsumer::HandleEvent\n"); 510 511 switch (event->type) { 512 case BTimedEventQueue::B_START: 513 PROGRESS("VideoConsumer::HandleEvent - START\n"); 514 _SetPerformanceTimeBase(event->event_time); 515 break; 516 case BTimedEventQueue::B_WARP: 517 case BTimedEventQueue::B_SEEK: 518 PROGRESS("VideoConsumer::HandleEvent - WARP or SEEK\n"); 519 _SetPerformanceTimeBase(event->bigdata); 520 break; 521 522 case BTimedEventQueue::B_STOP: 523 PROGRESS("VideoConsumer::HandleEvent - STOP\n"); 524 EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); 525 // unset the target's bitmap 526 _UnsetTargetBuffer(); 527 break; 528 529 case BTimedEventQueue::B_HANDLE_BUFFER: 530 LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n"); 531 _HandleBuffer(static_cast<BBuffer*>(event->pointer)); 532 break; 533 default: 534 ERROR("VideoConsumer::HandleEvent - BAD EVENT\n"); 535 break; 536 } 537 } 538 539 540 void 541 VideoConsumer::_SetPerformanceTimeBase(bigtime_t performanceTime) 542 { 543 fPerformanceTimeBase = performanceTime; 544 } 545 546 547 void 548 VideoConsumer::_HandleBuffer(BBuffer* buffer) 549 { 550 if (RunState() != B_STARTED || !fConnectionActive) { 551 TRACE("RunState() != B_STARTED\n"); 552 buffer->Recycle(); 553 return; 554 } 555 556 // See if this is one of our BBitmap buffers 557 uint32 index = 0; 558 fOurBuffers = true; 559 while (index < kBufferCount) { 560 if (buffer->ID() == fBufferMap[index]->ID()) 561 break; 562 else 563 index++; 564 } 565 if (index == kBufferCount) { 566 // Buffers belong to consumer 567 // NOTE: We maintain this in a member variable, since we still need 568 // to recycle this buffer later on, in case it was the last buffer 569 // received before shutting down. 570 fOurBuffers = false; 571 index = (fLastBufferIndex + 1) % kBufferCount; 572 } 573 574 bool recycle = true; 575 bigtime_t now = TimeSource()->Now(); 576 if (RunMode() == B_OFFLINE 577 || now < buffer->Header()->start_time + kMaxBufferLateness) { 578 // Only display the buffer if it's not too late, or if we are 579 // in B_OFFLINE run-mode. 580 if (!fOurBuffers) { 581 memcpy(fBitmap[index]->Bits(), buffer->Data(), 582 fBitmap[index]->BitsLength()); 583 } 584 bigtime_t tooEarly = buffer->Header()->start_time - now; 585 if (tooEarly > 3000) 586 snooze(tooEarly); 587 588 fTargetLock.Lock(); 589 if (fTarget) { 590 fTarget->SetBitmap(fBitmap[index]); 591 if (fOurBuffers) { 592 // recycle the previous but not the current buffer 593 if (fLastBufferIndex >= 0) 594 fBufferMap[fLastBufferIndex]->Recycle(); 595 recycle = false; 596 } 597 fLastBufferIndex = index; 598 } 599 fTargetLock.Unlock(); 600 } else { 601 // Drop the buffer if it's too late. 602 if (fManager->LockWithTimeout(10000) == B_OK) { 603 fManager->FrameDropped(); 604 fManager->Unlock(); 605 } 606 PROGRESS("VideoConsumer::HandleEvent - DROPPED FRAME\n" 607 " start_time: %lld, current: %lld, latency: %lld\n", 608 buffer->Header()->start_time, TimeSource()->Now(), 609 SchedulingLatency()); 610 } 611 if (recycle) 612 buffer->Recycle(); 613 } 614 615 616 void 617 VideoConsumer::_UnsetTargetBuffer() 618 { 619 fTargetLock.Lock(); 620 if (fLastBufferIndex >= 0) { 621 if (fTarget) 622 fTarget->SetBitmap(NULL); 623 if (fOurBuffers) 624 fBufferMap[fLastBufferIndex]->Recycle(); 625 fLastBufferIndex = -1; 626 } 627 fTargetLock.Unlock(); 628 } 629