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_TRANSLATION_CONTEXT 30 #define B_TRANSLATION_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 puts(B_TRANSLATE("Locking the window")); 99 if (fWindow->Lock()) { 100 puts(B_TRANSLATE("Closing the window")); 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(int32* 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 #%" B_PRId32 " received, start_time %" B_PRIdBIGTIME 247 "\n", buffer->ID(), buffer->Header()->start_time); 248 249 if (RunState() == B_STOPPED) { 250 buffer->Recycle(); 251 return; 252 } 253 254 media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER, 255 buffer, BTimedEventQueue::B_RECYCLE_BUFFER); 256 EventQueue()->AddEvent(event); 257 } 258 259 260 void 261 VideoConsumer::ProducerDataStatus(const media_destination& forWhom, int32 status, 262 bigtime_t atMediaTime) 263 { 264 FUNCTION("VideoConsumer::ProducerDataStatus\n"); 265 266 if (forWhom != fIn.destination) 267 return; 268 } 269 270 271 status_t 272 VideoConsumer::CreateBuffers(const media_format& withFormat) 273 { 274 FUNCTION("VideoConsumer::CreateBuffers\n"); 275 276 DeleteBuffers(); 277 // delete any old buffers 278 279 status_t status = B_OK; 280 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); 286 287 fBuffers = new BBufferGroup(); 288 status = fBuffers->InitCheck(); 289 if (status != B_OK) { 290 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n"); 291 return status; 292 } 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, 296 false, true); 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"); 301 info.offset = 0; 302 info.size = (size_t)fBitmap[j]->BitsLength(); 303 info.flags = j; 304 info.buffer = 0; 305 306 if ((status = fBuffers->AddBuffer(info)) != B_OK) { 307 ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER TO GROUP\n"); 308 return status; 309 } 310 else 311 PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD BUFFER TO GROUP\n"); 312 } else { 313 ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING " 314 "BUFFER: %08" B_PRIx32 "\n", status); 315 return B_ERROR; 316 } 317 } 318 319 BBuffer* buffList[3]; 320 for (int j = 0; j < 3; j++) 321 buffList[j] = NULL; 322 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]); 328 } else { 329 ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n"); 330 return B_ERROR; 331 } 332 else 333 ERROR("VideoConsumer::CreateBuffers ERROR IN GET BUFFER LIST\n"); 334 335 fFtpBitmap = new BBitmap(BRect(0, 0, xSize - 1, ySize - 1), B_RGB32, false, false); 336 337 FUNCTION("VideoConsumer::CreateBuffers - EXIT\n"); 338 return status; 339 } 340 341 342 void 343 VideoConsumer::DeleteBuffers() 344 { 345 FUNCTION("VideoConsumer::DeleteBuffers\n"); 346 347 if (fBuffers) { 348 delete fBuffers; 349 fBuffers = NULL; 350 351 for (uint32 j = 0; j < 3; j++) 352 if (fBitmap[j]->IsValid()) { 353 delete fBitmap[j]; 354 fBitmap[j] = NULL; 355 } 356 } 357 FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n"); 358 } 359 360 361 status_t 362 VideoConsumer::Connected(const media_source& producer, const media_destination& where, 363 const media_format& withFormat, media_input* outInput) 364 { 365 FUNCTION("VideoConsumer::Connected\n"); 366 367 fIn.source = producer; 368 fIn.format = withFormat; 369 fIn.node = Node(); 370 sprintf(fIn.name, "Video Consumer"); 371 *outInput = fIn; 372 373 uint32 userData = 0; 374 int32 changeTag = 1; 375 if (CreateBuffers(withFormat) == B_OK) 376 BBufferConsumer::SetOutputBuffersFor(producer, fDestination, 377 fBuffers, (void *)&userData, &changeTag, true); 378 else { 379 ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n"); 380 return B_ERROR; 381 } 382 383 fConnectionActive = true; 384 385 FUNCTION("VideoConsumer::Connected - EXIT\n"); 386 return B_OK; 387 } 388 389 390 void 391 VideoConsumer::Disconnected(const media_source& producer, const media_destination& where) 392 { 393 FUNCTION("VideoConsumer::Disconnected\n"); 394 395 if (where == fIn.destination && producer == fIn.source) { 396 // disconnect the connection 397 fIn.source = media_source::null; 398 delete fFtpBitmap; 399 fConnectionActive = false; 400 } 401 402 } 403 404 405 status_t 406 VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format) 407 { 408 FUNCTION("VideoConsumer::AcceptFormat\n"); 409 410 if (dest != fIn.destination) { 411 ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n"); 412 return B_MEDIA_BAD_DESTINATION; 413 } 414 415 if (format->type == B_MEDIA_NO_TYPE) 416 format->type = B_MEDIA_RAW_VIDEO; 417 418 if (format->type != B_MEDIA_RAW_VIDEO) { 419 ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n"); 420 return B_MEDIA_BAD_FORMAT; 421 } 422 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 427 && 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; 431 } 432 433 if (format->u.raw_video.display.format == media_raw_video_format::wildcard.display.format) { 434 format->u.raw_video.display.format = B_RGB16; 435 } 436 437 char formatString[256]; 438 string_for_format(*format, formatString, 256); 439 FUNCTION("VideoConsumer::AcceptFormat: %s\n", formatString); 440 441 return B_OK; 442 } 443 444 445 status_t 446 VideoConsumer::GetNextInput(int32* cookie, media_input* outInput) 447 { 448 FUNCTION("VideoConsumer::GetNextInput\n"); 449 450 // custom build a destination for this connection 451 // put connection number in id 452 453 if (*cookie < 1) { 454 fIn.node = Node(); 455 fIn.destination.id = *cookie; 456 sprintf(fIn.name, "Video Consumer"); 457 *outInput = fIn; 458 (*cookie)++; 459 return B_OK; 460 } else { 461 ERROR("VideoConsumer::GetNextInput - - BAD INDEX\n"); 462 return B_MEDIA_BAD_DESTINATION; 463 } 464 } 465 466 467 void 468 VideoConsumer::DisposeInputCookie(int32 /*cookie*/) 469 { 470 } 471 472 473 status_t 474 VideoConsumer::GetLatencyFor(const media_destination& forWhom, bigtime_t* outLatency, 475 media_node_id* out_timesource) 476 { 477 FUNCTION("VideoConsumer::GetLatencyFor\n"); 478 479 if (forWhom != fIn.destination) 480 return B_MEDIA_BAD_DESTINATION; 481 482 *outLatency = fMyLatency; 483 *out_timesource = TimeSource()->ID(); 484 return B_OK; 485 } 486 487 488 status_t 489 VideoConsumer::FormatChanged(const media_source& producer, const media_destination& consumer, 490 int32 fromChangeCount, const media_format& format) 491 { 492 FUNCTION("VideoConsumer::FormatChanged\n"); 493 494 if (consumer != fIn.destination) 495 return B_MEDIA_BAD_DESTINATION; 496 497 if (producer != fIn.source) 498 return B_MEDIA_BAD_SOURCE; 499 500 fIn.format = format; 501 502 return CreateBuffers(format); 503 } 504 505 506 void 507 VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness, 508 bool realTimeEvent) 509 { 510 LOOP("VideoConsumer::HandleEvent\n"); 511 512 BBuffer* buffer; 513 514 switch (event->type) { 515 case BTimedEventQueue::B_START: 516 PROGRESS("VideoConsumer::HandleEvent - START\n"); 517 break; 518 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); 523 break; 524 525 case BTimedEventQueue::B_USER_EVENT: 526 PROGRESS("VideoConsumer::HandleEvent - USER EVENT\n"); 527 if (RunState() == B_STARTED) { 528 fTimeToFtp = true; 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); 534 } 535 break; 536 537 case BTimedEventQueue::B_HANDLE_BUFFER: 538 { 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 543 uint32 index = 0; 544 fOurBuffers = true; 545 while (index < 3) 546 if (buffer == fBufferMap[index]) 547 break; 548 else 549 index++; 550 551 if (index == 3) { 552 // no, buffers belong to consumer 553 fOurBuffers = false; 554 index = 0; 555 } 556 557 if (fFtpComplete && fTimeToFtp) { 558 PROGRESS("VidConsumer::HandleEvent - SPAWNING FTP THREAD\n"); 559 fTimeToFtp = false; 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); 564 } 565 566 if ((RunMode() == B_OFFLINE) 567 || ((TimeSource()->Now() > (buffer->Header()->start_time - JITTER)) 568 && (TimeSource()->Now() < (buffer->Header()->start_time + JITTER)))) { 569 if (!fOurBuffers) 570 // not our buffers, so we need to copy 571 memcpy(fBitmap[index]->Bits(), buffer->Data(), fBitmap[index]->BitsLength()); 572 573 if (fWindow->Lock()) { 574 uint32 flags; 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; 583 } 584 585 fView->DrawBitmap(fBitmap[index], fView->Bounds()); 586 fWindow->Unlock(); 587 } 588 } 589 else 590 PROGRESS("VidConsumer::HandleEvent - DROPPED FRAME\n"); 591 buffer->Recycle(); 592 } 593 else 594 buffer->Recycle(); 595 break; 596 } 597 598 default: 599 ERROR("VideoConsumer::HandleEvent - BAD EVENT\n"); 600 break; 601 } 602 } 603 604 605 status_t 606 VideoConsumer::FtpRun(void* data) 607 { 608 FUNCTION("VideoConsumer::FtpRun\n"); 609 610 ((VideoConsumer *)data)->FtpThread(); 611 612 return 0; 613 } 614 615 616 void 617 VideoConsumer::FtpThread() 618 { 619 char fullPath[B_PATH_NAME_LENGTH]; 620 FUNCTION("VideoConsumer::FtpThread\n"); 621 if (fUploadClient == 2) { 622 // 64 + 64 = 128 max 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); 627 628 #if 0 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); 632 b->AddChild(v); 633 634 b->Lock(); 635 v->DrawBitmap(fFtpBitmap, v->Frame()); 636 v->Sync(); 637 b->Unlock(); 638 639 if (LocalSave("small.jpg", b) == B_OK) 640 FtpSave("small.jpg"); 641 642 delete b; 643 #endif 644 645 fFtpComplete = true; 646 } 647 648 649 void 650 VideoConsumer::UpdateFtpStatus(const char* status) 651 { 652 printf("FTP STATUS: %s\n",status); 653 if (fView->Window()->Lock()) { 654 fStatusLine->SetText(status); 655 fView->Window()->Unlock(); 656 } 657 } 658 659 660 status_t 661 VideoConsumer::LocalSave(char* filename, BBitmap* bitmap) 662 { 663 BFile* output; 664 665 UpdateFtpStatus(B_TRANSLATE("Capturing Image" B_UTF8_ELLIPSIS)); 666 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); 673 if (err == B_OK) { 674 err = SetFileType(output, fTranslator, fImageFormat); 675 if (err != B_OK) 676 UpdateFtpStatus(B_TRANSLATE("Error setting type of output file")); 677 } 678 else 679 UpdateFtpStatus(B_TRANSLATE("Error writing output file")); 680 681 input.DetachBitmap(&bitmap); 682 output->Unset(); 683 delete output; 684 return B_OK; 685 } else { 686 UpdateFtpStatus(B_TRANSLATE("Error creating output file")); 687 return B_ERROR; 688 } 689 } 690 691 692 status_t 693 VideoConsumer::FtpSave(char* filename) 694 { 695 FileUploadClient *ftp; 696 697 //XXX: make that cleaner 698 switch (fUploadClient) { 699 case 0: 700 ftp = new FtpClient; 701 break; 702 case 1: 703 ftp = new SftpClient; 704 break; 705 case 2: 706 return B_OK; 707 default: 708 fprintf(stderr, B_TRANSLATE("invalid upload client %ld\n"), 709 fUploadClient); 710 return EINVAL; 711 } 712 713 ftp->SetPassive(fPassiveFtp); 714 // ftp the local file to our web site 715 716 UpdateFtpStatus(B_TRANSLATE("Logging in" B_UTF8_ELLIPSIS)); 717 if (ftp->Connect((string)fServerText, (string)fLoginText, 718 (string)fPasswordText)) { 719 // connect to server 720 UpdateFtpStatus(B_TRANSLATE("Connected" B_UTF8_ELLIPSIS)); 721 722 if (ftp->ChangeDir((string)fDirectoryText)) { 723 // cd to the desired directory 724 UpdateFtpStatus(B_TRANSLATE("Upload" B_UTF8_ELLIPSIS)); 725 726 if (ftp->PutFile((string)filename, (string)"temp")) { 727 // send the file to the server 728 729 ftp->Chmod((string)"temp", (string)"644"); 730 // make it world readable 731 732 UpdateFtpStatus(B_TRANSLATE("Renaming" B_UTF8_ELLIPSIS)); 733 734 if (ftp->MoveFile((string)"temp", (string)filename)) { 735 // change to the desired name 736 uint32 time = real_time_clock(); 737 char s[80]; 738 strcpy(s, B_TRANSLATE("Last Capture: ")); 739 strcat(s, ctime((const time_t*)&time)); 740 s[strlen(s) - 1] = 0; 741 UpdateFtpStatus(s); 742 delete ftp; 743 return B_OK; 744 } else 745 UpdateFtpStatus(B_TRANSLATE("Rename failed")); 746 } else 747 UpdateFtpStatus(B_TRANSLATE("File upload failed")); 748 } else 749 UpdateFtpStatus(B_TRANSLATE("Couldn't find requested directory on " 750 "server")); 751 } 752 else 753 UpdateFtpStatus(B_TRANSLATE("Server login failed")); 754 755 delete ftp; 756 return B_ERROR; 757 } 758 759 760 status_t 761 SetFileType(BFile* file, int32 translator, uint32 type) 762 { 763 translation_format* formats; 764 int32 count; 765 766 status_t err = BTranslatorRoster::Default()->GetOutputFormats(translator, 767 (const translation_format **)&formats, &count); 768 if (err < B_OK) 769 return err; 770 771 const char* mime = NULL; 772 for (int ix = 0; ix < count; ix++) { 773 if (formats[ix].type == type) { 774 mime = formats[ix].MIME; 775 break; 776 } 777 } 778 779 if (mime == NULL) { 780 /* this should not happen, but being defensive might be prudent */ 781 return B_ERROR; 782 } 783 784 /* use BNodeInfo to set the file type */ 785 BNodeInfo ninfo(file); 786 return ninfo.SetType(mime); 787 } 788