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