vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / media / media-add-ons / videowindow / VideoNode.cpp
blobd8b7fe9e976b8b5475e8a70c7fdb968d515b1f64
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.
5 * Distributed under the terms of the MIT License.
6 */
7 #include "VideoNode.h"
8 #include "VideoView.h"
9 #include "VideoWindow.h"
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>
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)
39 _InitDisplay();
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)
58 _InitDisplay();
62 VideoNode::~VideoNode()
64 Quit();
65 DeleteBuffers();
66 delete fBitmapLocker;
67 if (fWindow && fWindow->Lock())
68 fWindow->Quit();
72 BMediaAddOn *
73 VideoNode::AddOn(int32 *internal_id) const
75 *internal_id = fInternalFlavorId;
76 return fAddOn;
80 void
81 VideoNode::NodeRegistered()
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");
91 SetPriority(B_DISPLAY_PRIORITY);
92 Run();
96 void
97 VideoNode::BufferReceived(BBuffer * buffer)
99 if (RunState() != B_STARTED) {
100 buffer->Recycle();
101 return;
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);
114 status_t
115 VideoNode::GetNextInput(int32 *cookie, media_input *out_input)
117 if (*cookie < 1) {
118 *out_input = fInput;
119 *cookie += 1;
120 return B_OK;
122 return B_ERROR;
126 void
127 VideoNode::DisposeInputCookie(int32 cookie)
129 // nothing to do
133 status_t
134 VideoNode:: HandleMessage(int32 message, const void *data, size_t size)
136 return B_ERROR;
140 void
141 VideoNode::HandleEvent(const media_timed_event *event, bigtime_t lateness,
142 bool realTimeEvent)
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;
165 void
166 VideoNode::ProducerDataStatus(const media_destination &dst, int32 status,
167 bigtime_t at_media_time)
169 // do nothing
173 status_t
174 VideoNode::GetLatencyFor(const media_destination &dst, bigtime_t *out_latency,
175 media_node_id *out_id)
177 if (dst != fInput.destination)
178 return B_MEDIA_BAD_DESTINATION;
180 *out_latency = 10000;
181 *out_id = TimeSource()->ID();
182 return B_OK;
186 status_t
187 VideoNode::AcceptFormat(const media_destination &dst, media_format *format)
189 /* The connection process:
190 * BBufferProducer::FormatProposal
191 * we are here => BBufferConsumer::AcceptFormat
192 * BBufferProducer::PrepareToConnect
193 * BBufferConsumer::Connected
194 * BBufferProducer::Connect
196 if (dst != fInput.destination)
197 return B_MEDIA_BAD_DESTINATION;
199 if (format->type == B_MEDIA_NO_TYPE)
200 format->type = B_MEDIA_RAW_VIDEO;
202 if (format->type != B_MEDIA_RAW_VIDEO)
203 return B_MEDIA_BAD_FORMAT;
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
210 BRect frame(0, 0, format->u.raw_video.display.line_width,
211 format->u.raw_video.display.line_count);
213 DeleteBuffers();
214 status_t err;
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);
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);
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();
243 return B_OK;
247 status_t
248 VideoNode::Connected(const media_source &src, const media_destination &dst,
249 const media_format &format, media_input *out_input)
251 /* The connection process:
252 * BBufferProducer::FormatProposal
253 * BBufferConsumer::AcceptFormat
254 * BBufferProducer::PrepareToConnect
255 * we are here => BBufferConsumer::Connected
256 * BBufferProducer::Connect
259 if (dst != fInput.destination)
260 return B_MEDIA_BAD_DESTINATION;
262 fInput.source = src;
263 fInput.format = format;
265 if (fInput.format.u.raw_video.field_rate < 1.0)
266 fInput.format.u.raw_video.field_rate = 25.0;
268 *out_input = fInput;
270 return B_OK;
274 void
275 VideoNode::Disconnected(const media_source &src, const media_destination &dst)
277 if (src != fInput.source)
278 return;
279 if (dst != fInput.destination)
280 return;
282 DeleteBuffers();
284 // disconnect the connection
285 fInput.source = media_source::null;
289 status_t
290 VideoNode::FormatChanged(const media_source &src, const media_destination &dst,
291 int32 from_change_count, const media_format &format)
293 if (src != fInput.source)
294 return B_MEDIA_BAD_SOURCE;
295 if (dst != fInput.destination)
296 return B_MEDIA_BAD_DESTINATION;
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);
302 status_t err;
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
312 } else {
313 err = CreateBuffers(frame, colorspace, false); // no overlay
316 if (err) {
317 fprintf(stderr, "VideoNode::FormatChanged failed (lost buffer group!)\n");
318 return B_MEDIA_BAD_FORMAT;
321 fInput.format = format;
323 return B_OK;
327 void
328 VideoNode::HandleBuffer(BBuffer *buffer)
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();
338 } else {
339 memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength());
341 // printf("overlay copy: %Ld usec\n", system_time() - start);
343 UnlockBitmap();
345 buffer->Recycle();
347 fVideoView->DrawFrame();
351 void
352 VideoNode::SetOverlayEnabled(bool yesno)
354 fOverlayEnabled = yesno;
358 void
359 VideoNode::LockBitmap()
361 fBitmapLocker->Lock();
365 BBitmap *
366 VideoNode::Bitmap()
368 return fBitmap;
372 void
373 VideoNode::UnlockBitmap()
375 fBitmapLocker->Unlock();
379 bool
380 VideoNode::IsOverlayActive()
382 return fOverlayActive;
386 status_t
387 VideoNode::CreateBuffers(BRect frame, color_space cspace, bool overlay)
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);
393 LockBitmap();
394 ASSERT(fBitmap == 0);
396 uint32 flags = 0;
397 if (overlay)
398 flags = B_BITMAP_WILL_OVERLAY | B_BITMAP_RESERVE_OVERLAY_CHANNEL;
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;
409 fOverlayActive = overlay;
410 UnlockBitmap();
412 return B_OK;
416 void
417 VideoNode::DeleteBuffers()
419 LockBitmap();
420 delete fBitmap;
421 fBitmap = NULL;
422 UnlockBitmap();
426 void
427 VideoNode::_InitDisplay()
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);
434 size.OffsetBy(40.f, 40.f);
435 fWindow = new VideoWindow(size, fVideoView);
436 fWindow->Show();