xref: /haiku/src/apps/codycam/VideoConsumer.cpp (revision f75a7bf508f3156d63a14f8fd77c5e0ca4d08c42)
1 //	Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
2 //	SMS
3 //	VideoConsumer.cpp
4 
5 
6 #include "FileUploadClient.h"
7 #include "FtpClient.h"
8 #include "SftpClient.h"
9 #include "VideoConsumer.h"
10 
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <string.h>
15 
16 #include <Application.h>
17 #include <Buffer.h>
18 #include <BufferGroup.h>
19 #include <MediaRoster.h>
20 #include <NodeInfo.h>
21 #include <scheduler.h>
22 #include <StringView.h>
23 #include <TimeSource.h>
24 #include <View.h>
25 
26 
27 #define M1 ((double)1000000.0)
28 #define JITTER		20000
29 
30 #define	FUNCTION	printf
31 #define ERROR		printf
32 #define PROGRESS	printf
33 #define LOOP		printf
34 
35 static status_t SetFileType(BFile* file,  int32 translator, uint32 type);
36 
37 const media_raw_video_format vid_format = {29.97, 1, 0, 239, B_VIDEO_TOP_LEFT_RIGHT,
38 	1, 1, {B_RGB16, 320, 240, 320 * 4, 0, 0}};
39 
40 
41 VideoConsumer::VideoConsumer(const char* name, BView* view, BStringView* statusLine,
42 	BMediaAddOn* addon, const uint32 internalId)
43 	: BMediaNode(name), BMediaEventLooper(), BBufferConsumer(B_MEDIA_RAW_VIDEO),
44 	fStatusLine(statusLine),
45 	fInternalID(internalId),
46 	fAddOn(addon),
47 	fConnectionActive(false),
48 	fMyLatency(20000),
49 	fWindow(NULL),
50 	fView(view),
51 	fOurBuffers(false),
52 	fBuffers(NULL),
53 	fTimeToFtp(false),
54 	fFtpComplete(true),
55 	fRate(1000000),
56 	fImageFormat(0),
57 	fTranslator(0),
58 	fUploadClient(0),
59 	fPassiveFtp(true)
60 {
61 	FUNCTION("VideoConsumer::VideoConsumer\n");
62 
63 	AddNodeKind(B_PHYSICAL_OUTPUT);
64 	SetEventLatency(0);
65 	fWindow = fView->Window();
66 
67 	for (uint32 j = 0; j < 3; j++) {
68 		fBitmap[j] = NULL;
69 		fBufferMap[j] = 0;
70 	}
71 
72 	strcpy(fFileNameText, "");
73 	strcpy(fServerText, "");
74 	strcpy(fLoginText, "");
75 	strcpy(fPasswordText, "");
76 	strcpy(fDirectoryText, "");
77 
78 	SetPriority(B_DISPLAY_PRIORITY);
79 }
80 
81 
82 VideoConsumer::~VideoConsumer()
83 {
84 	FUNCTION("VideoConsumer::~VideoConsumer\n");
85 
86 	Quit();
87 
88 	if (fWindow) {
89 		printf("Locking the window\n");
90 		if (fWindow->Lock()) {
91 			printf("Closing the window\n");
92 			fWindow->Close();
93 			fWindow = 0;
94 		}
95 	}
96 
97 	// clean up ftp thread
98 	// wait up to 30 seconds if ftp is in progress
99 	int32 count = 0;
100 	while (!fFtpComplete && count < 30) {
101 		snooze(1000000);
102 		count++;
103 	}
104 
105 	if (count == 30)
106 		kill_thread(fFtpThread);
107 
108 	DeleteBuffers();
109 
110 }
111 
112 /********************************
113 	From BMediaNode
114 ********************************/
115 
116 
117 BMediaAddOn*
118 VideoConsumer::AddOn(long* cookie) const
119 {
120 	FUNCTION("VideoConsumer::AddOn\n");
121 	// do the right thing if we're ever used with an add-on
122 	*cookie = fInternalID;
123 	return fAddOn;
124 }
125 
126 
127 // This implementation is required to get around a bug in
128 // the ppc compiler.
129 
130 void
131 VideoConsumer::Start(bigtime_t performanceTime)
132 {
133 	BMediaEventLooper::Start(performanceTime);
134 }
135 
136 
137 void
138 VideoConsumer::Stop(bigtime_t performanceTime, bool immediate)
139 {
140 	BMediaEventLooper::Stop(performanceTime, immediate);
141 }
142 
143 
144 void
145 VideoConsumer::Seek(bigtime_t mediaTime, bigtime_t performanceTime)
146 {
147 	BMediaEventLooper::Seek(mediaTime, performanceTime);
148 }
149 
150 
151 void
152 VideoConsumer::TimeWarp(bigtime_t atRealTime, bigtime_t toPerformanceTime)
153 {
154 	BMediaEventLooper::TimeWarp(atRealTime, toPerformanceTime);
155 }
156 
157 
158 status_t
159 VideoConsumer::DeleteHook(BMediaNode* node)
160 {
161 	return BMediaEventLooper::DeleteHook(node);
162 }
163 
164 
165 void
166 VideoConsumer::NodeRegistered()
167 {
168 	FUNCTION("VideoConsumer::NodeRegistered\n");
169 	fIn.destination.port = ControlPort();
170 	fIn.destination.id = 0;
171 	fIn.source = media_source::null;
172 	fIn.format.type = B_MEDIA_RAW_VIDEO;
173 	fIn.format.u.raw_video = vid_format;
174 
175 	Run();
176 }
177 
178 
179 status_t
180 VideoConsumer::RequestCompleted(const media_request_info& info)
181 {
182 	FUNCTION("VideoConsumer::RequestCompleted\n");
183 	switch (info.what) {
184 		case media_request_info::B_SET_OUTPUT_BUFFERS_FOR:
185 			if (info.status != B_OK)
186 					ERROR("VideoConsumer::RequestCompleted: Not using our buffers!\n");
187 			break;
188 
189 		default:
190 			ERROR("VideoConsumer::RequestCompleted: Invalid argument\n");
191 			break;
192 	}
193 	return B_OK;
194 }
195 
196 
197 status_t
198 VideoConsumer::HandleMessage(int32 message, const void* data, size_t size)
199 {
200 	//FUNCTION("VideoConsumer::HandleMessage\n");
201 	ftp_msg_info* info = (ftp_msg_info*)data;
202 	status_t status = B_OK;
203 
204 	switch (message) {
205 		case FTP_INFO:
206 			PROGRESS("VideoConsumer::HandleMessage - FTP_INFO message\n");
207 			fRate = info->rate;
208 			fImageFormat = info->imageFormat;
209 			fTranslator = info->translator;
210 			fPassiveFtp = info->passiveFtp;
211 			fUploadClient = info->uploadClient;
212 			strcpy(fFileNameText, info->fileNameText);
213 			strcpy(fServerText, info->serverText);
214 			strcpy(fLoginText, info->loginText);
215 			strcpy(fPasswordText, info->passwordText);
216 			strcpy(fDirectoryText, info->directoryText);
217 			// remove old user events
218 			EventQueue()->FlushEvents(TimeSource()->Now(), BTimedEventQueue::B_ALWAYS,
219 				true, BTimedEventQueue::B_USER_EVENT);
220 			if (fRate != B_INFINITE_TIMEOUT) {
221 				// if rate is not "Never," push an event
222 				// to restart captures 5 seconds from now
223 				media_timed_event event(TimeSource()->Now() + 5000000,
224 					BTimedEventQueue::B_USER_EVENT);
225 				EventQueue()->AddEvent(event);
226 			}
227 			break;
228 	}
229 
230 	return status;
231 }
232 
233 
234 void
235 VideoConsumer::BufferReceived(BBuffer* buffer)
236 {
237 	LOOP("VideoConsumer::Buffer #%ld received, start_time %Ld\n", buffer->ID(), buffer->Header()->start_time);
238 
239 	if (RunState() == B_STOPPED) {
240 		buffer->Recycle();
241 		return;
242 	}
243 
244 	media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER,
245 		buffer, BTimedEventQueue::B_RECYCLE_BUFFER);
246 	EventQueue()->AddEvent(event);
247 }
248 
249 
250 void
251 VideoConsumer::ProducerDataStatus(const media_destination& forWhom, int32 status,
252 	bigtime_t atMediaTime)
253 {
254 	FUNCTION("VideoConsumer::ProducerDataStatus\n");
255 
256 	if (forWhom != fIn.destination)
257 		return;
258 }
259 
260 
261 status_t
262 VideoConsumer::CreateBuffers(const media_format& withFormat)
263 {
264 	FUNCTION("VideoConsumer::CreateBuffers\n");
265 
266 	DeleteBuffers();
267 		// delete any old buffers
268 
269 	status_t status = B_OK;
270 
271 	// create a buffer group
272 	uint32 xSize = withFormat.u.raw_video.display.line_width;
273 	uint32 ySize = withFormat.u.raw_video.display.line_count;
274 	color_space colorspace = withFormat.u.raw_video.display.format;
275 	PROGRESS("VideoConsumer::CreateBuffers - Colorspace = %d\n", colorspace);
276 
277 	fBuffers = new BBufferGroup();
278 	status = fBuffers->InitCheck();
279 	if (status != B_OK) {
280 		ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n");
281 		return status;
282 	}
283 	// and attach the  bitmaps to the buffer group
284 	for (uint32 j = 0; j < 3; j++) {
285 		fBitmap[j] = new BBitmap(BRect(0, 0, (xSize - 1), (ySize - 1)), colorspace,
286 			false, true);
287 		if (fBitmap[j]->IsValid()) {
288 			buffer_clone_info info;
289 			if ((info.area = area_for(fBitmap[j]->Bits())) == B_ERROR)
290 				ERROR("VideoConsumer::CreateBuffers - ERROR IN AREA_FOR\n");
291 			info.offset = 0;
292 			info.size = (size_t)fBitmap[j]->BitsLength();
293 			info.flags = j;
294 			info.buffer = 0;
295 
296 			if ((status = fBuffers->AddBuffer(info)) != B_OK) {
297 				ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER TO GROUP\n");
298 				return status;
299 			}
300 			else
301 				PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD BUFFER TO GROUP\n");
302 		} else {
303 			ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING BUFFER: %08lx\n",
304 				status);
305 			return B_ERROR;
306 		}
307 	}
308 
309 	BBuffer** buffList = new BBuffer * [3];
310 	for (int j = 0; j < 3; j++)
311 		buffList[j] = 0;
312 
313 	if ((status = fBuffers->GetBufferList(3, buffList)) == B_OK)
314 		for (int j = 0; j < 3; j++)
315 			if (buffList[j] != NULL) {
316 				fBufferMap[j] = (uint32)buffList[j];
317 				PROGRESS(" j = %d buffer = %08lx\n", j, fBufferMap[j]);
318 			} else {
319 				ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n");
320 				return B_ERROR;
321 			}
322 	else
323 		ERROR("VideoConsumer::CreateBuffers ERROR IN GET BUFFER LIST\n");
324 
325 	fFtpBitmap = new BBitmap(BRect(0, 0, xSize - 1, ySize - 1), B_RGB32, false, false);
326 
327 	FUNCTION("VideoConsumer::CreateBuffers - EXIT\n");
328 	return status;
329 }
330 
331 
332 void
333 VideoConsumer::DeleteBuffers()
334 {
335 	FUNCTION("VideoConsumer::DeleteBuffers\n");
336 
337 	if (fBuffers) {
338 		delete fBuffers;
339 		fBuffers = NULL;
340 
341 		for (uint32 j = 0; j < 3; j++)
342 			if (fBitmap[j]->IsValid()) {
343 				delete fBitmap[j];
344 				fBitmap[j] = NULL;
345 			}
346 	}
347 	FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n");
348 }
349 
350 
351 status_t
352 VideoConsumer::Connected(const media_source& producer, const media_destination& where,
353 	const media_format& withFormat, media_input* outInput)
354 {
355 	FUNCTION("VideoConsumer::Connected\n");
356 
357 	fIn.source = producer;
358 	fIn.format = withFormat;
359 	fIn.node = Node();
360 	sprintf(fIn.name, "Video Consumer");
361 	*outInput = fIn;
362 
363 	uint32 userData = 0;
364 	int32 changeTag = 1;
365 	if (CreateBuffers(withFormat) == B_OK)
366 		BBufferConsumer::SetOutputBuffersFor(producer, fDestination,
367 			fBuffers, (void *)&userData, &changeTag, true);
368 	else {
369 		ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n");
370 		return B_ERROR;
371 	}
372 
373 	fConnectionActive = true;
374 
375 	FUNCTION("VideoConsumer::Connected - EXIT\n");
376 	return B_OK;
377 }
378 
379 
380 void
381 VideoConsumer::Disconnected(const media_source& producer, const media_destination& where)
382 {
383 	FUNCTION("VideoConsumer::Disconnected\n");
384 
385 	if (where == fIn.destination && producer == fIn.source) {
386 		// disconnect the connection
387 		fIn.source = media_source::null;
388 		delete fFtpBitmap;
389 		fConnectionActive = false;
390 	}
391 
392 }
393 
394 
395 status_t
396 VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format)
397 {
398 	FUNCTION("VideoConsumer::AcceptFormat\n");
399 
400 	if (dest != fIn.destination) {
401 		ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n");
402 		return B_MEDIA_BAD_DESTINATION;
403 	}
404 
405 	if (format->type == B_MEDIA_NO_TYPE)
406 		format->type = B_MEDIA_RAW_VIDEO;
407 
408 	if (format->type != B_MEDIA_RAW_VIDEO) {
409 		ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n");
410 		return B_MEDIA_BAD_FORMAT;
411 	}
412 
413 	if (format->u.raw_video.display.format != B_RGB32
414 		&& format->u.raw_video.display.format != B_RGB16
415 		&& format->u.raw_video.display.format != B_RGB15
416 		&& format->u.raw_video.display.format != B_GRAY8
417 		&&
418 		format->u.raw_video.display.format != media_raw_video_format::wildcard.display.format) {
419 		ERROR("AcceptFormat - not a format we know about!\n");
420 		return B_MEDIA_BAD_FORMAT;
421 	}
422 
423 	if (format->u.raw_video.display.format == media_raw_video_format::wildcard.display.format) {
424 		format->u.raw_video.display.format = B_RGB16;
425 	}
426 
427 	char formatString[256];
428 	string_for_format(*format, formatString, 256);
429 	FUNCTION("VideoConsumer::AcceptFormat: %s\n", formatString);
430 
431 	return B_OK;
432 }
433 
434 
435 status_t
436 VideoConsumer::GetNextInput(int32* cookie, media_input* outInput)
437 {
438 	FUNCTION("VideoConsumer::GetNextInput\n");
439 
440 	// custom build a destination for this connection
441 	// put connection number in id
442 
443 	if (*cookie < 1) {
444 		fIn.node = Node();
445 		fIn.destination.id = *cookie;
446 		sprintf(fIn.name, "Video Consumer");
447 		*outInput = fIn;
448 		(*cookie)++;
449 		return B_OK;
450 	} else {
451 		ERROR("VideoConsumer::GetNextInput - - BAD INDEX\n");
452 		return B_MEDIA_BAD_DESTINATION;
453 	}
454 }
455 
456 
457 void
458 VideoConsumer::DisposeInputCookie(int32 /*cookie*/)
459 {
460 }
461 
462 
463 status_t
464 VideoConsumer::GetLatencyFor(const media_destination& forWhom, bigtime_t* outLatency,
465 	media_node_id* out_timesource)
466 {
467 	FUNCTION("VideoConsumer::GetLatencyFor\n");
468 
469 	if (forWhom != fIn.destination)
470 		return B_MEDIA_BAD_DESTINATION;
471 
472 	*outLatency = fMyLatency;
473 	*out_timesource = TimeSource()->ID();
474 	return B_OK;
475 }
476 
477 
478 status_t
479 VideoConsumer::FormatChanged(const media_source& producer, const media_destination& consumer,
480 	int32 fromChangeCount, const media_format& format)
481 {
482 	FUNCTION("VideoConsumer::FormatChanged\n");
483 
484 	if (consumer != fIn.destination)
485 		return B_MEDIA_BAD_DESTINATION;
486 
487 	if (producer != fIn.source)
488 		return B_MEDIA_BAD_SOURCE;
489 
490 	fIn.format = format;
491 
492 	return CreateBuffers(format);
493 }
494 
495 
496 void
497 VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
498 	bool realTimeEvent)
499 {
500 	LOOP("VideoConsumer::HandleEvent\n");
501 
502 	BBuffer* buffer;
503 
504 	switch (event->type) {
505 		case BTimedEventQueue::B_START:
506 			PROGRESS("VideoConsumer::HandleEvent - START\n");
507 			break;
508 
509 		case BTimedEventQueue::B_STOP:
510 			PROGRESS("VideoConsumer::HandleEvent - STOP\n");
511 			EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS,
512 				true, BTimedEventQueue::B_HANDLE_BUFFER);
513 			break;
514 
515 		case BTimedEventQueue::B_USER_EVENT:
516 			PROGRESS("VideoConsumer::HandleEvent - USER EVENT\n");
517 			if (RunState() == B_STARTED) {
518 				fTimeToFtp = true;
519 				PROGRESS("Pushing user event for %.4f, time now %.4f\n",
520 					(event->event_time + fRate) / M1, event->event_time/M1);
521 				media_timed_event newEvent(event->event_time + fRate,
522 					BTimedEventQueue::B_USER_EVENT);
523 				EventQueue()->AddEvent(newEvent);
524 			}
525 			break;
526 
527 		case BTimedEventQueue::B_HANDLE_BUFFER:
528 		{
529 			LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n");
530 			buffer = (BBuffer *)event->pointer;
531 			if (RunState() == B_STARTED && fConnectionActive) {
532 				// see if this is one of our buffers
533 				uint32 index = 0;
534 				fOurBuffers = true;
535 				while (index < 3)
536 					if ((uint32)buffer == fBufferMap[index])
537 						break;
538 					else
539 						index++;
540 
541 				if (index == 3) {
542 					// no, buffers belong to consumer
543 					fOurBuffers = false;
544 					index = 0;
545 				}
546 
547 				if (fFtpComplete && fTimeToFtp) {
548 					PROGRESS("VidConsumer::HandleEvent - SPAWNING FTP THREAD\n");
549 					fTimeToFtp = false;
550 					fFtpComplete = false;
551 					memcpy(fFtpBitmap->Bits(), buffer->Data(), fFtpBitmap->BitsLength());
552 					fFtpThread = spawn_thread(FtpRun, "Video Window Ftp", B_NORMAL_PRIORITY, this);
553 					resume_thread(fFtpThread);
554 				}
555 
556 				if ((RunMode() == B_OFFLINE)
557 					|| ((TimeSource()->Now() > (buffer->Header()->start_time - JITTER))
558 						&& (TimeSource()->Now() < (buffer->Header()->start_time + JITTER)))) {
559 					if (!fOurBuffers)
560 						// not our buffers, so we need to copy
561 						memcpy(fBitmap[index]->Bits(), buffer->Data(), fBitmap[index]->BitsLength());
562 
563 					if (fWindow->Lock()) {
564 						uint32 flags;
565 						if ((fBitmap[index]->ColorSpace() == B_GRAY8) &&
566 							!bitmaps_support_space(fBitmap[index]->ColorSpace(), &flags)) {
567 							// handle mapping of GRAY8 until app server knows how
568 							uint32* start = (uint32*)fBitmap[index]->Bits();
569 							int32 size = fBitmap[index]->BitsLength();
570 							uint32* end = start + size / 4;
571 							for (uint32* p = start; p < end; p++)
572 								*p = (*p >> 3) & 0x1f1f1f1f;
573 						}
574 
575 						fView->DrawBitmap(fBitmap[index], fView->Bounds());
576 						fWindow->Unlock();
577 					}
578 				}
579 				else
580 					PROGRESS("VidConsumer::HandleEvent - DROPPED FRAME\n");
581 				buffer->Recycle();
582 			}
583 			else
584 				buffer->Recycle();
585 			break;
586 		}
587 
588 		default:
589 			ERROR("VideoConsumer::HandleEvent - BAD EVENT\n");
590 			break;
591 	}
592 }
593 
594 
595 status_t
596 VideoConsumer::FtpRun(void* data)
597 {
598 	FUNCTION("VideoConsumer::FtpRun\n");
599 
600 	((VideoConsumer *)data)->FtpThread();
601 
602 	return 0;
603 }
604 
605 
606 void
607 VideoConsumer::FtpThread()
608 {
609 	FUNCTION("VideoConsumer::FtpThread\n");
610 
611 	if (LocalSave(fFileNameText, fFtpBitmap) == B_OK)
612 		FtpSave(fFileNameText);
613 
614 #if 0
615 	// save a small version, too
616 	BBitmap* b = new BBitmap(BRect(0,0,159,119), B_RGB32, true, false);
617 	BView* v = new BView(BRect(0,0,159,119), "SmallView 1", 0, B_WILL_DRAW);
618 	b->AddChild(v);
619 
620 	b->Lock();
621 	v->DrawBitmap(fFtpBitmap, v->Frame());
622 	v->Sync();
623 	b->Unlock();
624 
625 	if (LocalSave("small.jpg", b) == B_OK)
626 		FtpSave("small.jpg");
627 
628 	delete b;
629 #endif
630 
631 	fFtpComplete = true;
632 }
633 
634 
635 void
636 VideoConsumer::UpdateFtpStatus(char* status)
637 {
638 	printf("FTP STATUS: %s\n",status);
639 	if (fView->Window()->Lock()) {
640 		fStatusLine->SetText(status);
641 		fView->Window()->Unlock();
642 	}
643 }
644 
645 
646 status_t
647 VideoConsumer::LocalSave(char* filename, BBitmap* bitmap)
648 {
649 	BFile* output;
650 
651 	UpdateFtpStatus("Capturing Image" B_UTF8_ELLIPSIS);
652 
653 	/* save a local copy of the image in the requested format */
654 	output = new BFile();
655 	if (output->SetTo(filename, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE) == B_NO_ERROR) {
656 		BBitmapStream input(bitmap);
657 		status_t err = BTranslatorRoster::Default()->Translate(&input, NULL, NULL,
658         	output, fImageFormat);
659         if (err == B_OK) {
660         	err = SetFileType(output, fTranslator, fImageFormat);
661         	if (err != B_OK)
662         		UpdateFtpStatus("Error setting type of output file");
663         }
664         else
665         	UpdateFtpStatus("Error writing output file");
666 
667 		input.DetachBitmap(&bitmap);
668 		output->Unset();
669 		delete output;
670 		return B_OK;
671     } else {
672 		UpdateFtpStatus("Error creating output file");
673     	return B_ERROR;
674     }
675 }
676 
677 
678 status_t
679 VideoConsumer::FtpSave(char* filename)
680 {
681 	FileUploadClient *ftp;
682 
683 	//XXX: make that cleaner
684 	switch (fUploadClient) {
685 		case 0:
686 			ftp = new FtpClient;
687 			break;
688 		case 1:
689 			ftp = new SftpClient;
690 			break;
691 		default:
692 			fprintf(stderr, "invalid upload client %ld\n", fUploadClient);
693 			return EINVAL;
694 	}
695 
696 	ftp->SetPassive(fPassiveFtp);
697 		// ftp the local file to our web site
698 
699 	UpdateFtpStatus("Logging in" B_UTF8_ELLIPSIS);
700 	if (ftp->Connect((string)fServerText, (string)fLoginText, (string)fPasswordText)) {
701 		// connect to server
702 		UpdateFtpStatus("Connected" B_UTF8_ELLIPSIS);
703 
704 		if (ftp->ChangeDir((string)fDirectoryText)) {
705 			// cd to the desired directory
706 			UpdateFtpStatus("Transmitting" B_UTF8_ELLIPSIS);
707 
708 			if (ftp->PutFile((string)filename, (string)"temp")) {
709 				// send the file to the server
710 
711 				ftp->Chmod((string)"temp", (string)"644");
712 				// make it world readable
713 
714 				UpdateFtpStatus("Renaming" B_UTF8_ELLIPSIS);
715 
716 				if (ftp->MoveFile((string)"temp", (string)filename)) {
717 					// change to the desired name
718 					uint32 time = real_time_clock();
719 					char s[80];
720 					strcpy(s, "Last Capture: ");
721 					strcat(s, ctime((const long*)&time));
722 					s[strlen(s) - 1] = 0;
723 					UpdateFtpStatus(s);
724 					delete ftp;
725 					return B_OK;
726 				}
727 				else
728 					UpdateFtpStatus("Rename failed");
729 			}
730 			else
731 				UpdateFtpStatus("File transmission failed");
732 		}
733 		else
734 			UpdateFtpStatus("Couldn't find requested directory on server");
735 	}
736 	else
737 		UpdateFtpStatus("Server login failed");
738 
739 	delete ftp;
740 	return B_ERROR;
741 }
742 
743 
744 status_t
745 SetFileType(BFile* file, int32 translator, uint32 type)
746 {
747 	translation_format* formats;
748 	int32 count;
749 
750 	status_t err = BTranslatorRoster::Default()->GetOutputFormats(translator,
751 		(const translation_format **)&formats, &count);
752 	if (err < B_OK)
753 		return err;
754 
755 	const char* mime = NULL;
756 	for (int ix = 0; ix < count; ix++) {
757 		if (formats[ix].type == type) {
758 			mime = formats[ix].MIME;
759 			break;
760 		}
761 	}
762 
763 	if (mime == NULL) {
764 		/* this should not happen, but being defensive might be prudent */
765 		return B_ERROR;
766 	}
767 
768 	/* use BNodeInfo to set the file type */
769 	BNodeInfo ninfo(file);
770 	return ninfo.SetType(mime);
771 }
772