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 FUNCTION("VideoConsumer::CreateBuffers - EXIT\n"); 326 return status; 327 } 328 329 330 void 331 VideoConsumer::DeleteBuffers() 332 { 333 FUNCTION("VideoConsumer::DeleteBuffers\n"); 334 335 if (fBuffers) { 336 delete fBuffers; 337 fBuffers = NULL; 338 339 for (uint32 j = 0; j < 3; j++) 340 if (fBitmap[j]->IsValid()) { 341 delete fBitmap[j]; 342 fBitmap[j] = NULL; 343 } 344 } 345 FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n"); 346 } 347 348 349 status_t 350 VideoConsumer::Connected(const media_source& producer, const media_destination& where, 351 const media_format& withFormat, media_input* outInput) 352 { 353 FUNCTION("VideoConsumer::Connected\n"); 354 355 fIn.source = producer; 356 fIn.format = withFormat; 357 fIn.node = Node(); 358 sprintf(fIn.name, "Video Consumer"); 359 *outInput = fIn; 360 361 uint32 userData = 0; 362 int32 changeTag = 1; 363 if (CreateBuffers(withFormat) == B_OK) 364 BBufferConsumer::SetOutputBuffersFor(producer, fDestination, 365 fBuffers, (void *)&userData, &changeTag, true); 366 else { 367 ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n"); 368 return B_ERROR; 369 } 370 371 fFtpBitmap = new BBitmap(BRect(0, 0, 320 - 1, 240 - 1), B_RGB32, false, false); 372 fConnectionActive = true; 373 374 FUNCTION("VideoConsumer::Connected - EXIT\n"); 375 return B_OK; 376 } 377 378 379 void 380 VideoConsumer::Disconnected(const media_source& producer, const media_destination& where) 381 { 382 FUNCTION("VideoConsumer::Disconnected\n"); 383 384 if (where == fIn.destination && producer == fIn.source) { 385 // disconnect the connection 386 fIn.source = media_source::null; 387 delete fFtpBitmap; 388 fConnectionActive = false; 389 } 390 391 } 392 393 394 status_t 395 VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format) 396 { 397 FUNCTION("VideoConsumer::AcceptFormat\n"); 398 399 if (dest != fIn.destination) { 400 ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n"); 401 return B_MEDIA_BAD_DESTINATION; 402 } 403 404 if (format->type == B_MEDIA_NO_TYPE) 405 format->type = B_MEDIA_RAW_VIDEO; 406 407 if (format->type != B_MEDIA_RAW_VIDEO) { 408 ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n"); 409 return B_MEDIA_BAD_FORMAT; 410 } 411 412 if (format->u.raw_video.display.format != B_RGB32 413 && format->u.raw_video.display.format != B_RGB16 414 && format->u.raw_video.display.format != B_RGB15 415 && format->u.raw_video.display.format != B_GRAY8 416 && 417 format->u.raw_video.display.format != media_raw_video_format::wildcard.display.format) { 418 ERROR("AcceptFormat - not a format we know about!\n"); 419 return B_MEDIA_BAD_FORMAT; 420 } 421 422 if (format->u.raw_video.display.format == media_raw_video_format::wildcard.display.format) { 423 format->u.raw_video.display.format = B_RGB16; 424 } 425 426 char formatString[256]; 427 string_for_format(*format, formatString, 256); 428 FUNCTION("VideoConsumer::AcceptFormat: %s\n", formatString); 429 430 return B_OK; 431 } 432 433 434 status_t 435 VideoConsumer::GetNextInput(int32* cookie, media_input* outInput) 436 { 437 FUNCTION("VideoConsumer::GetNextInput\n"); 438 439 // custom build a destination for this connection 440 // put connection number in id 441 442 if (*cookie < 1) { 443 fIn.node = Node(); 444 fIn.destination.id = *cookie; 445 sprintf(fIn.name, "Video Consumer"); 446 *outInput = fIn; 447 (*cookie)++; 448 return B_OK; 449 } else { 450 ERROR("VideoConsumer::GetNextInput - - BAD INDEX\n"); 451 return B_MEDIA_BAD_DESTINATION; 452 } 453 } 454 455 456 void 457 VideoConsumer::DisposeInputCookie(int32 /*cookie*/) 458 { 459 } 460 461 462 status_t 463 VideoConsumer::GetLatencyFor(const media_destination& forWhom, bigtime_t* outLatency, 464 media_node_id* out_timesource) 465 { 466 FUNCTION("VideoConsumer::GetLatencyFor\n"); 467 468 if (forWhom != fIn.destination) 469 return B_MEDIA_BAD_DESTINATION; 470 471 *outLatency = fMyLatency; 472 *out_timesource = TimeSource()->ID(); 473 return B_OK; 474 } 475 476 477 status_t 478 VideoConsumer::FormatChanged(const media_source& producer, const media_destination& consumer, 479 int32 fromChangeCount, const media_format& format) 480 { 481 FUNCTION("VideoConsumer::FormatChanged\n"); 482 483 if (consumer != fIn.destination) 484 return B_MEDIA_BAD_DESTINATION; 485 486 if (producer != fIn.source) 487 return B_MEDIA_BAD_SOURCE; 488 489 fIn.format = format; 490 491 return CreateBuffers(format); 492 } 493 494 495 void 496 VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness, 497 bool realTimeEvent) 498 { 499 LOOP("VideoConsumer::HandleEvent\n"); 500 501 BBuffer* buffer; 502 503 switch (event->type) { 504 case BTimedEventQueue::B_START: 505 PROGRESS("VideoConsumer::HandleEvent - START\n"); 506 break; 507 508 case BTimedEventQueue::B_STOP: 509 PROGRESS("VideoConsumer::HandleEvent - STOP\n"); 510 EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS, 511 true, BTimedEventQueue::B_HANDLE_BUFFER); 512 break; 513 514 case BTimedEventQueue::B_USER_EVENT: 515 PROGRESS("VideoConsumer::HandleEvent - USER EVENT\n"); 516 if (RunState() == B_STARTED) { 517 fTimeToFtp = true; 518 PROGRESS("Pushing user event for %.4f, time now %.4f\n", 519 (event->event_time + fRate) / M1, event->event_time/M1); 520 media_timed_event newEvent(event->event_time + fRate, 521 BTimedEventQueue::B_USER_EVENT); 522 EventQueue()->AddEvent(newEvent); 523 } 524 break; 525 526 case BTimedEventQueue::B_HANDLE_BUFFER: 527 { 528 LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n"); 529 buffer = (BBuffer *)event->pointer; 530 if (RunState() == B_STARTED && fConnectionActive) { 531 // see if this is one of our buffers 532 uint32 index = 0; 533 fOurBuffers = true; 534 while (index < 3) 535 if ((uint32)buffer == fBufferMap[index]) 536 break; 537 else 538 index++; 539 540 if (index == 3) { 541 // no, buffers belong to consumer 542 fOurBuffers = false; 543 index = 0; 544 } 545 546 if (fFtpComplete && fTimeToFtp) { 547 PROGRESS("VidConsumer::HandleEvent - SPAWNING FTP THREAD\n"); 548 fTimeToFtp = false; 549 fFtpComplete = false; 550 memcpy(fFtpBitmap->Bits(), buffer->Data(), fFtpBitmap->BitsLength()); 551 fFtpThread = spawn_thread(FtpRun, "Video Window Ftp", B_NORMAL_PRIORITY, this); 552 resume_thread(fFtpThread); 553 } 554 555 if ((RunMode() == B_OFFLINE) 556 || ((TimeSource()->Now() > (buffer->Header()->start_time - JITTER)) 557 && (TimeSource()->Now() < (buffer->Header()->start_time + JITTER)))) { 558 if (!fOurBuffers) 559 // not our buffers, so we need to copy 560 memcpy(fBitmap[index]->Bits(), buffer->Data(), fBitmap[index]->BitsLength()); 561 562 if (fWindow->Lock()) { 563 uint32 flags; 564 if ((fBitmap[index]->ColorSpace() == B_GRAY8) && 565 !bitmaps_support_space(fBitmap[index]->ColorSpace(), &flags)) { 566 // handle mapping of GRAY8 until app server knows how 567 uint32* start = (uint32*)fBitmap[index]->Bits(); 568 int32 size = fBitmap[index]->BitsLength(); 569 uint32* end = start + size / 4; 570 for (uint32* p = start; p < end; p++) 571 *p = (*p >> 3) & 0x1f1f1f1f; 572 } 573 574 fView->DrawBitmap(fBitmap[index]); 575 fWindow->Unlock(); 576 } 577 } 578 else 579 PROGRESS("VidConsumer::HandleEvent - DROPPED FRAME\n"); 580 buffer->Recycle(); 581 } 582 else 583 buffer->Recycle(); 584 break; 585 } 586 587 default: 588 ERROR("VideoConsumer::HandleEvent - BAD EVENT\n"); 589 break; 590 } 591 } 592 593 594 status_t 595 VideoConsumer::FtpRun(void* data) 596 { 597 FUNCTION("VideoConsumer::FtpRun\n"); 598 599 ((VideoConsumer *)data)->FtpThread(); 600 601 return 0; 602 } 603 604 605 void 606 VideoConsumer::FtpThread() 607 { 608 FUNCTION("VideoConsumer::FtpThread\n"); 609 610 if (LocalSave(fFileNameText, fFtpBitmap) == B_OK) 611 FtpSave(fFileNameText); 612 613 #if 0 614 // save a small version, too 615 BBitmap* b = new BBitmap(BRect(0,0,159,119), B_RGB32, true, false); 616 BView* v = new BView(BRect(0,0,159,119), "SmallView 1", 0, B_WILL_DRAW); 617 b->AddChild(v); 618 619 b->Lock(); 620 v->DrawBitmap(fFtpBitmap, v->Frame()); 621 v->Sync(); 622 b->Unlock(); 623 624 if (LocalSave("small.jpg", b) == B_OK) 625 FtpSave("small.jpg"); 626 627 delete b; 628 #endif 629 630 fFtpComplete = true; 631 } 632 633 634 void 635 VideoConsumer::UpdateFtpStatus(char* status) 636 { 637 printf("FTP STATUS: %s\n",status); 638 if (fView->Window()->Lock()) { 639 fStatusLine->SetText(status); 640 fView->Window()->Unlock(); 641 } 642 } 643 644 645 status_t 646 VideoConsumer::LocalSave(char* filename, BBitmap* bitmap) 647 { 648 BFile* output; 649 650 UpdateFtpStatus("Capturing Image" B_UTF8_ELLIPSIS); 651 652 /* save a local copy of the image in the requested format */ 653 output = new BFile(); 654 if (output->SetTo(filename, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE) == B_NO_ERROR) { 655 BBitmapStream input(bitmap); 656 status_t err = BTranslatorRoster::Default()->Translate(&input, NULL, NULL, 657 output, fImageFormat); 658 if (err == B_OK) { 659 err = SetFileType(output, fTranslator, fImageFormat); 660 if (err != B_OK) 661 UpdateFtpStatus("Error setting type of output file"); 662 } 663 else 664 UpdateFtpStatus("Error writing output file"); 665 666 input.DetachBitmap(&bitmap); 667 output->Unset(); 668 delete output; 669 return B_OK; 670 } else { 671 UpdateFtpStatus("Error creating output file"); 672 return B_ERROR; 673 } 674 } 675 676 677 status_t 678 VideoConsumer::FtpSave(char* filename) 679 { 680 FileUploadClient *ftp; 681 682 //XXX: make that cleaner 683 switch (fUploadClient) { 684 case 0: 685 ftp = new FtpClient; 686 break; 687 case 1: 688 ftp = new SftpClient; 689 break; 690 default: 691 fprintf(stderr, "invalid upload client %ld\n", fUploadClient); 692 return EINVAL; 693 } 694 695 ftp->SetPassive(fPassiveFtp); 696 // ftp the local file to our web site 697 698 UpdateFtpStatus("Logging in" B_UTF8_ELLIPSIS); 699 if (ftp->Connect((string)fServerText, (string)fLoginText, (string)fPasswordText)) { 700 // connect to server 701 UpdateFtpStatus("Connected" B_UTF8_ELLIPSIS); 702 703 if (ftp->ChangeDir((string)fDirectoryText)) { 704 // cd to the desired directory 705 UpdateFtpStatus("Transmitting" B_UTF8_ELLIPSIS); 706 707 if (ftp->PutFile((string)filename, (string)"temp")) { 708 // send the file to the server 709 710 ftp->Chmod((string)"temp", (string)"644"); 711 // make it world readable 712 713 UpdateFtpStatus("Renaming" B_UTF8_ELLIPSIS); 714 715 if (ftp->MoveFile((string)"temp", (string)filename)) { 716 // change to the desired name 717 uint32 time = real_time_clock(); 718 char s[80]; 719 strcpy(s, "Last Capture: "); 720 strcat(s, ctime((const long*)&time)); 721 s[strlen(s) - 1] = 0; 722 UpdateFtpStatus(s); 723 delete ftp; 724 return B_OK; 725 } 726 else 727 UpdateFtpStatus("Rename failed"); 728 } 729 else 730 UpdateFtpStatus("File transmission failed"); 731 } 732 else 733 UpdateFtpStatus("Couldn't find requested directory on server"); 734 } 735 else 736 UpdateFtpStatus("Server login failed"); 737 738 delete ftp; 739 return B_ERROR; 740 } 741 742 743 status_t 744 SetFileType(BFile* file, int32 translator, uint32 type) 745 { 746 translation_format* formats; 747 int32 count; 748 749 status_t err = BTranslatorRoster::Default()->GetOutputFormats(translator, 750 (const translation_format **)&formats, &count); 751 if (err < B_OK) 752 return err; 753 754 const char* mime = NULL; 755 for (int ix = 0; ix < count; ix++) { 756 if (formats[ix].type == type) { 757 mime = formats[ix].MIME; 758 break; 759 } 760 } 761 762 if (mime == NULL) { 763 /* this should not happen, but being defensive might be prudent */ 764 return B_ERROR; 765 } 766 767 /* use BNodeInfo to set the file type */ 768 BNodeInfo ninfo(file); 769 return ninfo.SetType(mime); 770 } 771