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 return B_OK; 206 } 207 208 209 status_t 210 VideoNode::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 276 void 277 VideoNode::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 291 status_t 292 VideoNode::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 329 void 330 VideoNode::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 353 void 354 VideoNode::SetOverlayEnabled(bool yesno) 355 { 356 fOverlayEnabled = yesno; 357 } 358 359 360 void 361 VideoNode::LockBitmap() 362 { 363 fBitmapLocker->Lock(); 364 } 365 366 367 BBitmap * 368 VideoNode::Bitmap() 369 { 370 return fBitmap; 371 } 372 373 374 void 375 VideoNode::UnlockBitmap() 376 { 377 fBitmapLocker->Unlock(); 378 } 379 380 381 bool 382 VideoNode::IsOverlayActive() 383 { 384 return fOverlayActive; 385 } 386 387 388 status_t 389 VideoNode::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 418 void 419 VideoNode::DeleteBuffers() 420 { 421 LockBitmap(); 422 delete fBitmap; 423 fBitmap = NULL; 424 UnlockBitmap(); 425 } 426 427 428 void 429 VideoNode::_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