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