vfs: check userland buffers before reading them.
[haiku.git] / src / apps / codycam / VideoConsumer.cpp
blob15d3440437a2930db08b5e90f2a7a91858a04025
1 // Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
2 // SMS
3 // VideoConsumer.cpp
6 #include "FileUploadClient.h"
7 #include "FtpClient.h"
8 #include "SftpClient.h"
9 #include "VideoConsumer.h"
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <string.h>
16 #include <Application.h>
17 #include <Buffer.h>
18 #include <BufferGroup.h>
19 #include <Catalog.h>
20 #include <Locale.h>
21 #include <MediaRoster.h>
22 #include <NodeInfo.h>
23 #include <scheduler.h>
24 #include <StringView.h>
25 #include <TimeSource.h>
26 #include <View.h>
29 #undef B_TRANSLATION_CONTEXT
30 #define B_TRANSLATION_CONTEXT "VideoConsumer.cpp"
32 #define M1 ((double)1000000.0)
33 #define JITTER 20000
35 #define FUNCTION printf
36 #define ERROR printf
37 #define PROGRESS printf
38 #define LOOP printf
41 static status_t SetFileType(BFile* file, int32 translator, uint32 type);
43 const media_raw_video_format vid_format = {29.97, 1, 0, 239,
44 B_VIDEO_TOP_LEFT_RIGHT, 1, 1, {B_RGB16, 320, 240, 320 * 4, 0, 0}};
47 VideoConsumer::VideoConsumer(const char* name, BView* view,
48 BStringView* statusLine,
49 BMediaAddOn* addon, const uint32 internalId)
50 : BMediaNode(name),
51 BMediaEventLooper(),
52 BBufferConsumer(B_MEDIA_RAW_VIDEO),
53 fStatusLine(statusLine),
54 fInternalID(internalId),
55 fAddOn(addon),
56 fConnectionActive(false),
57 fMyLatency(20000),
58 fWindow(NULL),
59 fView(view),
60 fOurBuffers(false),
61 fBuffers(NULL),
62 fTimeToFtp(false),
63 fFtpComplete(true),
64 fRate(1000000),
65 fImageFormat(0),
66 fTranslator(0),
67 fUploadClient(0),
68 fPassiveFtp(true)
70 FUNCTION("VideoConsumer::VideoConsumer\n");
72 AddNodeKind(B_PHYSICAL_OUTPUT);
73 SetEventLatency(0);
74 fWindow = fView->Window();
76 for (uint32 j = 0; j < 3; j++) {
77 fBitmap[j] = NULL;
78 fBufferMap[j] = 0;
81 strcpy(fFileNameText, "");
82 strcpy(fServerText, "");
83 strcpy(fLoginText, "");
84 strcpy(fPasswordText, "");
85 strcpy(fDirectoryText, "");
87 SetPriority(B_DISPLAY_PRIORITY);
91 VideoConsumer::~VideoConsumer()
93 FUNCTION("VideoConsumer::~VideoConsumer\n");
95 Quit();
97 if (fWindow) {
98 printf(B_TRANSLATE("Locking the window\n"));
99 if (fWindow->Lock()) {
100 printf(B_TRANSLATE("Closing the window\n"));
101 fWindow->Close();
102 fWindow = 0;
106 // clean up ftp thread
107 // wait up to 30 seconds if ftp is in progress
108 int32 count = 0;
109 while (!fFtpComplete && count < 30) {
110 snooze(1000000);
111 count++;
114 if (count == 30)
115 kill_thread(fFtpThread);
117 DeleteBuffers();
121 /********************************
122 From BMediaNode
123 ********************************/
126 BMediaAddOn*
127 VideoConsumer::AddOn(int32* cookie) const
129 FUNCTION("VideoConsumer::AddOn\n");
130 // do the right thing if we're ever used with an add-on
131 *cookie = fInternalID;
132 return fAddOn;
136 // This implementation is required to get around a bug in
137 // the ppc compiler.
139 void
140 VideoConsumer::Start(bigtime_t performanceTime)
142 BMediaEventLooper::Start(performanceTime);
146 void
147 VideoConsumer::Stop(bigtime_t performanceTime, bool immediate)
149 BMediaEventLooper::Stop(performanceTime, immediate);
153 void
154 VideoConsumer::Seek(bigtime_t mediaTime, bigtime_t performanceTime)
156 BMediaEventLooper::Seek(mediaTime, performanceTime);
160 void
161 VideoConsumer::TimeWarp(bigtime_t atRealTime, bigtime_t toPerformanceTime)
163 BMediaEventLooper::TimeWarp(atRealTime, toPerformanceTime);
167 status_t
168 VideoConsumer::DeleteHook(BMediaNode* node)
170 return BMediaEventLooper::DeleteHook(node);
174 void
175 VideoConsumer::NodeRegistered()
177 FUNCTION("VideoConsumer::NodeRegistered\n");
178 fIn.destination.port = ControlPort();
179 fIn.destination.id = 0;
180 fIn.source = media_source::null;
181 fIn.format.type = B_MEDIA_RAW_VIDEO;
182 fIn.format.u.raw_video = vid_format;
184 Run();
188 status_t
189 VideoConsumer::RequestCompleted(const media_request_info& info)
191 FUNCTION("VideoConsumer::RequestCompleted\n");
192 switch (info.what) {
193 case media_request_info::B_SET_OUTPUT_BUFFERS_FOR:
194 if (info.status != B_OK)
195 ERROR("VideoConsumer::RequestCompleted: Not using our buffers!\n");
196 break;
198 default:
199 ERROR("VideoConsumer::RequestCompleted: Invalid argument\n");
200 break;
202 return B_OK;
206 status_t
207 VideoConsumer::HandleMessage(int32 message, const void* data, size_t size)
209 //FUNCTION("VideoConsumer::HandleMessage\n");
210 ftp_msg_info* info = (ftp_msg_info*)data;
211 status_t status = B_OK;
213 switch (message) {
214 case FTP_INFO:
215 PROGRESS("VideoConsumer::HandleMessage - FTP_INFO message\n");
216 fRate = info->rate;
217 fImageFormat = info->imageFormat;
218 fTranslator = info->translator;
219 fPassiveFtp = info->passiveFtp;
220 fUploadClient = info->uploadClient;
221 strcpy(fFileNameText, info->fileNameText);
222 strcpy(fServerText, info->serverText);
223 strcpy(fLoginText, info->loginText);
224 strcpy(fPasswordText, info->passwordText);
225 strcpy(fDirectoryText, info->directoryText);
226 // remove old user events
227 EventQueue()->FlushEvents(TimeSource()->Now(), BTimedEventQueue::B_ALWAYS,
228 true, BTimedEventQueue::B_USER_EVENT);
229 if (fRate != B_INFINITE_TIMEOUT) {
230 // if rate is not "Never," push an event
231 // to restart captures 5 seconds from now
232 media_timed_event event(TimeSource()->Now() + 5000000,
233 BTimedEventQueue::B_USER_EVENT);
234 EventQueue()->AddEvent(event);
236 break;
239 return status;
243 void
244 VideoConsumer::BufferReceived(BBuffer* buffer)
246 LOOP("VideoConsumer::Buffer #%" B_PRId32 " received, start_time %" B_PRIdBIGTIME
247 "\n", buffer->ID(), buffer->Header()->start_time);
249 if (RunState() == B_STOPPED) {
250 buffer->Recycle();
251 return;
254 media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER,
255 buffer, BTimedEventQueue::B_RECYCLE_BUFFER);
256 EventQueue()->AddEvent(event);
260 void
261 VideoConsumer::ProducerDataStatus(const media_destination& forWhom, int32 status,
262 bigtime_t atMediaTime)
264 FUNCTION("VideoConsumer::ProducerDataStatus\n");
266 if (forWhom != fIn.destination)
267 return;
271 status_t
272 VideoConsumer::CreateBuffers(const media_format& withFormat)
274 FUNCTION("VideoConsumer::CreateBuffers\n");
276 DeleteBuffers();
277 // delete any old buffers
279 status_t status = B_OK;
281 // create a buffer group
282 uint32 xSize = withFormat.u.raw_video.display.line_width;
283 uint32 ySize = withFormat.u.raw_video.display.line_count;
284 color_space colorspace = withFormat.u.raw_video.display.format;
285 PROGRESS("VideoConsumer::CreateBuffers - Colorspace = %d\n", colorspace);
287 fBuffers = new BBufferGroup();
288 status = fBuffers->InitCheck();
289 if (status != B_OK) {
290 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n");
291 return status;
293 // and attach the bitmaps to the buffer group
294 for (uint32 j = 0; j < 3; j++) {
295 fBitmap[j] = new BBitmap(BRect(0, 0, (xSize - 1), (ySize - 1)), colorspace,
296 false, true);
297 if (fBitmap[j]->IsValid()) {
298 buffer_clone_info info;
299 if ((info.area = area_for(fBitmap[j]->Bits())) == B_ERROR)
300 ERROR("VideoConsumer::CreateBuffers - ERROR IN AREA_FOR\n");
301 info.offset = 0;
302 info.size = (size_t)fBitmap[j]->BitsLength();
303 info.flags = j;
304 info.buffer = 0;
306 if ((status = fBuffers->AddBuffer(info)) != B_OK) {
307 ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER TO GROUP\n");
308 return status;
310 else
311 PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD BUFFER TO GROUP\n");
312 } else {
313 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING "
314 "BUFFER: %08" B_PRIx32 "\n", status);
315 return B_ERROR;
319 BBuffer* buffList[3];
320 for (int j = 0; j < 3; j++)
321 buffList[j] = NULL;
323 if ((status = fBuffers->GetBufferList(3, buffList)) == B_OK)
324 for (int j = 0; j < 3; j++)
325 if (buffList[j] != NULL) {
326 fBufferMap[j] = buffList[j];
327 PROGRESS(" j = %d buffer = %p\n", j, fBufferMap[j]);
328 } else {
329 ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n");
330 return B_ERROR;
332 else
333 ERROR("VideoConsumer::CreateBuffers ERROR IN GET BUFFER LIST\n");
335 fFtpBitmap = new BBitmap(BRect(0, 0, xSize - 1, ySize - 1), B_RGB32, false, false);
337 FUNCTION("VideoConsumer::CreateBuffers - EXIT\n");
338 return status;
342 void
343 VideoConsumer::DeleteBuffers()
345 FUNCTION("VideoConsumer::DeleteBuffers\n");
347 if (fBuffers) {
348 delete fBuffers;
349 fBuffers = NULL;
351 for (uint32 j = 0; j < 3; j++)
352 if (fBitmap[j]->IsValid()) {
353 delete fBitmap[j];
354 fBitmap[j] = NULL;
357 FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n");
361 status_t
362 VideoConsumer::Connected(const media_source& producer, const media_destination& where,
363 const media_format& withFormat, media_input* outInput)
365 FUNCTION("VideoConsumer::Connected\n");
367 fIn.source = producer;
368 fIn.format = withFormat;
369 fIn.node = Node();
370 sprintf(fIn.name, "Video Consumer");
371 *outInput = fIn;
373 uint32 userData = 0;
374 int32 changeTag = 1;
375 if (CreateBuffers(withFormat) == B_OK)
376 BBufferConsumer::SetOutputBuffersFor(producer, fDestination,
377 fBuffers, (void *)&userData, &changeTag, true);
378 else {
379 ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n");
380 return B_ERROR;
383 fConnectionActive = true;
385 FUNCTION("VideoConsumer::Connected - EXIT\n");
386 return B_OK;
390 void
391 VideoConsumer::Disconnected(const media_source& producer, const media_destination& where)
393 FUNCTION("VideoConsumer::Disconnected\n");
395 if (where == fIn.destination && producer == fIn.source) {
396 // disconnect the connection
397 fIn.source = media_source::null;
398 delete fFtpBitmap;
399 fConnectionActive = false;
405 status_t
406 VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format)
408 FUNCTION("VideoConsumer::AcceptFormat\n");
410 if (dest != fIn.destination) {
411 ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n");
412 return B_MEDIA_BAD_DESTINATION;
415 if (format->type == B_MEDIA_NO_TYPE)
416 format->type = B_MEDIA_RAW_VIDEO;
418 if (format->type != B_MEDIA_RAW_VIDEO) {
419 ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n");
420 return B_MEDIA_BAD_FORMAT;
423 if (format->u.raw_video.display.format != B_RGB32
424 && format->u.raw_video.display.format != B_RGB16
425 && format->u.raw_video.display.format != B_RGB15
426 && format->u.raw_video.display.format != B_GRAY8
428 format->u.raw_video.display.format != media_raw_video_format::wildcard.display.format) {
429 ERROR("AcceptFormat - not a format we know about!\n");
430 return B_MEDIA_BAD_FORMAT;
433 if (format->u.raw_video.display.format == media_raw_video_format::wildcard.display.format) {
434 format->u.raw_video.display.format = B_RGB16;
437 char formatString[256];
438 string_for_format(*format, formatString, 256);
439 FUNCTION("VideoConsumer::AcceptFormat: %s\n", formatString);
441 return B_OK;
445 status_t
446 VideoConsumer::GetNextInput(int32* cookie, media_input* outInput)
448 FUNCTION("VideoConsumer::GetNextInput\n");
450 // custom build a destination for this connection
451 // put connection number in id
453 if (*cookie < 1) {
454 fIn.node = Node();
455 fIn.destination.id = *cookie;
456 sprintf(fIn.name, "Video Consumer");
457 *outInput = fIn;
458 (*cookie)++;
459 return B_OK;
460 } else {
461 ERROR("VideoConsumer::GetNextInput - - BAD INDEX\n");
462 return B_MEDIA_BAD_DESTINATION;
467 void
468 VideoConsumer::DisposeInputCookie(int32 /*cookie*/)
473 status_t
474 VideoConsumer::GetLatencyFor(const media_destination& forWhom, bigtime_t* outLatency,
475 media_node_id* out_timesource)
477 FUNCTION("VideoConsumer::GetLatencyFor\n");
479 if (forWhom != fIn.destination)
480 return B_MEDIA_BAD_DESTINATION;
482 *outLatency = fMyLatency;
483 *out_timesource = TimeSource()->ID();
484 return B_OK;
488 status_t
489 VideoConsumer::FormatChanged(const media_source& producer, const media_destination& consumer,
490 int32 fromChangeCount, const media_format& format)
492 FUNCTION("VideoConsumer::FormatChanged\n");
494 if (consumer != fIn.destination)
495 return B_MEDIA_BAD_DESTINATION;
497 if (producer != fIn.source)
498 return B_MEDIA_BAD_SOURCE;
500 fIn.format = format;
502 return CreateBuffers(format);
506 void
507 VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
508 bool realTimeEvent)
510 LOOP("VideoConsumer::HandleEvent\n");
512 BBuffer* buffer;
514 switch (event->type) {
515 case BTimedEventQueue::B_START:
516 PROGRESS("VideoConsumer::HandleEvent - START\n");
517 break;
519 case BTimedEventQueue::B_STOP:
520 PROGRESS("VideoConsumer::HandleEvent - STOP\n");
521 EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS,
522 true, BTimedEventQueue::B_HANDLE_BUFFER);
523 break;
525 case BTimedEventQueue::B_USER_EVENT:
526 PROGRESS("VideoConsumer::HandleEvent - USER EVENT\n");
527 if (RunState() == B_STARTED) {
528 fTimeToFtp = true;
529 PROGRESS("Pushing user event for %.4f, time now %.4f\n",
530 (event->event_time + fRate) / M1, event->event_time/M1);
531 media_timed_event newEvent(event->event_time + fRate,
532 BTimedEventQueue::B_USER_EVENT);
533 EventQueue()->AddEvent(newEvent);
535 break;
537 case BTimedEventQueue::B_HANDLE_BUFFER:
539 LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n");
540 buffer = (BBuffer *)event->pointer;
541 if (RunState() == B_STARTED && fConnectionActive) {
542 // see if this is one of our buffers
543 uint32 index = 0;
544 fOurBuffers = true;
545 while (index < 3)
546 if (buffer == fBufferMap[index])
547 break;
548 else
549 index++;
551 if (index == 3) {
552 // no, buffers belong to consumer
553 fOurBuffers = false;
554 index = 0;
557 if (fFtpComplete && fTimeToFtp) {
558 PROGRESS("VidConsumer::HandleEvent - SPAWNING FTP THREAD\n");
559 fTimeToFtp = false;
560 fFtpComplete = false;
561 memcpy(fFtpBitmap->Bits(), buffer->Data(), fFtpBitmap->BitsLength());
562 fFtpThread = spawn_thread(FtpRun, "Video Window Ftp", B_NORMAL_PRIORITY, this);
563 resume_thread(fFtpThread);
566 if ((RunMode() == B_OFFLINE)
567 || ((TimeSource()->Now() > (buffer->Header()->start_time - JITTER))
568 && (TimeSource()->Now() < (buffer->Header()->start_time + JITTER)))) {
569 if (!fOurBuffers)
570 // not our buffers, so we need to copy
571 memcpy(fBitmap[index]->Bits(), buffer->Data(), fBitmap[index]->BitsLength());
573 if (fWindow->Lock()) {
574 uint32 flags;
575 if ((fBitmap[index]->ColorSpace() == B_GRAY8) &&
576 !bitmaps_support_space(fBitmap[index]->ColorSpace(), &flags)) {
577 // handle mapping of GRAY8 until app server knows how
578 uint32* start = (uint32*)fBitmap[index]->Bits();
579 int32 size = fBitmap[index]->BitsLength();
580 uint32* end = start + size / 4;
581 for (uint32* p = start; p < end; p++)
582 *p = (*p >> 3) & 0x1f1f1f1f;
585 fView->DrawBitmap(fBitmap[index], fView->Bounds());
586 fWindow->Unlock();
589 else
590 PROGRESS("VidConsumer::HandleEvent - DROPPED FRAME\n");
591 buffer->Recycle();
593 else
594 buffer->Recycle();
595 break;
598 default:
599 ERROR("VideoConsumer::HandleEvent - BAD EVENT\n");
600 break;
605 status_t
606 VideoConsumer::FtpRun(void* data)
608 FUNCTION("VideoConsumer::FtpRun\n");
610 ((VideoConsumer *)data)->FtpThread();
612 return 0;
616 void
617 VideoConsumer::FtpThread()
619 char fullPath[B_PATH_NAME_LENGTH];
620 FUNCTION("VideoConsumer::FtpThread\n");
621 if (fUploadClient == 2) {
622 // 64 + 64 = 128 max
623 snprintf(fullPath, B_PATH_NAME_LENGTH, "%s/%s", fDirectoryText, fFileNameText);
624 LocalSave(fullPath, fFtpBitmap);
625 } else if (LocalSave(fFileNameText, fFtpBitmap) == B_OK)
626 FtpSave(fFileNameText);
628 #if 0
629 // save a small version, too
630 BBitmap* b = new BBitmap(BRect(0,0,159,119), B_RGB32, true, false);
631 BView* v = new BView(BRect(0,0,159,119), "SmallView 1", 0, B_WILL_DRAW);
632 b->AddChild(v);
634 b->Lock();
635 v->DrawBitmap(fFtpBitmap, v->Frame());
636 v->Sync();
637 b->Unlock();
639 if (LocalSave("small.jpg", b) == B_OK)
640 FtpSave("small.jpg");
642 delete b;
643 #endif
645 fFtpComplete = true;
649 void
650 VideoConsumer::UpdateFtpStatus(const char* status)
652 printf("FTP STATUS: %s\n",status);
653 if (fView->Window()->Lock()) {
654 fStatusLine->SetText(status);
655 fView->Window()->Unlock();
660 status_t
661 VideoConsumer::LocalSave(char* filename, BBitmap* bitmap)
663 BFile* output;
665 UpdateFtpStatus(B_TRANSLATE("Capturing Image" B_UTF8_ELLIPSIS));
667 /* save a local copy of the image in the requested format */
668 output = new BFile();
669 if (output->SetTo(filename, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE) == B_NO_ERROR) {
670 BBitmapStream input(bitmap);
671 status_t err = BTranslatorRoster::Default()->Translate(&input, NULL, NULL,
672 output, fImageFormat);
673 if (err == B_OK) {
674 err = SetFileType(output, fTranslator, fImageFormat);
675 if (err != B_OK)
676 UpdateFtpStatus(B_TRANSLATE("Error setting type of output file"));
678 else
679 UpdateFtpStatus(B_TRANSLATE("Error writing output file"));
681 input.DetachBitmap(&bitmap);
682 output->Unset();
683 delete output;
684 return B_OK;
685 } else {
686 UpdateFtpStatus(B_TRANSLATE("Error creating output file"));
687 return B_ERROR;
692 status_t
693 VideoConsumer::FtpSave(char* filename)
695 FileUploadClient *ftp;
697 //XXX: make that cleaner
698 switch (fUploadClient) {
699 case 0:
700 ftp = new FtpClient;
701 break;
702 case 1:
703 ftp = new SftpClient;
704 break;
705 case 2:
706 return B_OK;
707 default:
708 fprintf(stderr, B_TRANSLATE("invalid upload client %ld\n"),
709 fUploadClient);
710 return EINVAL;
713 ftp->SetPassive(fPassiveFtp);
714 // ftp the local file to our web site
716 UpdateFtpStatus(B_TRANSLATE("Logging in" B_UTF8_ELLIPSIS));
717 if (ftp->Connect((string)fServerText, (string)fLoginText,
718 (string)fPasswordText)) {
719 // connect to server
720 UpdateFtpStatus(B_TRANSLATE("Connected" B_UTF8_ELLIPSIS));
722 if (ftp->ChangeDir((string)fDirectoryText)) {
723 // cd to the desired directory
724 UpdateFtpStatus(B_TRANSLATE("Upload" B_UTF8_ELLIPSIS));
726 if (ftp->PutFile((string)filename, (string)"temp")) {
727 // send the file to the server
729 ftp->Chmod((string)"temp", (string)"644");
730 // make it world readable
732 UpdateFtpStatus(B_TRANSLATE("Renaming" B_UTF8_ELLIPSIS));
734 if (ftp->MoveFile((string)"temp", (string)filename)) {
735 // change to the desired name
736 uint32 time = real_time_clock();
737 char s[80];
738 strcpy(s, B_TRANSLATE("Last Capture: "));
739 strcat(s, ctime((const time_t*)&time));
740 s[strlen(s) - 1] = 0;
741 UpdateFtpStatus(s);
742 delete ftp;
743 return B_OK;
744 } else
745 UpdateFtpStatus(B_TRANSLATE("Rename failed"));
746 } else
747 UpdateFtpStatus(B_TRANSLATE("File upload failed"));
748 } else
749 UpdateFtpStatus(B_TRANSLATE("Couldn't find requested directory on "
750 "server"));
752 else
753 UpdateFtpStatus(B_TRANSLATE("Server login failed"));
755 delete ftp;
756 return B_ERROR;
760 status_t
761 SetFileType(BFile* file, int32 translator, uint32 type)
763 translation_format* formats;
764 int32 count;
766 status_t err = BTranslatorRoster::Default()->GetOutputFormats(translator,
767 (const translation_format **)&formats, &count);
768 if (err < B_OK)
769 return err;
771 const char* mime = NULL;
772 for (int ix = 0; ix < count; ix++) {
773 if (formats[ix].type == type) {
774 mime = formats[ix].MIME;
775 break;
779 if (mime == NULL) {
780 /* this should not happen, but being defensive might be prudent */
781 return B_ERROR;
784 /* use BNodeInfo to set the file type */
785 BNodeInfo ninfo(file);
786 return ninfo.SetType(mime);