1 // Copyright 1999, Be Incorporated. All Rights Reserved. 2 // Copyright 2000-2004, Jun Suzuki. All Rights Reserved. 3 // Copyright 2007, Stephan Aßmus. All Rights Reserved. 4 // This file may be used under the terms of the Be Sample Code License. 5 #include "MediaConverterApp.h" 6 7 #include <inttypes.h> 8 #include <new> 9 #include <stdio.h> 10 #include <string.h> 11 12 #include <Alert.h> 13 #include <Catalog.h> 14 #include <fs_attr.h> 15 #include <Locale.h> 16 #include <MediaFile.h> 17 #include <MediaTrack.h> 18 #include <Mime.h> 19 #include <Path.h> 20 #include <String.h> 21 #include <View.h> 22 23 #include "MediaConverterWindow.h" 24 #include "MediaEncoderWindow.h" 25 #include "MessageConstants.h" 26 27 28 #undef B_TRANSLATION_CONTEXT 29 #define B_TRANSLATION_CONTEXT "MediaConverter" 30 31 32 const char APP_SIGNATURE[] = "application/x-vnd.Haiku-MediaConverter"; 33 34 35 MediaConverterApp::MediaConverterApp() 36 : 37 BApplication(APP_SIGNATURE), 38 fWin(NULL), 39 fConvertThreadID(-1), 40 fConverting(false), 41 fCancel(false) 42 { 43 // TODO: implement settings for window pos 44 fWin = new MediaConverterWindow(BRect(50, 50, 520, 555)); 45 } 46 47 48 MediaConverterApp::~MediaConverterApp() 49 { 50 if (fConvertThreadID >= 0) { 51 fCancel = true; 52 status_t exitValue; 53 wait_for_thread(fConvertThreadID, &exitValue); 54 } 55 } 56 57 58 // #pragma mark - 59 60 61 void 62 MediaConverterApp::MessageReceived(BMessage *msg) 63 { 64 switch (msg->what) { 65 case FILE_LIST_CHANGE_MESSAGE: 66 if (fWin->Lock()) { 67 bool enable = fWin->CountSourceFiles() > 0; 68 fWin->SetEnabled(enable, enable); 69 fWin->Unlock(); 70 } 71 break; 72 73 case START_CONVERSION_MESSAGE: 74 if (!fConverting) 75 StartConverting(); 76 break; 77 78 case CANCEL_CONVERSION_MESSAGE: 79 fCancel = true; 80 break; 81 82 case CONVERSION_DONE_MESSAGE: 83 fCancel = false; 84 fConverting = false; 85 DetachCurrentMessage(); 86 BMessenger(fWin).SendMessage(msg); 87 break; 88 89 default: 90 BApplication::MessageReceived(msg); 91 } 92 } 93 94 95 void 96 MediaConverterApp::ReadyToRun() 97 { 98 fWin->Show(); 99 fWin->PostMessage(INIT_FORMAT_MENUS); 100 } 101 102 103 void 104 MediaConverterApp::RefsReceived(BMessage* msg) 105 { 106 entry_ref ref; 107 int32 i = 0; 108 BString errorFiles; 109 int32 errors = 0; 110 111 // from Open dialog or drag & drop 112 113 while (msg->FindRef("refs", i++, &ref) == B_OK) { 114 115 uint32 flags = 0; // B_MEDIA_FILE_NO_READ_AHEAD 116 BMediaFile* file = new(std::nothrow) BMediaFile(&ref, flags); 117 118 if (file == NULL || file->InitCheck() != B_OK) { 119 errorFiles << ref.name << "\n"; 120 errors++; 121 delete file; 122 continue; 123 } 124 if (fWin->Lock()) { 125 if (!fWin->AddSourceFile(file, ref)) 126 delete file; 127 fWin->Unlock(); 128 } 129 } 130 131 if (errors) { 132 BString alertText; 133 if (errors > 1) { 134 alertText = B_TRANSLATE( 135 "%amountOfFiles files were not recognized" 136 " as supported media files:"); 137 BString amount; 138 amount << errors; 139 alertText.ReplaceAll("%amountOfFiles", amount); 140 } else { 141 alertText = B_TRANSLATE( 142 "The file was not recognized as a supported media file:"); 143 } 144 145 alertText << "\n" << errorFiles; 146 BAlert* alert = new BAlert((errors > 1) ? 147 B_TRANSLATE("Error loading files") : 148 B_TRANSLATE("Error loading a file"), 149 alertText.String(), B_TRANSLATE("Continue") , NULL, NULL, 150 B_WIDTH_AS_USUAL, B_STOP_ALERT); 151 alert->Go(); 152 } 153 } 154 155 156 // #pragma mark - 157 158 159 bool 160 MediaConverterApp::IsConverting() const 161 { 162 return fConverting; 163 } 164 165 166 void 167 MediaConverterApp::StartConverting() 168 { 169 bool locked = fWin->Lock(); 170 171 if (locked && (fWin->CountSourceFiles() > 0)) { 172 fConvertThreadID = spawn_thread(MediaConverterApp::_RunConvertEntry, 173 "converter thread", B_LOW_PRIORITY, (void *)this); 174 if (fConvertThreadID >= 0) { 175 fConverting = true; 176 fCancel = false; 177 resume_thread(fConvertThreadID); 178 } 179 } 180 181 if (locked) { 182 fWin->Unlock(); 183 } 184 } 185 186 187 void 188 MediaConverterApp::SetStatusMessage(const char* message) 189 { 190 if (fWin != NULL && fWin->Lock()) { 191 fWin->SetStatusMessage(message); 192 fWin->Unlock(); 193 } 194 } 195 196 197 // #pragma mark - 198 199 BEntry 200 MediaConverterApp::_CreateOutputFile(BDirectory directory, 201 entry_ref* ref, media_file_format* outputFormat) 202 { 203 BString name(ref->name); 204 // create output file name 205 int32 extIndex = name.FindLast('.'); 206 if (extIndex != B_ERROR) 207 name.Truncate(extIndex + 1); 208 else 209 name.Append("."); 210 211 name.Append(outputFormat->file_extension); 212 213 BEntry inEntry(ref); 214 BEntry outEntry; 215 216 if (inEntry.InitCheck() == B_OK) { 217 // ensure that output name is unique 218 int32 len = name.Length(); 219 int32 i = 1; 220 while (directory.Contains(name.String())) { 221 name.Truncate(len); 222 name << " " << i; 223 i++; 224 } 225 outEntry.SetTo(&directory, name.String()); 226 } 227 228 return outEntry; 229 } 230 231 232 int32 233 MediaConverterApp::_RunConvertEntry(void* castToMediaConverterApp) 234 { 235 MediaConverterApp* app = (MediaConverterApp*)castToMediaConverterApp; 236 app->_RunConvert(); 237 return 0; 238 } 239 240 241 void 242 MediaConverterApp::_RunConvert() 243 { 244 bigtime_t start = 0; 245 bigtime_t end = 0; 246 int32 audioQuality = 75; 247 int32 videoQuality = 75; 248 249 if (fWin->Lock()) { 250 char *a; 251 start = strtoimax(fWin->StartDuration(), &a, 0) * 1000; 252 end = strtoimax(fWin->EndDuration(), &a, 0) * 1000; 253 audioQuality = fWin->AudioQuality(); 254 videoQuality = fWin->VideoQuality(); 255 fWin->Unlock(); 256 } 257 258 int32 srcIndex = 0; 259 260 BMediaFile *inFile(NULL), *outFile(NULL); 261 BEntry outEntry; 262 entry_ref inRef; 263 entry_ref outRef; 264 BPath path; 265 BString name; 266 267 while (!fCancel) { 268 if (fWin->Lock()) { 269 status_t r = fWin->GetSourceFileAt(srcIndex, &inFile, &inRef); 270 if (r == B_OK) { 271 media_codec_info* audioCodec; 272 media_codec_info* videoCodec; 273 media_file_format* fileFormat; 274 fWin->GetSelectedFormatInfo(&fileFormat, &audioCodec, &videoCodec); 275 BDirectory directory = fWin->OutputDirectory(); 276 fWin->Unlock(); 277 outEntry = _CreateOutputFile(directory, &inRef, fileFormat); 278 279 // display file name 280 281 outEntry.GetPath(&path); 282 name.SetTo(path.Leaf()); 283 284 if (outEntry.InitCheck() == B_OK) { 285 entry_ref outRef; 286 outEntry.GetRef(&outRef); 287 outFile = new BMediaFile(&outRef, fileFormat); 288 289 BString tmp( 290 B_TRANSLATE("Output file '%filename' created")); 291 tmp.ReplaceAll("%filename", name); 292 name = tmp; 293 } else { 294 BString tmp(B_TRANSLATE("Error creating '%filename'")); 295 tmp.ReplaceAll("%filename", name); 296 name = tmp; 297 } 298 299 if (fWin->Lock()) { 300 fWin->SetFileMessage(name.String()); 301 fWin->Unlock(); 302 } 303 304 if (outFile != NULL) { 305 r = _ConvertFile(inFile, outFile, audioCodec, videoCodec, 306 audioQuality, videoQuality, start, end); 307 308 // set mime 309 update_mime_info(path.Path(), false, false, false); 310 311 fWin->Lock(); 312 if (r == B_OK) { 313 fWin->RemoveSourceFile(srcIndex); 314 } else { 315 srcIndex++; 316 BString error( 317 B_TRANSLATE("Error converting '%filename'")); 318 error.ReplaceAll("%filename", inRef.name); 319 fWin->SetStatusMessage(error.String()); 320 } 321 fWin->Unlock(); 322 } 323 324 325 } else { 326 fWin->Unlock(); 327 break; 328 } 329 } else { 330 break; 331 } 332 } 333 334 BMessenger(this).SendMessage(CONVERSION_DONE_MESSAGE); 335 } 336 337 338 // #pragma mark - 339 340 341 status_t 342 MediaConverterApp::_ConvertFile(BMediaFile* inFile, BMediaFile* outFile, 343 media_codec_info* audioCodec, media_codec_info* videoCodec, 344 int32 audioQuality, int32 videoQuality, 345 bigtime_t startDuration, bigtime_t endDuration) 346 { 347 BMediaTrack* inVidTrack = NULL; 348 BMediaTrack* inAudTrack = NULL; 349 BMediaTrack* outVidTrack = NULL; 350 BMediaTrack* outAudTrack = NULL; 351 352 media_format inFormat; 353 media_format outAudFormat; 354 media_format outVidFormat; 355 356 media_raw_audio_format* raf = NULL; 357 media_raw_video_format* rvf = NULL; 358 359 int32 width = -1; 360 int32 height = -1; 361 362 uint8* videoBuffer = NULL; 363 uint8* audioBuffer = NULL; 364 365 // gather the necessary format information and construct output tracks 366 int64 videoFrameCount = 0; 367 int64 audioFrameCount = 0; 368 369 status_t ret = B_OK; 370 371 int32 tracks = inFile->CountTracks(); 372 for (int32 i = 0; i < tracks && (!outAudTrack || !outVidTrack); i++) { 373 BMediaTrack* inTrack = inFile->TrackAt(i); 374 memset(&inFormat, 0, sizeof(media_format)); 375 inTrack->EncodedFormat(&inFormat); 376 if (inFormat.IsAudio() && (audioCodec != NULL)) { 377 inAudTrack = inTrack; 378 memset(&outAudFormat, 0, sizeof(media_format)); 379 outAudFormat.type = B_MEDIA_RAW_AUDIO; 380 raf = &(outAudFormat.u.raw_audio); 381 inTrack->DecodedFormat(&outAudFormat); 382 383 audioBuffer = new uint8[raf->buffer_size]; 384 // audioFrameSize = (raf->format & media_raw_audio_format::B_AUDIO_SIZE_MASK) 385 // audioFrameSize = (raf->format & 0xf) * raf->channel_count; 386 outAudTrack = outFile->CreateTrack(&outAudFormat, audioCodec); 387 388 if (outAudTrack != NULL) { 389 if (outAudTrack->SetQuality(audioQuality / 100.0f) != B_OK 390 && fWin->Lock()) { 391 fWin->SetAudioQualityLabel( 392 B_TRANSLATE("Audio quality not supported")); 393 fWin->Unlock(); 394 } 395 } 396 397 } else if (inFormat.IsVideo() && (videoCodec != NULL)) { 398 inVidTrack = inTrack; 399 width = (int32)inFormat.Width(); 400 height = (int32)inFormat.Height(); 401 402 // construct desired decoded video format 403 memset(&outVidFormat, 0, sizeof(outVidFormat)); 404 outVidFormat.type = B_MEDIA_RAW_VIDEO; 405 rvf = &(outVidFormat.u.raw_video); 406 rvf->last_active = (uint32)(height - 1); 407 rvf->orientation = B_VIDEO_TOP_LEFT_RIGHT; 408 rvf->display.format = B_RGB32; 409 rvf->display.bytes_per_row = 4 * width; 410 rvf->display.line_width = width; 411 rvf->display.line_count = height; 412 413 inVidTrack->DecodedFormat(&outVidFormat); 414 415 if (rvf->display.format == B_RGBA32) { 416 printf("fixing color space (B_RGBA32 -> B_RGB32)"); 417 rvf->display.format = B_RGB32; 418 } 419 // Transfer the display aspect ratio. 420 if (inFormat.type == B_MEDIA_ENCODED_VIDEO) { 421 rvf->pixel_width_aspect 422 = inFormat.u.encoded_video.output.pixel_width_aspect; 423 rvf->pixel_height_aspect 424 = inFormat.u.encoded_video.output.pixel_height_aspect; 425 } else { 426 rvf->pixel_width_aspect 427 = inFormat.u.raw_video.pixel_width_aspect; 428 rvf->pixel_height_aspect 429 = inFormat.u.raw_video.pixel_height_aspect; 430 } 431 432 videoBuffer = new (std::nothrow) uint8[height 433 * rvf->display.bytes_per_row]; 434 outVidTrack = outFile->CreateTrack(&outVidFormat, videoCodec); 435 436 if (outVidTrack != NULL) { 437 // DLM Added to use 3ivx Parameter View 438 const char* videoQualitySupport = NULL; 439 BView* encoderView = outVidTrack->GetParameterView(); 440 if (encoderView) { 441 MediaEncoderWindow* encoderWin 442 = new MediaEncoderWindow(BRect(50, 50, 520, 555), 443 encoderView); 444 encoderWin->Go(); 445 // blocks until the window is quit 446 447 // The quality setting is ignored by the 3ivx encoder if the 448 // view was displayed, but this method is the trigger to read 449 // all the parameter settings 450 outVidTrack->SetQuality(videoQuality / 100.0f); 451 452 // We can now delete the encoderView created for us by the encoder 453 delete encoderView; 454 encoderView = NULL; 455 456 videoQualitySupport = 457 B_TRANSLATE("Video using parameters form settings"); 458 } else { 459 if (outVidTrack->SetQuality(videoQuality / 100.0f) >= B_OK) 460 videoQualitySupport = 461 B_TRANSLATE("Video quality not supported"); 462 } 463 if (videoQualitySupport && fWin->Lock()) { 464 fWin->SetVideoQualityLabel(videoQualitySupport); 465 fWin->Unlock(); 466 } 467 } 468 } else { 469 // didn't do anything with the track 470 inFile->ReleaseTrack(inTrack); 471 } 472 } 473 474 if (!outVidTrack && !outAudTrack) { 475 printf("MediaConverterApp::_ConvertFile() - no tracks found!\n"); 476 ret = B_ERROR; 477 } 478 479 if (fCancel) { 480 // don't have any video or audio tracks here, or cancelled 481 printf("MediaConverterApp::_ConvertFile()" 482 " - user canceled before transcoding\n"); 483 ret = B_CANCELED; 484 } 485 486 if (ret < B_OK) { 487 delete[] audioBuffer; 488 delete[] videoBuffer; 489 delete outFile; 490 return ret; 491 } 492 493 outFile->CommitHeader(); 494 // this is where you would call outFile->AddCopyright(...) 495 496 int64 framesRead; 497 media_header mh; 498 int32 lastPercent, currPercent; 499 float completePercent; 500 BString status; 501 502 int64 start; 503 int64 end; 504 int32 stat = 0; 505 506 // read video from source and write to destination, if necessary 507 if (outVidTrack != NULL) { 508 lastPercent = -1; 509 videoFrameCount = inVidTrack->CountFrames(); 510 if (endDuration == 0 || endDuration < startDuration) { 511 start = 0; 512 end = videoFrameCount; 513 } else { 514 inVidTrack->SeekToTime(&endDuration, stat); 515 end = inVidTrack->CurrentFrame(); 516 inVidTrack->SeekToTime(&startDuration, stat); 517 start = inVidTrack->CurrentFrame(); 518 if (end > videoFrameCount) 519 end = videoFrameCount; 520 if (start > end) 521 start = 0; 522 } 523 524 framesRead = 0; 525 for (int64 i = start; (i <= end) && !fCancel; i += framesRead) { 526 if ((ret = inVidTrack->ReadFrames(videoBuffer, &framesRead, 527 &mh)) != B_OK) { 528 fprintf(stderr, "Error reading video frame %Ld: %s\n", i, 529 strerror(ret)); 530 snprintf(status.LockBuffer(128), 128, 531 B_TRANSLATE("Error read video frame %Ld"), i); 532 status.UnlockBuffer(); 533 SetStatusMessage(status.String()); 534 535 break; 536 } 537 538 if ((ret = outVidTrack->WriteFrames(videoBuffer, framesRead, 539 mh.u.encoded_video.field_flags)) != B_OK) { 540 fprintf(stderr, "Error writing video frame %Ld: %s\n", i, 541 strerror(ret)); 542 snprintf(status.LockBuffer(128), 128, 543 B_TRANSLATE("Error writing video frame %Ld"), i); 544 status.UnlockBuffer(); 545 SetStatusMessage(status.String()); 546 547 break; 548 } 549 completePercent = (float)(i - start) / (float)(end - start) * 100; 550 currPercent = (int32)completePercent; 551 if (currPercent > lastPercent) { 552 lastPercent = currPercent; 553 snprintf(status.LockBuffer(128), 128, 554 B_TRANSLATE("Writing video track: %ld%% complete"), 555 currPercent); 556 status.UnlockBuffer(); 557 SetStatusMessage(status.String()); 558 559 } 560 } 561 outVidTrack->Flush(); 562 inFile->ReleaseTrack(inVidTrack); 563 } 564 565 // read audio from source and write to destination, if necessary 566 if (outAudTrack != NULL) { 567 lastPercent = -1; 568 569 audioFrameCount = inAudTrack->CountFrames(); 570 571 if (endDuration == 0 || endDuration < startDuration) { 572 start = 0; 573 end = audioFrameCount; 574 } else { 575 inAudTrack->SeekToTime(&endDuration, stat); 576 end = inAudTrack->CurrentFrame(); 577 inAudTrack->SeekToTime(&startDuration, stat); 578 start = inAudTrack->CurrentFrame(); 579 if (end > audioFrameCount) 580 end = audioFrameCount; 581 if (start > end) 582 start = 0; 583 } 584 585 for (int64 i = start; (i <= end) && !fCancel; i += framesRead) { 586 if ((ret = inAudTrack->ReadFrames(audioBuffer, &framesRead, 587 &mh)) != B_OK) { 588 fprintf(stderr, "Error reading audio frames: %s\n", strerror(ret)); 589 snprintf(status.LockBuffer(128), 128, 590 B_TRANSLATE("Error read audio frame %Ld"), i); 591 status.UnlockBuffer(); 592 SetStatusMessage(status.String()); 593 594 break; 595 } 596 597 if ((ret = outAudTrack->WriteFrames(audioBuffer, 598 framesRead)) != B_OK) { 599 fprintf(stderr, "Error writing audio frames: %s\n", strerror(ret)); 600 snprintf(status.LockBuffer(128), 128, 601 B_TRANSLATE("Error writing audio frame %Ld"), i); 602 status.UnlockBuffer(); 603 SetStatusMessage(status.String()); 604 605 break; 606 } 607 completePercent = (float)(i - start) / (float)(end - start) * 100; 608 currPercent = (int32)completePercent; 609 if (currPercent > lastPercent) { 610 lastPercent = currPercent; 611 snprintf(status.LockBuffer(128), 128, 612 B_TRANSLATE("Writing audio track: %ld%% complete"), 613 currPercent); 614 status.UnlockBuffer(); 615 SetStatusMessage(status.String()); 616 } 617 } 618 outAudTrack->Flush(); 619 inFile->ReleaseTrack(inAudTrack); 620 621 } 622 623 outFile->CloseFile(); 624 delete outFile; 625 626 delete[] videoBuffer; 627 delete[] audioBuffer; 628 629 return ret; 630 } 631 632 633 // #pragma mark - 634 635 636 int 637 main(int, char **) 638 { 639 MediaConverterApp app; 640 app.Run(); 641 642 return 0; 643 } 644