1 /* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
2 * Distributed under the terms of the Be Sample Code license.
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.
8 #include "VideoConsumer.h"
16 #include <BufferGroup.h>
20 #include <scheduler.h>
21 #include <TimeSource.h>
22 #include <MediaRoster.h>
24 #include "ColorSpaceToString.h"
25 #include "NodeManager.h"
26 #include "VideoTarget.h"
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)
39 # define PROGRESS(x...)
40 # define FUNCTION(x...)
42 # define ERROR(x...) fprintf(stderr, x)
45 #define M1 ((double)1000000.0)
46 static const bigtime_t kMaxBufferLateness
= 20000LL;
49 VideoConsumer::VideoConsumer(const char* name
, BMediaAddOn
* addon
,
50 const uint32 internal_id
, NodeManager
* manager
,
54 BBufferConsumer(B_MEDIA_RAW_VIDEO
),
55 fInternalID(internal_id
),
57 fConnectionActive(false),
67 FUNCTION("VideoConsumer::VideoConsumer\n");
69 AddNodeKind(B_PHYSICAL_OUTPUT
);
72 for (uint32 i
= 0; i
< kBufferCount
; i
++) {
77 SetPriority(B_DISPLAY_PRIORITY
);
81 VideoConsumer::~VideoConsumer()
89 VideoConsumer::AddOn(int32
* cookie
) const
91 FUNCTION("VideoConsumer::AddOn\n");
92 // do the right thing if we're ever used with an add-on
93 *cookie
= fInternalID
;
99 VideoConsumer::NodeRegistered()
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
;
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;
119 VideoConsumer::RequestCompleted(const media_request_info
& info
)
121 FUNCTION("VideoConsumer::RequestCompleted\n");
123 case media_request_info::B_SET_OUTPUT_BUFFERS_FOR
:
124 if (info
.status
!= B_OK
)
125 ERROR("VideoConsumer::RequestCompleted: Not using our "
137 VideoConsumer::HandleMessage(int32 message
, const void* data
, size_t size
)
144 VideoConsumer::BufferReceived(BBuffer
* buffer
)
146 LOOP("VideoConsumer::Buffer #%" B_PRId32
" received\n", buffer
->ID());
148 if (RunState() == B_STOPPED
) {
153 media_timed_event
event(buffer
->Header()->start_time
,
154 BTimedEventQueue::B_HANDLE_BUFFER
, buffer
,
155 BTimedEventQueue::B_RECYCLE_BUFFER
);
156 EventQueue()->AddEvent(event
);
161 VideoConsumer::ProducerDataStatus(const media_destination
& forWhom
,
162 int32 status
, bigtime_t atMediaTime
)
164 FUNCTION("VideoConsumer::ProducerDataStatus\n");
166 if (forWhom
!= fIn
.destination
)
172 VideoConsumer::CreateBuffers(const media_format
& format
)
174 FUNCTION("VideoConsumer::CreateBuffers\n");
176 // delete any old buffers
179 status_t status
= B_OK
;
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 = %" B_PRIu32
" - "
186 "Height = %" B_PRIu32
" - Colorspace = %d\n",
187 width
, height
, colorSpace
);
189 fBuffers
= new BBufferGroup();
190 status
= fBuffers
->InitCheck();
191 if (B_OK
!= status
) {
192 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n");
196 // and attach the bitmaps to the buffer group
197 BRect
bounds(0, 0, width
- 1, height
- 1);
198 for (uint32 i
= 0; i
< kBufferCount
; i
++) {
199 // figure out the bitmap creation flags
200 uint32 bitmapFlags
= 0;
202 // try to use hardware overlay
203 bitmapFlags
|= B_BITMAP_WILL_OVERLAY
;
205 bitmapFlags
|= B_BITMAP_RESERVE_OVERLAY_CHANNEL
;
207 bitmapFlags
= B_BITMAP_IS_LOCKED
;
209 fBitmap
[i
] = new BBitmap(bounds
, bitmapFlags
, colorSpace
);
210 status
= fBitmap
[i
]->InitCheck();
211 if (status
>= B_OK
) {
212 buffer_clone_info info
;
214 uint8
* bits
= (uint8
*)fBitmap
[i
]->Bits();
215 info
.area
= area_for(bits
);
216 area_info bitmapAreaInfo
;
217 status
= get_area_info(info
.area
, &bitmapAreaInfo
);
218 if (status
!= B_OK
) {
219 fprintf(stderr
, "VideoConsumer::CreateBuffers() - "
220 "get_area_info(): %s\n", strerror(status
));
224 //printf("area info for bitmap %ld (%p):\n", i, fBitmap[i]->Bits());
225 //printf(" area: %ld\n", bitmapAreaInfo.area);
226 //printf(" size: %ld\n", bitmapAreaInfo.size);
227 //printf(" lock: %ld\n", bitmapAreaInfo.lock);
228 //printf(" protection: %ld\n", bitmapAreaInfo.protection);
229 //printf(" ram size: %ld\n", bitmapAreaInfo.ram_size);
230 //printf(" copy_count: %ld\n", bitmapAreaInfo.copy_count);
231 //printf(" out_count: %ld\n", bitmapAreaInfo.out_count);
232 //printf(" address: %p\n", bitmapAreaInfo.address);
234 info
.offset
= bits
- (uint8
*)bitmapAreaInfo
.address
;
235 info
.size
= (size_t)fBitmap
[i
]->BitsLength();
238 // the media buffer id
240 BBuffer
* buffer
= NULL
;
241 if ((status
= fBuffers
->AddBuffer(info
, &buffer
)) != B_OK
) {
242 ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER "
243 "TO GROUP (%" B_PRId32
"): %s\n", i
, strerror(status
));
246 PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD "
247 "BUFFER TO GROUP\n");
249 fBufferMap
[i
] = buffer
;
251 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING "
252 "BUFFER (Index %" B_PRId32
" Width %" B_PRId32
" Height %"
253 B_PRId32
" Colorspace %d: %s\n", i
, width
, height
, colorSpace
,
259 FUNCTION("VideoConsumer::CreateBuffers - EXIT\n");
265 VideoConsumer::DeleteBuffers()
267 FUNCTION("VideoConsumer::DeleteBuffers\n");
270 if (fLastBufferIndex
>= 0) {
272 fTarget
->SetBitmap(NULL
);
273 fLastBufferIndex
= -1;
275 fTargetLock
.Unlock();
280 for (uint32 i
= 0; i
< kBufferCount
; i
++) {
286 FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n");
291 VideoConsumer::SetTarget(VideoTarget
* target
)
295 fTarget
->SetBitmap(NULL
);
297 if (fTarget
&& fLastBufferIndex
>= 0)
298 fTarget
->SetBitmap(fBitmap
[fLastBufferIndex
]);
299 fTargetLock
.Unlock();
304 VideoConsumer::SetTryOverlay(bool tryOverlay
)
306 fTryOverlay
= tryOverlay
;
311 VideoConsumer::Connected(const media_source
& producer
,
312 const media_destination
& where
, const media_format
& format
,
313 media_input
* outInput
)
315 FUNCTION("VideoConsumer::Connected\n");
317 fIn
.source
= producer
;
320 sprintf(fIn
.name
, "Video Consumer");
325 status_t ret
= CreateBuffers(format
);
327 // TODO: With overlay bitmaps, there seems to be a problem with
328 // mapping the BBitmap areas into the BBuffers. Until that is fixed,
329 // don't enable a shared BBufferGroup.
331 ret
= SetOutputBuffersFor(producer
, fIn
.destination
,
332 fBuffers
, &userData
, &changeTag
, true);
334 ERROR("SetOutputBuffersFor() failed: %s\n", strerror(ret
));
336 fIn
.format
.u
.raw_video
.display
.bytes_per_row
337 = fBitmap
[0]->BytesPerRow();
339 ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n");
344 // bytes per row might have changed
345 fConnectionActive
= true;
347 FUNCTION("VideoConsumer::Connected - EXIT\n");
353 VideoConsumer::Disconnected(const media_source
& producer
,
354 const media_destination
& where
)
356 FUNCTION("VideoConsumer::Disconnected\n");
358 if (where
!= fIn
.destination
|| producer
!= fIn
.source
)
361 // reclaim our buffers
363 SetOutputBuffersFor(producer
, fIn
.destination
, NULL
, NULL
, &changeTag
,
366 status_t reclaimError
= fBuffers
->ReclaimAllBuffers();
367 if (reclaimError
!= B_OK
) {
368 fprintf(stderr
, "VideoConsumer::Disconnected() - Failed to "
369 "reclaim our buffers: %s\n", strerror(reclaimError
));
372 // disconnect the connection
373 fIn
.source
= media_source::null
;
374 fConnectionActive
= false;
376 // Unset the target's bitmap. Just to be safe -- since it is usually
377 // done when the stop event arrives, but someone may disonnect
378 // without stopping us before.
379 _UnsetTargetBuffer();
384 VideoConsumer::AcceptFormat(const media_destination
& dest
, media_format
* format
)
386 FUNCTION("VideoConsumer::AcceptFormat\n");
388 if (dest
!= fIn
.destination
) {
389 ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n");
390 return B_MEDIA_BAD_DESTINATION
;
393 if (format
->type
== B_MEDIA_NO_TYPE
)
394 format
->type
= B_MEDIA_RAW_VIDEO
;
396 if (format
->type
!= B_MEDIA_RAW_VIDEO
) {
397 ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n");
398 return B_MEDIA_BAD_FORMAT
;
401 if (format
->u
.raw_video
.display
.format
402 != media_raw_video_format::wildcard
.display
.format
) {
404 bool supported
= bitmaps_support_space(
405 format
->u
.raw_video
.display
.format
, &flags
);
406 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
407 // GRRR! BeOS implementation claims not
408 // to support these formats, while they work just fine.
409 switch (format
->u
.raw_video
.display
.format
) {
421 // cannot create bitmaps with such a color space
422 ERROR("AcceptFormat - unsupported color space for BBitmaps "
424 color_space_to_string(format
->u
.raw_video
.display
.format
));
425 return B_MEDIA_BAD_FORMAT
;
427 if (!fTryOverlay
&& (flags
& B_VIEWS_SUPPORT_DRAW_BITMAP
) == 0) {
428 // BViews do not support drawing such a bitmap
429 ERROR("AcceptFormat - BViews cannot draw bitmaps in given "
430 "colorspace (%s)!\n",
431 color_space_to_string(format
->u
.raw_video
.display
.format
));
432 return B_MEDIA_BAD_FORMAT
;
436 #ifdef TRACE_VIDEO_CONSUMER
439 string_for_format(*format
, string
, 256);
440 FUNCTION("VideoConsumer::AcceptFormat: %s\n", string
);
448 VideoConsumer::GetNextInput(int32
* cookie
, media_input
* outInput
)
450 FUNCTION("VideoConsumer::GetNextInput\n");
452 // custom build a destination for this connection
453 // put connection number in id
457 fIn
.destination
.id
= *cookie
;
458 sprintf(fIn
.name
, "Video Consumer");
463 return B_MEDIA_BAD_DESTINATION
;
469 VideoConsumer::DisposeInputCookie(int32
/*cookie*/)
475 VideoConsumer::GetLatencyFor(const media_destination
& whom
,
476 bigtime_t
* _latency
, media_node_id
* _timeSource
)
478 FUNCTION("VideoConsumer::GetLatencyFor\n");
480 if (whom
!= fIn
.destination
)
481 return B_MEDIA_BAD_DESTINATION
;
483 *_latency
= fMyLatency
;
484 *_timeSource
= TimeSource()->ID();
490 VideoConsumer::FormatChanged(const media_source
& producer
,
491 const media_destination
& consumer
, int32 fromChangeCount
,
492 const media_format
& format
)
494 FUNCTION("VideoConsumer::FormatChanged\n");
496 if (consumer
!= fIn
.destination
)
497 return B_MEDIA_BAD_DESTINATION
;
499 if (producer
!= fIn
.source
)
500 return B_MEDIA_BAD_SOURCE
;
504 return CreateBuffers(format
);
509 VideoConsumer::HandleEvent(const media_timed_event
* event
, bigtime_t lateness
,
512 LOOP("VideoConsumer::HandleEvent\n");
514 switch (event
->type
) {
515 case BTimedEventQueue::B_START
:
516 PROGRESS("VideoConsumer::HandleEvent - START\n");
517 _SetPerformanceTimeBase(event
->event_time
);
519 case BTimedEventQueue::B_WARP
:
520 case BTimedEventQueue::B_SEEK
:
521 PROGRESS("VideoConsumer::HandleEvent - WARP or SEEK\n");
522 _SetPerformanceTimeBase(event
->bigdata
);
525 case BTimedEventQueue::B_STOP
:
526 PROGRESS("VideoConsumer::HandleEvent - STOP\n");
527 EventQueue()->FlushEvents(event
->event_time
, BTimedEventQueue::B_ALWAYS
, true, BTimedEventQueue::B_HANDLE_BUFFER
);
528 // unset the target's bitmap
529 _UnsetTargetBuffer();
532 case BTimedEventQueue::B_HANDLE_BUFFER
:
533 LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n");
534 _HandleBuffer(static_cast<BBuffer
*>(event
->pointer
));
537 ERROR("VideoConsumer::HandleEvent - BAD EVENT\n");
544 VideoConsumer::_SetPerformanceTimeBase(bigtime_t performanceTime
)
546 fPerformanceTimeBase
= performanceTime
;
551 VideoConsumer::_HandleBuffer(BBuffer
* buffer
)
553 if (RunState() != B_STARTED
|| !fConnectionActive
) {
554 TRACE("RunState() != B_STARTED\n");
559 // See if this is one of our BBitmap buffers
562 while (index
< kBufferCount
) {
563 if (buffer
->ID() == fBufferMap
[index
]->ID())
568 if (index
== kBufferCount
) {
569 // Buffers belong to consumer
570 // NOTE: We maintain this in a member variable, since we still need
571 // to recycle this buffer later on, in case it was the last buffer
572 // received before shutting down.
574 index
= (fLastBufferIndex
+ 1) % kBufferCount
;
578 bigtime_t now
= TimeSource()->Now();
579 if (RunMode() == B_OFFLINE
580 || now
< buffer
->Header()->start_time
+ kMaxBufferLateness
) {
581 // Only display the buffer if it's not too late, or if we are
582 // in B_OFFLINE run-mode.
584 memcpy(fBitmap
[index
]->Bits(), buffer
->Data(),
585 fBitmap
[index
]->BitsLength());
587 bigtime_t tooEarly
= buffer
->Header()->start_time
- now
;
593 fTarget
->SetBitmap(fBitmap
[index
]);
595 // recycle the previous but not the current buffer
596 if (fLastBufferIndex
>= 0)
597 fBufferMap
[fLastBufferIndex
]->Recycle();
600 fLastBufferIndex
= index
;
602 fTargetLock
.Unlock();
604 // Drop the buffer if it's too late.
605 if (fManager
->LockWithTimeout(10000) == B_OK
) {
606 fManager
->FrameDropped();
609 PROGRESS("VideoConsumer::HandleEvent - DROPPED FRAME\n"
610 " start_time: %" B_PRIdBIGTIME
", current: %" B_PRIdBIGTIME
", "
611 "latency: %" B_PRIdBIGTIME
"\n", buffer
->Header()->start_time
,
612 TimeSource()->Now(), SchedulingLatency());
620 VideoConsumer::_UnsetTargetBuffer()
623 if (fLastBufferIndex
>= 0) {
625 fTarget
->SetBitmap(NULL
);
627 fBufferMap
[fLastBufferIndex
]->Recycle();
628 fLastBufferIndex
= -1;
630 fTargetLock
.Unlock();