// Copyright 1999, Be Incorporated. All Rights Reserved.
// Copyright 2000-2004, Jun Suzuki. All Rights Reserved.
// Copyright 2007, Stephan Aßmus. All Rights Reserved.
// This file may be used under the terms of the Be Sample Code License.
#include "MediaConverterApp.h"
#include <inttypes.h>
#include <new>
#include <stdio.h>
#include <string.h>
#include <Alert.h>
#include <Catalog.h>
#include <fs_attr.h>
#include <Locale.h>
#include <MediaFile.h>
#include <MediaTrack.h>
#include <Mime.h>
#include <Path.h>
#include <String.h>
#include <StringFormat.h>
#include <View.h>
#include "MediaConverterWindow.h"
#include "MediaEncoderWindow.h"
#include "MessageConstants.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "MediaConverter"
const char APP_SIGNATURE[] = "application/x-vnd.Haiku-MediaConverter";
MediaConverterApp::MediaConverterApp()
:
BApplication(APP_SIGNATURE),
fWin(NULL),
fConvertThreadID(-1),
fConverting(false),
fCancel(false)
{
// TODO: implement settings for window pos
fWin = new MediaConverterWindow(BRect(50, 50, 520, 555));
}
MediaConverterApp::~MediaConverterApp()
{
if (fConvertThreadID >= 0) {
fCancel = true;
status_t exitValue;
wait_for_thread(fConvertThreadID, &exitValue);
}
}
// #pragma mark -
void
MediaConverterApp::MessageReceived(BMessage *msg)
{
switch (msg->what) {
case FILE_LIST_CHANGE_MESSAGE:
if (fWin->Lock()) {
bool enable = fWin->CountSourceFiles() > 0;
fWin->SetEnabled(enable, enable);
fWin->Unlock();
}
break;
case START_CONVERSION_MESSAGE:
if (!fConverting)
StartConverting();
break;
case CANCEL_CONVERSION_MESSAGE:
fCancel = true;
break;
case CONVERSION_DONE_MESSAGE:
fCancel = false;
fConverting = false;
DetachCurrentMessage();
BMessenger(fWin).SendMessage(msg);
break;
default:
BApplication::MessageReceived(msg);
}
}
void
MediaConverterApp::ReadyToRun()
{
fWin->Show();
fWin->PostMessage(INIT_FORMAT_MENUS);
}
void
MediaConverterApp::RefsReceived(BMessage* msg)
{
entry_ref ref;
int32 i = 0;
BString errorFiles;
int32 errors = 0;
// from Open dialog or drag & drop
while (msg->FindRef("refs", i++, &ref) == B_OK) {
uint32 flags = 0; // B_MEDIA_FILE_NO_READ_AHEAD
BMediaFile* file = new(std::nothrow) BMediaFile(&ref, flags);
if (file == NULL || file->InitCheck() != B_OK) {
errorFiles << ref.name << "\n";
errors++;
delete file;
continue;
}
if (fWin->Lock()) {
if (!fWin->AddSourceFile(file, ref))
delete file;
fWin->Unlock();
}
}
if (errors) {
BString alertText;
static BStringFormat format(B_TRANSLATE("{0, plural, "
"one{The file was not recognized as a supported media file:} "
"other{# files were not recognized as supported media files:}}"));
format.Format(alertText, errors);
alertText << "\n" << errorFiles;
BAlert* alert = new BAlert((errors > 1) ?
B_TRANSLATE("Error loading files") :
B_TRANSLATE("Error loading a file"),
alertText.String(), B_TRANSLATE("Continue"), NULL, NULL,
B_WIDTH_AS_USUAL, B_STOP_ALERT);
alert->Go();
}
}
// #pragma mark -
bool
MediaConverterApp::IsConverting() const
{
return fConverting;
}
void
MediaConverterApp::StartConverting()
{
bool locked = fWin->Lock();
if (locked && (fWin->CountSourceFiles() > 0)) {
fConvertThreadID = spawn_thread(MediaConverterApp::_RunConvertEntry,
"converter thread", B_LOW_PRIORITY, (void *)this);
if (fConvertThreadID >= 0) {
fConverting = true;
fCancel = false;
resume_thread(fConvertThreadID);
}
}
if (locked) {
fWin->Unlock();
}
}
void
MediaConverterApp::SetStatusMessage(const char* message)
{
if (fWin != NULL && fWin->Lock()) {
fWin->SetStatusMessage(message);
fWin->Unlock();
}
}
// #pragma mark -
BEntry
MediaConverterApp::_CreateOutputFile(BDirectory directory,
entry_ref* ref, media_file_format* outputFormat)
{
BString name(ref->name);
// create output file name
int32 extIndex = name.FindLast('.');
if (extIndex != B_ERROR)
name.Truncate(extIndex + 1);
else
name.Append(".");
name.Append(outputFormat->file_extension);
BEntry directoryEntry;
directory.GetEntry(&directoryEntry);
if (!directoryEntry.Exists()) {
BAlert* alert = new BAlert(B_TRANSLATE("Error"),
B_TRANSLATE("Selected directory not found. "
"Defaulting to /boot/home"),
B_TRANSLATE("OK"));
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
directory.SetTo("/boot/home");
}
BEntry inEntry(ref);
BEntry outEntry;
if (inEntry.InitCheck() == B_OK) {
// ensure that output name is unique
int32 len = name.Length();
int32 i = 1;
while (directory.Contains(name.String())) {
name.Truncate(len);
name << " " << i;
i++;
}
outEntry.SetTo(&directory, name.String());
}
return outEntry;
}
int32
MediaConverterApp::_RunConvertEntry(void* castToMediaConverterApp)
{
MediaConverterApp* app = (MediaConverterApp*)castToMediaConverterApp;
app->_RunConvert();
return 0;
}
void
MediaConverterApp::_RunConvert()
{
bigtime_t start = 0;
bigtime_t end = 0;
int32 audioQuality = 75;
int32 videoQuality = 75;
if (fWin->Lock()) {
char *a;
start = strtoimax(fWin->StartDuration(), &a, 0) * 1000;
end = strtoimax(fWin->EndDuration(), &a, 0) * 1000;
audioQuality = fWin->AudioQuality();
videoQuality = fWin->VideoQuality();
fWin->Unlock();
}
int32 srcIndex = 0;
BMediaFile *inFile(NULL), *outFile(NULL);
BEntry outEntry;
entry_ref inRef;
entry_ref outRef;
BPath path;
BString name;
while (!fCancel) {
if (fWin->Lock()) {
status_t r = fWin->GetSourceFileAt(srcIndex, &inFile, &inRef);
if (r == B_OK) {
media_codec_info* audioCodec;
media_codec_info* videoCodec;
media_file_format* fileFormat;
fWin->GetSelectedFormatInfo(&fileFormat, &audioCodec,
&videoCodec);
BDirectory directory = fWin->OutputDirectory();
fWin->Unlock();
outEntry = _CreateOutputFile(directory, &inRef, fileFormat);
// display file name
outEntry.GetPath(&path);
name.SetTo(path.Leaf());
if (outEntry.InitCheck() == B_OK) {
entry_ref outRef;
outEntry.GetRef(&outRef);
outFile = new BMediaFile(&outRef, fileFormat);
BString tmp(
B_TRANSLATE("Output file '%filename' created"));
tmp.ReplaceAll("%filename", name);
name = tmp;
} else {
BString tmp(B_TRANSLATE("Error creating '%filename'"));
tmp.ReplaceAll("%filename", name);
name = tmp;
}
if (fWin->Lock()) {
fWin->SetFileMessage(name.String());
fWin->Unlock();
}
if (outFile != NULL) {
r = _ConvertFile(inFile, outFile, audioCodec, videoCodec,
audioQuality, videoQuality, start, end);
// set mime
update_mime_info(path.Path(), false, false, false);
fWin->Lock();
if (r == B_OK) {
fWin->RemoveSourceFile(srcIndex);
} else {
srcIndex++;
BString error(
B_TRANSLATE("Error converting '%filename'"));
error.ReplaceAll("%filename", inRef.name);
fWin->SetStatusMessage(error.String());
}
fWin->Unlock();
}
} else {
srcIndex++;
BString error(
B_TRANSLATE("Error converting '%filename'"));
error.ReplaceAll("%filename", inRef.name);
fWin->SetStatusMessage(error.String());
fWin->Unlock();
break;
}
} else {
break;
}
}
BMessenger(this).SendMessage(CONVERSION_DONE_MESSAGE);
}
// #pragma mark -
status_t
MediaConverterApp::_ConvertFile(BMediaFile* inFile, BMediaFile* outFile,
media_codec_info* audioCodec, media_codec_info* videoCodec,
int32 audioQuality, int32 videoQuality,
bigtime_t startDuration, bigtime_t endDuration)
{
BMediaTrack* inVidTrack = NULL;
BMediaTrack* inAudTrack = NULL;
BMediaTrack* outVidTrack = NULL;
BMediaTrack* outAudTrack = NULL;
media_format inFormat;
media_format outAudFormat;
media_format outVidFormat;
media_raw_audio_format* raf = NULL;
media_raw_video_format* rvf = NULL;
int32 width = -1;
int32 height = -1;
uint8* videoBuffer = NULL;
uint8* audioBuffer = NULL;
// gather the necessary format information and construct output tracks
int64 videoFrameCount = 0;
int64 audioFrameCount = 0;
status_t ret = B_OK;
int32 tracks = inFile->CountTracks();
for (int32 i = 0; i < tracks && (!outAudTrack || !outVidTrack); i++) {
BMediaTrack* inTrack = inFile->TrackAt(i);
inFormat.Clear();
inTrack->EncodedFormat(&inFormat);
if (inFormat.IsAudio() && (audioCodec != NULL)) {
inAudTrack = inTrack;
outAudFormat.Clear();
outAudFormat.type = B_MEDIA_RAW_AUDIO;
raf = &(outAudFormat.u.raw_audio);
inTrack->DecodedFormat(&outAudFormat);
audioBuffer = new uint8[raf->buffer_size];
// audioFrameSize = (raf->format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
// audioFrameSize = (raf->format & 0xf) * raf->channel_count;
outAudTrack = outFile->CreateTrack(&outAudFormat, audioCodec);
// Negociate the format with the inTrack again in case the codec
// made some changes to it...
inTrack->DecodedFormat(&outAudFormat);
if (outAudTrack != NULL) {
if (outAudTrack->SetQuality(audioQuality / 100.0f) != B_OK
&& fWin->Lock()) {
fWin->SetAudioQualityLabel(
B_TRANSLATE("Audio quality not supported"));
fWin->Unlock();
}
} else {
SetStatusMessage(B_TRANSLATE("Error creating track."));
}
} else if (inFormat.IsVideo() && (videoCodec != NULL)) {
inVidTrack = inTrack;
width = (int32)inFormat.Width();
height = (int32)inFormat.Height();
// construct desired decoded video format
outVidFormat.Clear();
outVidFormat.type = B_MEDIA_RAW_VIDEO;
rvf = &(outVidFormat.u.raw_video);
rvf->last_active = (uint32)(height - 1);
rvf->orientation = B_VIDEO_TOP_LEFT_RIGHT;
rvf->display.format = B_RGB32;
rvf->display.bytes_per_row = 4 * width;
rvf->display.line_width = width;
rvf->display.line_count = height;
inVidTrack->DecodedFormat(&outVidFormat);
if (rvf->display.format == B_RGBA32) {
printf("fixing color space (B_RGBA32 -> B_RGB32)");
rvf->display.format = B_RGB32;
}
// Transfer the display aspect ratio.
if (inFormat.type == B_MEDIA_ENCODED_VIDEO) {
rvf->pixel_width_aspect
= inFormat.u.encoded_video.output.pixel_width_aspect;
rvf->pixel_height_aspect
= inFormat.u.encoded_video.output.pixel_height_aspect;
} else {
rvf->pixel_width_aspect
= inFormat.u.raw_video.pixel_width_aspect;
rvf->pixel_height_aspect
= inFormat.u.raw_video.pixel_height_aspect;
}
videoBuffer = new (std::nothrow) uint8[height
* rvf->display.bytes_per_row];
outVidTrack = outFile->CreateTrack(&outVidFormat, videoCodec);
if (outVidTrack != NULL) {
// DLM Added to use 3ivx Parameter View
const char* videoQualitySupport = NULL;
BView* encoderView = outVidTrack->GetParameterView();
if (encoderView) {
MediaEncoderWindow* encoderWin
= new MediaEncoderWindow(BRect(50, 50, 520, 555),
encoderView);
encoderWin->Go();
// blocks until the window is quit
// The quality setting is ignored by the 3ivx encoder if the
// view was displayed, but this method is the trigger to
// read all the parameter settings
outVidTrack->SetQuality(videoQuality / 100.0f);
// We can now delete the encoderView created for us by the
// encoder
delete encoderView;
encoderView = NULL;
videoQualitySupport
= B_TRANSLATE("Video using parameters form settings");
} else if (outVidTrack->SetQuality(videoQuality / 100.0f)
>= B_OK) {
videoQualitySupport
= B_TRANSLATE("Video quality not supported");
}
if (videoQualitySupport && fWin->Lock()) {
fWin->SetVideoQualityLabel(videoQualitySupport);
fWin->Unlock();
}
} else {
SetStatusMessage(B_TRANSLATE("Error creating video."));
}
} else {
// didn't do anything with the track
SetStatusMessage(
B_TRANSLATE("Input file not recognized as Audio or Video"));
inFile->ReleaseTrack(inTrack);
}
}
if (!outVidTrack && !outAudTrack) {
printf("MediaConverterApp::_ConvertFile() - no tracks found!\n");
ret = B_ERROR;
}
if (fCancel) {
// don't have any video or audio tracks here, or cancelled
printf("MediaConverterApp::_ConvertFile()"
" - user canceled before transcoding\n");
ret = B_CANCELED;
}
if (ret < B_OK) {
delete[] audioBuffer;
delete[] videoBuffer;
delete outFile;
return ret;
}
outFile->CommitHeader();
// this is where you would call outFile->AddCopyright(...)
int64 framesRead;
media_header mh;
int32 lastPercent, currPercent;
float completePercent;
BString status;
int64 start;
int64 end;
int32 stat = 0;
// read video from source and write to destination, if necessary
if (outVidTrack != NULL) {
lastPercent = -1;
videoFrameCount = inVidTrack->CountFrames();
if (endDuration == 0 || endDuration < startDuration) {
start = 0;
end = videoFrameCount;
} else {
inVidTrack->SeekToTime(&endDuration, stat);
end = inVidTrack->CurrentFrame();
inVidTrack->SeekToTime(&startDuration, stat);
start = inVidTrack->CurrentFrame();
if (end > videoFrameCount)
end = videoFrameCount;
if (start > end)
start = 0;
}
framesRead = 0;
for (int64 i = start; (i < end) && !fCancel; i += framesRead) {
if ((ret = inVidTrack->ReadFrames(videoBuffer, &framesRead,
&mh)) != B_OK) {
fprintf(stderr, "Error reading video frame %" B_PRId64 ": %s\n",
i, strerror(ret));
snprintf(status.LockBuffer(128), 128,
B_TRANSLATE("Error read video frame %" B_PRId64), i);
status.UnlockBuffer();
SetStatusMessage(status.String());
break;
}
if ((ret = outVidTrack->WriteFrames(videoBuffer, framesRead,
mh.u.encoded_video.field_flags)) != B_OK) {
fprintf(stderr, "Error writing video frame %" B_PRId64 ": %s\n",
i, strerror(ret));
snprintf(status.LockBuffer(128), 128,
B_TRANSLATE("Error writing video frame %" B_PRId64), i);
status.UnlockBuffer();
SetStatusMessage(status.String());
break;
}
completePercent = (float)(i - start) / (float)(end - start) * 100;
currPercent = (int32)completePercent;
if (currPercent > lastPercent) {
lastPercent = currPercent;
snprintf(status.LockBuffer(128), 128,
B_TRANSLATE("Writing video track: %" B_PRId32 "%% complete"),
currPercent);
status.UnlockBuffer();
SetStatusMessage(status.String());
}
}
outVidTrack->Flush();
inFile->ReleaseTrack(inVidTrack);
}
// read audio from source and write to destination, if necessary
if (outAudTrack != NULL) {
lastPercent = -1;
audioFrameCount = inAudTrack->CountFrames();
if (endDuration == 0 || endDuration < startDuration) {
start = 0;
end = audioFrameCount;
} else {
inAudTrack->SeekToTime(&endDuration, stat);
end = inAudTrack->CurrentFrame();
inAudTrack->SeekToTime(&startDuration, stat);
start = inAudTrack->CurrentFrame();
if (end > audioFrameCount)
end = audioFrameCount;
if (start > end)
start = 0;
}
for (int64 i = start; (i < end) && !fCancel; i += framesRead) {
if ((ret = inAudTrack->ReadFrames(audioBuffer, &framesRead,
&mh)) != B_OK) {
fprintf(stderr, "Error reading audio frames: %s\n", strerror(ret));
snprintf(status.LockBuffer(128), 128,
B_TRANSLATE("Error read audio frame %" B_PRId64), i);
status.UnlockBuffer();
SetStatusMessage(status.String());
break;
}
if ((ret = outAudTrack->WriteFrames(audioBuffer,
framesRead)) != B_OK) {
fprintf(stderr, "Error writing audio frames: %s\n", strerror(ret));
snprintf(status.LockBuffer(128), 128,
B_TRANSLATE("Error writing audio frame %" B_PRId64), i);
status.UnlockBuffer();
SetStatusMessage(status.String());
break;
}
completePercent = (float)(i - start) / (float)(end - start) * 100;
currPercent = (int32)completePercent;
if (currPercent > lastPercent) {
lastPercent = currPercent;
snprintf(status.LockBuffer(128), 128,
B_TRANSLATE("Writing audio track: %" B_PRId32 "%% complete"),
currPercent);
status.UnlockBuffer();
SetStatusMessage(status.String());
}
}
outAudTrack->Flush();
inFile->ReleaseTrack(inAudTrack);
}
outFile->CloseFile();
delete outFile;
delete[] videoBuffer;
delete[] audioBuffer;
return ret;
}
// #pragma mark -
int
main(int, char **)
{
MediaConverterApp app;
app.Run();
return 0;
}
↑ V547 Expression 'rvf->display.format == B_RGBA32' is always false.
↑ V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.
↑ V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.