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 VideoNode::~VideoNode() 62 { 63 Quit(); 64 DeleteBuffers(); 65 delete fBitmapLocker; 66 if (fWindow) 67 fWindow->Quit(); 68 } 69 70 71 BMediaAddOn * 72 VideoNode::AddOn(int32 *internal_id) const 73 { 74 *internal_id = fInternalFlavorId; 75 return fAddOn; 76 } 77 78 79 void 80 VideoNode::NodeRegistered() 81 { 82 fInput.node = Node(); 83 fInput.source = media_source::null; 84 fInput.destination.port = ControlPort(); 85 fInput.destination.id = 0; 86 fInput.format.type = B_MEDIA_RAW_VIDEO; 87 fInput.format.u.raw_video = media_raw_video_format::wildcard; 88 strcpy(fInput.name, "video in"); 89 90 SetPriority(B_DISPLAY_PRIORITY); 91 Run(); 92 } 93 94 95 void 96 VideoNode::BufferReceived(BBuffer * buffer) 97 { 98 if (RunState() != B_STARTED) { 99 buffer->Recycle(); 100 return; 101 } 102 if (fOverlayActive && fDirectOverlayBuffer) { 103 HandleBuffer(buffer); 104 } else { 105 media_timed_event event(buffer->Header()->start_time, 106 BTimedEventQueue::B_HANDLE_BUFFER, 107 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, 135 const void *data, 136 size_t size) 137 { 138 return B_ERROR; 139 } 140 141 142 void 143 VideoNode::HandleEvent(const media_timed_event *event, 144 bigtime_t lateness, 145 bool realTimeEvent) 146 { 147 switch (event->type) { 148 case BTimedEventQueue::B_START: 149 break; 150 case BTimedEventQueue::B_STOP: 151 EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); 152 break; 153 case BTimedEventQueue::B_HANDLE_BUFFER: 154 HandleBuffer((BBuffer *)event->pointer); 155 break; 156 default: 157 fprintf(stderr, "VideoNode::HandleEvent unknown event"); 158 break; 159 } 160 } 161 162 163 void 164 VideoNode::ProducerDataStatus(const media_destination &dst, 165 int32 status, 166 bigtime_t at_media_time) 167 { 168 // do nothing 169 } 170 171 172 status_t 173 VideoNode::GetLatencyFor(const media_destination &dst, 174 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, 188 media_format *format) 189 { 190 /* The connection process: 191 * BBufferProducer::FormatProposal 192 * we are here => BBufferConsumer::AcceptFormat 193 * BBufferProducer::PrepareToConnect 194 * BBufferConsumer::Connected 195 * BBufferProducer::Connect 196 */ 197 if (dst != fInput.destination) 198 return B_MEDIA_BAD_DESTINATION; 199 200 if (format->type == B_MEDIA_NO_TYPE) 201 format->type = B_MEDIA_RAW_VIDEO; 202 203 if (format->type != B_MEDIA_RAW_VIDEO) 204 return B_MEDIA_BAD_FORMAT; 205 206 return B_OK; 207 } 208 209 210 status_t 211 VideoNode::Connected(const media_source &src, 212 const media_destination &dst, 213 const media_format &format, 214 media_input *out_input) 215 { 216 /* The connection process: 217 * BBufferProducer::FormatProposal 218 * BBufferConsumer::AcceptFormat 219 * BBufferProducer::PrepareToConnect 220 * we are here => BBufferConsumer::Connected 221 * BBufferProducer::Connect 222 */ 223 224 if (dst != fInput.destination) 225 return B_MEDIA_BAD_DESTINATION; 226 227 fInput.source = src; 228 fInput.format = format; 229 230 if (fInput.format.u.raw_video.field_rate < 1.0) 231 fInput.format.u.raw_video.field_rate = 25.0; 232 233 color_space colorspace = format.u.raw_video.display.format; 234 BRect frame(0, 0, format.u.raw_video.display.line_width - 1, format.u.raw_video.display.line_count - 1); 235 status_t err; 236 237 DeleteBuffers(); 238 err = CreateBuffers(frame, colorspace, fOverlayEnabled); 239 if (err && fOverlayEnabled) { 240 SetOverlayEnabled(false); 241 err = CreateBuffers(frame, colorspace, fOverlayEnabled); 242 } 243 244 if (err) { 245 fprintf(stderr, "VideoNode::Connected failed, fOverlayEnabled = %d\n", fOverlayEnabled); 246 return err; 247 } 248 249 *out_input = fInput; 250 251 return B_OK; 252 } 253 254 255 void 256 VideoNode::Disconnected(const media_source &src, 257 const media_destination &dst) 258 { 259 if (src != fInput.source) 260 return; 261 if (dst != fInput.destination) 262 return; 263 264 DeleteBuffers(); 265 266 // disconnect the connection 267 fInput.source = media_source::null; 268 } 269 270 271 status_t 272 VideoNode::FormatChanged(const media_source &src, 273 const media_destination &dst, 274 int32 from_change_count, 275 const media_format &format) 276 { 277 if (src != fInput.source) 278 return B_MEDIA_BAD_SOURCE; 279 if (dst != fInput.destination) 280 return B_MEDIA_BAD_DESTINATION; 281 282 color_space colorspace = format.u.raw_video.display.format; 283 BRect frame(0, 0, format.u.raw_video.display.line_width - 1, format.u.raw_video.display.line_count - 1); 284 status_t err; 285 286 DeleteBuffers(); 287 if (fOverlayEnabled) { 288 fVideoView->RemoveOverlay(); 289 err = CreateBuffers(frame, colorspace, true); // try overlay 290 if (err) { 291 fprintf(stderr, "VideoNode::FormatChanged creating overlay buffer failed\n"); 292 err = CreateBuffers(frame, colorspace, false); // no overlay 293 } 294 } else { 295 err = CreateBuffers(frame, colorspace, false); // no overlay 296 } 297 298 if (err) { 299 fprintf(stderr, "VideoNode::FormatChanged failed (lost buffer group!)\n"); 300 return B_MEDIA_BAD_FORMAT; 301 } 302 303 fInput.format = format; 304 305 return B_OK; 306 } 307 308 309 void 310 VideoNode::HandleBuffer(BBuffer *buffer) 311 { 312 LockBitmap(); 313 if (fBitmap && fWindow && fVideoView) { 314 // bigtime_t start = system_time(); 315 if (fOverlayActive) { 316 if (B_OK == fBitmap->LockBits()) { 317 memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength()); 318 fBitmap->UnlockBits(); 319 } 320 } else { 321 memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength()); 322 } 323 // printf("overlay copy: %Ld usec\n", system_time() - start); 324 } 325 UnlockBitmap(); 326 327 buffer->Recycle(); 328 329 fVideoView->DrawFrame(); 330 } 331 332 333 void 334 VideoNode::SetOverlayEnabled(bool yesno) 335 { 336 fOverlayEnabled = yesno; 337 } 338 339 340 void 341 VideoNode::LockBitmap() 342 { 343 fBitmapLocker->Lock(); 344 } 345 346 347 BBitmap * 348 VideoNode::Bitmap() 349 { 350 return fBitmap; 351 } 352 353 354 void 355 VideoNode::UnlockBitmap() 356 { 357 fBitmapLocker->Unlock(); 358 } 359 360 361 bool 362 VideoNode::IsOverlayActive() 363 { 364 return fOverlayActive; 365 } 366 367 368 status_t 369 VideoNode::CreateBuffers(BRect frame, color_space cspace, bool overlay) 370 { 371 //printf("VideoNode::CreateBuffers: frame %d,%d,%d,%d colorspace 0x%08x, overlay %d\n", 372 // int(frame.left), int(frame.top), int(frame.right), int(frame.bottom), int(cspace), overlay); 373 374 LockBitmap(); 375 ASSERT(fBitmap == 0); 376 uint32 flags = overlay ? (B_BITMAP_WILL_OVERLAY | B_BITMAP_RESERVE_OVERLAY_CHANNEL) : 0; 377 fBitmap = new BBitmap(frame, flags, cspace); 378 if (!(fBitmap && fBitmap->InitCheck() == B_OK && fBitmap->IsValid())) { 379 delete fBitmap; 380 fBitmap = 0; 381 fOverlayActive = false; 382 UnlockBitmap(); 383 fprintf(stderr, "VideoNode::CreateBuffers failed\n"); 384 return B_ERROR; 385 } 386 fOverlayActive = overlay; 387 UnlockBitmap(); 388 389 return B_OK; 390 } 391 392 393 void 394 VideoNode::DeleteBuffers() 395 { 396 LockBitmap(); 397 delete fBitmap; 398 fBitmap = NULL; 399 UnlockBitmap(); 400 } 401 402 403 void 404 VideoNode::_InitDisplay() 405 { 406 // TODO: Get rid of hardcoded values 407 BRect size(0,0,320,240); 408 fVideoView = new VideoView(size, "Video View", B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE, this); 409 410 size.OffsetBy(40.f, 40.f); 411 fWindow = new VideoWindow(size, fVideoView); 412 fWindow->Show(); 413 } 414