1/* 2 * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>. All rights reserved. 3 * Copyright (C) 2008 Maurice Kalinowski <haiku@kaldience.com>. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7#include "VideoNode.h" 8#include "VideoView.h" 9#include "VideoWindow.h" 10 11 12#include <Bitmap.h> 13#include <Buffer.h> 14#include <BufferGroup.h> 15#include <Debug.h> 16#include <MediaRoster.h> 17#include <Locker.h> 18#include <TimeSource.h> 19#include <Window.h> 20#include <stdio.h> 21#include <string.h> 22 23 24VideoNode::VideoNode(const char *name) 25 : BMediaNode(name) 26 , BMediaEventLooper() 27 , BBufferConsumer(B_MEDIA_RAW_VIDEO) 28 , fWindow(0) 29 , fVideoView(0) 30 , fInput() 31 , fOverlayEnabled(true) 32 , fOverlayActive(false) 33 , fDirectOverlayBuffer(false) 34 , fBitmap(0) 35 , fBitmapLocker(new BLocker("Video Node Locker")) 36 , fAddOn(0) 37 , fInternalFlavorId(0) 38{ 39 _InitDisplay(); 40} 41 42 43VideoNode::VideoNode(const char *name, BMediaAddOn* addon, int32 id) 44 : BMediaNode(name) 45 , BMediaEventLooper() 46 , BBufferConsumer(B_MEDIA_RAW_VIDEO) 47 , fWindow(0) 48 , fVideoView(0) 49 , fInput() 50 , fOverlayEnabled(true) 51 , fOverlayActive(false) 52 , fDirectOverlayBuffer(false) 53 , fBitmap(0) 54 , fBitmapLocker(new BLocker("Video Node Locker")) 55 , fAddOn(addon) 56 , fInternalFlavorId(id) 57{ 58 _InitDisplay(); 59} 60 61 62VideoNode::~VideoNode() 63{ 64 Quit(); 65 DeleteBuffers(); 66 delete fBitmapLocker; 67 if (fWindow && fWindow->Lock()) 68 fWindow->Quit(); 69} 70 71 72BMediaAddOn * 73VideoNode::AddOn(int32 *internal_id) const 74{ 75 *internal_id = fInternalFlavorId; 76 return fAddOn; 77} 78 79 80void 81VideoNode::NodeRegistered() 82{ 83 fInput.node = Node(); 84 fInput.source = media_source::null; 85 fInput.destination.port = ControlPort(); 86 fInput.destination.id = 0; 87 fInput.format.type = B_MEDIA_RAW_VIDEO; 88 fInput.format.u.raw_video = media_raw_video_format::wildcard; 89 strcpy(fInput.name, "video in"); 90 91 SetPriority(B_DISPLAY_PRIORITY); 92 Run(); 93} 94 95 96void 97VideoNode::BufferReceived(BBuffer * buffer) 98{ 99 if (RunState() != B_STARTED) { 100 buffer->Recycle(); 101 return; 102 } 103 if (fOverlayActive && fDirectOverlayBuffer) { 104 HandleBuffer(buffer); 105 } else { 106 media_timed_event event(buffer->Header()->start_time, 107 BTimedEventQueue::B_HANDLE_BUFFER, buffer, 108 BTimedEventQueue::B_RECYCLE_BUFFER); 109 EventQueue()->AddEvent(event); 110 } 111} 112 113 114status_t 115VideoNode::GetNextInput(int32 *cookie, media_input *out_input) 116{ 117 if (*cookie < 1) { 118 *out_input = fInput; 119 *cookie += 1; 120 return B_OK; 121 } 122 return B_ERROR; 123} 124 125 126void 127VideoNode::DisposeInputCookie(int32 cookie) 128{ 129 // nothing to do 130} 131 132 133status_t 134VideoNode:: HandleMessage(int32 message, const void *data, size_t size) 135{ 136 return B_ERROR; 137} 138 139 140void 141VideoNode::HandleEvent(const media_timed_event *event, bigtime_t lateness, 142 bool realTimeEvent) 143{ 144 switch (event->type) { 145 case BTimedEventQueue::B_START: 146 break; 147 case BTimedEventQueue::B_STOP: 148 EventQueue()->FlushEvents(event->event_time, 149 BTimedEventQueue::B_ALWAYS, true, 150 BTimedEventQueue::B_HANDLE_BUFFER); 151 break; 152 case BTimedEventQueue::B_HANDLE_BUFFER: 153 HandleBuffer((BBuffer *)event->pointer); 154 break; 155 case BTimedEventQueue::B_SEEK: 156 fprintf(stderr, "VideoNode::HandleEvent Seek event not handled\n"); 157 break; 158 default: 159 fprintf(stderr, "VideoNode::HandleEvent unknown event\n"); 160 break; 161 } 162} 163 164 165void 166VideoNode::ProducerDataStatus(const media_destination &dst, int32 status, 167 bigtime_t at_media_time) 168{ 169 // do nothing 170} 171 172 173status_t 174VideoNode::GetLatencyFor(const media_destination &dst, bigtime_t *out_latency, 175 media_node_id *out_id) 176{ 177 if (dst != fInput.destination) 178 return B_MEDIA_BAD_DESTINATION; 179 180 *out_latency = 10000; 181 *out_id = TimeSource()->ID(); 182 return B_OK; 183} 184 185 186status_t 187VideoNode::AcceptFormat(const media_destination &dst, media_format *format) 188{ 189 /* The connection process: 190 * BBufferProducer::FormatProposal 191 * we are here => BBufferConsumer::AcceptFormat 192 * BBufferProducer::PrepareToConnect 193 * BBufferConsumer::Connected 194 * BBufferProducer::Connect 195 */ 196 if (dst != fInput.destination) 197 return B_MEDIA_BAD_DESTINATION; 198 199 if (format->type == B_MEDIA_NO_TYPE) 200 format->type = B_MEDIA_RAW_VIDEO; 201 202 if (format->type != B_MEDIA_RAW_VIDEO) 203 return B_MEDIA_BAD_FORMAT; 204 205 return B_OK; 206} 207 208 209status_t 210VideoNode::Connected(const media_source &src, const media_destination &dst, 211 const media_format &format, media_input *out_input) 212{ 213 /* The connection process: 214 * BBufferProducer::FormatProposal 215 * BBufferConsumer::AcceptFormat 216 * BBufferProducer::PrepareToConnect 217 * we are here => BBufferConsumer::Connected 218 * BBufferProducer::Connect 219 */ 220 221 if (dst != fInput.destination) 222 return B_MEDIA_BAD_DESTINATION; 223 224 fInput.source = src; 225 fInput.format = format; 226 227 if (fInput.format.u.raw_video.field_rate < 1.0) 228 fInput.format.u.raw_video.field_rate = 25.0; 229 230 // In order to display video we need to create a buffer that is either 231 // in the overlay colorspace B_YCbCr422 232 // or the requested colorspace if not B_YCbCr422 233 // and we need to tell the node upstream of our choice 234 235 BRect frame(0, 0, format.u.raw_video.display.line_width - 1, 236 format.u.raw_video.display.line_count - 1); 237 238 DeleteBuffers(); 239 status_t err; 240 241 if (format.u.raw_video.display.format == B_NO_COLOR_SPACE) { 242 // upstream node is leaving it up to us so we try overlay then 243 // B_RGBA32 (We probably should try what format the screen is) 244 err = CreateBuffers(frame, B_YCbCr422, true); 245 SetOverlayEnabled(err == B_OK); 246 if (!fOverlayEnabled) { 247 // no overlay available so fall back to RGBA32 248 err = CreateBuffers(frame, B_RGBA32, false); 249 } 250 } else if (format.u.raw_video.display.format == B_YCbCr422) { 251 // upstream node is likely requesting overlay 252 err = CreateBuffers(frame, B_YCbCr422, true); 253 SetOverlayEnabled(err == B_OK); 254 // if we cannot give them what they want then return error 255 } else { 256 // upstream node is requesting some other format 257 SetOverlayEnabled(false); 258 err = CreateBuffers(frame, format.u.raw_video.display.format, 259 fOverlayEnabled); 260 } 261 262 if (err) { 263 fprintf(stderr, "VideoNode::Connected failed, fOverlayEnabled = %d\n", 264 fOverlayEnabled); 265 return err; 266 } else { 267 fInput.format.u.raw_video.display.format = fBitmap->ColorSpace(); 268 } 269 270 *out_input = fInput; 271 272 return B_OK; 273} 274 275 276void 277VideoNode::Disconnected(const media_source &src, const media_destination &dst) 278{ 279 if (src != fInput.source) 280 return; 281 if (dst != fInput.destination) 282 return; 283 284 DeleteBuffers(); 285 286 // disconnect the connection 287 fInput.source = media_source::null; 288} 289 290 291status_t 292VideoNode::FormatChanged(const media_source &src, const media_destination &dst, 293 int32 from_change_count, const media_format &format) 294{ 295 if (src != fInput.source) 296 return B_MEDIA_BAD_SOURCE; 297 if (dst != fInput.destination) 298 return B_MEDIA_BAD_DESTINATION; 299 300 color_space colorspace = format.u.raw_video.display.format; 301 BRect frame(0, 0, format.u.raw_video.display.line_width - 1, 302 format.u.raw_video.display.line_count - 1); 303 304 status_t err; 305 306 DeleteBuffers(); 307 if (fOverlayEnabled) { 308 fVideoView->RemoveOverlay(); 309 err = CreateBuffers(frame, colorspace, true); // try overlay 310 if (err) { 311 fprintf(stderr, "VideoNode::FormatChanged creating overlay buffer failed\n"); 312 err = CreateBuffers(frame, colorspace, false); // no overlay 313 } 314 } else { 315 err = CreateBuffers(frame, colorspace, false); // no overlay 316 } 317 318 if (err) { 319 fprintf(stderr, "VideoNode::FormatChanged failed (lost buffer group!)\n"); 320 return B_MEDIA_BAD_FORMAT; 321 } 322 323 fInput.format = format; 324 325 return B_OK; 326} 327 328 329void 330VideoNode::HandleBuffer(BBuffer *buffer) 331{ 332 LockBitmap(); 333 if (fBitmap && fWindow && fVideoView) { 334// bigtime_t start = system_time(); 335 if (fOverlayActive) { 336 if (B_OK == fBitmap->LockBits()) { 337 memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength()); 338 fBitmap->UnlockBits(); 339 } 340 } else { 341 memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength()); 342 } 343// printf("overlay copy: %lld usec\n", system_time() - start); 344 } 345 UnlockBitmap(); 346 347 buffer->Recycle(); 348 349 fVideoView->DrawFrame(); 350} 351 352 353void 354VideoNode::SetOverlayEnabled(bool yesno) 355{ 356 fOverlayEnabled = yesno; 357} 358 359 360void 361VideoNode::LockBitmap() 362{ 363 fBitmapLocker->Lock(); 364} 365 366 367BBitmap * 368VideoNode::Bitmap() 369{ 370 return fBitmap; 371} 372 373 374void 375VideoNode::UnlockBitmap() 376{ 377 fBitmapLocker->Unlock(); 378} 379 380 381bool 382VideoNode::IsOverlayActive() 383{ 384 return fOverlayActive; 385} 386 387 388status_t 389VideoNode::CreateBuffers(BRect frame, color_space cspace, bool overlay) 390{ 391 printf("VideoNode::CreateBuffers: frame %d,%d,%d,%d colorspace 0x%08x, " 392 "overlay %d\n", int(frame.left), int(frame.top), int(frame.right), 393 int(frame.bottom), int(cspace), overlay); 394 395 LockBitmap(); 396 ASSERT(fBitmap == 0); 397 398 uint32 flags = 0; 399 if (overlay) 400 flags = B_BITMAP_WILL_OVERLAY | B_BITMAP_RESERVE_OVERLAY_CHANNEL; 401 402 fBitmap = new BBitmap(frame, flags, cspace); 403 if (!(fBitmap && fBitmap->InitCheck() == B_OK && fBitmap->IsValid())) { 404 delete fBitmap; 405 fBitmap = NULL; 406 fOverlayActive = false; 407 UnlockBitmap(); 408 fprintf(stderr, "VideoNode::CreateBuffers failed\n"); 409 return B_MEDIA_BAD_FORMAT; 410 } 411 fOverlayActive = overlay; 412 UnlockBitmap(); 413 414 return B_OK; 415} 416 417 418void 419VideoNode::DeleteBuffers() 420{ 421 LockBitmap(); 422 delete fBitmap; 423 fBitmap = NULL; 424 UnlockBitmap(); 425} 426 427 428void 429VideoNode::_InitDisplay() 430{ 431 // TODO: Get rid of hardcoded values 432 BRect size(0,0,320,240); 433 fVideoView = new VideoView(size, "Video View", B_FOLLOW_ALL_SIDES, 434 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE, this); 435 436 size.OffsetBy(40.f, 40.f); 437 fWindow = new VideoWindow(size, fVideoView); 438 fWindow->Show(); 439} 440