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