From 1219b069b13ffc7a91afd05bc7232bd981c76030 Mon Sep 17 00:00:00 2001 From: Amar Takhar Date: Wed, 2 Feb 2011 23:11:17 +0000 Subject: [PATCH] Copy audio_provider_ffmpegsource.(cpp|h) to ./ffms_audio.(cpp|h) and remove wx usage.. The plan is to move a/v support to libaegisub and write unit tests to make sure we can open/close/seek the "official" formats we want to support. Originally committed to SVN as r5269. --- aegisub/libaegisub/common/ffms_audio.cpp | 220 +++++++++++++++++++++++ aegisub/libaegisub/common/ffms_audio.h | 57 ++++++ 2 files changed, 277 insertions(+) create mode 100644 aegisub/libaegisub/common/ffms_audio.cpp create mode 100644 aegisub/libaegisub/common/ffms_audio.h diff --git a/aegisub/libaegisub/common/ffms_audio.cpp b/aegisub/libaegisub/common/ffms_audio.cpp new file mode 100644 index 000000000..b33e260dd --- /dev/null +++ b/aegisub/libaegisub/common/ffms_audio.cpp @@ -0,0 +1,220 @@ +// Copyright (c) 2008-2009, Karl Blomster +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// $Id$ + +/// @file ffms_audio.cpp +/// @brief FFmpegSource Audio support. +/// @ingroup fmms audio + + +#include "config.h" + +#ifndef AGI_PRE +#endif + +#include "ffms_audio.h" + +namespace ffms { + + +/// @brief Constructor +/// @param filename +/// +Audio::Audio(std::string filename) +: AudioSource(NULL) +, COMInited(false) +{ +#ifdef WIN32 + HRESULT res; + res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + if (SUCCEEDED(res)) + COMInited = true; + else if (res != RPC_E_CHANGED_MODE) + throw AudioOpenError("COM initialization failure"); +#endif + // initialize ffmpegsource + // FIXME: CPU detection? +#if FFMS_VERSION >= ((2 << 24) | (14 << 16) | (0 << 8) | 0) + FFMS_Init(0, 1); +#else + FFMS_Init(0); +#endif + + ErrInfo.Buffer = FFMSErrMsg; + ErrInfo.BufferSize = sizeof(FFMSErrMsg); + ErrInfo.ErrorType = FFMS_ERROR_SUCCESS; + ErrInfo.SubType = FFMS_ERROR_SUCCESS; +// SetLogLevel(); + + try { + LoadAudio(filename); + } catch (...) { + Close(); + throw; + } +} + +/// @brief Load audio file +/// @param filename +/// +void Audio::LoadAudio(std::string filename) { +// wxString FileNameShort = wxFileName(filename).GetShortPath(); + + FFMS_Indexer *Indexer = FFMS_CreateIndexer(filename.c_str(), &ErrInfo); + if (Indexer == NULL) { + throw agi::FileNotFoundError(ErrInfo.Buffer); + } + + std::map TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO); + if (TrackList.size() <= 0) + throw AudioOpenError("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 agi::UserCancelException("audio loading cancelled by user"); + } + + // generate a name for the cache file + std::string CacheName = GetCacheFilename(filename); + + // try to read index + FFMS_Index *Index = NULL; + Index = FFMS_ReadIndex(CacheName.c_str(), &ErrInfo); + bool IndexIsValid = false; + if (Index != NULL) { + if (FFMS_IndexBelongsToFile(Index, filename.c_str(), &ErrInfo)) { + FFMS_DestroyIndex(Index); + Index = NULL; + } + else + IndexIsValid = true; + } + + // index valid but track number still not set? + if (IndexIsValid) { + // track number not set? just grab the first track + if (TrackNumber < 0) + TrackNumber = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, &ErrInfo); + if (TrackNumber < 0) { + FFMS_DestroyIndex(Index); + Index = NULL; + throw AudioOpenError(std::string("Couldn't find any audio tracks: ") + ErrInfo.Buffer); + } + + // index is valid and track number is now set, + // but do we have indexing info for the desired audio track? + FFMS_Track *TempTrackData = FFMS_GetTrackFromIndex(Index, TrackNumber); + if (FFMS_GetNumFrames(TempTrackData) <= 0) { + IndexIsValid = false; + FFMS_DestroyIndex(Index); + Index = NULL; + } + } + // no valid index exists and the file only has one audio track, index it + else if (TrackNumber < 0) + TrackNumber = FFMS_TRACKMASK_ALL; + // else: do nothing (keep track mask as it is) + + // moment of truth + if (!IndexIsValid) { + int TrackMask; +// if (OPT_GET("Provider/FFmpegSource/Index All Tracks")->GetBool() || TrackNumber == FFMS_TRACKMASK_ALL) + if (TrackNumber == FFMS_TRACKMASK_ALL) + TrackMask = FFMS_TRACKMASK_ALL; + else + TrackMask = (1 << TrackNumber); + + try { + Index = DoIndexing(Indexer, CacheName, TrackMask, GetErrorHandlingMode()); + } + catch (std::string const& err) { + throw AudioOpenError(err); + } + + // if tracknumber still isn't set we need to set it now + if (TrackNumber == FFMS_TRACKMASK_ALL) + TrackNumber = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, &ErrInfo); + } + + // update access time of index file so it won't get cleaned away +///XXX: Add something to libaegisub to support this. +// if (!wxFileName(CacheName).Touch()) { + // warn user? +// } + +#if FFMS_VERSION >= ((2 << 24) | (14 << 16) | (1 << 8) | 0) + AudioSource = FFMS_CreateAudioSource(FileNameShort.utf8_str(), TrackNumber, Index, -1, &ErrInfo); +#else + AudioSource = FFMS_CreateAudioSource(FileNameShort.utf8_str(), TrackNumber, Index, &ErrInfo); +#endif + FFMS_DestroyIndex(Index); + Index = NULL; + if (!AudioSource) { + throw AudioOpenError(std::string("Failed to open audio track: %s") + ErrInfo.Buffer); + } + + const FFMS_AudioProperties AudioInfo = *FFMS_GetAudioProperties(AudioSource); + + channels = AudioInfo.Channels; + sample_rate = AudioInfo.SampleRate; + num_samples = AudioInfo.NumSamples; + if (channels <= 0 || sample_rate <= 0 || num_samples <= 0) + throw AudioOpenError("sanity check failed, consult your local psychiatrist"); + + // 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... + switch (AudioInfo.BitsPerSample) { + case 8: bytes_per_sample = 1; break; + case 16: bytes_per_sample = 2; break; + case 24: bytes_per_sample = 3; break; + case 32: bytes_per_sample = 4; break; + default: + throw AudioOpenError("unknown or unsupported sample format"); + } +} + +/// @brief Destructor +/// +Audio::~Audio() { + Close(); +} + +/// @brief Clean up +/// +void FFmpegSourceAudioProvider::Close() { + if (AudioSource) FFMS_DestroyAudioSource(AudioSource); +#ifdef WIN32 + if (COMInited) + CoUninitialize(); +#endif +} + +/// @brief Get audio +/// @param Buf +/// @param Start +/// @param Count +/// +void Audio::GetAudio(void *Buf, int64_t Start, int64_t Count) const { + if (FFMS_GetAudio(AudioSource, Buf, Start, Count, &ErrInfo)) { + throw AudioDecodeError(std::string("Failed to get audio samples: ") + ErrInfo.Buffer); + } +} + +} // namespace ffms diff --git a/aegisub/libaegisub/common/ffms_audio.h b/aegisub/libaegisub/common/ffms_audio.h new file mode 100644 index 000000000..111ca1864 --- /dev/null +++ b/aegisub/libaegisub/common/ffms_audio.h @@ -0,0 +1,57 @@ +// Copyright (c) 2008-2009, Karl Blomster +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// $Id$ + +/// @file ffms_audio.h +/// @brief FFmpegSource Audio support. +/// @ingroup fmms audio + +#include "../../libffms/include/ffms.h" +#include + +#ifndef AGI_PRE +#ifdef WIN32 +#include +#endif + +#include +#endif + +namespace ffms { + +class Audio { + FFMS_AudioSource *AudioSource; ///< audio source object + bool COMInited; ///< COM initialization state + + mutable char FFMSErrMsg[1024]; ///< FFMS error message + mutable FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages + + void Close(); + void LoadAudio(std::string filename); + + Audio(std::string filename); + virtual ~Audio(); + + /// @brief Checks sample endianness + /// @return Returns true. + /// FFMS always delivers native endian samples. + bool AreSamplesNativeEndian() const { return true; } + bool NeedsCache() const { return true; } + + virtual void GetAudio(void *buf, int64_t start, int64_t count) const; + +}; + +} // namespace ffms