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"
16 #include <MediaFile.h>
17 #include <MediaTrack.h>
18 #include <MessageFormat.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
),
44 // TODO: implement settings for window pos
45 fWin
= new MediaConverterWindow(BRect(50, 50, 520, 555));
49 MediaConverterApp::~MediaConverterApp()
51 if (fConvertThreadID
>= 0) {
54 wait_for_thread(fConvertThreadID
, &exitValue
);
63 MediaConverterApp::MessageReceived(BMessage
*msg
)
66 case FILE_LIST_CHANGE_MESSAGE
:
68 bool enable
= fWin
->CountSourceFiles() > 0;
69 fWin
->SetEnabled(enable
, enable
);
74 case START_CONVERSION_MESSAGE
:
79 case CANCEL_CONVERSION_MESSAGE
:
83 case CONVERSION_DONE_MESSAGE
:
86 DetachCurrentMessage();
87 BMessenger(fWin
).SendMessage(msg
);
91 BApplication::MessageReceived(msg
);
97 MediaConverterApp::ReadyToRun()
100 fWin
->PostMessage(INIT_FORMAT_MENUS
);
105 MediaConverterApp::RefsReceived(BMessage
* msg
)
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";
126 if (!fWin
->AddSourceFile(file
, ref
))
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
);
154 MediaConverterApp::IsConverting() const
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) {
171 resume_thread(fConvertThreadID
);
182 MediaConverterApp::SetStatusMessage(const char* message
)
184 if (fWin
!= NULL
&& fWin
->Lock()) {
185 fWin
->SetStatusMessage(message
);
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);
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"),
214 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
216 directory
.SetTo("/boot/home");
222 if (inEntry
.InitCheck() == B_OK
) {
223 // ensure that output name is unique
224 int32 len
= name
.Length();
226 while (directory
.Contains(name
.String())) {
231 outEntry
.SetTo(&directory
, name
.String());
239 MediaConverterApp::_RunConvertEntry(void* castToMediaConverterApp
)
241 MediaConverterApp
* app
= (MediaConverterApp
*)castToMediaConverterApp
;
248 MediaConverterApp::_RunConvert()
252 int32 audioQuality
= 75;
253 int32 videoQuality
= 75;
257 start
= strtoimax(fWin
->StartDuration(), &a
, 0) * 1000;
258 end
= strtoimax(fWin
->EndDuration(), &a
, 0) * 1000;
259 audioQuality
= fWin
->AudioQuality();
260 videoQuality
= fWin
->VideoQuality();
266 BMediaFile
*inFile(NULL
), *outFile(NULL
);
275 status_t r
= fWin
->GetSourceFileAt(srcIndex
, &inFile
, &inRef
);
277 media_codec_info
* audioCodec
;
278 media_codec_info
* videoCodec
;
279 media_file_format
* fileFormat
;
280 fWin
->GetSelectedFormatInfo(&fileFormat
, &audioCodec
,
282 BDirectory directory
= fWin
->OutputDirectory();
284 outEntry
= _CreateOutputFile(directory
, &inRef
, fileFormat
);
288 outEntry
.GetPath(&path
);
289 name
.SetTo(path
.Leaf());
291 if (outEntry
.InitCheck() == B_OK
) {
293 outEntry
.GetRef(&outRef
);
294 outFile
= new BMediaFile(&outRef
, fileFormat
);
297 B_TRANSLATE("Output file '%filename' created"));
298 tmp
.ReplaceAll("%filename", name
);
301 BString
tmp(B_TRANSLATE("Error creating '%filename'"));
302 tmp
.ReplaceAll("%filename", name
);
307 fWin
->SetFileMessage(name
.String());
311 if (outFile
!= NULL
) {
312 r
= _ConvertFile(inFile
, outFile
, audioCodec
, videoCodec
,
313 audioQuality
, videoQuality
, start
, end
);
316 update_mime_info(path
.Path(), false, false, false);
320 fWin
->RemoveSourceFile(srcIndex
);
324 B_TRANSLATE("Error converting '%filename'"));
325 error
.ReplaceAll("%filename", inRef
.name
);
326 fWin
->SetStatusMessage(error
.String());
335 B_TRANSLATE("Error converting '%filename'"));
336 error
.ReplaceAll("%filename", inRef
.name
);
337 fWin
->SetStatusMessage(error
.String());
346 BMessenger(this).SendMessage(CONVERSION_DONE_MESSAGE
);
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
;
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;
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
407 fWin
->SetAudioQualityLabel(
408 B_TRANSLATE("Audio quality not supported"));
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
;
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();
460 MediaEncoderWindow
* encoderWin
461 = new MediaEncoderWindow(BRect(50, 50, 520, 555),
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
477 = B_TRANSLATE("Video using parameters form settings");
478 } else if (outVidTrack
->SetQuality(videoQuality
/ 100.0f
)
481 = B_TRANSLATE("Video quality not supported");
484 if (videoQualitySupport
&& fWin
->Lock()) {
485 fWin
->SetVideoQualityLabel(videoQualitySupport
);
489 fWin
->SetStatusMessage(
490 B_TRANSLATE("Error creating video."));
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");
506 // don't have any video or audio tracks here, or cancelled
507 printf("MediaConverterApp::_ConvertFile()"
508 " - user canceled before transcoding\n");
513 delete[] audioBuffer
;
514 delete[] videoBuffer
;
519 outFile
->CommitHeader();
520 // this is where you would call outFile->AddCopyright(...)
524 int32 lastPercent
, currPercent
;
525 float completePercent
;
532 // read video from source and write to destination, if necessary
533 if (outVidTrack
!= NULL
) {
535 videoFrameCount
= inVidTrack
->CountFrames();
536 if (endDuration
== 0 || endDuration
< startDuration
) {
538 end
= videoFrameCount
;
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
;
551 for (int64 i
= start
; (i
< end
) && !fCancel
; i
+= framesRead
) {
552 if ((ret
= inVidTrack
->ReadFrames(videoBuffer
, &framesRead
,
554 fprintf(stderr
, "Error reading video frame %" B_PRId64
": %s\n",
556 snprintf(status
.LockBuffer(128), 128,
557 B_TRANSLATE("Error read video frame %" B_PRId64
), i
);
558 status
.UnlockBuffer();
559 SetStatusMessage(status
.String());
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",
568 snprintf(status
.LockBuffer(128), 128,
569 B_TRANSLATE("Error writing video frame %" B_PRId64
), i
);
570 status
.UnlockBuffer();
571 SetStatusMessage(status
.String());
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"),
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
) {
595 audioFrameCount
= inAudTrack
->CountFrames();
597 if (endDuration
== 0 || endDuration
< startDuration
) {
599 end
= audioFrameCount
;
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
;
611 for (int64 i
= start
; (i
< end
) && !fCancel
; i
+= framesRead
) {
612 if ((ret
= inAudTrack
->ReadFrames(audioBuffer
, &framesRead
,
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());
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());
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"),
640 status
.UnlockBuffer();
641 SetStatusMessage(status
.String());
644 outAudTrack
->Flush();
645 inFile
->ReleaseTrack(inAudTrack
);
649 outFile
->CloseFile();
652 delete[] videoBuffer
;
653 delete[] audioBuffer
;
665 MediaConverterApp app
;