forked from mia/Aegisub
Implement support for multiple tracks in the FFMS2 audio and video providers. Files with more than one audio or video track will now let the user pick which one he/she wishes to load. Closes #905.
Originally committed to SVN as r3145.
This commit is contained in:
parent
5cb4d1cae3
commit
c33ed91b12
7 changed files with 207 additions and 118 deletions
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2008, Karl Blomster
|
// Copyright (c) 2008-2009, Karl Blomster
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -41,6 +41,8 @@
|
||||||
// Headers
|
// Headers
|
||||||
#include "include/aegisub/aegisub.h"
|
#include "include/aegisub/aegisub.h"
|
||||||
#include "audio_provider_ffmpegsource.h"
|
#include "audio_provider_ffmpegsource.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include <map>
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <objbase.h>
|
#include <objbase.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -61,7 +63,7 @@ FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(Aegisub::String filename) {
|
||||||
FFMS_Init(0);
|
FFMS_Init(0);
|
||||||
|
|
||||||
MsgSize = sizeof(FFMSErrMsg);
|
MsgSize = sizeof(FFMSErrMsg);
|
||||||
MsgString = _T("FFmpegSource audio provider: ");
|
ErrorMsg = _T("FFmpegSource audio provider: ");
|
||||||
|
|
||||||
AudioSource = NULL;
|
AudioSource = NULL;
|
||||||
|
|
||||||
|
@ -82,60 +84,78 @@ void FFmpegSourceAudioProvider::LoadAudio(Aegisub::String filename) {
|
||||||
|
|
||||||
wxString FileNameWX = wxFileName(wxString(filename.c_str(), wxConvFile)).GetShortPath();
|
wxString FileNameWX = wxFileName(wxString(filename.c_str(), wxConvFile)).GetShortPath();
|
||||||
|
|
||||||
// generate a default name for the cache file
|
FFIndexer *Indexer = FFMS_CreateIndexer(FileNameWX.mb_str(wxConvUTF8), FFMSErrMsg, MsgSize);
|
||||||
|
if (Indexer == NULL) {
|
||||||
|
// error messages that can possibly contain a filename use this method instead of
|
||||||
|
// wxString::Format because they may contain utf8 characters
|
||||||
|
ErrorMsg.Append(_T("Failed to create indexer: ")).Append(wxString(FFMSErrMsg, wxConvUTF8));
|
||||||
|
throw ErrorMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<int,wxString> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO);
|
||||||
|
if (TrackList.size() <= 0)
|
||||||
|
throw _T("FFmpegSource audio provider: no audio tracks found");
|
||||||
|
|
||||||
|
// initialize the track number to an invalid value so we can detect later on
|
||||||
|
// whether the user actually had to choose a track or not
|
||||||
|
int TrackNumber = -1;
|
||||||
|
if (TrackList.size() > 1) {
|
||||||
|
TrackNumber = AskForTrackSelection(TrackList, FFMS_TYPE_AUDIO);
|
||||||
|
// if it's still -1 here, user pressed cancel
|
||||||
|
if (TrackNumber == -1)
|
||||||
|
throw _T("FFmpegSource audio provider: audio loading cancelled by user");
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a name for the cache file
|
||||||
wxString CacheName = GetCacheFilename(filename.c_str());
|
wxString CacheName = GetCacheFilename(filename.c_str());
|
||||||
|
|
||||||
|
// try to read index
|
||||||
FFIndex *Index = NULL;
|
FFIndex *Index = NULL;
|
||||||
bool ReIndex = false;
|
|
||||||
Index = FFMS_ReadIndex(CacheName.mb_str(wxConvUTF8), FFMSErrMsg, MsgSize);
|
Index = FFMS_ReadIndex(CacheName.mb_str(wxConvUTF8), FFMSErrMsg, MsgSize);
|
||||||
if (Index == NULL) {
|
bool IndexIsValid = false;
|
||||||
ReIndex = true;
|
if (Index != NULL) {
|
||||||
}
|
if (FFMS_IndexBelongsToFile(Index, FileNameWX.mb_str(wxConvUTF8), FFMSErrMsg, MsgSize)) {
|
||||||
// index exists, but is it the index we think it is?
|
|
||||||
else if (FFMS_IndexBelongsToFile(Index, FileNameWX.mb_str(wxConvUTF8), FFMSErrMsg, MsgSize)) {
|
|
||||||
FFMS_DestroyIndex(Index);
|
FFMS_DestroyIndex(Index);
|
||||||
Index = NULL;
|
Index = NULL;
|
||||||
ReIndex = true;
|
|
||||||
}
|
}
|
||||||
// it is, but does it have indexing info for the audio track(s)?
|
else
|
||||||
else {
|
IndexIsValid = true;
|
||||||
int NumTracks = FFMS_GetNumTracks(Index);
|
|
||||||
// sanity check
|
|
||||||
if (NumTracks <= 0) {
|
|
||||||
FFMS_DestroyIndex(Index);
|
|
||||||
Index = NULL;
|
|
||||||
throw _T("FFmpegSource audio provider: no tracks found in index file");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < NumTracks; i++) {
|
// index valid but track number still not set?
|
||||||
FFTrack *TrackData = FFMS_GetTrackFromIndex(Index, i);
|
if (IndexIsValid) {
|
||||||
// more sanity checking
|
// track number not set? just grab the first track
|
||||||
if (TrackData == NULL) {
|
if (TrackNumber < 0)
|
||||||
|
TrackNumber = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, FFMSErrMsg, MsgSize);
|
||||||
|
if (TrackNumber < 0) {
|
||||||
FFMS_DestroyIndex(Index);
|
FFMS_DestroyIndex(Index);
|
||||||
Index = NULL;
|
Index = NULL;
|
||||||
wxString temp(FFMSErrMsg, wxConvUTF8);
|
ErrorMsg.Append(wxString::Format(_T("Couldn't find any audio tracks: %s"), FFMSErrMsg));
|
||||||
MsgString << _T("Couldn't get track data: ") << temp;
|
throw ErrorMsg;
|
||||||
throw MsgString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// does the track have any indexed frames?
|
// index is valid and track number is now set,
|
||||||
if (FFMS_GetNumFrames(TrackData) <= 0 && (FFMS_GetTrackType(TrackData) == FFMS_TYPE_AUDIO)) {
|
// but do we have indexing info for the desired audio track?
|
||||||
// found an unindexed audio track, we'll need to reindex
|
FFTrack *TempTrackData = FFMS_GetTrackFromIndex(Index, TrackNumber);
|
||||||
|
if (FFMS_GetNumFrames(TempTrackData) <= 0) {
|
||||||
|
IndexIsValid = false;
|
||||||
FFMS_DestroyIndex(Index);
|
FFMS_DestroyIndex(Index);
|
||||||
Index = NULL;
|
Index = NULL;
|
||||||
ReIndex = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// no valid index exists and the file only has one audio track, index all tracks
|
||||||
|
else if (TrackNumber < 0)
|
||||||
|
TrackNumber = FFMSTrackMaskAll;
|
||||||
|
// else: do nothing (keep track mask as it is)
|
||||||
|
|
||||||
// index didn't exist or was invalid, we'll have to (re)create it
|
// moment of truth
|
||||||
if (ReIndex) {
|
if (!IndexIsValid) {
|
||||||
|
int TrackMask = Options.AsBool(_T("FFmpegSource always index all tracks")) ? FFMSTrackMaskAll : 1 << TrackNumber;
|
||||||
try {
|
try {
|
||||||
Index = DoIndexing(Index, FileNameWX, CacheName, FFMSTrackMaskAll, false);
|
Index = DoIndexing(Indexer, CacheName, TrackMask, false);
|
||||||
} catch (wxString temp) {
|
} catch (wxString temp) {
|
||||||
MsgString << temp;
|
ErrorMsg.Append(temp);
|
||||||
throw MsgString;
|
throw ErrorMsg;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -144,23 +164,12 @@ void FFmpegSourceAudioProvider::LoadAudio(Aegisub::String filename) {
|
||||||
// update access time of index file so it won't get cleaned away
|
// update access time of index file so it won't get cleaned away
|
||||||
wxFileName(CacheName).Touch();
|
wxFileName(CacheName).Touch();
|
||||||
|
|
||||||
// FIXME: provide a way to choose which audio track to load?
|
|
||||||
int TrackNumber = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, FFMSErrMsg, MsgSize);
|
|
||||||
if (TrackNumber < 0) {
|
|
||||||
FFMS_DestroyIndex(Index);
|
|
||||||
Index = NULL;
|
|
||||||
wxString temp(FFMSErrMsg, wxConvUTF8);
|
|
||||||
MsgString << _T("Couldn't find any audio tracks: ") << temp;
|
|
||||||
throw MsgString;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioSource = FFMS_CreateAudioSource(FileNameWX.mb_str(wxConvUTF8), TrackNumber, Index, FFMSErrMsg, MsgSize);
|
AudioSource = FFMS_CreateAudioSource(FileNameWX.mb_str(wxConvUTF8), TrackNumber, Index, FFMSErrMsg, MsgSize);
|
||||||
FFMS_DestroyIndex(Index);
|
FFMS_DestroyIndex(Index);
|
||||||
Index = NULL;
|
Index = NULL;
|
||||||
if (!AudioSource) {
|
if (!AudioSource) {
|
||||||
wxString temp(FFMSErrMsg, wxConvUTF8);
|
ErrorMsg.Append(wxString::Format(_T("Failed to open audio track: %s"), FFMSErrMsg));
|
||||||
MsgString << _T("Failed to open audio track: ") << temp;
|
throw ErrorMsg;
|
||||||
throw MsgString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FFAudioProperties AudioInfo = *FFMS_GetAudioProperties(AudioSource);
|
const FFAudioProperties AudioInfo = *FFMS_GetAudioProperties(AudioSource);
|
||||||
|
@ -171,7 +180,7 @@ void FFmpegSourceAudioProvider::LoadAudio(Aegisub::String filename) {
|
||||||
if (channels <= 0 || sample_rate <= 0 || num_samples <= 0)
|
if (channels <= 0 || sample_rate <= 0 || num_samples <= 0)
|
||||||
throw _T("FFmpegSource audio provider: sanity check failed, consult your local psychiatrist");
|
throw _T("FFmpegSource audio provider: sanity check failed, consult your local psychiatrist");
|
||||||
|
|
||||||
// FIXME: use
|
// FIXME: use the actual sample format too?
|
||||||
// why not just bits_per_sample/8? maybe there's some oddball format with half bytes out there somewhere...
|
// why not just bits_per_sample/8? maybe there's some oddball format with half bytes out there somewhere...
|
||||||
switch (AudioInfo.BitsPerSample) {
|
switch (AudioInfo.BitsPerSample) {
|
||||||
case 8: bytes_per_sample = 1; break;
|
case 8: bytes_per_sample = 1; break;
|
||||||
|
@ -207,9 +216,8 @@ void FFmpegSourceAudioProvider::Close() {
|
||||||
// Get audio
|
// Get audio
|
||||||
void FFmpegSourceAudioProvider::GetAudio(void *Buf, int64_t Start, int64_t Count) {
|
void FFmpegSourceAudioProvider::GetAudio(void *Buf, int64_t Start, int64_t Count) {
|
||||||
if (FFMS_GetAudio(AudioSource, Buf, Start, Count, FFMSErrMsg, MsgSize)) {
|
if (FFMS_GetAudio(AudioSource, Buf, Start, Count, FFMSErrMsg, MsgSize)) {
|
||||||
wxString temp(FFMSErrMsg, wxConvUTF8);
|
ErrorMsg.Append(wxString::Format(_T("Failed to get audio samples: %s"), FFMSErrMsg));
|
||||||
MsgString << _T("Failed to get audio samples: ") << temp;
|
throw ErrorMsg;
|
||||||
throw MsgString;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2008, Karl Blomster
|
// Copyright (c) 2008-2009, Karl Blomster
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -49,7 +49,7 @@ private:
|
||||||
|
|
||||||
char FFMSErrMsg[1024];
|
char FFMSErrMsg[1024];
|
||||||
unsigned MsgSize;
|
unsigned MsgSize;
|
||||||
wxString MsgString;
|
wxString ErrorMsg;
|
||||||
|
|
||||||
bool COMInited;
|
bool COMInited;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2008, Karl Blomster
|
// Copyright (c) 2008-2009, Karl Blomster
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
|
||||||
// lookit dis here static shit
|
// lookit dis here static storage
|
||||||
wxMutex FFmpegSourceProvider::CleaningInProgress;
|
wxMutex FFmpegSourceProvider::CleaningInProgress;
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ int FFMS_CC FFmpegSourceProvider::UpdateIndexingProgress(int64_t Current, int64_
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// no one cares about a little bit of a rounding error here anyway
|
// no one cares about a little bit of a rounding error here anyway
|
||||||
Progress->ProgressDialog->SetProgress((1000*Current)/Total, 1000);
|
Progress->ProgressDialog->SetProgress(((int64_t)1000*Current)/Total, 1000);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ int FFMS_CC FFmpegSourceProvider::UpdateIndexingProgress(int64_t Current, int64_
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
// Do indexing
|
// Do indexing
|
||||||
FFIndex *FFmpegSourceProvider::DoIndexing(FFIndex *Index, wxString FileNameWX, wxString CacheName, int Trackmask, bool IgnoreDecodeErrors) {
|
FFIndex *FFmpegSourceProvider::DoIndexing(FFIndexer *Indexer, const wxString &CacheName, int Trackmask, bool IgnoreDecodeErrors) {
|
||||||
char FFMSErrMsg[1024];
|
char FFMSErrMsg[1024];
|
||||||
unsigned MsgSize = sizeof(FFMSErrMsg);
|
unsigned MsgSize = sizeof(FFMSErrMsg);
|
||||||
wxString MsgString;
|
wxString MsgString;
|
||||||
|
@ -79,16 +79,17 @@ FFIndex *FFmpegSourceProvider::DoIndexing(FFIndex *Index, wxString FileNameWX, w
|
||||||
// set up progress dialog callback
|
// set up progress dialog callback
|
||||||
IndexingProgressDialog Progress;
|
IndexingProgressDialog Progress;
|
||||||
Progress.IndexingCanceled = false;
|
Progress.IndexingCanceled = false;
|
||||||
Progress.ProgressDialog = new DialogProgress(AegisubApp::Get()->frame, _("Indexing"), &Progress.IndexingCanceled, _("Reading timecodes and frame/sample data"), 0, 1);
|
Progress.ProgressDialog = new DialogProgress(AegisubApp::Get()->frame, _("Indexing"), &Progress.IndexingCanceled,
|
||||||
|
_("Reading timecodes and frame/sample data"), 0, 1);
|
||||||
Progress.ProgressDialog->Show();
|
Progress.ProgressDialog->Show();
|
||||||
Progress.ProgressDialog->SetProgress(0,1);
|
Progress.ProgressDialog->SetProgress(0,1);
|
||||||
|
|
||||||
// index all audio tracks
|
// index all audio tracks
|
||||||
Index = FFMS_MakeIndex(FileNameWX.mb_str(wxConvUTF8), Trackmask, FFMSTrackMaskNone, NULL, NULL, IgnoreDecodeErrors, FFmpegSourceProvider::UpdateIndexingProgress, &Progress, FFMSErrMsg, MsgSize);
|
FFIndex *Index = FFMS_DoIndexing(Indexer, Trackmask, FFMSTrackMaskNone, NULL, NULL, IgnoreDecodeErrors,
|
||||||
if (!Index) {
|
FFmpegSourceProvider::UpdateIndexingProgress, &Progress, FFMSErrMsg, MsgSize);
|
||||||
|
if (Index == NULL) {
|
||||||
Progress.ProgressDialog->Destroy();
|
Progress.ProgressDialog->Destroy();
|
||||||
wxString temp(FFMSErrMsg, wxConvUTF8);
|
MsgString.Append(_T("Failed to index: ")).Append(wxString(FFMSErrMsg, wxConvUTF8));
|
||||||
MsgString << _T("Failed to index: ") << temp;
|
|
||||||
throw MsgString;
|
throw MsgString;
|
||||||
}
|
}
|
||||||
Progress.ProgressDialog->Destroy();
|
Progress.ProgressDialog->Destroy();
|
||||||
|
@ -105,6 +106,47 @@ FFIndex *FFmpegSourceProvider::DoIndexing(FFIndex *Index, wxString FileNameWX, w
|
||||||
return Index;
|
return Index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////
|
||||||
|
// Find all tracks of the given typo and return their track numbers and respective codec names
|
||||||
|
std::map<int,wxString> FFmpegSourceProvider::GetTracksOfType(FFIndexer *Indexer, FFMS_TrackType Type) {
|
||||||
|
std::map<int,wxString> TrackList;
|
||||||
|
int NumTracks = FFMS_GetNumTracksI(Indexer);
|
||||||
|
|
||||||
|
for (int i=0; i<NumTracks; i++) {
|
||||||
|
if (FFMS_GetTrackTypeI(Indexer, i) == Type) {
|
||||||
|
wxString CodecName(FFMS_GetCodecNameI(Indexer, i), wxConvUTF8);
|
||||||
|
TrackList.insert(std::pair<int,wxString>(i, CodecName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TrackList;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////
|
||||||
|
// Ask user for which track he wants to load
|
||||||
|
int FFmpegSourceProvider::AskForTrackSelection(const std::map<int,wxString> &TrackList, FFMS_TrackType Type) {
|
||||||
|
std::vector<int> TrackNumbers;
|
||||||
|
wxArrayString Choices;
|
||||||
|
wxString TypeName = _T("");
|
||||||
|
if (Type == FFMS_TYPE_VIDEO)
|
||||||
|
TypeName = _("video");
|
||||||
|
else if (Type == FFMS_TYPE_AUDIO)
|
||||||
|
TypeName = _("audio");
|
||||||
|
|
||||||
|
for (std::map<int,wxString>::const_iterator i = TrackList.begin(); i != TrackList.end(); i++) {
|
||||||
|
Choices.Add(wxString::Format(_("Track %02d: %s"), i->first, i->second.c_str()));
|
||||||
|
TrackNumbers.push_back(i->first);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Choice = wxGetSingleChoiceIndex(wxString::Format(_("Multiple %s tracks detected, please choose the one you wish to load:"), TypeName.c_str()),
|
||||||
|
wxString::Format(_("Choose %s track"), TypeName.c_str()), Choices);
|
||||||
|
|
||||||
|
if (Choice < 0)
|
||||||
|
return Choice;
|
||||||
|
else
|
||||||
|
return TrackNumbers[Choice];
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////
|
/////////////////////
|
||||||
// Creates a name for the ffmpegsource2 index and prepares the folder if it doesn't exist
|
// Creates a name for the ffmpegsource2 index and prepares the folder if it doesn't exist
|
||||||
// method by amz
|
// method by amz
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2008, Karl Blomster
|
// Copyright (c) 2008-2009, Karl Blomster
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -60,7 +60,10 @@ public:
|
||||||
bool CleanCache();
|
bool CleanCache();
|
||||||
|
|
||||||
static int FFMS_CC UpdateIndexingProgress(int64_t Current, int64_t Total, void *Private);
|
static int FFMS_CC UpdateIndexingProgress(int64_t Current, int64_t Total, void *Private);
|
||||||
FFIndex *DoIndexing(FFIndex *Index, wxString Filename, wxString Cachename, int Trackmask, bool IgnoreDecodeErrors);
|
|
||||||
|
FFIndex *DoIndexing(FFIndexer *Indexer, const wxString& Cachename, int Trackmask, bool IgnoreDecodeErrors);
|
||||||
|
std::map<int,wxString> GetTracksOfType(FFIndexer *Indexer, FFMS_TrackType Type);
|
||||||
|
int AskForTrackSelection(const std::map<int,wxString>& TrackList, FFMS_TrackType Type);
|
||||||
wxString GetCacheFilename(const wxString& filename);
|
wxString GetCacheFilename(const wxString& filename);
|
||||||
|
|
||||||
virtual FFmpegSourceProvider::~FFmpegSourceProvider() {}
|
virtual FFmpegSourceProvider::~FFmpegSourceProvider() {}
|
||||||
|
|
|
@ -176,6 +176,7 @@ void OptionsManager::LoadDefaults(bool onlyDefaults,bool doOverride) {
|
||||||
SetBool(_T("Video Use Pixel Shaders"),false,1700);
|
SetBool(_T("Video Use Pixel Shaders"),false,1700);
|
||||||
SetInt(_T("FFmpegSource max cache size"),42);
|
SetInt(_T("FFmpegSource max cache size"),42);
|
||||||
SetInt(_T("FFmpegSource max cache files"),20);
|
SetInt(_T("FFmpegSource max cache files"),20);
|
||||||
|
SetInt(_T("FFmpegSource always index all tracks"), true);
|
||||||
|
|
||||||
// Audio Options
|
// Audio Options
|
||||||
SetModificationType(MOD_AUTOMATIC);
|
SetModificationType(MOD_AUTOMATIC);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2008, Karl Blomster
|
// Copyright (c) 2008-2009, Karl Blomster
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -40,6 +40,8 @@
|
||||||
///////////
|
///////////
|
||||||
// Headers
|
// Headers
|
||||||
#include <wx/utils.h>
|
#include <wx/utils.h>
|
||||||
|
#include <wx/choicdlg.h>
|
||||||
|
#include <map>
|
||||||
#include "include/aegisub/aegisub.h"
|
#include "include/aegisub/aegisub.h"
|
||||||
#include "video_provider_ffmpegsource.h"
|
#include "video_provider_ffmpegsource.h"
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
|
@ -72,7 +74,7 @@ FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(Aegisub::String filename, d
|
||||||
LastDstFormat = FFMS_GetPixFmt("none");
|
LastDstFormat = FFMS_GetPixFmt("none");
|
||||||
KeyFramesLoaded = false;
|
KeyFramesLoaded = false;
|
||||||
FrameNumber = -1;
|
FrameNumber = -1;
|
||||||
MessageSize = sizeof(FFMSErrorMessage);
|
MsgSize = sizeof(FFMSErrMsg);
|
||||||
ErrorMsg = _T("FFmpegSource video provider: ");
|
ErrorMsg = _T("FFmpegSource video provider: ");
|
||||||
|
|
||||||
// and here we go
|
// and here we go
|
||||||
|
@ -102,34 +104,69 @@ void FFmpegSourceVideoProvider::LoadVideo(Aegisub::String filename, double fps)
|
||||||
|
|
||||||
wxString FileNameWX = wxFileName(wxString(filename.c_str(), wxConvFile)).GetShortPath();
|
wxString FileNameWX = wxFileName(wxString(filename.c_str(), wxConvFile)).GetShortPath();
|
||||||
|
|
||||||
|
FFIndexer *Indexer = FFMS_CreateIndexer(FileNameWX.mb_str(wxConvUTF8), FFMSErrMsg, MsgSize);
|
||||||
|
if (Indexer == NULL) {
|
||||||
|
// error messages that can possibly contain a filename use this method instead of
|
||||||
|
// wxString::Format because they may contain utf8 characters
|
||||||
|
ErrorMsg.Append(_T("Failed to create indexer: ")).Append(wxString(FFMSErrMsg, wxConvUTF8));
|
||||||
|
throw ErrorMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<int,wxString> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_VIDEO);
|
||||||
|
if (TrackList.size() <= 0)
|
||||||
|
throw _T("FFmpegSource video provider: no video tracks found");
|
||||||
|
|
||||||
|
// initialize the track number to an invalid value so we can detect later on
|
||||||
|
// whether the user actually had to choose a track or not
|
||||||
|
int TrackNumber = -1;
|
||||||
|
if (TrackList.size() > 1) {
|
||||||
|
TrackNumber = AskForTrackSelection(TrackList, FFMS_TYPE_VIDEO);
|
||||||
|
// if it's still -1 here, user pressed cancel
|
||||||
|
if (TrackNumber == -1)
|
||||||
|
throw _T("FFmpegSource video provider: video loading cancelled by user");
|
||||||
|
}
|
||||||
|
|
||||||
// generate a name for the cache file
|
// generate a name for the cache file
|
||||||
wxString CacheName = GetCacheFilename(filename.c_str());
|
wxString CacheName = GetCacheFilename(filename.c_str());
|
||||||
|
|
||||||
// try to read index
|
// try to read index
|
||||||
FFIndex *Index = NULL;
|
FFIndex *Index = NULL;
|
||||||
Index = FFMS_ReadIndex(CacheName.mb_str(wxConvUTF8), FFMSErrorMessage, MessageSize);
|
Index = FFMS_ReadIndex(CacheName.mb_str(wxConvUTF8), FFMSErrMsg, MsgSize);
|
||||||
bool ReIndex = false;
|
bool IndexIsValid = false;
|
||||||
if (Index == NULL) {
|
if (Index != NULL) {
|
||||||
ReIndex = true;
|
if (FFMS_IndexBelongsToFile(Index, FileNameWX.mb_str(wxConvUTF8), FFMSErrMsg, MsgSize)) {
|
||||||
}
|
|
||||||
else if (FFMS_IndexBelongsToFile(Index, FileNameWX.mb_str(wxConvUTF8), FFMSErrorMessage, MessageSize)) {
|
|
||||||
FFMS_DestroyIndex(Index);
|
FFMS_DestroyIndex(Index);
|
||||||
Index = NULL;
|
Index = NULL;
|
||||||
ReIndex = true;
|
}
|
||||||
|
else
|
||||||
|
IndexIsValid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// index didn't exist or was invalid, we'll have to (re)create it
|
// time to examine the index and check if the track we want is indexed
|
||||||
if (ReIndex) {
|
// technically this isn't really needed since all video tracks should always be indexed,
|
||||||
|
// but a bit of sanity checking never hurt anyone
|
||||||
|
if (IndexIsValid && TrackNumber >= 0) {
|
||||||
|
FFTrack *TempTrackData = FFMS_GetTrackFromIndex(Index, TrackNumber);
|
||||||
|
if (FFMS_GetNumFrames(TempTrackData) <= 0) {
|
||||||
|
IndexIsValid = false;
|
||||||
|
FFMS_DestroyIndex(Index);
|
||||||
|
Index = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// moment of truth
|
||||||
|
if (!IndexIsValid) {
|
||||||
|
int TrackMask = Options.AsBool(_T("FFmpegSource always index all tracks")) ? FFMSTrackMaskAll : FFMSTrackMaskNone;
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
// ignore audio decoding errors here, we don't care right now
|
// ignore audio decoding errors here, we don't care right now
|
||||||
Index = DoIndexing(Index, FileNameWX, CacheName, FFMSTrackMaskAll, true);
|
Index = DoIndexing(Indexer, CacheName, TrackMask, true);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
// Try without audio
|
// something borked, try if it works without audio
|
||||||
Index = DoIndexing(Index, FileNameWX, CacheName, FFMSTrackMaskNone, true);
|
Index = DoIndexing(Indexer, CacheName, FFMSTrackMaskNone, true);
|
||||||
}
|
}
|
||||||
} catch (wxString temp) {
|
} catch (wxString temp) {
|
||||||
ErrorMsg << temp;
|
ErrorMsg.Append(temp);
|
||||||
throw ErrorMsg;
|
throw ErrorMsg;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
throw;
|
throw;
|
||||||
|
@ -144,6 +181,18 @@ void FFmpegSourceVideoProvider::LoadVideo(Aegisub::String filename, double fps)
|
||||||
//do something?
|
//do something?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// track number still not set?
|
||||||
|
if (TrackNumber < 0) {
|
||||||
|
// just grab the first track
|
||||||
|
TrackNumber = FFMS_GetFirstIndexedTrackOfType(Index, FFMS_TYPE_VIDEO, FFMSErrMsg, MsgSize);
|
||||||
|
if (TrackNumber < 0) {
|
||||||
|
FFMS_DestroyIndex(Index);
|
||||||
|
Index = NULL;
|
||||||
|
ErrorMsg.Append(wxString::Format(_T("Couldn't find any video tracks: %s"), FFMSErrMsg));
|
||||||
|
throw ErrorMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set thread count
|
// set thread count
|
||||||
int Threads = Options.AsInt(_T("FFmpegSource decoding threads"));
|
int Threads = Options.AsInt(_T("FFmpegSource decoding threads"));
|
||||||
if (Threads < 1)
|
if (Threads < 1)
|
||||||
|
@ -157,22 +206,11 @@ void FFmpegSourceVideoProvider::LoadVideo(Aegisub::String filename, double fps)
|
||||||
else
|
else
|
||||||
SeekMode = FFMS_SEEK_NORMAL;
|
SeekMode = FFMS_SEEK_NORMAL;
|
||||||
|
|
||||||
// FIXME: provide a way to choose which audio track to load?
|
VideoSource = FFMS_CreateVideoSource(FileNameWX.mb_str(wxConvUTF8), TrackNumber, Index, "", Threads, SeekMode, FFMSErrMsg, MsgSize);
|
||||||
int TrackNumber = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_VIDEO, FFMSErrorMessage, MessageSize);
|
|
||||||
if (TrackNumber < 0) {
|
|
||||||
FFMS_DestroyIndex(Index);
|
|
||||||
Index = NULL;
|
|
||||||
wxString temp(FFMSErrorMessage, wxConvUTF8);
|
|
||||||
ErrorMsg << _T("Couldn't find any video tracks: ") << temp;
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
VideoSource = FFMS_CreateVideoSource(FileNameWX.mb_str(wxConvUTF8), TrackNumber, Index, "", Threads, SeekMode, FFMSErrorMessage, MessageSize);
|
|
||||||
FFMS_DestroyIndex(Index);
|
FFMS_DestroyIndex(Index);
|
||||||
Index = NULL;
|
Index = NULL;
|
||||||
if (VideoSource == NULL) {
|
if (VideoSource == NULL) {
|
||||||
wxString temp(FFMSErrorMessage, wxConvUTF8);
|
ErrorMsg.Append(wxString::Format(_T("Failed to open video track: %s"), FFMSErrMsg));
|
||||||
ErrorMsg << _T("Failed to open video track: ") << temp;
|
|
||||||
throw ErrorMsg;
|
throw ErrorMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,8 +231,7 @@ void FFmpegSourceVideoProvider::LoadVideo(Aegisub::String filename, double fps)
|
||||||
for (int CurFrameNum = 0; CurFrameNum < VideoInfo->NumFrames; CurFrameNum++) {
|
for (int CurFrameNum = 0; CurFrameNum < VideoInfo->NumFrames; CurFrameNum++) {
|
||||||
CurFrameData = FFMS_GetFrameInfo(FrameData, CurFrameNum);
|
CurFrameData = FFMS_GetFrameInfo(FrameData, CurFrameNum);
|
||||||
if (CurFrameData == NULL) {
|
if (CurFrameData == NULL) {
|
||||||
wxString temp(FFMSErrorMessage, wxConvUTF8);
|
ErrorMsg.Append(wxString::Format(_T("Couldn't get info about frame %d"), CurFrameNum));
|
||||||
ErrorMsg << _T("Couldn't get framedata for frame ") << CurFrameNum << _T(": ") << temp;
|
|
||||||
throw ErrorMsg;
|
throw ErrorMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,19 +315,17 @@ const AegiVideoFrame FFmpegSourceVideoProvider::GetFrame(int _n, int FormatType)
|
||||||
|
|
||||||
// requested format was changed since last time we were called, (re)set output format
|
// requested format was changed since last time we were called, (re)set output format
|
||||||
if (LastDstFormat != DstFormat) {
|
if (LastDstFormat != DstFormat) {
|
||||||
if (FFMS_SetOutputFormatV(VideoSource, 1 << DstFormat, w, h, FFMS_RESIZER_BICUBIC, FFMSErrorMessage, MessageSize)) {
|
if (FFMS_SetOutputFormatV(VideoSource, 1 << DstFormat, w, h, FFMS_RESIZER_BICUBIC, FFMSErrMsg, MsgSize)) {
|
||||||
wxString temp(FFMSErrorMessage, wxConvUTF8);
|
ErrorMsg.Append(wxString::Format(_T("Failed to set output format: %s"), FFMSErrMsg));
|
||||||
ErrorMsg << _T("Failed to set output format: ") << temp;
|
|
||||||
throw ErrorMsg;
|
throw ErrorMsg;
|
||||||
}
|
}
|
||||||
LastDstFormat = DstFormat;
|
LastDstFormat = DstFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
// decode frame
|
// decode frame
|
||||||
const FFAVFrame *SrcFrame = FFMS_GetFrame(VideoSource, n, FFMSErrorMessage, MessageSize);
|
const FFAVFrame *SrcFrame = FFMS_GetFrame(VideoSource, n, FFMSErrMsg, MsgSize);
|
||||||
if (SrcFrame == NULL) {
|
if (SrcFrame == NULL) {
|
||||||
wxString temp(FFMSErrorMessage, wxConvUTF8);
|
ErrorMsg.Append(wxString::Format(_T("Failed to retrieve frame: %s"), FFMSErrMsg));
|
||||||
ErrorMsg << _T("Failed to retrieve frame: ") << temp;
|
|
||||||
throw ErrorMsg;
|
throw ErrorMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2008, Karl Blomster
|
// Copyright (c) 2008-2009, Karl Blomster
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -60,8 +60,8 @@ private:
|
||||||
int LastDstFormat;
|
int LastDstFormat;
|
||||||
AegiVideoFrame CurFrame;
|
AegiVideoFrame CurFrame;
|
||||||
|
|
||||||
char FFMSErrorMessage[1024];
|
char FFMSErrMsg[1024];
|
||||||
unsigned MessageSize;
|
unsigned MsgSize;
|
||||||
wxString ErrorMsg;
|
wxString ErrorMsg;
|
||||||
|
|
||||||
bool COMInited;
|
bool COMInited;
|
||||||
|
|
Loading…
Reference in a new issue