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 24 VideoNode::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 43 VideoNode::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 62 VideoNode::~VideoNode() 63 { 64 Quit(); 65 DeleteBuffers(); 66 delete fBitmapLocker; 67 if (fWindow && fWindow->Lock()) 68 fWindow->Quit(); 69 } 70 71 72 BMediaAddOn * 73 VideoNode::AddOn(int32 *internal_id) const 74 { 75 *internal_id = fInternalFlavorId; 76 return fAddOn; 77 } 78 79 80 void 81 VideoNode::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 96 void 97 VideoNode::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 114 status_t 115 VideoNode::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 126 void 127 VideoNode::DisposeInputCookie(int32 cookie) 128 { 129 // nothing to do 130 } 131 132 133 status_t 134 VideoNode:: HandleMessage(int32 message, const void *data, size_t size) 135 { 136 return B_ERROR; 137 } 138 139 140 void 141 VideoNode::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 165 void 166 VideoNode::ProducerDataStatus(const media_destination &dst, int32 status, 167 bigtime_t at_media_time) 168 { 169 // do nothing 170 } 171 172 173 status_t 174 VideoNode::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 186 status_t 187 VideoNode::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 // In order to display video we need to create a buffer that is either 206 // in the overlay colorspace B_YCbCr422 207 // or the requested colorspace if not B_YCbCr422 208 // and we need to tell the node upstream of our choice 209 210 BRect frame(0, 0, format->u.raw_video.display.line_width - 1, 211 format->u.raw_video.display.line_count - 1); 212 213 DeleteBuffers(); 214 status_t err; 215 216 if (format->u.raw_video.display.format == B_NO_COLOR_SPACE) { 217 // upstream node is leaving it up to us so we try overlay then B_RGBA32 (We probably should try what format the screen is) 218 err = CreateBuffers(frame, B_YCbCr422, true); 219 SetOverlayEnabled(err == B_OK); 220 if (!fOverlayEnabled) { 221 // no overlay available so fall back to RGBA32 222 err = CreateBuffers(frame, B_RGBA32, false); 223 } 224 } else if (format->u.raw_video.display.format == B_YCbCr422) { 225 // upstream node is likely requesting overlay 226 err = CreateBuffers(frame, B_YCbCr422, true); 227 SetOverlayEnabled(err == B_OK); 228 // if we cannot give them what they want then return error 229 } else { 230 // upstream node is requesting some other format 231 SetOverlayEnabled(false); 232 err = CreateBuffers(frame, format->u.raw_video.display.format, fOverlayEnabled); 233 } 234 235 if (err) { 236 fprintf(stderr, "VideoNode::Connected failed, fOverlayEnabled = %d\n", 237 fOverlayEnabled); 238 return err; 239 } else { 240 format->u.raw_video.display.format = fBitmap->ColorSpace(); 241 } 242 243 return B_OK; 244 } 245 246 247 status_t 248 VideoNode::Connected(const media_source &src, const media_destination &dst, 249 const media_format &format, media_input *out_input) 250 { 251 /* The connection process: 252 * BBufferProducer::FormatProposal 253 * BBufferConsumer::AcceptFormat 254 * BBufferProducer::PrepareToConnect 255 * we are here => BBufferConsumer::Connected 256 * BBufferProducer::Connect 257 */ 258 259 if (dst != fInput.destination) 260 return B_MEDIA_BAD_DESTINATION; 261 262 fInput.source = src; 263 fInput.format = format; 264 265 if (fInput.format.u.raw_video.field_rate < 1.0) 266 fInput.format.u.raw_video.field_rate = 25.0; 267 268 *out_input = fInput; 269 270 return B_OK; 271 } 272 273 274 void 275 VideoNode::Disconnected(const media_source &src, const media_destination &dst) 276 { 277 if (src != fInput.source) 278 return; 279 if (dst != fInput.destination) 280 return; 281 282 DeleteBuffers(); 283 284 // disconnect the connection 285 fInput.source = media_source::null; 286 } 287 288 289 status_t 290 VideoNode::FormatChanged(const media_source &src, const media_destination &dst, 291 int32 from_change_count, const media_format &format) 292 { 293 if (src != fInput.source) 294 return B_MEDIA_BAD_SOURCE; 295 if (dst != fInput.destination) 296 return B_MEDIA_BAD_DESTINATION; 297 298 color_space colorspace = format.u.raw_video.display.format; 299 BRect frame(0, 0, format.u.raw_video.display.line_width - 1, 300 format.u.raw_video.display.line_count - 1); 301 302 status_t err; 303 304 DeleteBuffers(); 305 if (fOverlayEnabled) { 306 fVideoView->RemoveOverlay(); 307 err = CreateBuffers(frame, colorspace, true); // try overlay 308 if (err) { 309 fprintf(stderr, "VideoNode::FormatChanged creating overlay buffer failed\n"); 310 err = CreateBuffers(frame, colorspace, false); // no overlay 311 } 312 } else { 313 err = CreateBuffers(frame, colorspace, false); // no overlay 314 } 315 316 if (err) { 317 fprintf(stderr, "VideoNode::FormatChanged failed (lost buffer group!)\n"); 318 return B_MEDIA_BAD_FORMAT; 319 } 320 321 fInput.format = format; 322 323 return B_OK; 324 } 325 326 327 void 328 VideoNode::HandleBuffer(BBuffer *buffer) 329 { 330 LockBitmap(); 331 if (fBitmap && fWindow && fVideoView) { 332 // bigtime_t start = system_time(); 333 if (fOverlayActive) { 334 if (B_OK == fBitmap->LockBits()) { 335 memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength()); 336 fBitmap->UnlockBits(); 337 } 338 } else { 339 memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength()); 340 } 341 // printf("overlay copy: %Ld usec\n", system_time() - start); 342 } 343 UnlockBitmap(); 344 345 buffer->Recycle(); 346 347 fVideoView->DrawFrame(); 348 } 349 350 351 void 352 VideoNode::SetOverlayEnabled(bool yesno) 353 { 354 fOverlayEnabled = yesno; 355 } 356 357 358 void 359 VideoNode::LockBitmap() 360 { 361 fBitmapLocker->Lock(); 362 } 363 364 365 BBitmap * 366 VideoNode::Bitmap() 367 { 368 return fBitmap; 369 } 370 371 372 void 373 VideoNode::UnlockBitmap() 374 { 375 fBitmapLocker->Unlock(); 376 } 377 378 379 bool 380 VideoNode::IsOverlayActive() 381 { 382 return fOverlayActive; 383 } 384 385 386 status_t 387 VideoNode::CreateBuffers(BRect frame, color_space cspace, bool overlay) 388 { 389 printf("VideoNode::CreateBuffers: frame %d,%d,%d,%d colorspace 0x%08x, " 390 "overlay %d\n", int(frame.left), int(frame.top), int(frame.right), 391 int(frame.bottom), int(cspace), overlay); 392 393 LockBitmap(); 394 ASSERT(fBitmap == 0); 395 396 uint32 flags = 0; 397 if (overlay) 398 flags = B_BITMAP_WILL_OVERLAY | B_BITMAP_RESERVE_OVERLAY_CHANNEL; 399 400 fBitmap = new BBitmap(frame, flags, cspace); 401 if (!(fBitmap && fBitmap->InitCheck() == B_OK && fBitmap->IsValid())) { 402 delete fBitmap; 403 fBitmap = NULL; 404 fOverlayActive = false; 405 UnlockBitmap(); 406 fprintf(stderr, "VideoNode::CreateBuffers failed\n"); 407 return B_MEDIA_BAD_FORMAT; 408 } 409 fOverlayActive = overlay; 410 UnlockBitmap(); 411 412 return B_OK; 413 } 414 415 416 void 417 VideoNode::DeleteBuffers() 418 { 419 LockBitmap(); 420 delete fBitmap; 421 fBitmap = NULL; 422 UnlockBitmap(); 423 } 424 425 426 void 427 VideoNode::_InitDisplay() 428 { 429 // TODO: Get rid of hardcoded values 430 BRect size(0,0,320,240); 431 fVideoView = new VideoView(size, "Video View", B_FOLLOW_ALL_SIDES, 432 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE, this); 433 434 size.OffsetBy(40.f, 40.f); 435 fWindow = new VideoWindow(size, fVideoView); 436 fWindow->Show(); 437 } 438