1 // Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
6 #include "FileUploadClient.h"
8 #include "SftpClient.h"
9 #include "VideoConsumer.h"
16 #include <Application.h>
18 #include <BufferGroup.h>
21 #include <MediaRoster.h>
23 #include <scheduler.h>
24 #include <StringView.h>
25 #include <TimeSource.h>
29 #undef B_TRANSLATION_CONTEXT
30 #define B_TRANSLATION_CONTEXT "VideoConsumer.cpp"
32 #define M1 ((double)1000000.0)
35 #define FUNCTION printf
37 #define PROGRESS 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
)
52 BBufferConsumer(B_MEDIA_RAW_VIDEO
),
53 fStatusLine(statusLine
),
54 fInternalID(internalId
),
56 fConnectionActive(false),
70 FUNCTION("VideoConsumer::VideoConsumer\n");
72 AddNodeKind(B_PHYSICAL_OUTPUT
);
74 fWindow
= fView
->Window();
76 for (uint32 j
= 0; j
< 3; j
++) {
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");
98 printf(B_TRANSLATE("Locking the window\n"));
99 if (fWindow
->Lock()) {
100 printf(B_TRANSLATE("Closing the window\n"));
106 // clean up ftp thread
107 // wait up to 30 seconds if ftp is in progress
109 while (!fFtpComplete
&& count
< 30) {
115 kill_thread(fFtpThread
);
121 /********************************
123 ********************************/
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
;
136 // This implementation is required to get around a bug in
140 VideoConsumer::Start(bigtime_t performanceTime
)
142 BMediaEventLooper::Start(performanceTime
);
147 VideoConsumer::Stop(bigtime_t performanceTime
, bool immediate
)
149 BMediaEventLooper::Stop(performanceTime
, immediate
);
154 VideoConsumer::Seek(bigtime_t mediaTime
, bigtime_t performanceTime
)
156 BMediaEventLooper::Seek(mediaTime
, performanceTime
);
161 VideoConsumer::TimeWarp(bigtime_t atRealTime
, bigtime_t toPerformanceTime
)
163 BMediaEventLooper::TimeWarp(atRealTime
, toPerformanceTime
);
168 VideoConsumer::DeleteHook(BMediaNode
* node
)
170 return BMediaEventLooper::DeleteHook(node
);
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
;
189 VideoConsumer::RequestCompleted(const media_request_info
& info
)
191 FUNCTION("VideoConsumer::RequestCompleted\n");
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");
199 ERROR("VideoConsumer::RequestCompleted: Invalid argument\n");
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
;
215 PROGRESS("VideoConsumer::HandleMessage - FTP_INFO message\n");
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
);
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
) {
254 media_timed_event
event(buffer
->Header()->start_time
, BTimedEventQueue::B_HANDLE_BUFFER
,
255 buffer
, BTimedEventQueue::B_RECYCLE_BUFFER
);
256 EventQueue()->AddEvent(event
);
261 VideoConsumer::ProducerDataStatus(const media_destination
& forWhom
, int32 status
,
262 bigtime_t atMediaTime
)
264 FUNCTION("VideoConsumer::ProducerDataStatus\n");
266 if (forWhom
!= fIn
.destination
)
272 VideoConsumer::CreateBuffers(const media_format
& withFormat
)
274 FUNCTION("VideoConsumer::CreateBuffers\n");
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");
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
,
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");
302 info
.size
= (size_t)fBitmap
[j
]->BitsLength();
306 if ((status
= fBuffers
->AddBuffer(info
)) != B_OK
) {
307 ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER TO GROUP\n");
311 PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD BUFFER TO GROUP\n");
313 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING "
314 "BUFFER: %08" B_PRIx32
"\n", status
);
319 BBuffer
* buffList
[3];
320 for (int j
= 0; j
< 3; j
++)
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
]);
329 ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n");
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");
343 VideoConsumer::DeleteBuffers()
345 FUNCTION("VideoConsumer::DeleteBuffers\n");
351 for (uint32 j
= 0; j
< 3; j
++)
352 if (fBitmap
[j
]->IsValid()) {
357 FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n");
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
;
370 sprintf(fIn
.name
, "Video Consumer");
375 if (CreateBuffers(withFormat
) == B_OK
)
376 BBufferConsumer::SetOutputBuffersFor(producer
, fDestination
,
377 fBuffers
, (void *)&userData
, &changeTag
, true);
379 ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n");
383 fConnectionActive
= true;
385 FUNCTION("VideoConsumer::Connected - EXIT\n");
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
;
399 fConnectionActive
= false;
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
);
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
455 fIn
.destination
.id
= *cookie
;
456 sprintf(fIn
.name
, "Video Consumer");
461 ERROR("VideoConsumer::GetNextInput - - BAD INDEX\n");
462 return B_MEDIA_BAD_DESTINATION
;
468 VideoConsumer::DisposeInputCookie(int32
/*cookie*/)
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();
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
;
502 return CreateBuffers(format
);
507 VideoConsumer::HandleEvent(const media_timed_event
* event
, bigtime_t lateness
,
510 LOOP("VideoConsumer::HandleEvent\n");
514 switch (event
->type
) {
515 case BTimedEventQueue::B_START
:
516 PROGRESS("VideoConsumer::HandleEvent - START\n");
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
);
525 case BTimedEventQueue::B_USER_EVENT
:
526 PROGRESS("VideoConsumer::HandleEvent - USER EVENT\n");
527 if (RunState() == B_STARTED
) {
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
);
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
546 if (buffer
== fBufferMap
[index
])
552 // no, buffers belong to consumer
557 if (fFtpComplete
&& fTimeToFtp
) {
558 PROGRESS("VidConsumer::HandleEvent - SPAWNING FTP THREAD\n");
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
)))) {
570 // not our buffers, so we need to copy
571 memcpy(fBitmap
[index
]->Bits(), buffer
->Data(), fBitmap
[index
]->BitsLength());
573 if (fWindow
->Lock()) {
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());
590 PROGRESS("VidConsumer::HandleEvent - DROPPED FRAME\n");
599 ERROR("VideoConsumer::HandleEvent - BAD EVENT\n");
606 VideoConsumer::FtpRun(void* data
)
608 FUNCTION("VideoConsumer::FtpRun\n");
610 ((VideoConsumer
*)data
)->FtpThread();
617 VideoConsumer::FtpThread()
619 char fullPath
[B_PATH_NAME_LENGTH
];
620 FUNCTION("VideoConsumer::FtpThread\n");
621 if (fUploadClient
== 2) {
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
);
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
);
635 v
->DrawBitmap(fFtpBitmap
, v
->Frame());
639 if (LocalSave("small.jpg", b
) == B_OK
)
640 FtpSave("small.jpg");
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();
661 VideoConsumer::LocalSave(char* filename
, BBitmap
* bitmap
)
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
);
674 err
= SetFileType(output
, fTranslator
, fImageFormat
);
676 UpdateFtpStatus(B_TRANSLATE("Error setting type of output file"));
679 UpdateFtpStatus(B_TRANSLATE("Error writing output file"));
681 input
.DetachBitmap(&bitmap
);
686 UpdateFtpStatus(B_TRANSLATE("Error creating output file"));
693 VideoConsumer::FtpSave(char* filename
)
695 FileUploadClient
*ftp
;
697 //XXX: make that cleaner
698 switch (fUploadClient
) {
703 ftp
= new SftpClient
;
708 fprintf(stderr
, B_TRANSLATE("invalid upload client %ld\n"),
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
)) {
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();
738 strcpy(s
, B_TRANSLATE("Last Capture: "));
739 strcat(s
, ctime((const time_t*)&time
));
740 s
[strlen(s
) - 1] = 0;
745 UpdateFtpStatus(B_TRANSLATE("Rename failed"));
747 UpdateFtpStatus(B_TRANSLATE("File upload failed"));
749 UpdateFtpStatus(B_TRANSLATE("Couldn't find requested directory on "
753 UpdateFtpStatus(B_TRANSLATE("Server login failed"));
761 SetFileType(BFile
* file
, int32 translator
, uint32 type
)
763 translation_format
* formats
;
766 status_t err
= BTranslatorRoster::Default()->GetOutputFormats(translator
,
767 (const translation_format
**)&formats
, &count
);
771 const char* mime
= NULL
;
772 for (int ix
= 0; ix
< count
; ix
++) {
773 if (formats
[ix
].type
== type
) {
774 mime
= formats
[ix
].MIME
;
780 /* this should not happen, but being defensive might be prudent */
784 /* use BNodeInfo to set the file type */
785 BNodeInfo
ninfo(file
);
786 return ninfo
.SetType(mime
);