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