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