BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / apps / mediaconverter / MediaConverterApp.cpp
blob5fc41d0b5fd407f496096d698ded639982608f90
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"
7 #include <inttypes.h>
8 #include <new>
9 #include <stdio.h>
10 #include <string.h>
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 <MessageFormat.h>
19 #include <Mime.h>
20 #include <Path.h>
21 #include <String.h>
22 #include <View.h>
24 #include "MediaConverterWindow.h"
25 #include "MediaEncoderWindow.h"
26 #include "MessageConstants.h"
29 #undef B_TRANSLATION_CONTEXT
30 #define B_TRANSLATION_CONTEXT "MediaConverter"
33 const char APP_SIGNATURE[] = "application/x-vnd.Haiku-MediaConverter";
36 MediaConverterApp::MediaConverterApp()
38 BApplication(APP_SIGNATURE),
39 fWin(NULL),
40 fConvertThreadID(-1),
41 fConverting(false),
42 fCancel(false)
44 // TODO: implement settings for window pos
45 fWin = new MediaConverterWindow(BRect(50, 50, 520, 555));
49 MediaConverterApp::~MediaConverterApp()
51 if (fConvertThreadID >= 0) {
52 fCancel = true;
53 status_t exitValue;
54 wait_for_thread(fConvertThreadID, &exitValue);
59 // #pragma mark -
62 void
63 MediaConverterApp::MessageReceived(BMessage *msg)
65 switch (msg->what) {
66 case FILE_LIST_CHANGE_MESSAGE:
67 if (fWin->Lock()) {
68 bool enable = fWin->CountSourceFiles() > 0;
69 fWin->SetEnabled(enable, enable);
70 fWin->Unlock();
72 break;
74 case START_CONVERSION_MESSAGE:
75 if (!fConverting)
76 StartConverting();
77 break;
79 case CANCEL_CONVERSION_MESSAGE:
80 fCancel = true;
81 break;
83 case CONVERSION_DONE_MESSAGE:
84 fCancel = false;
85 fConverting = false;
86 DetachCurrentMessage();
87 BMessenger(fWin).SendMessage(msg);
88 break;
90 default:
91 BApplication::MessageReceived(msg);
96 void
97 MediaConverterApp::ReadyToRun()
99 fWin->Show();
100 fWin->PostMessage(INIT_FORMAT_MENUS);
104 void
105 MediaConverterApp::RefsReceived(BMessage* msg)
107 entry_ref ref;
108 int32 i = 0;
109 BString errorFiles;
110 int32 errors = 0;
112 // from Open dialog or drag & drop
114 while (msg->FindRef("refs", i++, &ref) == B_OK) {
116 uint32 flags = 0; // B_MEDIA_FILE_NO_READ_AHEAD
117 BMediaFile* file = new(std::nothrow) BMediaFile(&ref, flags);
119 if (file == NULL || file->InitCheck() != B_OK) {
120 errorFiles << ref.name << "\n";
121 errors++;
122 delete file;
123 continue;
125 if (fWin->Lock()) {
126 if (!fWin->AddSourceFile(file, ref))
127 delete file;
128 fWin->Unlock();
132 if (errors) {
133 BString alertText;
134 static BMessageFormat format(B_TRANSLATE("{0, plural, "
135 "one{The file was not recognized as a supported media file:} "
136 "other{# files were not recognized as supported media files:}}"));
137 format.Format(alertText, errors);
139 alertText << "\n" << errorFiles;
140 BAlert* alert = new BAlert((errors > 1) ?
141 B_TRANSLATE("Error loading files") :
142 B_TRANSLATE("Error loading a file"),
143 alertText.String(), B_TRANSLATE("Continue"), NULL, NULL,
144 B_WIDTH_AS_USUAL, B_STOP_ALERT);
145 alert->Go();
150 // #pragma mark -
153 bool
154 MediaConverterApp::IsConverting() const
156 return fConverting;
160 void
161 MediaConverterApp::StartConverting()
163 bool locked = fWin->Lock();
165 if (locked && (fWin->CountSourceFiles() > 0)) {
166 fConvertThreadID = spawn_thread(MediaConverterApp::_RunConvertEntry,
167 "converter thread", B_LOW_PRIORITY, (void *)this);
168 if (fConvertThreadID >= 0) {
169 fConverting = true;
170 fCancel = false;
171 resume_thread(fConvertThreadID);
175 if (locked) {
176 fWin->Unlock();
181 void
182 MediaConverterApp::SetStatusMessage(const char* message)
184 if (fWin != NULL && fWin->Lock()) {
185 fWin->SetStatusMessage(message);
186 fWin->Unlock();
191 // #pragma mark -
193 BEntry
194 MediaConverterApp::_CreateOutputFile(BDirectory directory,
195 entry_ref* ref, media_file_format* outputFormat)
197 BString name(ref->name);
198 // create output file name
199 int32 extIndex = name.FindLast('.');
200 if (extIndex != B_ERROR)
201 name.Truncate(extIndex + 1);
202 else
203 name.Append(".");
205 name.Append(outputFormat->file_extension);
207 BEntry directoryEntry;
208 directory.GetEntry(&directoryEntry);
209 if (!directoryEntry.Exists()) {
210 BAlert* alert = new BAlert(B_TRANSLATE("Error"),
211 B_TRANSLATE("Selected directory not found. "
212 "Defaulting to /boot/home"),
213 B_TRANSLATE("OK"));
214 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
215 alert->Go();
216 directory.SetTo("/boot/home");
219 BEntry inEntry(ref);
220 BEntry outEntry;
222 if (inEntry.InitCheck() == B_OK) {
223 // ensure that output name is unique
224 int32 len = name.Length();
225 int32 i = 1;
226 while (directory.Contains(name.String())) {
227 name.Truncate(len);
228 name << " " << i;
229 i++;
231 outEntry.SetTo(&directory, name.String());
234 return outEntry;
238 int32
239 MediaConverterApp::_RunConvertEntry(void* castToMediaConverterApp)
241 MediaConverterApp* app = (MediaConverterApp*)castToMediaConverterApp;
242 app->_RunConvert();
243 return 0;
247 void
248 MediaConverterApp::_RunConvert()
250 bigtime_t start = 0;
251 bigtime_t end = 0;
252 int32 audioQuality = 75;
253 int32 videoQuality = 75;
255 if (fWin->Lock()) {
256 char *a;
257 start = strtoimax(fWin->StartDuration(), &a, 0) * 1000;
258 end = strtoimax(fWin->EndDuration(), &a, 0) * 1000;
259 audioQuality = fWin->AudioQuality();
260 videoQuality = fWin->VideoQuality();
261 fWin->Unlock();
264 int32 srcIndex = 0;
266 BMediaFile *inFile(NULL), *outFile(NULL);
267 BEntry outEntry;
268 entry_ref inRef;
269 entry_ref outRef;
270 BPath path;
271 BString name;
273 while (!fCancel) {
274 if (fWin->Lock()) {
275 status_t r = fWin->GetSourceFileAt(srcIndex, &inFile, &inRef);
276 if (r == B_OK) {
277 media_codec_info* audioCodec;
278 media_codec_info* videoCodec;
279 media_file_format* fileFormat;
280 fWin->GetSelectedFormatInfo(&fileFormat, &audioCodec,
281 &videoCodec);
282 BDirectory directory = fWin->OutputDirectory();
283 fWin->Unlock();
284 outEntry = _CreateOutputFile(directory, &inRef, fileFormat);
286 // display file name
288 outEntry.GetPath(&path);
289 name.SetTo(path.Leaf());
291 if (outEntry.InitCheck() == B_OK) {
292 entry_ref outRef;
293 outEntry.GetRef(&outRef);
294 outFile = new BMediaFile(&outRef, fileFormat);
296 BString tmp(
297 B_TRANSLATE("Output file '%filename' created"));
298 tmp.ReplaceAll("%filename", name);
299 name = tmp;
300 } else {
301 BString tmp(B_TRANSLATE("Error creating '%filename'"));
302 tmp.ReplaceAll("%filename", name);
303 name = tmp;
306 if (fWin->Lock()) {
307 fWin->SetFileMessage(name.String());
308 fWin->Unlock();
311 if (outFile != NULL) {
312 r = _ConvertFile(inFile, outFile, audioCodec, videoCodec,
313 audioQuality, videoQuality, start, end);
315 // set mime
316 update_mime_info(path.Path(), false, false, false);
318 fWin->Lock();
319 if (r == B_OK) {
320 fWin->RemoveSourceFile(srcIndex);
321 } else {
322 srcIndex++;
323 BString error(
324 B_TRANSLATE("Error converting '%filename'"));
325 error.ReplaceAll("%filename", inRef.name);
326 fWin->SetStatusMessage(error.String());
328 fWin->Unlock();
332 } else {
333 srcIndex++;
334 BString error(
335 B_TRANSLATE("Error converting '%filename'"));
336 error.ReplaceAll("%filename", inRef.name);
337 fWin->SetStatusMessage(error.String());
338 fWin->Unlock();
339 break;
341 } else {
342 break;
346 BMessenger(this).SendMessage(CONVERSION_DONE_MESSAGE);
350 // #pragma mark -
353 status_t
354 MediaConverterApp::_ConvertFile(BMediaFile* inFile, BMediaFile* outFile,
355 media_codec_info* audioCodec, media_codec_info* videoCodec,
356 int32 audioQuality, int32 videoQuality,
357 bigtime_t startDuration, bigtime_t endDuration)
359 BMediaTrack* inVidTrack = NULL;
360 BMediaTrack* inAudTrack = NULL;
361 BMediaTrack* outVidTrack = NULL;
362 BMediaTrack* outAudTrack = NULL;
364 media_format inFormat;
365 media_format outAudFormat;
366 media_format outVidFormat;
368 media_raw_audio_format* raf = NULL;
369 media_raw_video_format* rvf = NULL;
371 int32 width = -1;
372 int32 height = -1;
374 uint8* videoBuffer = NULL;
375 uint8* audioBuffer = NULL;
377 // gather the necessary format information and construct output tracks
378 int64 videoFrameCount = 0;
379 int64 audioFrameCount = 0;
381 status_t ret = B_OK;
383 int32 tracks = inFile->CountTracks();
384 for (int32 i = 0; i < tracks && (!outAudTrack || !outVidTrack); i++) {
385 BMediaTrack* inTrack = inFile->TrackAt(i);
386 memset(&inFormat, 0, sizeof(media_format));
387 inTrack->EncodedFormat(&inFormat);
388 if (inFormat.IsAudio() && (audioCodec != NULL)) {
389 inAudTrack = inTrack;
390 memset(&outAudFormat, 0, sizeof(media_format));
391 outAudFormat.type = B_MEDIA_RAW_AUDIO;
392 raf = &(outAudFormat.u.raw_audio);
393 inTrack->DecodedFormat(&outAudFormat);
395 audioBuffer = new uint8[raf->buffer_size];
396 // audioFrameSize = (raf->format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
397 // audioFrameSize = (raf->format & 0xf) * raf->channel_count;
398 outAudTrack = outFile->CreateTrack(&outAudFormat, audioCodec);
400 // Negociate the format with the inTrack again in case the codec
401 // made some changes to it...
402 inTrack->DecodedFormat(&outAudFormat);
404 if (outAudTrack != NULL) {
405 if (outAudTrack->SetQuality(audioQuality / 100.0f) != B_OK
406 && fWin->Lock()) {
407 fWin->SetAudioQualityLabel(
408 B_TRANSLATE("Audio quality not supported"));
409 fWin->Unlock();
411 } else {
412 fWin->SetStatusMessage(
413 B_TRANSLATE("Error creating track."));
416 } else if (inFormat.IsVideo() && (videoCodec != NULL)) {
417 inVidTrack = inTrack;
418 width = (int32)inFormat.Width();
419 height = (int32)inFormat.Height();
421 // construct desired decoded video format
422 memset(&outVidFormat, 0, sizeof(outVidFormat));
423 outVidFormat.type = B_MEDIA_RAW_VIDEO;
424 rvf = &(outVidFormat.u.raw_video);
425 rvf->last_active = (uint32)(height - 1);
426 rvf->orientation = B_VIDEO_TOP_LEFT_RIGHT;
427 rvf->display.format = B_RGB32;
428 rvf->display.bytes_per_row = 4 * width;
429 rvf->display.line_width = width;
430 rvf->display.line_count = height;
432 inVidTrack->DecodedFormat(&outVidFormat);
434 if (rvf->display.format == B_RGBA32) {
435 printf("fixing color space (B_RGBA32 -> B_RGB32)");
436 rvf->display.format = B_RGB32;
438 // Transfer the display aspect ratio.
439 if (inFormat.type == B_MEDIA_ENCODED_VIDEO) {
440 rvf->pixel_width_aspect
441 = inFormat.u.encoded_video.output.pixel_width_aspect;
442 rvf->pixel_height_aspect
443 = inFormat.u.encoded_video.output.pixel_height_aspect;
444 } else {
445 rvf->pixel_width_aspect
446 = inFormat.u.raw_video.pixel_width_aspect;
447 rvf->pixel_height_aspect
448 = inFormat.u.raw_video.pixel_height_aspect;
451 videoBuffer = new (std::nothrow) uint8[height
452 * rvf->display.bytes_per_row];
453 outVidTrack = outFile->CreateTrack(&outVidFormat, videoCodec);
455 if (outVidTrack != NULL) {
456 // DLM Added to use 3ivx Parameter View
457 const char* videoQualitySupport = NULL;
458 BView* encoderView = outVidTrack->GetParameterView();
459 if (encoderView) {
460 MediaEncoderWindow* encoderWin
461 = new MediaEncoderWindow(BRect(50, 50, 520, 555),
462 encoderView);
463 encoderWin->Go();
464 // blocks until the window is quit
466 // The quality setting is ignored by the 3ivx encoder if the
467 // view was displayed, but this method is the trigger to
468 // read all the parameter settings
469 outVidTrack->SetQuality(videoQuality / 100.0f);
471 // We can now delete the encoderView created for us by the
472 // encoder
473 delete encoderView;
474 encoderView = NULL;
476 videoQualitySupport
477 = B_TRANSLATE("Video using parameters form settings");
478 } else if (outVidTrack->SetQuality(videoQuality / 100.0f)
479 >= B_OK) {
480 videoQualitySupport
481 = B_TRANSLATE("Video quality not supported");
484 if (videoQualitySupport && fWin->Lock()) {
485 fWin->SetVideoQualityLabel(videoQualitySupport);
486 fWin->Unlock();
488 } else {
489 fWin->SetStatusMessage(
490 B_TRANSLATE("Error creating video."));
492 } else {
493 // didn't do anything with the track
494 fWin->SetStatusMessage(
495 B_TRANSLATE("Input file not recognized as Audio or Video"));
496 inFile->ReleaseTrack(inTrack);
500 if (!outVidTrack && !outAudTrack) {
501 printf("MediaConverterApp::_ConvertFile() - no tracks found!\n");
502 ret = B_ERROR;
505 if (fCancel) {
506 // don't have any video or audio tracks here, or cancelled
507 printf("MediaConverterApp::_ConvertFile()"
508 " - user canceled before transcoding\n");
509 ret = B_CANCELED;
512 if (ret < B_OK) {
513 delete[] audioBuffer;
514 delete[] videoBuffer;
515 delete outFile;
516 return ret;
519 outFile->CommitHeader();
520 // this is where you would call outFile->AddCopyright(...)
522 int64 framesRead;
523 media_header mh;
524 int32 lastPercent, currPercent;
525 float completePercent;
526 BString status;
528 int64 start;
529 int64 end;
530 int32 stat = 0;
532 // read video from source and write to destination, if necessary
533 if (outVidTrack != NULL) {
534 lastPercent = -1;
535 videoFrameCount = inVidTrack->CountFrames();
536 if (endDuration == 0 || endDuration < startDuration) {
537 start = 0;
538 end = videoFrameCount;
539 } else {
540 inVidTrack->SeekToTime(&endDuration, stat);
541 end = inVidTrack->CurrentFrame();
542 inVidTrack->SeekToTime(&startDuration, stat);
543 start = inVidTrack->CurrentFrame();
544 if (end > videoFrameCount)
545 end = videoFrameCount;
546 if (start > end)
547 start = 0;
550 framesRead = 0;
551 for (int64 i = start; (i < end) && !fCancel; i += framesRead) {
552 if ((ret = inVidTrack->ReadFrames(videoBuffer, &framesRead,
553 &mh)) != B_OK) {
554 fprintf(stderr, "Error reading video frame %" B_PRId64 ": %s\n",
555 i, strerror(ret));
556 snprintf(status.LockBuffer(128), 128,
557 B_TRANSLATE("Error read video frame %" B_PRId64), i);
558 status.UnlockBuffer();
559 SetStatusMessage(status.String());
561 break;
564 if ((ret = outVidTrack->WriteFrames(videoBuffer, framesRead,
565 mh.u.encoded_video.field_flags)) != B_OK) {
566 fprintf(stderr, "Error writing video frame %" B_PRId64 ": %s\n",
567 i, strerror(ret));
568 snprintf(status.LockBuffer(128), 128,
569 B_TRANSLATE("Error writing video frame %" B_PRId64), i);
570 status.UnlockBuffer();
571 SetStatusMessage(status.String());
573 break;
575 completePercent = (float)(i - start) / (float)(end - start) * 100;
576 currPercent = (int32)completePercent;
577 if (currPercent > lastPercent) {
578 lastPercent = currPercent;
579 snprintf(status.LockBuffer(128), 128,
580 B_TRANSLATE("Writing video track: %" B_PRId32 "%% complete"),
581 currPercent);
582 status.UnlockBuffer();
583 SetStatusMessage(status.String());
587 outVidTrack->Flush();
588 inFile->ReleaseTrack(inVidTrack);
591 // read audio from source and write to destination, if necessary
592 if (outAudTrack != NULL) {
593 lastPercent = -1;
595 audioFrameCount = inAudTrack->CountFrames();
597 if (endDuration == 0 || endDuration < startDuration) {
598 start = 0;
599 end = audioFrameCount;
600 } else {
601 inAudTrack->SeekToTime(&endDuration, stat);
602 end = inAudTrack->CurrentFrame();
603 inAudTrack->SeekToTime(&startDuration, stat);
604 start = inAudTrack->CurrentFrame();
605 if (end > audioFrameCount)
606 end = audioFrameCount;
607 if (start > end)
608 start = 0;
611 for (int64 i = start; (i < end) && !fCancel; i += framesRead) {
612 if ((ret = inAudTrack->ReadFrames(audioBuffer, &framesRead,
613 &mh)) != B_OK) {
614 fprintf(stderr, "Error reading audio frames: %s\n", strerror(ret));
615 snprintf(status.LockBuffer(128), 128,
616 B_TRANSLATE("Error read audio frame %" B_PRId64), i);
617 status.UnlockBuffer();
618 SetStatusMessage(status.String());
620 break;
623 if ((ret = outAudTrack->WriteFrames(audioBuffer,
624 framesRead)) != B_OK) {
625 fprintf(stderr, "Error writing audio frames: %s\n", strerror(ret));
626 snprintf(status.LockBuffer(128), 128,
627 B_TRANSLATE("Error writing audio frame %" B_PRId64), i);
628 status.UnlockBuffer();
629 SetStatusMessage(status.String());
631 break;
633 completePercent = (float)(i - start) / (float)(end - start) * 100;
634 currPercent = (int32)completePercent;
635 if (currPercent > lastPercent) {
636 lastPercent = currPercent;
637 snprintf(status.LockBuffer(128), 128,
638 B_TRANSLATE("Writing audio track: %" B_PRId32 "%% complete"),
639 currPercent);
640 status.UnlockBuffer();
641 SetStatusMessage(status.String());
644 outAudTrack->Flush();
645 inFile->ReleaseTrack(inAudTrack);
649 outFile->CloseFile();
650 delete outFile;
652 delete[] videoBuffer;
653 delete[] audioBuffer;
655 return ret;
659 // #pragma mark -
663 main(int, char **)
665 MediaConverterApp app;
666 app.Run();
668 return 0;