forked from mia/Aegisub
Delete libmedia
It's nowhere close to working and just getting further away from working as time goes on. Originally committed to SVN as r6096.
This commit is contained in:
parent
e2eb7255a9
commit
48526bf341
53 changed files with 0 additions and 7437 deletions
|
@ -457,10 +457,6 @@
|
||||||
RelativePath="..\..\libaegisub\include\libaegisub\mru.h"
|
RelativePath="..\..\libaegisub\include\libaegisub\mru.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
|
||||||
RelativePath="..\..\libaegisub\include\libaegisub\mutex.h"
|
|
||||||
>
|
|
||||||
</File>
|
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\libaegisub\include\libaegisub\option.h"
|
RelativePath="..\..\libaegisub\include\libaegisub\option.h"
|
||||||
>
|
>
|
||||||
|
|
|
@ -60,11 +60,9 @@
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\line_iterator.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\line_iterator.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\log.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\log.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\mru.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\mru.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\mutex.h" />
|
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\option.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\option.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\option_value.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\option_value.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\path.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\path.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\progress.h" />
|
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\scoped_ptr.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\scoped_ptr.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\signal.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\signal.h" />
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\types.h" />
|
<ClInclude Include="$(SrcDir)include\libaegisub\types.h" />
|
||||||
|
@ -97,7 +95,6 @@
|
||||||
<ClCompile Include="$(SrcDir)common\option.cpp" />
|
<ClCompile Include="$(SrcDir)common\option.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\option_visit.cpp" />
|
<ClCompile Include="$(SrcDir)common\option_visit.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\path.cpp" />
|
<ClCompile Include="$(SrcDir)common\path.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\progress.cpp" />
|
|
||||||
<ClCompile Include="$(SrcDir)common\util.cpp" />
|
<ClCompile Include="$(SrcDir)common\util.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\validator.cpp" />
|
<ClCompile Include="$(SrcDir)common\validator.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)common\vfr.cpp" />
|
<ClCompile Include="$(SrcDir)common\vfr.cpp" />
|
||||||
|
|
|
@ -59,9 +59,6 @@
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\mru.h">
|
<ClInclude Include="$(SrcDir)include\libaegisub\mru.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\mutex.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\option.h">
|
<ClInclude Include="$(SrcDir)include\libaegisub\option.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -116,9 +113,6 @@
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\keyframe.h">
|
<ClInclude Include="$(SrcDir)include\libaegisub\keyframe.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\progress.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="$(SrcDir)include\libaegisub\signal.h">
|
<ClInclude Include="$(SrcDir)include\libaegisub\signal.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -187,9 +181,6 @@
|
||||||
<ClCompile Include="$(SrcDir)windows\path_win.cpp">
|
<ClCompile Include="$(SrcDir)windows\path_win.cpp">
|
||||||
<Filter>Source Files\Windows</Filter>
|
<Filter>Source Files\Windows</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="$(SrcDir)common\progress.cpp">
|
|
||||||
<Filter>Source Files\Common</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="$(SrcDir)common\util.cpp">
|
<ClCompile Include="$(SrcDir)common\util.cpp">
|
||||||
<Filter>Source Files\Common</Filter>
|
<Filter>Source Files\Common</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
|
@ -206,18 +206,6 @@ AC_SUBST(DMG_STRING)
|
||||||
ACX_PTHREAD([], [AC_MSG_FAILURE([You must have working pthreads.])])
|
ACX_PTHREAD([], [AC_MSG_FAILURE([You must have working pthreads.])])
|
||||||
CC="$PTHREAD_CC"
|
CC="$PTHREAD_CC"
|
||||||
|
|
||||||
AC_AGI_COMPILE([pthread_rwlock_*], [pthread_rwlock], [$PTHREAD_CFLAGS], [$PTHREAD_LIBS],[
|
|
||||||
#include <pthread.h>
|
|
||||||
#define _XOPEN_SOURCE 500
|
|
||||||
int main(void) {
|
|
||||||
pthread_rwlock_t l; pthread_rwlock_init(&l, NULL);
|
|
||||||
pthread_rwlock_rdlock(&l);
|
|
||||||
return 0;
|
|
||||||
}])
|
|
||||||
|
|
||||||
AS_IF([test x$agi_cv_with_pthread_rwlock = xyes],
|
|
||||||
[AC_DEFINE(HAVE_RWLOCK,1,[define if the compiler implements pthread_rwlock_*])])
|
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# Check compiler flags
|
# Check compiler flags
|
||||||
######################
|
######################
|
||||||
|
|
|
@ -32,7 +32,6 @@ SRC = \
|
||||||
common/option.cpp \
|
common/option.cpp \
|
||||||
common/option_visit.cpp \
|
common/option_visit.cpp \
|
||||||
common/path.cpp \
|
common/path.cpp \
|
||||||
common/progress.cpp \
|
|
||||||
common/keyframe.cpp \
|
common/keyframe.cpp \
|
||||||
common/util.cpp \
|
common/util.cpp \
|
||||||
common/log.cpp \
|
common/log.cpp \
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
// Copyright (c) 2011, Niels Martin Hansen <nielsm@aegisub.org>
|
|
||||||
//
|
|
||||||
// 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 progress.cpp
|
|
||||||
/// @brief Progress bars.
|
|
||||||
/// @ingroup libaegisub
|
|
||||||
|
|
||||||
#ifndef LAG_PRE
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <iostream>
|
|
||||||
#include <cmath>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "libaegisub/progress.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace agi {
|
|
||||||
|
|
||||||
|
|
||||||
class NullProgressSink : public ProgressSink {
|
|
||||||
public:
|
|
||||||
virtual void set_progress(int steps, int max) { }
|
|
||||||
virtual void set_operation(const std::string &operation) { }
|
|
||||||
virtual bool get_cancelled() const { return false; }
|
|
||||||
};
|
|
||||||
|
|
||||||
ProgressSink * NullProgressSinkFactory::create_progress_sink(const std::string &title) const {
|
|
||||||
return new NullProgressSink;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class StdioProgressSink : public ProgressSink {
|
|
||||||
private:
|
|
||||||
std::string operation;
|
|
||||||
float progress;
|
|
||||||
|
|
||||||
void print();
|
|
||||||
|
|
||||||
public:
|
|
||||||
StdioProgressSink(const std::string &title);
|
|
||||||
virtual ~StdioProgressSink();
|
|
||||||
virtual void set_progress(int steps, int max);
|
|
||||||
virtual void set_operation(const std::string &operation);
|
|
||||||
virtual bool get_cancelled() const { return false; } // or maybe see if there is an ESC waiting or get a ^C sent flag
|
|
||||||
};
|
|
||||||
|
|
||||||
ProgressSink * StdioProgressSinkFactory::create_progress_sink(const std::string &title) const {
|
|
||||||
return new StdioProgressSink(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
StdioProgressSink::StdioProgressSink(const std::string &title)
|
|
||||||
: operation(title)
|
|
||||||
, progress(0) {
|
|
||||||
std::cout << title << ": ";
|
|
||||||
print();
|
|
||||||
}
|
|
||||||
|
|
||||||
StdioProgressSink::~StdioProgressSink()
|
|
||||||
{
|
|
||||||
std::cout << "end" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void StdioProgressSink::set_progress(int steps, int max) {
|
|
||||||
assert(steps >= 0);
|
|
||||||
assert(steps <= max);
|
|
||||||
|
|
||||||
float old_progress = progress;
|
|
||||||
progress = (100.f * steps)/max;
|
|
||||||
|
|
||||||
if (std::fabs(progress-old_progress) > 0.8)
|
|
||||||
{
|
|
||||||
print();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void StdioProgressSink::set_operation(const std::string &operation) {
|
|
||||||
this->operation = operation;
|
|
||||||
print();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void StdioProgressSink::print() {
|
|
||||||
std::cout << operation << ": " << std::setprecision(2) << progress << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace agi
|
|
|
@ -1,325 +0,0 @@
|
||||||
// Copyright (c) 2007, Google Inc.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// ---
|
|
||||||
// Author: Craig Silverstein.
|
|
||||||
//
|
|
||||||
// A simple mutex wrapper, supporting locks and read-write locks.
|
|
||||||
// You should assume the locks are *not* re-entrant.
|
|
||||||
//
|
|
||||||
// To use: you should define the following macros in your configure.ac:
|
|
||||||
// ACX_PTHREAD
|
|
||||||
// AC_RWLOCK
|
|
||||||
// The latter is defined in ../autoconf.
|
|
||||||
//
|
|
||||||
// This class is meant to be internal-only and should be wrapped by an
|
|
||||||
// internal namespace. Before you use this module, please give the
|
|
||||||
// name of your internal namespace for this module. Or, if you want
|
|
||||||
// to expose it, you'll want to move it to the Google namespace. We
|
|
||||||
// cannot put this class in global namespace because there can be some
|
|
||||||
// problems when we have multiple versions of Mutex in each shared object.
|
|
||||||
//
|
|
||||||
// NOTE: by default, we have #ifdef'ed out the TryLock() method.
|
|
||||||
// This is for two reasons:
|
|
||||||
// 1) TryLock() under Windows is a bit annoying (it requires a
|
|
||||||
// #define to be defined very early).
|
|
||||||
// 2) TryLock() is broken for NO_THREADS mode, at least in NDEBUG
|
|
||||||
// mode.
|
|
||||||
// If you need TryLock(), and either these two caveats are not a
|
|
||||||
// problem for you, or you're willing to work around them, then
|
|
||||||
// feel free to #define GMUTEX_TRYLOCK, or to remove the #ifdefs
|
|
||||||
// in the code below.
|
|
||||||
//
|
|
||||||
// CYGWIN NOTE: Cygwin support for rwlock seems to be buggy:
|
|
||||||
// http://www.cygwin.com/ml/cygwin/2008-12/msg00017.html
|
|
||||||
// Because of that, we might as well use windows locks for
|
|
||||||
// cygwin. They seem to be more reliable than the cygwin pthreads layer.
|
|
||||||
//
|
|
||||||
// TRICKY IMPLEMENTATION NOTE:
|
|
||||||
// This class is designed to be safe to use during
|
|
||||||
// dynamic-initialization -- that is, by global constructors that are
|
|
||||||
// run before main() starts. The issue in this case is that
|
|
||||||
// dynamic-initialization happens in an unpredictable order, and it
|
|
||||||
// could be that someone else's dynamic initializer could call a
|
|
||||||
// function that tries to acquire this mutex -- but that all happens
|
|
||||||
// before this mutex's constructor has run. (This can happen even if
|
|
||||||
// the mutex and the function that uses the mutex are in the same .cc
|
|
||||||
// file.) Basically, because Mutex does non-trivial work in its
|
|
||||||
// constructor, it's not, in the naive implementation, safe to use
|
|
||||||
// before dynamic initialization has run on it.
|
|
||||||
//
|
|
||||||
// The solution used here is to pair the actual mutex primitive with a
|
|
||||||
// bool that is set to true when the mutex is dynamically initialized.
|
|
||||||
// (Before that it's false.) Then we modify all mutex routines to
|
|
||||||
// look at the bool, and not try to lock/unlock until the bool makes
|
|
||||||
// it to true (which happens after the Mutex constructor has run.)
|
|
||||||
//
|
|
||||||
// This works because before main() starts -- particularly, during
|
|
||||||
// dynamic initialization -- there are no threads, so a) it's ok that
|
|
||||||
// the mutex operations are a no-op, since we don't need locking then
|
|
||||||
// anyway; and b) we can be quite confident our bool won't change
|
|
||||||
// state between a call to Lock() and a call to Unlock() (that would
|
|
||||||
// require a global constructor in one translation unit to call Lock()
|
|
||||||
// and another global constructor in another translation unit to call
|
|
||||||
// Unlock() later, which is pretty perverse).
|
|
||||||
//
|
|
||||||
// That said, it's tricky, and can conceivably fail; it's safest to
|
|
||||||
// avoid trying to acquire a mutex in a global constructor, if you
|
|
||||||
// can. One way it can fail is that a really smart compiler might
|
|
||||||
// initialize the bool to true at static-initialization time (too
|
|
||||||
// early) rather than at dynamic-initialization time. To discourage
|
|
||||||
// that, we set is_safe_ to true in code (not the constructor
|
|
||||||
// colon-initializer) and set it to true via a function that always
|
|
||||||
// evaluates to true, but that the compiler can't know always
|
|
||||||
// evaluates to true. This should be good enough.
|
|
||||||
|
|
||||||
#ifndef GOOGLE_MUTEX_H_
|
|
||||||
#define GOOGLE_MUTEX_H_
|
|
||||||
|
|
||||||
#if defined(NO_THREADS)
|
|
||||||
typedef int MutexType; // to keep a lock-count
|
|
||||||
#elif defined(_WIN32) || defined(__CYGWIN32__) || defined(__CYGWIN64__)
|
|
||||||
# define WIN32_LEAN_AND_MEAN // We only need minimal includes
|
|
||||||
# ifdef GMUTEX_TRYLOCK
|
|
||||||
// We need Windows NT or later for TryEnterCriticalSection(). If you
|
|
||||||
// don't need that functionality, you can remove these _WIN32_WINNT
|
|
||||||
// lines, and change TryLock() to assert(0) or something.
|
|
||||||
# ifndef _WIN32_WINNT
|
|
||||||
# define _WIN32_WINNT 0x0400
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
// To avoid macro definition of ERROR.
|
|
||||||
# define NOGDI
|
|
||||||
// To avoid macro definition of min/max.
|
|
||||||
# ifndef NOMINMAX
|
|
||||||
# define NOMINMAX
|
|
||||||
# endif // NOMINMAX
|
|
||||||
# include <windows.h>
|
|
||||||
typedef CRITICAL_SECTION MutexType;
|
|
||||||
#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
|
|
||||||
// Needed for pthread_rwlock_*. If it causes problems, you could take it
|
|
||||||
// out, but then you'd have to unset HAVE_RWLOCK (at least on linux -- it
|
|
||||||
// *does* cause problems for FreeBSD, or MacOSX, but isn't needed
|
|
||||||
// for locking there.)
|
|
||||||
# ifdef __linux__
|
|
||||||
# define _XOPEN_SOURCE 500 // may be needed to get the rwlock calls
|
|
||||||
# endif
|
|
||||||
# include <pthread.h>
|
|
||||||
typedef pthread_rwlock_t MutexType;
|
|
||||||
#elif defined(HAVE_PTHREAD)
|
|
||||||
# include <pthread.h>
|
|
||||||
typedef pthread_mutex_t MutexType;
|
|
||||||
#else
|
|
||||||
# error Need to implement mutex.h for your architecture, or #define NO_THREADS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// We need to include these header files after defining _XOPEN_SOURCE
|
|
||||||
// as they may define the _XOPEN_SOURCE macro.
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h> // for abort()
|
|
||||||
|
|
||||||
#define MUTEX_NAMESPACE agi
|
|
||||||
|
|
||||||
namespace MUTEX_NAMESPACE {
|
|
||||||
|
|
||||||
class Mutex {
|
|
||||||
public:
|
|
||||||
// Create a Mutex that is not held by anybody. This constructor is
|
|
||||||
// typically used for Mutexes allocated on the heap or the stack.
|
|
||||||
// See below for a recommendation for constructing global Mutex
|
|
||||||
// objects.
|
|
||||||
inline Mutex();
|
|
||||||
|
|
||||||
// Destructor
|
|
||||||
inline ~Mutex();
|
|
||||||
|
|
||||||
inline void Lock(); // Block if needed until free then acquire exclusively
|
|
||||||
inline void Unlock(); // Release a lock acquired via Lock()
|
|
||||||
#ifdef GMUTEX_TRYLOCK
|
|
||||||
inline bool TryLock(); // If free, Lock() and return true, else return false
|
|
||||||
#endif
|
|
||||||
// Note that on systems that don't support read-write locks, these may
|
|
||||||
// be implemented as synonyms to Lock() and Unlock(). So you can use
|
|
||||||
// these for efficiency, but don't use them anyplace where being able
|
|
||||||
// to do shared reads is necessary to avoid deadlock.
|
|
||||||
inline void ReaderLock(); // Block until free or shared then acquire a share
|
|
||||||
inline void ReaderUnlock(); // Release a read share of this Mutex
|
|
||||||
inline void WriterLock() { Lock(); } // Acquire an exclusive lock
|
|
||||||
inline void WriterUnlock() { Unlock(); } // Release a lock from WriterLock()
|
|
||||||
|
|
||||||
// TODO(hamaji): Do nothing, implement correctly.
|
|
||||||
inline void AssertHeld() {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
MutexType mutex_;
|
|
||||||
// We want to make sure that the compiler sets is_safe_ to true only
|
|
||||||
// when we tell it to, and never makes assumptions is_safe_ is
|
|
||||||
// always true. volatile is the most reliable way to do that.
|
|
||||||
volatile bool is_safe_;
|
|
||||||
|
|
||||||
inline void SetIsSafe() { is_safe_ = true; }
|
|
||||||
|
|
||||||
// Catch the error of writing Mutex when intending MutexLock.
|
|
||||||
Mutex(Mutex* /*ignored*/) {}
|
|
||||||
// Disallow "evil" constructors
|
|
||||||
Mutex(const Mutex&);
|
|
||||||
void operator=(const Mutex&);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Now the implementation of Mutex for various systems
|
|
||||||
#if defined(NO_THREADS)
|
|
||||||
|
|
||||||
// When we don't have threads, we can be either reading or writing,
|
|
||||||
// but not both. We can have lots of readers at once (in no-threads
|
|
||||||
// mode, that's most likely to happen in recursive function calls),
|
|
||||||
// but only one writer. We represent this by having mutex_ be -1 when
|
|
||||||
// writing and a number > 0 when reading (and 0 when no lock is held).
|
|
||||||
//
|
|
||||||
// In debug mode, we assert these invariants, while in non-debug mode
|
|
||||||
// we do nothing, for efficiency. That's why everything is in an
|
|
||||||
// assert.
|
|
||||||
|
|
||||||
Mutex::Mutex() : mutex_(0) { }
|
|
||||||
Mutex::~Mutex() { assert(mutex_ == 0); }
|
|
||||||
void Mutex::Lock() { assert(--mutex_ == -1); }
|
|
||||||
void Mutex::Unlock() { assert(mutex_++ == -1); }
|
|
||||||
#ifdef GMUTEX_TRYLOCK
|
|
||||||
bool Mutex::TryLock() { if (mutex_) return false; Lock(); return true; }
|
|
||||||
#endif
|
|
||||||
void Mutex::ReaderLock() { assert(++mutex_ > 0); }
|
|
||||||
void Mutex::ReaderUnlock() { assert(mutex_-- > 0); }
|
|
||||||
|
|
||||||
#elif defined(_WIN32) || defined(__CYGWIN32__) || defined(__CYGWIN64__)
|
|
||||||
|
|
||||||
Mutex::Mutex() { InitializeCriticalSection(&mutex_); SetIsSafe(); }
|
|
||||||
Mutex::~Mutex() { DeleteCriticalSection(&mutex_); }
|
|
||||||
void Mutex::Lock() { if (is_safe_) EnterCriticalSection(&mutex_); }
|
|
||||||
void Mutex::Unlock() { if (is_safe_) LeaveCriticalSection(&mutex_); }
|
|
||||||
#ifdef GMUTEX_TRYLOCK
|
|
||||||
bool Mutex::TryLock() { return is_safe_ ?
|
|
||||||
TryEnterCriticalSection(&mutex_) != 0 : true; }
|
|
||||||
#endif
|
|
||||||
void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks
|
|
||||||
void Mutex::ReaderUnlock() { Unlock(); }
|
|
||||||
|
|
||||||
#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
|
|
||||||
|
|
||||||
#define SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \
|
|
||||||
if (is_safe_ && fncall(&mutex_) != 0) abort(); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
Mutex::Mutex() {
|
|
||||||
SetIsSafe();
|
|
||||||
if (is_safe_ && pthread_rwlock_init(&mutex_, NULL) != 0) abort();
|
|
||||||
}
|
|
||||||
Mutex::~Mutex() { SAFE_PTHREAD(pthread_rwlock_destroy); }
|
|
||||||
void Mutex::Lock() { SAFE_PTHREAD(pthread_rwlock_wrlock); }
|
|
||||||
void Mutex::Unlock() { SAFE_PTHREAD(pthread_rwlock_unlock); }
|
|
||||||
#ifdef GMUTEX_TRYLOCK
|
|
||||||
bool Mutex::TryLock() { return is_safe_ ?
|
|
||||||
pthread_rwlock_trywrlock(&mutex_) == 0 :
|
|
||||||
true; }
|
|
||||||
#endif
|
|
||||||
void Mutex::ReaderLock() { SAFE_PTHREAD(pthread_rwlock_rdlock); }
|
|
||||||
void Mutex::ReaderUnlock() { SAFE_PTHREAD(pthread_rwlock_unlock); }
|
|
||||||
#undef SAFE_PTHREAD
|
|
||||||
|
|
||||||
#elif defined(HAVE_PTHREAD)
|
|
||||||
|
|
||||||
#define SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \
|
|
||||||
if (is_safe_ && fncall(&mutex_) != 0) abort(); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
Mutex::Mutex() {
|
|
||||||
SetIsSafe();
|
|
||||||
if (is_safe_ && pthread_mutex_init(&mutex_, NULL) != 0) abort();
|
|
||||||
}
|
|
||||||
Mutex::~Mutex() { SAFE_PTHREAD(pthread_mutex_destroy); }
|
|
||||||
void Mutex::Lock() { SAFE_PTHREAD(pthread_mutex_lock); }
|
|
||||||
void Mutex::Unlock() { SAFE_PTHREAD(pthread_mutex_unlock); }
|
|
||||||
#ifdef GMUTEX_TRYLOCK
|
|
||||||
bool Mutex::TryLock() { return is_safe_ ?
|
|
||||||
pthread_mutex_trylock(&mutex_) == 0 : true; }
|
|
||||||
#endif
|
|
||||||
void Mutex::ReaderLock() { Lock(); }
|
|
||||||
void Mutex::ReaderUnlock() { Unlock(); }
|
|
||||||
#undef SAFE_PTHREAD
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// Some helper classes
|
|
||||||
|
|
||||||
// MutexLock(mu) acquires mu when constructed and releases it when destroyed.
|
|
||||||
class MutexLock {
|
|
||||||
public:
|
|
||||||
explicit MutexLock(Mutex *mu) : mu_(mu) { mu_->Lock(); }
|
|
||||||
~MutexLock() { mu_->Unlock(); }
|
|
||||||
private:
|
|
||||||
Mutex * const mu_;
|
|
||||||
// Disallow "evil" constructors
|
|
||||||
MutexLock(const MutexLock&);
|
|
||||||
void operator=(const MutexLock&);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ReaderMutexLock and WriterMutexLock do the same, for rwlocks
|
|
||||||
class ReaderMutexLock {
|
|
||||||
public:
|
|
||||||
explicit ReaderMutexLock(Mutex *mu) : mu_(mu) { mu_->ReaderLock(); }
|
|
||||||
~ReaderMutexLock() { mu_->ReaderUnlock(); }
|
|
||||||
private:
|
|
||||||
Mutex * const mu_;
|
|
||||||
// Disallow "evil" constructors
|
|
||||||
ReaderMutexLock(const ReaderMutexLock&);
|
|
||||||
void operator=(const ReaderMutexLock&);
|
|
||||||
};
|
|
||||||
|
|
||||||
class WriterMutexLock {
|
|
||||||
public:
|
|
||||||
explicit WriterMutexLock(Mutex *mu) : mu_(mu) { mu_->WriterLock(); }
|
|
||||||
~WriterMutexLock() { mu_->WriterUnlock(); }
|
|
||||||
private:
|
|
||||||
Mutex * const mu_;
|
|
||||||
// Disallow "evil" constructors
|
|
||||||
WriterMutexLock(const WriterMutexLock&);
|
|
||||||
void operator=(const WriterMutexLock&);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Catch bug where variable name is omitted, e.g. MutexLock (&mu);
|
|
||||||
#define MutexLock(x) COMPILE_ASSERT(0, mutex_lock_decl_missing_var_name)
|
|
||||||
#define ReaderMutexLock(x) COMPILE_ASSERT(0, rmutex_lock_decl_missing_var_name)
|
|
||||||
#define WriterMutexLock(x) COMPILE_ASSERT(0, wmutex_lock_decl_missing_var_name)
|
|
||||||
|
|
||||||
} // namespace MUTEX_NAMESPACE
|
|
||||||
|
|
||||||
using namespace MUTEX_NAMESPACE;
|
|
||||||
|
|
||||||
#undef MUTEX_NAMESPACE
|
|
||||||
|
|
||||||
#endif /* #define GOOGLE_MUTEX_H__ */
|
|
|
@ -1,59 +0,0 @@
|
||||||
// Copyright (c) 2011, Niels Martin Hansen <nielsm@aegisub.org>
|
|
||||||
//
|
|
||||||
// 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 progress.h
|
|
||||||
/// @brief Progress bars.
|
|
||||||
/// @ingroup libaegisub
|
|
||||||
|
|
||||||
#ifndef LAG_PRE
|
|
||||||
#include <string>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace agi {
|
|
||||||
|
|
||||||
class ProgressSink;
|
|
||||||
|
|
||||||
class ProgressSinkFactory {
|
|
||||||
public:
|
|
||||||
virtual ~ProgressSinkFactory() { }
|
|
||||||
virtual ProgressSink * create_progress_sink(const std::string &title) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class ProgressSink {
|
|
||||||
public:
|
|
||||||
virtual ~ProgressSink() { }
|
|
||||||
virtual void set_progress(int steps, int max) = 0;
|
|
||||||
virtual void set_operation(const std::string &operation) = 0;
|
|
||||||
virtual bool get_cancelled() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class NullProgressSinkFactory : public ProgressSinkFactory {
|
|
||||||
public:
|
|
||||||
virtual ProgressSink * create_progress_sink(const std::string &title) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class StdioProgressSinkFactory : public ProgressSinkFactory {
|
|
||||||
private:
|
|
||||||
|
|
||||||
public:
|
|
||||||
StdioProgressSinkFactory();
|
|
||||||
virtual ProgressSink * create_progress_sink(const std::string &title) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace agi
|
|
|
@ -1,46 +0,0 @@
|
||||||
# $Id$
|
|
||||||
include ../Makefile.inc
|
|
||||||
|
|
||||||
|
|
||||||
LIB_SHARED = libmedia_aegisub-3.0.so
|
|
||||||
LIB_SHARED_INSTALL = yes
|
|
||||||
LIB_VERSION = 3
|
|
||||||
|
|
||||||
CXXFLAGS = -Iinclude -I../libaegisub/include -I../src -I.. -DMAGI -fPIC -Wno-variadic-macros
|
|
||||||
|
|
||||||
PRECOMPILED_HEADER_NAME = magi_pre.h
|
|
||||||
magi_pre.h.gch: CXXFLAGS := $(CXXFLAGS)
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# AUDIO / VIDEO SUPPORT
|
|
||||||
#######################
|
|
||||||
ifeq (yes, $(HAVE_PROVIDER_FFMPEGSOURCE))
|
|
||||||
SRC_OPT += audio/ffms_audio.cpp common/ffms_common.cpp video/ffms_video.cpp
|
|
||||||
common/ffms_common.o: CXXFLAGS += $(CFLAGS_FFMPEGSOURCE) $(CFLAGS_LIBAVFORMAT) $(CFLAGS_LIBAVCODEC) $(CFLAGS_LIBSWSCALE) $(CFLAGS_LIBAVUTIL) $(CFLAGS_LIBPOSTPROC)
|
|
||||||
audio/ffms_audio.o: CXXFLAGS += $(CFLAGS_FFMPEGSOURCE) $(CFLAGS_LIBAVFORMAT) $(CFLAGS_LIBAVCODEC) $(CFLAGS_LIBSWSCALE) $(CFLAGS_LIBAVUTIL) $(CFLAGS_LIBPOSTPROC)
|
|
||||||
video/ffms_video.o: CXXFLAGS += $(CFLAGS_FFMPEGSOURCE) $(CFLAGS_LIBAVFORMAT) $(CFLAGS_LIBAVCODEC) $(CFLAGS_LIBSWSCALE) $(CFLAGS_LIBAVUTIL) $(CFLAGS_LIBPOSTPROC)
|
|
||||||
LDFLAGS_POST += $(LDFLAGS_FFMPEGSOURCE)
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
SRC = \
|
|
||||||
audio/downmix.cpp \
|
|
||||||
audio/convert.cpp \
|
|
||||||
audio/dummy_audio.cpp \
|
|
||||||
audio/pcm.cpp \
|
|
||||||
cache/audio_ram.cpp \
|
|
||||||
cache/audio_hd.cpp \
|
|
||||||
cache/video_cache.cpp \
|
|
||||||
common/audio_manager.cpp \
|
|
||||||
common/video_frame.cpp \
|
|
||||||
common/video_manager.cpp \
|
|
||||||
video/yuv4mpeg.cpp \
|
|
||||||
$(SRC_OPT)
|
|
||||||
|
|
||||||
HEADERS = \
|
|
||||||
*/*.h \
|
|
||||||
include/libmedia/*.h \
|
|
||||||
|
|
||||||
|
|
||||||
include ../Makefile.target
|
|
||||||
-include */*.d
|
|
|
@ -1,174 +0,0 @@
|
||||||
// Copyright (c) 2005-2006, Rodrigo Braz Monteiro, Fredrik Mellbin
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_avs.cpp
|
|
||||||
/// @brief Avisynth-based audio provider
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifdef WITH_AVISYNTH
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <Mmreg.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include <wx/filename.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "audio_provider_avs.h"
|
|
||||||
#include "charset_conv.h"
|
|
||||||
#include "compat.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "standard_paths.h"
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param _filename
|
|
||||||
///
|
|
||||||
AvisynthAudioProvider::AvisynthAudioProvider(wxString filename) try : filename(filename) {
|
|
||||||
AVSValue script;
|
|
||||||
wxMutexLocker lock(AviSynthMutex);
|
|
||||||
|
|
||||||
wxFileName fn(filename);
|
|
||||||
if (!fn.FileExists())
|
|
||||||
throw agi::FileNotFoundError(STD_STR(filename));
|
|
||||||
|
|
||||||
// Include
|
|
||||||
if (filename.EndsWith(_T(".avs"))) {
|
|
||||||
char *fname = env->SaveString(fn.GetShortPath().mb_str(csConvLocal));
|
|
||||||
script = env->Invoke("Import", fname);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use DirectShowSource
|
|
||||||
else {
|
|
||||||
const char * argnames[3] = { 0, "video", "audio" };
|
|
||||||
AVSValue args[3] = { env->SaveString(fn.GetShortPath().mb_str(csConvLocal)), false, true };
|
|
||||||
|
|
||||||
// Load DirectShowSource.dll from app dir if it exists
|
|
||||||
wxFileName dsspath(StandardPaths::DecodePath(_T("?data/DirectShowSource.dll")));
|
|
||||||
if (dsspath.FileExists()) {
|
|
||||||
env->Invoke("LoadPlugin",env->SaveString(dsspath.GetShortPath().mb_str(csConvLocal)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load audio with DSS if it exists
|
|
||||||
if (env->FunctionExists("DirectShowSource")) {
|
|
||||||
script = env->Invoke("DirectShowSource", AVSValue(args,3),argnames);
|
|
||||||
}
|
|
||||||
// Otherwise fail
|
|
||||||
else {
|
|
||||||
throw AudioOpenError("No suitable audio source filter found. Try placing DirectShowSource.dll in the Aegisub application directory.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadFromClip(script);
|
|
||||||
}
|
|
||||||
catch (AvisynthError &err) {
|
|
||||||
throw AudioOpenError("Avisynth error: " + std::string(err.msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Read from environment
|
|
||||||
/// @param _clip
|
|
||||||
///
|
|
||||||
void AvisynthAudioProvider::LoadFromClip(AVSValue _clip) {
|
|
||||||
AVSValue script;
|
|
||||||
|
|
||||||
// Check if it has audio
|
|
||||||
VideoInfo vi = _clip.AsClip()->GetVideoInfo();
|
|
||||||
if (!vi.HasAudio()) throw AudioOpenError("No audio found.");
|
|
||||||
|
|
||||||
// Convert to one channel
|
|
||||||
char buffer[1024];
|
|
||||||
strcpy(buffer,lagi_wxString(OPT_GET("Audio/Downmixer")->GetString()).mb_str(csConvLocal));
|
|
||||||
script = env->Invoke(buffer, _clip);
|
|
||||||
|
|
||||||
// Convert to 16 bits per sample
|
|
||||||
script = env->Invoke("ConvertAudioTo16bit", script);
|
|
||||||
vi = script.AsClip()->GetVideoInfo();
|
|
||||||
|
|
||||||
// Convert sample rate
|
|
||||||
int setsample = OPT_GET("Provider/Audio/AVS/Sample Rate")->GetInt();
|
|
||||||
if (vi.SamplesPerSecond() < 32000) setsample = 44100;
|
|
||||||
if (setsample != 0) {
|
|
||||||
AVSValue args[2] = { script, setsample };
|
|
||||||
script = env->Invoke("ResampleAudio", AVSValue(args,2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set clip
|
|
||||||
PClip tempclip = script.AsClip();
|
|
||||||
vi = tempclip->GetVideoInfo();
|
|
||||||
|
|
||||||
// Read properties
|
|
||||||
channels = vi.AudioChannels();
|
|
||||||
num_samples = vi.num_audio_samples;
|
|
||||||
sample_rate = vi.SamplesPerSecond();
|
|
||||||
bytes_per_sample = vi.BytesPerAudioSample();
|
|
||||||
|
|
||||||
clip = tempclip;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get audio
|
|
||||||
/// @param buf
|
|
||||||
/// @param start
|
|
||||||
/// @param count
|
|
||||||
///
|
|
||||||
void AvisynthAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
|
|
||||||
// Requested beyond the length of audio
|
|
||||||
if (start+count > num_samples) {
|
|
||||||
int64_t oldcount = count;
|
|
||||||
count = num_samples-start;
|
|
||||||
if (count < 0) count = 0;
|
|
||||||
|
|
||||||
// Fill beyond with zero
|
|
||||||
if (bytes_per_sample == 1) {
|
|
||||||
char *temp = (char *) buf;
|
|
||||||
for (int i=count;i<oldcount;i++) {
|
|
||||||
temp[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bytes_per_sample == 2) {
|
|
||||||
short *temp = (short *) buf;
|
|
||||||
for (int i=count;i<oldcount;i++) {
|
|
||||||
temp[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count) {
|
|
||||||
clip->GetAudio(buf,start,count,env);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace
|
|
|
@ -1,73 +0,0 @@
|
||||||
// Copyright (c) 2005-2006, Rodrigo Braz Monteiro, Fredrik Mellbin
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_avs.h
|
|
||||||
/// @see audio_provider_avs.cpp
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#ifdef WITH_AVISYNTH
|
|
||||||
#include "include/aegisub/audio_provider.h"
|
|
||||||
#include "avisynth_wrap.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class AvisynthAudioProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class AvisynthAudioProvider : public AudioProvider, public AviSynthWrapper {
|
|
||||||
/// DOCME
|
|
||||||
wxString filename;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
PClip clip;
|
|
||||||
|
|
||||||
void LoadFromClip(AVSValue clip);
|
|
||||||
void SetFile();
|
|
||||||
|
|
||||||
public:
|
|
||||||
AvisynthAudioProvider(wxString _filename);
|
|
||||||
|
|
||||||
wxString GetFilename() const { return filename; }
|
|
||||||
|
|
||||||
bool AreSamplesNativeEndian() const { return true; }
|
|
||||||
bool NeedsCache() const { return true; }
|
|
||||||
|
|
||||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
|
||||||
void GetWaveForm(int *min,int *peak,int64_t start,int w,int h,int samples,float scale);
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
|
@ -1,225 +0,0 @@
|
||||||
// Copyright (c) 2008, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_convert.cpp
|
|
||||||
/// @brief Intermediate sample format-converting audio provider
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "aegisub_endian.h"
|
|
||||||
#include "convert.h"
|
|
||||||
#include "downmix.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param src
|
|
||||||
///
|
|
||||||
ConvertAudioProvider::ConvertAudioProvider(AudioProvider *src) : source(src) {
|
|
||||||
channels = source->GetChannels();
|
|
||||||
num_samples = source->GetNumSamples();
|
|
||||||
sample_rate = source->GetSampleRate();
|
|
||||||
bytes_per_sample = 2;
|
|
||||||
|
|
||||||
sampleMult = 1;
|
|
||||||
if (sample_rate < 16000) sampleMult = 4;
|
|
||||||
else if (sample_rate < 32000) sampleMult = 2;
|
|
||||||
sample_rate *= sampleMult;
|
|
||||||
num_samples *= sampleMult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Convert to 16-bit
|
|
||||||
/// @param src
|
|
||||||
/// @param dst
|
|
||||||
/// @param count
|
|
||||||
///
|
|
||||||
void ConvertAudioProvider::Make16Bit(const char *src, short *dst, int64_t count) const {
|
|
||||||
for (int64_t i=0;i<count;i++) {
|
|
||||||
dst[i] = (short(src[i])-128)*255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////
|
|
||||||
// Change sample rate
|
|
||||||
// This requres 16-bit input
|
|
||||||
// The SampleConverter is a class overloading operator() with a function from short to short
|
|
||||||
template<class SampleConverter>
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param src
|
|
||||||
/// @param dst
|
|
||||||
/// @param count
|
|
||||||
/// @param converter
|
|
||||||
///
|
|
||||||
void ConvertAudioProvider::ChangeSampleRate(const short *src, short *dst, int64_t count, const SampleConverter &converter) const {
|
|
||||||
// Upsample by 2
|
|
||||||
if (sampleMult == 2) {
|
|
||||||
int64_t size = count/2;
|
|
||||||
short cur;
|
|
||||||
short next = 0;
|
|
||||||
for (int64_t i=0;i<size;i++) {
|
|
||||||
cur = next;
|
|
||||||
next = converter(*src++);
|
|
||||||
*(dst++) = cur;
|
|
||||||
*(dst++) = (cur+next)/2;
|
|
||||||
}
|
|
||||||
if (count%2) *(dst++) = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upsample by 4
|
|
||||||
else if (sampleMult == 4) {
|
|
||||||
int64_t size = count/4;
|
|
||||||
short cur;
|
|
||||||
short next = 0;
|
|
||||||
for (int64_t i=0;i<size;i++) {
|
|
||||||
cur = next;
|
|
||||||
next = converter(*src++);
|
|
||||||
*(dst++) = cur;
|
|
||||||
*(dst++) = (cur*3+next)/4;
|
|
||||||
*(dst++) = (cur+next)/2;
|
|
||||||
*(dst++) = (cur+next*3)/4;
|
|
||||||
}
|
|
||||||
for (int i=0;i<count%4;i++) *(dst++) = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nothing much to do, just ensure correct endedness
|
|
||||||
else if (sampleMult == 1) {
|
|
||||||
while (count-- > 0) {
|
|
||||||
*dst++ = converter(*src++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
struct NullSampleConverter {
|
|
||||||
inline short operator()(const short val) const {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
struct EndianSwapSampleConverter {
|
|
||||||
inline short operator()(const short val) const {
|
|
||||||
return (short)Endian::Reverse((uint16_t)val);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Get audio
|
|
||||||
/// @param destination
|
|
||||||
/// @param start
|
|
||||||
/// @param count
|
|
||||||
///
|
|
||||||
void ConvertAudioProvider::GetAudio(void *destination, int64_t start, int64_t count) const {
|
|
||||||
// Bits per sample
|
|
||||||
int srcBps = source->GetBytesPerSample();
|
|
||||||
|
|
||||||
// Nothing to do
|
|
||||||
if (sampleMult == 1 && srcBps == 2) {
|
|
||||||
source->GetAudio(destination,start,count);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert
|
|
||||||
else {
|
|
||||||
// Allocate buffers with sufficient size for the entire operation
|
|
||||||
size_t fullSize = count;
|
|
||||||
int64_t srcCount = count / sampleMult;
|
|
||||||
short *buffer1 = NULL;
|
|
||||||
short *buffer2 = NULL;
|
|
||||||
short *last = NULL;
|
|
||||||
|
|
||||||
// Read audio
|
|
||||||
buffer1 = new short[fullSize * channels];
|
|
||||||
source->GetAudio(buffer1,start/sampleMult,srcCount);
|
|
||||||
|
|
||||||
// Convert from 8-bit to 16-bit
|
|
||||||
if (srcBps == 1) {
|
|
||||||
if (sampleMult == 1) {
|
|
||||||
Make16Bit((const char*)buffer1,(short*)destination,srcCount * channels);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
buffer2 = new short[fullSize * channels];
|
|
||||||
Make16Bit((const char*)buffer1,buffer2,srcCount * channels);
|
|
||||||
last = buffer2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Already 16-bit
|
|
||||||
else if (srcBps == 2) last = buffer1;
|
|
||||||
|
|
||||||
// Convert sample rate
|
|
||||||
if (sampleMult != 1 && source->AreSamplesNativeEndian()) {
|
|
||||||
ChangeSampleRate(last,(short*)destination,count * channels, NullSampleConverter());
|
|
||||||
}
|
|
||||||
else if (!source->AreSamplesNativeEndian()) {
|
|
||||||
ChangeSampleRate(last,(short*)destination,count * channels, EndianSwapSampleConverter());
|
|
||||||
}
|
|
||||||
|
|
||||||
delete [] buffer1;
|
|
||||||
delete [] buffer2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief See if we need to downmix the number of channels
|
|
||||||
/// @param source_provider
|
|
||||||
///
|
|
||||||
AudioProvider *CreateConvertAudioProvider(AudioProvider *source_provider) {
|
|
||||||
AudioProvider *provider = source_provider;
|
|
||||||
|
|
||||||
// Aegisub requires 16 bit samples,
|
|
||||||
// some audio players break with low samplerates,
|
|
||||||
// everything breaks with wrong-ended samples.
|
|
||||||
if (provider->GetBytesPerSample() != 2 ||
|
|
||||||
provider->GetSampleRate() < 32000 ||
|
|
||||||
!provider->AreSamplesNativeEndian())
|
|
||||||
{
|
|
||||||
// @todo add support for more bitdepths (i.e. 24- and 32-bit audio)
|
|
||||||
if (provider->GetBytesPerSample() > 2)
|
|
||||||
throw AudioOpenError("Audio format converter: audio with bitdepths greater than 16 bits/sample is currently unsupported");
|
|
||||||
|
|
||||||
provider = new ConvertAudioProvider(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We also require mono audio for historical reasons
|
|
||||||
if (provider->GetChannels() != 1)
|
|
||||||
{
|
|
||||||
provider = new DownmixingAudioProvider(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
return provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
// Copyright (c) 2008, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_convert.h
|
|
||||||
/// @see audio_provider_convert.cpp
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "libmedia/audio.h"
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <tr1/memory>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class ConvertAudioProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class ConvertAudioProvider : public AudioProvider {
|
|
||||||
/// DOCME
|
|
||||||
int sampleMult;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
std::tr1::shared_ptr<AudioProvider> source;
|
|
||||||
void Make16Bit(const char *src, short *dst, int64_t count) const;
|
|
||||||
template<class SampleConverter>
|
|
||||||
void ChangeSampleRate(const short *src, short *dst, int64_t count, const SampleConverter &converter) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ConvertAudioProvider(AudioProvider *source);
|
|
||||||
|
|
||||||
/// By its nature, the ConvertAudioProvider always delivers machine endian.
|
|
||||||
/// That's one of the points of it!
|
|
||||||
bool AreSamplesNativeEndian() const { return true; }
|
|
||||||
|
|
||||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
|
||||||
|
|
||||||
std::string GetFilename() const { return source->GetFilename(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
AudioProvider *CreateConvertAudioProvider(AudioProvider *source_provider);
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
|
@ -1,130 +0,0 @@
|
||||||
// Copyright (c) 2007-2008, Niels Martin Hansen
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_downmix.cpp
|
|
||||||
/// @brief Intermediate audio provider downmixing the signal to mono
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////
|
|
||||||
// Headers
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "downmix.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param source
|
|
||||||
///
|
|
||||||
DownmixingAudioProvider::DownmixingAudioProvider(AudioProvider *source) : provider(source) {
|
|
||||||
filename = source->GetFilename();
|
|
||||||
channels = 1; // target
|
|
||||||
src_channels = source->GetChannels();
|
|
||||||
num_samples = source->GetNumSamples();
|
|
||||||
bytes_per_sample = source->GetBytesPerSample();
|
|
||||||
sample_rate = source->GetSampleRate();
|
|
||||||
|
|
||||||
if (!(bytes_per_sample == 1 || bytes_per_sample == 2))
|
|
||||||
throw AudioOpenError("Downmixing Audio Provider: Can only downmix 8 and 16 bit audio");
|
|
||||||
if (!source->AreSamplesNativeEndian())
|
|
||||||
throw AudioOpenError("Downmixing Audio Provider: Source must have machine endian samples");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Actual work happens here
|
|
||||||
/// @param buf
|
|
||||||
/// @param start
|
|
||||||
/// @param count
|
|
||||||
///
|
|
||||||
void DownmixingAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
|
|
||||||
if (count == 0) return;
|
|
||||||
|
|
||||||
// We can do this ourselves
|
|
||||||
if (start >= num_samples) {
|
|
||||||
if (bytes_per_sample == 1)
|
|
||||||
// 8 bit formats are usually unsigned with bias 127
|
|
||||||
memset(buf, 127, count);
|
|
||||||
else
|
|
||||||
// While everything else is signed
|
|
||||||
memset(buf, 0, count*bytes_per_sample);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// So alloc some temporary memory for this
|
|
||||||
// Depending on use, this might be made faster by using
|
|
||||||
// a pre-allocced block of memory...?
|
|
||||||
char *tmp = new char[count*bytes_per_sample*src_channels];
|
|
||||||
|
|
||||||
try {
|
|
||||||
provider->GetAudio(tmp, start, count);
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
delete tmp;
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now downmix
|
|
||||||
// Just average the samples over the channels (really bad if they're out of phase!)
|
|
||||||
// XXX: Assuming here that sample data are in machine endian, an upstream provider should ensure that
|
|
||||||
if (bytes_per_sample == 1) {
|
|
||||||
uint8_t *src = (uint8_t *)tmp;
|
|
||||||
uint8_t *dst = (uint8_t *)buf;
|
|
||||||
|
|
||||||
while (count > 0) {
|
|
||||||
int sum = 0;
|
|
||||||
for (int c = 0; c < src_channels; c++)
|
|
||||||
sum += *(src++);
|
|
||||||
*(dst++) = (uint8_t)(sum / src_channels);
|
|
||||||
count--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (bytes_per_sample == 2) {
|
|
||||||
int16_t *src = (int16_t *)tmp;
|
|
||||||
int16_t *dst = (int16_t *)buf;
|
|
||||||
|
|
||||||
while (count > 0) {
|
|
||||||
int sum = 0;
|
|
||||||
for (int c = 0; c < src_channels; c++)
|
|
||||||
sum += *(src++);
|
|
||||||
*(dst++) = (int16_t)(sum / src_channels);
|
|
||||||
count--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Done downmixing, free the work buffer
|
|
||||||
delete[] tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
// Copyright (c) 2007-2008, Niels Martin Hansen
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_downmix.h
|
|
||||||
/// @see audio_provider_downmix.cpp
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "libmedia/audio.h"
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <tr1/memory>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class DownmixingAudioProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class DownmixingAudioProvider : public AudioProvider {
|
|
||||||
std::tr1::shared_ptr<AudioProvider> provider;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int src_channels;
|
|
||||||
public:
|
|
||||||
DownmixingAudioProvider(AudioProvider *source);
|
|
||||||
|
|
||||||
/// @brief Downmixing requires samples to be native endian beforehand
|
|
||||||
///
|
|
||||||
bool AreSamplesNativeEndian() const { return true; }
|
|
||||||
|
|
||||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
// Copyright (c) 2005-2006, Rodrigo Braz Monteiro, Fredrik Mellbin
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_dummy.cpp
|
|
||||||
/// @brief Dummy (silence or noise) audio provider
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "dummy_audio.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param dur_ms
|
|
||||||
/// @param _noise
|
|
||||||
///
|
|
||||||
DummyAudioProvider::DummyAudioProvider(unsigned long dur_ms, bool _noise) {
|
|
||||||
noise = _noise;
|
|
||||||
channels = 1;
|
|
||||||
sample_rate = 44100;
|
|
||||||
bytes_per_sample = 2;
|
|
||||||
num_samples = (int64_t)dur_ms * sample_rate / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Destructor
|
|
||||||
///
|
|
||||||
DummyAudioProvider::~DummyAudioProvider() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get audio
|
|
||||||
/// @param buf
|
|
||||||
/// @param start
|
|
||||||
/// @param count
|
|
||||||
///
|
|
||||||
void DummyAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
|
|
||||||
short *workbuf = (short*)buf;
|
|
||||||
|
|
||||||
if (noise) {
|
|
||||||
while (--count > 0)
|
|
||||||
*workbuf++ = (rand() - RAND_MAX/2) * 10000 / RAND_MAX;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
while (--count > 0)
|
|
||||||
*workbuf++ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
// Copyright (c) 2006, Niels Martin Hansen
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_dummy.h
|
|
||||||
/// @see audio_provider_dummy.cpp
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "libmedia/audio.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class DummyAudioProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class DummyAudioProvider : public AudioProvider {
|
|
||||||
/// DOCME
|
|
||||||
bool noise;
|
|
||||||
|
|
||||||
public:
|
|
||||||
DummyAudioProvider(unsigned long dur_ms, bool _noise);
|
|
||||||
~DummyAudioProvider();
|
|
||||||
|
|
||||||
bool AreSamplesNativeEndian() const { return true; }
|
|
||||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
|
@ -1,225 +0,0 @@
|
||||||
// Copyright (c) 2008-2009, Karl Blomster <thefluff@aegisub.org>
|
|
||||||
//
|
|
||||||
// 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"
|
|
||||||
#include "libmedia/audio.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
namespace ffms {
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param filename
|
|
||||||
///
|
|
||||||
FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(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 FFmpegSourceAudioProvider::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<int,std::string> 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(filename.c_str(), TrackNumber, Index, -1, &ErrInfo);
|
|
||||||
#else
|
|
||||||
AudioSource = FFMS_CreateAudioSource(filename.c_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
|
|
||||||
///
|
|
||||||
FFmpegSourceAudioProvider::~FFmpegSourceAudioProvider() {
|
|
||||||
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 FFmpegSourceAudioProvider::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
|
|
||||||
} // namespace media
|
|
||||||
|
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
// Copyright (c) 2008-2009, Karl Blomster <thefluff@aegisub.org>
|
|
||||||
//
|
|
||||||
// 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 <libmedia/audio.h>
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#ifdef WIN32
|
|
||||||
#include <objbase.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "../common/ffms_common.h"
|
|
||||||
namespace media {
|
|
||||||
namespace ffms {
|
|
||||||
|
|
||||||
/// @class Audio
|
|
||||||
/// Audio file support.
|
|
||||||
class FFmpegSourceAudioProvider : public AudioProvider, FFmpegSourceProvider {
|
|
||||||
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);
|
|
||||||
|
|
||||||
public:
|
|
||||||
FFmpegSourceAudioProvider(std::string filename);
|
|
||||||
virtual ~FFmpegSourceAudioProvider();
|
|
||||||
|
|
||||||
/// @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
|
|
||||||
} // namespace media
|
|
|
@ -1,633 +0,0 @@
|
||||||
// Copyright (c) 2007-2008, Niels Martin Hansen
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_pcm.cpp
|
|
||||||
/// @brief PCM WAV and WAV64 audio provider
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#ifndef __WINDOWS__
|
|
||||||
#include <sys/fcntl.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libaegisub/log.h>
|
|
||||||
|
|
||||||
#include "aegisub_endian.h"
|
|
||||||
#include "pcm.h"
|
|
||||||
//#include "compat.h"
|
|
||||||
//#include "utils.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param filename
|
|
||||||
///
|
|
||||||
PCMAudioProvider::PCMAudioProvider(const std::string &filename)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
file_handle = CreateFile(
|
|
||||||
filename.c_str(),
|
|
||||||
FILE_READ_DATA,
|
|
||||||
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
||||||
0,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS,
|
|
||||||
0);
|
|
||||||
|
|
||||||
if (file_handle == INVALID_HANDLE_VALUE) {
|
|
||||||
throw agi::FileNotFoundError(STD_STR(filename));
|
|
||||||
}
|
|
||||||
|
|
||||||
LARGE_INTEGER li_file_size = {0};
|
|
||||||
if (!GetFileSizeEx(file_handle, &li_file_size)) {
|
|
||||||
CloseHandle(file_handle);
|
|
||||||
throw AudioOpenError("Failed getting file size");
|
|
||||||
}
|
|
||||||
file_size = li_file_size.QuadPart;
|
|
||||||
|
|
||||||
file_mapping = CreateFileMapping(
|
|
||||||
file_handle,
|
|
||||||
0,
|
|
||||||
PAGE_READONLY,
|
|
||||||
0, 0,
|
|
||||||
0);
|
|
||||||
|
|
||||||
if (file_mapping == 0) {
|
|
||||||
CloseHandle(file_handle);
|
|
||||||
throw AudioOpenError("Failed creating file mapping");
|
|
||||||
}
|
|
||||||
|
|
||||||
current_mapping = 0;
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
file_handle = open(filename.c_str(), O_RDONLY);
|
|
||||||
|
|
||||||
if (file_handle == -1) {
|
|
||||||
throw agi::FileNotFoundError(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct stat filestats;
|
|
||||||
memset(&filestats, 0, sizeof(filestats));
|
|
||||||
if (fstat(file_handle, &filestats)) {
|
|
||||||
close(file_handle);
|
|
||||||
throw AudioOpenError("Could not stat file to get size");
|
|
||||||
}
|
|
||||||
file_size = filestats.st_size;
|
|
||||||
|
|
||||||
current_mapping = 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
PCMAudioProvider::~PCMAudioProvider()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (current_mapping) {
|
|
||||||
UnmapViewOfFile(current_mapping);
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(file_mapping);
|
|
||||||
CloseHandle(file_handle);
|
|
||||||
#else
|
|
||||||
if (current_mapping) {
|
|
||||||
munmap(current_mapping, mapping_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(file_handle);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param range_start
|
|
||||||
/// @param range_length
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t range_length) const
|
|
||||||
{
|
|
||||||
if (range_start + range_length > file_size) {
|
|
||||||
throw AudioDecodeError("Attempted to map beyond end of file");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the requested range is already visible
|
|
||||||
if (!current_mapping || range_start < mapping_start || range_start+range_length > mapping_start+(int64_t)mapping_length) {
|
|
||||||
// It's not visible, change the current mapping
|
|
||||||
if (current_mapping) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
UnmapViewOfFile(current_mapping);
|
|
||||||
#else
|
|
||||||
munmap(current_mapping, mapping_length);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Align range start on a 1 MB boundary and go 16 MB back
|
|
||||||
mapping_start = (range_start & ~0xFFFFF) - 0x1000000;
|
|
||||||
if (mapping_start < 0) mapping_start = 0;
|
|
||||||
|
|
||||||
if (sizeof(void*) > 4)
|
|
||||||
// Large address space, use a 2 GB mapping
|
|
||||||
mapping_length = 0x80000000;
|
|
||||||
else
|
|
||||||
// Small (32 bit) address space, use a 256 MB mapping
|
|
||||||
mapping_length = 0x10000000;
|
|
||||||
|
|
||||||
// Make sure to always make a mapping at least as large as the requested range
|
|
||||||
if ((int64_t)mapping_length < range_length) {
|
|
||||||
if (range_length > (int64_t)(~(size_t)0))
|
|
||||||
throw AudioDecodeError("Requested range larger than max size_t, cannot create view of file");
|
|
||||||
mapping_length = range_length;
|
|
||||||
}
|
|
||||||
// But also make sure we don't try to make a mapping larger than the file
|
|
||||||
if (mapping_start + (int64_t)mapping_length > file_size)
|
|
||||||
mapping_length = (size_t)(file_size - mapping_start);
|
|
||||||
// We already checked that the requested range doesn't extend over the end of the file
|
|
||||||
// Hopefully this should ensure that small files are always mapped in their entirety
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
LARGE_INTEGER mapping_start_li;
|
|
||||||
mapping_start_li.QuadPart = mapping_start;
|
|
||||||
current_mapping = MapViewOfFile(
|
|
||||||
file_mapping, // Mapping handle
|
|
||||||
FILE_MAP_READ, // Access type
|
|
||||||
mapping_start_li.HighPart, // Offset high-part
|
|
||||||
mapping_start_li.LowPart, // Offset low-part
|
|
||||||
mapping_length); // Length of view
|
|
||||||
#else
|
|
||||||
current_mapping = mmap(0, mapping_length, PROT_READ, MAP_PRIVATE, file_handle, mapping_start);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!current_mapping) {
|
|
||||||
throw AudioDecodeError("Failed mapping a view of the file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(current_mapping);
|
|
||||||
assert(range_start >= mapping_start);
|
|
||||||
|
|
||||||
// Difference between actual current mapping start and requested range start
|
|
||||||
ptrdiff_t rel_ofs = (ptrdiff_t)(range_start - mapping_start);
|
|
||||||
|
|
||||||
// Calculate a pointer into current mapping for the requested range
|
|
||||||
return ((char*)current_mapping) + rel_ofs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param buf
|
|
||||||
/// @param start
|
|
||||||
/// @param count
|
|
||||||
///
|
|
||||||
void PCMAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const
|
|
||||||
{
|
|
||||||
// Read blocks from the file
|
|
||||||
size_t index = 0;
|
|
||||||
while (count > 0 && index < index_points.size()) {
|
|
||||||
// Check if this index contains the samples we're looking for
|
|
||||||
const IndexPoint &ip = index_points[index];
|
|
||||||
if (ip.start_sample <= start && ip.start_sample+ip.num_samples > start) {
|
|
||||||
|
|
||||||
// How many samples we can maximum take from this block
|
|
||||||
int64_t samples_can_do = ip.num_samples - start + ip.start_sample;
|
|
||||||
if (samples_can_do > count) samples_can_do = count;
|
|
||||||
|
|
||||||
// Read as many samples we can
|
|
||||||
char *src = EnsureRangeAccessible(
|
|
||||||
ip.start_byte + (start - ip.start_sample) * bytes_per_sample * channels,
|
|
||||||
samples_can_do * bytes_per_sample * channels);
|
|
||||||
memcpy(buf, src, samples_can_do * bytes_per_sample * channels);
|
|
||||||
|
|
||||||
// Update data
|
|
||||||
buf = (char*)buf + samples_can_do * bytes_per_sample * channels;
|
|
||||||
start += samples_can_do;
|
|
||||||
count -= samples_can_do;
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we exhausted all sample sections zerofill the rest
|
|
||||||
if (count > 0) {
|
|
||||||
if (bytes_per_sample == 1)
|
|
||||||
// 8 bit formats are usually unsigned with bias 127
|
|
||||||
memset(buf, 127, count*channels);
|
|
||||||
else
|
|
||||||
// While everything else is signed
|
|
||||||
memset(buf, 0, count*bytes_per_sample*channels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class RiffWavPCMAudioProvider
|
|
||||||
/// @brief RIFF WAV PCM provider
|
|
||||||
///
|
|
||||||
/// Overview of RIFF WAV: <http://www.sonicspot.com/guide/wavefiles.html>
|
|
||||||
class RiffWavPCMAudioProvider : public PCMAudioProvider {
|
|
||||||
/// DOCME
|
|
||||||
struct ChunkHeader {
|
|
||||||
/// Always "RIFF"
|
|
||||||
char type[4];
|
|
||||||
/// File size minus sizeof(ChunkHeader) (i.e. 8)
|
|
||||||
uint32_t size;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
struct RIFFChunk {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
ChunkHeader ch;
|
|
||||||
|
|
||||||
/// Always "WAVE"
|
|
||||||
char format[4];
|
|
||||||
};
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
struct fmtChunk {
|
|
||||||
|
|
||||||
/// compression format used
|
|
||||||
/// We support only PCM (0x1)
|
|
||||||
uint16_t compression;
|
|
||||||
|
|
||||||
/// Number of channels
|
|
||||||
uint16_t channels;
|
|
||||||
|
|
||||||
/// Samples per second
|
|
||||||
uint32_t samplerate;
|
|
||||||
|
|
||||||
/// Bytes per second
|
|
||||||
/// can't always be trusted
|
|
||||||
uint32_t avg_bytes_sec;
|
|
||||||
|
|
||||||
/// Bytes per sample
|
|
||||||
uint16_t block_align;
|
|
||||||
|
|
||||||
/// Bits per sample that are actually used; rest should be ignored
|
|
||||||
uint16_t significant_bits_sample;
|
|
||||||
// Here was supposed to be some more fields but we don't need them
|
|
||||||
// and just skipping by the size of the struct wouldn't be safe
|
|
||||||
// either way, as the fields can depend on the compression.
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param str1[]
|
|
||||||
/// @param str2[]
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
static bool CheckFourcc(const char str1[], const char str2[])
|
|
||||||
{
|
|
||||||
assert(str1);
|
|
||||||
assert(str2);
|
|
||||||
return
|
|
||||||
(str1[0] == str2[0]) &&
|
|
||||||
(str1[1] == str2[1]) &&
|
|
||||||
(str1[2] == str2[2]) &&
|
|
||||||
(str1[3] == str2[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param _filename
|
|
||||||
///
|
|
||||||
RiffWavPCMAudioProvider(const std::string &_filename)
|
|
||||||
: PCMAudioProvider(_filename)
|
|
||||||
{
|
|
||||||
filename = _filename;
|
|
||||||
|
|
||||||
// Read header
|
|
||||||
void *filestart = EnsureRangeAccessible(0, sizeof(RIFFChunk));
|
|
||||||
RIFFChunk &header = *(RIFFChunk*)filestart;
|
|
||||||
|
|
||||||
// Check magic values
|
|
||||||
if (!CheckFourcc(header.ch.type, "RIFF"))
|
|
||||||
throw AudioOpenError("File is not a RIFF file");
|
|
||||||
if (!CheckFourcc(header.format, "WAVE"))
|
|
||||||
throw AudioOpenError("File is not a RIFF WAV file");
|
|
||||||
|
|
||||||
// Count how much more data we can have in the entire file
|
|
||||||
// The first 4 bytes are already eaten by the header.format field
|
|
||||||
uint32_t data_left = Endian::LittleToMachine(header.ch.size) - 4;
|
|
||||||
// How far into the file we have processed.
|
|
||||||
// Must be incremented by the riff chunk size fields.
|
|
||||||
uint32_t filepos = sizeof(header);
|
|
||||||
|
|
||||||
bool got_fmt_header = false;
|
|
||||||
|
|
||||||
// Inherited from AudioProvider
|
|
||||||
num_samples = 0;
|
|
||||||
|
|
||||||
// Continue reading chunks until out of data
|
|
||||||
while (data_left) {
|
|
||||||
ChunkHeader &ch = *(ChunkHeader*)EnsureRangeAccessible(filepos, sizeof(ChunkHeader));
|
|
||||||
|
|
||||||
// Update counters
|
|
||||||
data_left -= sizeof(ch);
|
|
||||||
filepos += sizeof(ch);
|
|
||||||
|
|
||||||
if (CheckFourcc(ch.type, "fmt ")) {
|
|
||||||
if (got_fmt_header) throw AudioOpenError("Invalid file, multiple 'fmt ' chunks");
|
|
||||||
got_fmt_header = true;
|
|
||||||
|
|
||||||
fmtChunk &fmt = *(fmtChunk*)EnsureRangeAccessible(filepos, sizeof(fmtChunk));
|
|
||||||
|
|
||||||
if (Endian::LittleToMachine(fmt.compression) != 1)
|
|
||||||
throw AudioOpenError("Can't use file, not PCM encoding");
|
|
||||||
|
|
||||||
// Set stuff inherited from the AudioProvider class
|
|
||||||
sample_rate = Endian::LittleToMachine(fmt.samplerate);
|
|
||||||
channels = Endian::LittleToMachine(fmt.channels);
|
|
||||||
bytes_per_sample = (Endian::LittleToMachine(fmt.significant_bits_sample) + 7) / 8; // round up to nearest whole byte
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (CheckFourcc(ch.type, "data")) {
|
|
||||||
// This won't pick up 'data' chunks inside 'wavl' chunks
|
|
||||||
// since the 'wavl' chunks wrap those.
|
|
||||||
|
|
||||||
if (!got_fmt_header) throw AudioOpenError("Found 'data' chunk before 'fmt ' chunk, file is invalid.");
|
|
||||||
|
|
||||||
int64_t samples = Endian::LittleToMachine(ch.size) / bytes_per_sample;
|
|
||||||
int64_t frames = samples / channels;
|
|
||||||
|
|
||||||
IndexPoint ip;
|
|
||||||
ip.start_sample = num_samples;
|
|
||||||
ip.num_samples = frames;
|
|
||||||
ip.start_byte = filepos;
|
|
||||||
index_points.push_back(ip);
|
|
||||||
|
|
||||||
num_samples += frames;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Support wavl (wave list) chunks too?
|
|
||||||
|
|
||||||
// Update counters
|
|
||||||
// Make sure they're word aligned
|
|
||||||
data_left -= (Endian::LittleToMachine(ch.size) + 1) & ~1;
|
|
||||||
filepos += (Endian::LittleToMachine(ch.size) + 1) & ~1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
bool AreSamplesNativeEndian() const
|
|
||||||
{
|
|
||||||
// 8 bit samples don't consider endianness
|
|
||||||
if (bytes_per_sample < 2) return true;
|
|
||||||
// Otherwise test whether we're little endian
|
|
||||||
uint32_t testvalue = 0x008800ff;
|
|
||||||
return testvalue == Endian::LittleToMachine(testvalue);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
static const uint8_t w64GuidRIFF[16] = {
|
|
||||||
// {66666972-912E-11CF-A5D6-28DB04C10000}
|
|
||||||
0x72, 0x69, 0x66, 0x66, 0x2E, 0x91, 0xCF, 0x11, 0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
static const uint8_t w64GuidWAVE[16] = {
|
|
||||||
// {65766177-ACF3-11D3-8CD1-00C04F8EDB8A}
|
|
||||||
0x77, 0x61, 0x76, 0x65, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
static const uint8_t w64Guidfmt[16] = {
|
|
||||||
// {20746D66-ACF3-11D3-8CD1-00C04F8EDB8A}
|
|
||||||
0x66, 0x6D, 0x74, 0x20, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
static const uint8_t w64Guiddata[16] = {
|
|
||||||
// {61746164-ACF3-11D3-8CD1-00C04F8EDB8A}
|
|
||||||
0x64, 0x61, 0x74, 0x61, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class Wave64AudioProvider
|
|
||||||
/// @brief Sony Wave64 audio provider
|
|
||||||
///
|
|
||||||
/// http://www.vcs.de/fileadmin/user_upload/MBS/PDF/Whitepaper/Informations_about_Sony_Wave64.pdf
|
|
||||||
class Wave64AudioProvider : public PCMAudioProvider {
|
|
||||||
// Here's some copy-paste from the FFmpegSource2 code
|
|
||||||
|
|
||||||
/// http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx
|
|
||||||
struct WaveFormatEx {
|
|
||||||
uint16_t wFormatTag;
|
|
||||||
uint16_t nChannels;
|
|
||||||
uint32_t nSamplesPerSec;
|
|
||||||
uint32_t nAvgBytesPerSec;
|
|
||||||
uint16_t nBlockAlign;
|
|
||||||
uint16_t wBitsPerSample;
|
|
||||||
uint16_t cbSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
struct RiffChunk {
|
|
||||||
/// DOCME
|
|
||||||
uint8_t riff_guid[16];
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
uint64_t file_size;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
uint8_t format_guid[16];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
struct FormatChunk {
|
|
||||||
/// DOCME
|
|
||||||
uint8_t chunk_guid[16];
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
uint64_t chunk_size;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
WaveFormatEx format;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
uint8_t padding[6];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
struct DataChunk {
|
|
||||||
/// DOCME
|
|
||||||
uint8_t chunk_guid[16];
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
uint64_t chunk_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param guid1
|
|
||||||
/// @param guid2
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
inline bool CheckGuid(const uint8_t *guid1, const uint8_t *guid2)
|
|
||||||
{
|
|
||||||
return memcmp(guid1, guid2, 16) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param _filename
|
|
||||||
///
|
|
||||||
Wave64AudioProvider(const std::string &_filename)
|
|
||||||
: PCMAudioProvider(_filename)
|
|
||||||
{
|
|
||||||
filename = _filename;
|
|
||||||
|
|
||||||
int64_t smallest_possible_file = sizeof(RiffChunk) + sizeof(FormatChunk) + sizeof(DataChunk);
|
|
||||||
|
|
||||||
if (file_size < smallest_possible_file)
|
|
||||||
throw AudioOpenError("File is too small to be a Wave64 file");
|
|
||||||
|
|
||||||
// Read header
|
|
||||||
// This should throw an exception if the mapping fails
|
|
||||||
void *filestart = EnsureRangeAccessible(0, sizeof(RiffChunk));
|
|
||||||
assert(filestart);
|
|
||||||
RiffChunk &header = *(RiffChunk*)filestart;
|
|
||||||
|
|
||||||
// Check magic values
|
|
||||||
if (!CheckGuid(header.riff_guid, w64GuidRIFF))
|
|
||||||
throw AudioOpenError("File is not a Wave64 RIFF file");
|
|
||||||
if (!CheckGuid(header.format_guid, w64GuidWAVE))
|
|
||||||
throw AudioOpenError("File is not a Wave64 WAVE file");
|
|
||||||
|
|
||||||
// Count how much more data we can have in the entire file
|
|
||||||
uint64_t data_left = Endian::LittleToMachine(header.file_size) - sizeof(RiffChunk);
|
|
||||||
// How far into the file we have processed.
|
|
||||||
// Must be incremented by the riff chunk size fields.
|
|
||||||
uint64_t filepos = sizeof(header);
|
|
||||||
|
|
||||||
bool got_fmt_header = false;
|
|
||||||
|
|
||||||
// Inherited from AudioProvider
|
|
||||||
num_samples = 0;
|
|
||||||
|
|
||||||
// Continue reading chunks until out of data
|
|
||||||
while (data_left) {
|
|
||||||
uint8_t *chunk_guid = (uint8_t*)EnsureRangeAccessible(filepos, 16);
|
|
||||||
uint64_t chunk_size = Endian::LittleToMachine(*(uint64_t*)EnsureRangeAccessible(filepos+16, sizeof(uint64_t)));
|
|
||||||
|
|
||||||
if (CheckGuid(chunk_guid, w64Guidfmt)) {
|
|
||||||
if (got_fmt_header)
|
|
||||||
throw AudioOpenError("Bad file, found more than one 'fmt' chunk");
|
|
||||||
|
|
||||||
FormatChunk &fmt = *(FormatChunk*)EnsureRangeAccessible(filepos, sizeof(FormatChunk));
|
|
||||||
got_fmt_header = true;
|
|
||||||
|
|
||||||
if (Endian::LittleToMachine(fmt.format.wFormatTag) == 3)
|
|
||||||
throw AudioOpenError("File is IEEE 32 bit float format which isn't supported. Bug the developers if this matters.");
|
|
||||||
if (Endian::LittleToMachine(fmt.format.wFormatTag) != 1)
|
|
||||||
throw AudioOpenError("Can't use file, not PCM encoding");
|
|
||||||
|
|
||||||
// Set stuff inherited from the AudioProvider class
|
|
||||||
sample_rate = Endian::LittleToMachine(fmt.format.nSamplesPerSec);
|
|
||||||
channels = Endian::LittleToMachine(fmt.format.nChannels);
|
|
||||||
bytes_per_sample = (Endian::LittleToMachine(fmt.format.wBitsPerSample) + 7) / 8; // round up to nearest whole byte
|
|
||||||
}
|
|
||||||
else if (CheckGuid(chunk_guid, w64Guiddata)) {
|
|
||||||
if (!got_fmt_header)
|
|
||||||
throw AudioOpenError("Found 'data' chunk before 'fmt ' chunk, file is invalid.");
|
|
||||||
|
|
||||||
int64_t samples = chunk_size / bytes_per_sample;
|
|
||||||
int64_t frames = samples / channels;
|
|
||||||
|
|
||||||
IndexPoint ip;
|
|
||||||
ip.start_sample = num_samples;
|
|
||||||
ip.num_samples = frames;
|
|
||||||
ip.start_byte = filepos;
|
|
||||||
index_points.push_back(ip);
|
|
||||||
|
|
||||||
num_samples += frames;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update counters
|
|
||||||
// Make sure they're 64 bit aligned
|
|
||||||
data_left -= (chunk_size + 7) & ~7;
|
|
||||||
filepos += (chunk_size + 7) & ~7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
bool AreSamplesNativeEndian() const
|
|
||||||
{
|
|
||||||
// 8 bit samples don't consider endianness
|
|
||||||
if (bytes_per_sample < 2) return true;
|
|
||||||
// Otherwise test whether we're little endian
|
|
||||||
uint32_t testvalue = 0x008800ff;
|
|
||||||
return testvalue == Endian::LittleToMachine(testvalue);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param filename
|
|
||||||
///
|
|
||||||
AudioProvider *CreatePCMAudioProvider(const std::string &filename)
|
|
||||||
{
|
|
||||||
std::string msg;
|
|
||||||
try {
|
|
||||||
return new RiffWavPCMAudioProvider(filename);
|
|
||||||
}
|
|
||||||
catch (AudioOpenError const& err) {
|
|
||||||
msg = "RIFF PCM WAV audio provider: " + err.GetMessage();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return new Wave64AudioProvider(filename);
|
|
||||||
}
|
|
||||||
catch (AudioOpenError const& err) {
|
|
||||||
msg += "\nWave64 audio provider: " + err.GetMessage();
|
|
||||||
throw AudioOpenError(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
|
@ -1,117 +0,0 @@
|
||||||
// Copyright (c) 2007-2008, Niels Martin Hansen
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_pcm.h
|
|
||||||
/// @see audio_provider_pcm.cpp
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <vector>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "libmedia/audio.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class PCMAudioProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class PCMAudioProvider : public AudioProvider {
|
|
||||||
private:
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
HANDLE file_handle;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
HANDLE file_mapping;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
mutable void *current_mapping;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
mutable int64_t mapping_start;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
mutable size_t mapping_length;
|
|
||||||
#else
|
|
||||||
int file_handle;
|
|
||||||
mutable void *current_mapping;
|
|
||||||
mutable off_t mapping_start;
|
|
||||||
mutable size_t mapping_length;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
protected:
|
|
||||||
PCMAudioProvider(const std::string &filename); // Create base object and open the file mapping
|
|
||||||
virtual ~PCMAudioProvider(); // Closes the file mapping
|
|
||||||
char * EnsureRangeAccessible(int64_t range_start, int64_t range_length) const; // Ensure that the given range of bytes are accessible in the file mapping and return a pointer to the first byte of the requested range
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int64_t file_size; // Size of the opened file
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
struct IndexPoint {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int64_t start_byte;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int64_t start_sample;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int64_t num_samples;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
typedef std::vector<IndexPoint> IndexVector;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
IndexVector index_points;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual void GetAudio(void *buf, int64_t start, int64_t count) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Construct the right PCM audio provider (if any) for the file
|
|
||||||
AudioProvider *CreatePCMAudioProvider(const std::string &filename);
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
180
aegisub/libmedia/cache/audio_hd.cpp
vendored
180
aegisub/libmedia/cache/audio_hd.cpp
vendored
|
@ -1,180 +0,0 @@
|
||||||
// Copyright (c) 2005-2006, Rodrigo Braz Monteiro, Fredrik Mellbin
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_hd.cpp
|
|
||||||
/// @brief Caching audio provider using a file for backing
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libaegisub/io.h>
|
|
||||||
|
|
||||||
#include "audio_hd.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param source
|
|
||||||
///
|
|
||||||
HDAudioProvider::HDAudioProvider(AudioProvider *src, agi::ProgressSinkFactory *progress_factory) {
|
|
||||||
std::auto_ptr<AudioProvider> source(src);
|
|
||||||
// Copy parameters
|
|
||||||
bytes_per_sample = source->GetBytesPerSample();
|
|
||||||
num_samples = source->GetNumSamples();
|
|
||||||
channels = source->GetChannels();
|
|
||||||
sample_rate = source->GetSampleRate();
|
|
||||||
filename = source->GetFilename();
|
|
||||||
samples_native_endian = source->AreSamplesNativeEndian();
|
|
||||||
|
|
||||||
// Check free space
|
|
||||||
uint64_t freespace;
|
|
||||||
// XXX: fixme (add diskspace method to agi::util)
|
|
||||||
// if (wxGetDiskSpace(DiskCachePath(), NULL, &freespace)) {
|
|
||||||
// if (num_samples * channels * bytes_per_sample > freespace) {
|
|
||||||
// throw AudioOpenError("Not enough free disk space in " + STD_STR(DiskCachePath()) + " to cache the audio");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Open output file
|
|
||||||
diskCacheFilename = DiskCacheName();
|
|
||||||
// file_cache.Create(diskCacheFilename,true,wxS_DEFAULT);
|
|
||||||
io::Save file(diskCacheFilename);
|
|
||||||
std::ofstream& file_cache = file.Get();
|
|
||||||
// file_cache.Open(diskCacheFilename,wxFile::read_write);
|
|
||||||
if (!file_cache.is_open()) throw AudioOpenError("Unable to write to audio disk cache.");
|
|
||||||
|
|
||||||
// Start progress
|
|
||||||
ProgressSink *progress = progress_factory->create_progress_sink("Reading to Hard Disk cache");
|
|
||||||
|
|
||||||
volatile bool canceled = progress->get_cancelled();
|
|
||||||
|
|
||||||
// Write to disk
|
|
||||||
int block = 4096;
|
|
||||||
data = new char[block * channels * bytes_per_sample];
|
|
||||||
for (int64_t i=0;i<num_samples && !canceled; i+=block) {
|
|
||||||
if (block+i > num_samples) block = num_samples - i;
|
|
||||||
source->GetAudio(data,i,block);
|
|
||||||
file_cache.write(data,block * channels * bytes_per_sample);
|
|
||||||
progress->set_progress(i,num_samples);
|
|
||||||
}
|
|
||||||
file_cache.seekp(0);
|
|
||||||
|
|
||||||
// Finish
|
|
||||||
if (canceled) {
|
|
||||||
// file_cache.Close();
|
|
||||||
delete[] data;
|
|
||||||
throw agi::UserCancelException("Audio loading cancelled by user");
|
|
||||||
}
|
|
||||||
|
|
||||||
delete progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Destructor
|
|
||||||
///
|
|
||||||
HDAudioProvider::~HDAudioProvider() {
|
|
||||||
//XXX wxRemoveFile(diskCacheFilename);
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get audio
|
|
||||||
/// @param buf
|
|
||||||
/// @param start
|
|
||||||
/// @param count
|
|
||||||
///
|
|
||||||
void HDAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
|
|
||||||
// Requested beyond the length of audio
|
|
||||||
if (start+count > num_samples) {
|
|
||||||
int64_t oldcount = count;
|
|
||||||
count = num_samples-start;
|
|
||||||
if (count < 0) count = 0;
|
|
||||||
|
|
||||||
// Fill beyond with zero
|
|
||||||
if (bytes_per_sample == 1) {
|
|
||||||
char *temp = (char *) buf;
|
|
||||||
for (int i=count;i<oldcount;i++) {
|
|
||||||
temp[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bytes_per_sample == 2) {
|
|
||||||
short *temp = (short *) buf;
|
|
||||||
for (int i=count;i<oldcount;i++) {
|
|
||||||
temp[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count) {
|
|
||||||
diskmutex.Lock();
|
|
||||||
file_cache.seekp(start*bytes_per_sample);
|
|
||||||
file_cache.read((char*)buf,count*bytes_per_sample*channels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get disk cache path
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
std::string HDAudioProvider::DiskCachePath() {
|
|
||||||
// Default
|
|
||||||
/*
|
|
||||||
std::string path = lagi_wxString(OPT_GET("Audio/Cache/HD/Location")->GetString());
|
|
||||||
if (path == _T("default")) return StandardPaths::DecodePath(_T("?temp/"));
|
|
||||||
|
|
||||||
// Specified
|
|
||||||
return DecodeRelativePath(path,StandardPaths::DecodePath(_T("?user/")));
|
|
||||||
*/
|
|
||||||
return "XXX: fixme";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get disk cache filename
|
|
||||||
///
|
|
||||||
std::string HDAudioProvider::DiskCacheName() {
|
|
||||||
/*
|
|
||||||
XXX: fixme
|
|
||||||
// Get pattern
|
|
||||||
std::string pattern = lagi_wxString(OPT_GET("Audio/Cache/HD/Name")->GetString());
|
|
||||||
if (pattern.Find(_T("%02i")) == wxNOT_FOUND) pattern = _T("audio%02i.tmp");
|
|
||||||
|
|
||||||
// Try from 00 to 99
|
|
||||||
for (int i=0;i<100;i++) {
|
|
||||||
// File exists?
|
|
||||||
wxString curStringTry = DiskCachePath() + wxString::Format(pattern.c_str(),i);
|
|
||||||
if (!wxFile::Exists(curStringTry)) return curStringTry;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace media
|
|
78
aegisub/libmedia/cache/audio_hd.h
vendored
78
aegisub/libmedia/cache/audio_hd.h
vendored
|
@ -1,78 +0,0 @@
|
||||||
// Copyright (c) 2006, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_hd.h
|
|
||||||
/// @see audio_provider_hd.cpp
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libaegisub/mutex.h>
|
|
||||||
|
|
||||||
#include "libmedia/audio.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
/// DOCME
|
|
||||||
/// @class HDAudioProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class HDAudioProvider : public AudioProvider {
|
|
||||||
/// DOCME
|
|
||||||
mutable agi::Mutex diskmutex;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
mutable std::fstream file_cache;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
std::string diskCacheFilename;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
bool samples_native_endian;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
char *data;
|
|
||||||
|
|
||||||
static std::string DiskCachePath();
|
|
||||||
static std::string DiskCacheName();
|
|
||||||
|
|
||||||
public:
|
|
||||||
HDAudioProvider(AudioProvider *source, agi::ProgressSinkFactory *progress_factory);
|
|
||||||
~HDAudioProvider();
|
|
||||||
|
|
||||||
bool AreSamplesNativeEndian() const { return samples_native_endian; }
|
|
||||||
|
|
||||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace media
|
|
179
aegisub/libmedia/cache/audio_ram.cpp
vendored
179
aegisub/libmedia/cache/audio_ram.cpp
vendored
|
@ -1,179 +0,0 @@
|
||||||
// Copyright (c) 2005-2006, Rodrigo Braz Monteiro, Fredrik Mellbin
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_ram.cpp
|
|
||||||
/// @brief Caching audio provider using heap memory for backing
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "audio_ram.h"
|
|
||||||
//#include "dialog_progress.h"
|
|
||||||
//#include "frame_main.h"
|
|
||||||
//#include "main.h"
|
|
||||||
//#include "utils.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
#define CacheBits ((22))
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
#define CacheBlockSize ((1 << CacheBits))
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param source
|
|
||||||
///
|
|
||||||
RAMAudioProvider::RAMAudioProvider(AudioProvider *src) {
|
|
||||||
std::auto_ptr<AudioProvider> source(src);
|
|
||||||
// Init
|
|
||||||
blockcache = NULL;
|
|
||||||
blockcount = 0;
|
|
||||||
samples_native_endian = source->AreSamplesNativeEndian();
|
|
||||||
|
|
||||||
// Allocate cache
|
|
||||||
int64_t ssize = source->GetNumSamples() * source->GetBytesPerSample();
|
|
||||||
blockcount = (ssize + CacheBlockSize - 1) >> CacheBits;
|
|
||||||
blockcache = new char*[blockcount];
|
|
||||||
for (int i = 0; i < blockcount; i++) {
|
|
||||||
blockcache[i] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate cache blocks
|
|
||||||
try {
|
|
||||||
for (int i = 0; i < blockcount; i++) {
|
|
||||||
blockcache[i] = new char[std::min<size_t>(CacheBlockSize,ssize-i*CacheBlockSize)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
Clear();
|
|
||||||
throw AudioOpenError("Couldn't open audio, not enough ram available.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy parameters
|
|
||||||
bytes_per_sample = source->GetBytesPerSample();
|
|
||||||
num_samples = source->GetNumSamples();
|
|
||||||
channels = source->GetChannels();
|
|
||||||
sample_rate = source->GetSampleRate();
|
|
||||||
filename = source->GetFilename();
|
|
||||||
|
|
||||||
// Start progress
|
|
||||||
//XXX fixme:
|
|
||||||
volatile bool canceled = false;
|
|
||||||
// DialogProgress *progress = new DialogProgress(AegisubApp::Get()->frame,_("Load audio"),&canceled,_("Reading into RAM"),0,source->GetNumSamples());
|
|
||||||
// progress->Show();
|
|
||||||
// progress->SetProgress(0,1);
|
|
||||||
|
|
||||||
// Read cache
|
|
||||||
int readsize = CacheBlockSize / source->GetBytesPerSample();
|
|
||||||
for (int i=0;i<blockcount && !canceled; i++) {
|
|
||||||
source->GetAudio((char*)blockcache[i],i*readsize, i == blockcount-1 ? (source->GetNumSamples() - i*readsize) : readsize);
|
|
||||||
// progress->SetProgress(i,blockcount-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up progress
|
|
||||||
if (canceled) {
|
|
||||||
Clear();
|
|
||||||
throw agi::UserCancelException("Audio loading cancelled by user");
|
|
||||||
}
|
|
||||||
// progress->Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Destructor
|
|
||||||
///
|
|
||||||
RAMAudioProvider::~RAMAudioProvider() {
|
|
||||||
Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Clear
|
|
||||||
///
|
|
||||||
void RAMAudioProvider::Clear() {
|
|
||||||
// Free ram cache
|
|
||||||
if (blockcache) {
|
|
||||||
for (int i = 0; i < blockcount; i++) {
|
|
||||||
delete [] blockcache[i];
|
|
||||||
}
|
|
||||||
delete [] blockcache;
|
|
||||||
}
|
|
||||||
blockcache = NULL;
|
|
||||||
blockcount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get audio
|
|
||||||
/// @param buf
|
|
||||||
/// @param start
|
|
||||||
/// @param count
|
|
||||||
///
|
|
||||||
void RAMAudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
|
|
||||||
// Requested beyond the length of audio
|
|
||||||
if (start+count > num_samples) {
|
|
||||||
int64_t oldcount = count;
|
|
||||||
count = num_samples-start;
|
|
||||||
if (count < 0) count = 0;
|
|
||||||
|
|
||||||
// Fill beyond with zero
|
|
||||||
if (bytes_per_sample == 1) {
|
|
||||||
char *temp = (char *) buf;
|
|
||||||
for (int i=count;i<oldcount;i++) {
|
|
||||||
temp[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bytes_per_sample == 2) {
|
|
||||||
short *temp = (short *) buf;
|
|
||||||
for (int i=count;i<oldcount;i++) {
|
|
||||||
temp[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count) {
|
|
||||||
// Prepare copy
|
|
||||||
char *charbuf = (char *)buf;
|
|
||||||
int i = (start*bytes_per_sample) >> CacheBits;
|
|
||||||
int start_offset = (start*bytes_per_sample) & (CacheBlockSize-1);
|
|
||||||
int64_t bytesremaining = count*bytes_per_sample;
|
|
||||||
|
|
||||||
// Copy
|
|
||||||
while (bytesremaining) {
|
|
||||||
int readsize = std::min<int>(bytesremaining, CacheBlockSize - start_offset);
|
|
||||||
|
|
||||||
memcpy(charbuf,(char *)(blockcache[i++]+start_offset),readsize);
|
|
||||||
|
|
||||||
charbuf+=readsize;
|
|
||||||
|
|
||||||
start_offset=0;
|
|
||||||
bytesremaining-=readsize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace media
|
|
65
aegisub/libmedia/cache/audio_ram.h
vendored
65
aegisub/libmedia/cache/audio_ram.h
vendored
|
@ -1,65 +0,0 @@
|
||||||
// Copyright (c) 2006, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider_ram.h
|
|
||||||
/// @see audio_provider_ram.cpp
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "libmedia/audio.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
/// DOCME
|
|
||||||
/// @class RAMAudioProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class RAMAudioProvider : public AudioProvider {
|
|
||||||
/// DOCME
|
|
||||||
char** blockcache;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int blockcount;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
bool samples_native_endian;
|
|
||||||
|
|
||||||
void Clear();
|
|
||||||
|
|
||||||
public:
|
|
||||||
RAMAudioProvider(AudioProvider *source);
|
|
||||||
~RAMAudioProvider();
|
|
||||||
|
|
||||||
bool AreSamplesNativeEndian() const { return samples_native_endian; }
|
|
||||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace media
|
|
129
aegisub/libmedia/cache/video_cache.cpp
vendored
129
aegisub/libmedia/cache/video_cache.cpp
vendored
|
@ -1,129 +0,0 @@
|
||||||
// Copyright (c) 2008, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file video_provider_cache.cpp
|
|
||||||
/// @brief Aggregate video provider caching previously requested frames
|
|
||||||
/// @ingroup video_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
//#include "main.h"
|
|
||||||
|
|
||||||
#include "video_cache.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class CachedFrame
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
struct CachedFrame {
|
|
||||||
/// DOCME
|
|
||||||
AegiVideoFrame frame;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int n;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param parent
|
|
||||||
///
|
|
||||||
VideoProviderCache::VideoProviderCache(VideoProvider *parent)
|
|
||||||
: master(parent)
|
|
||||||
//XXX: , cacheMax(OPT_GET("Provider/Video/Cache/Size")->GetInt() << 20) // convert MB to bytes
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Destructor
|
|
||||||
///
|
|
||||||
VideoProviderCache::~VideoProviderCache() {
|
|
||||||
while (cache.size()) {
|
|
||||||
cache.front().frame.Clear();
|
|
||||||
cache.pop_front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get frame
|
|
||||||
/// @param n
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
const AegiVideoFrame VideoProviderCache::GetFrame(int n) {
|
|
||||||
// See if frame is cached
|
|
||||||
CachedFrame cached;
|
|
||||||
for (std::list<CachedFrame>::iterator cur=cache.begin();cur!=cache.end();cur++) {
|
|
||||||
cached = *cur;
|
|
||||||
if (cached.n == n) {
|
|
||||||
cache.erase(cur);
|
|
||||||
cache.push_back(cached);
|
|
||||||
return cached.frame;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not cached, retrieve it
|
|
||||||
const AegiVideoFrame frame = master->GetFrame(n);
|
|
||||||
const AegiVideoFrame *srcFrame = &frame;
|
|
||||||
|
|
||||||
// Cache frame
|
|
||||||
Cache(n,*srcFrame);
|
|
||||||
return *srcFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Add to cache
|
|
||||||
/// @param n
|
|
||||||
/// @param frame
|
|
||||||
void VideoProviderCache::Cache(int n,const AegiVideoFrame frame) {
|
|
||||||
// Cache full, use frame at front
|
|
||||||
if (GetCurCacheSize() >= cacheMax) {
|
|
||||||
cache.push_back(cache.front());
|
|
||||||
cache.pop_front();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache not full, insert new one
|
|
||||||
else {
|
|
||||||
cache.push_back(CachedFrame());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache
|
|
||||||
cache.back().n = n;
|
|
||||||
cache.back().frame.CopyFrom(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get the current size of the cache
|
|
||||||
/// @return Returns the size in bytes
|
|
||||||
unsigned VideoProviderCache::GetCurCacheSize() {
|
|
||||||
int sz = 0;
|
|
||||||
for (std::list<CachedFrame>::iterator i = cache.begin(); i != cache.end(); i++)
|
|
||||||
sz += i->frame.memSize;
|
|
||||||
return sz;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace media
|
|
86
aegisub/libmedia/cache/video_cache.h
vendored
86
aegisub/libmedia/cache/video_cache.h
vendored
|
@ -1,86 +0,0 @@
|
||||||
// Copyright (c) 2008, Rodrigo Braz Monteiro, Fredrik Mellbin
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file video_provider_cache.h
|
|
||||||
/// @see video_provider_cache.cpp
|
|
||||||
/// @ingroup video_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <list>
|
|
||||||
#include <memory>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "libmedia/video.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
struct CachedFrame;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class VideoProviderCache
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class VideoProviderCache : public VideoProvider {
|
|
||||||
/// DOCME
|
|
||||||
std::auto_ptr<VideoProvider> master;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
unsigned int cacheMax;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
std::list<CachedFrame> cache;
|
|
||||||
|
|
||||||
void Cache(int n,const AegiVideoFrame frame);
|
|
||||||
AegiVideoFrame GetCachedFrame(int n);
|
|
||||||
|
|
||||||
// Cache functions
|
|
||||||
unsigned GetCurCacheSize();
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Base methods
|
|
||||||
const AegiVideoFrame GetFrame(int n);
|
|
||||||
VideoProviderCache(VideoProvider *master);
|
|
||||||
virtual ~VideoProviderCache();
|
|
||||||
|
|
||||||
// Override the following methods:
|
|
||||||
virtual int GetPosition() const { return master->GetPosition(); }
|
|
||||||
virtual int GetFrameCount() const { return master->GetFrameCount(); }
|
|
||||||
virtual int GetWidth() const { return master->GetWidth(); }
|
|
||||||
virtual int GetHeight() const { return master->GetHeight(); }
|
|
||||||
virtual agi::vfr::Framerate GetFPS() const { return master->GetFPS(); }
|
|
||||||
virtual std::vector<int> GetKeyFrames() const { return master->GetKeyFrames(); }
|
|
||||||
virtual std::string GetWarning() const { return master->GetWarning(); }
|
|
||||||
virtual std::string GetDecoderName() const { return master->GetDecoderName(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace media
|
|
|
@ -1,193 +0,0 @@
|
||||||
// Copyright (c) 2005-2006, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider.cpp
|
|
||||||
/// @brief Baseclass for audio providers
|
|
||||||
/// @ingroup audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WITH_AVISYNTH
|
|
||||||
#include "../audio/avs_audio.h"
|
|
||||||
#endif
|
|
||||||
#include "../audio/convert.h"
|
|
||||||
#ifdef WITH_FFMPEGSOURCE
|
|
||||||
#include "../audio/ffms_audio.h"
|
|
||||||
#endif
|
|
||||||
#include "../cache/audio_hd.h"
|
|
||||||
#include "../cache/audio_ram.h"
|
|
||||||
#include "../audio/pcm.h"
|
|
||||||
|
|
||||||
|
|
||||||
//#include "compat.h"
|
|
||||||
//#include "main.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
///
|
|
||||||
AudioProvider::AudioProvider() : raw(NULL) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Destructor
|
|
||||||
///
|
|
||||||
AudioProvider::~AudioProvider() {
|
|
||||||
delete[] raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get audio with volume
|
|
||||||
/// @param buf
|
|
||||||
/// @param start
|
|
||||||
/// @param count
|
|
||||||
/// @param volume
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const {
|
|
||||||
try {
|
|
||||||
GetAudio(buf,start,count);
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
// FIXME: Poor error handling though better than none, to patch issue #800.
|
|
||||||
// Just return blank audio if real provider fails.
|
|
||||||
memset(buf, 0, count*bytes_per_sample);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (volume == 1.0) return;
|
|
||||||
|
|
||||||
if (bytes_per_sample == 2) {
|
|
||||||
// Read raw samples
|
|
||||||
short *buffer = (short*) buf;
|
|
||||||
int value;
|
|
||||||
|
|
||||||
// Modify
|
|
||||||
for (int64_t i=0;i<count;i++) {
|
|
||||||
value = (int)(buffer[i]*volume+0.5);
|
|
||||||
if (value < -0x8000) value = -0x8000;
|
|
||||||
if (value > 0x7FFF) value = 0x7FFF;
|
|
||||||
buffer[i] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get provider
|
|
||||||
/// @param filename
|
|
||||||
/// @param cache
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
AudioProvider *AudioProviderFactory::GetProvider(std::string filename, agi::ProgressSinkFactory *progress_factory, int cache) {
|
|
||||||
AudioProvider *provider = NULL;
|
|
||||||
bool found = false;
|
|
||||||
std::string msg;
|
|
||||||
|
|
||||||
//XXX if (!OPT_GET("Provider/Audio/PCM/Disable")->GetBool()) {
|
|
||||||
if (1) {
|
|
||||||
// Try a PCM provider first
|
|
||||||
try {
|
|
||||||
provider = CreatePCMAudioProvider(filename);
|
|
||||||
}
|
|
||||||
catch (agi::FileNotFoundError const& err) {
|
|
||||||
msg = "PCM audio provider: " + err.GetMessage() + " not found.\n";
|
|
||||||
}
|
|
||||||
catch (AudioOpenError const& err) {
|
|
||||||
found = true;
|
|
||||||
msg += err.GetMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!provider) {
|
|
||||||
//XXX std::vector<std::string> list = GetClasses(OPT_GET("Audio/Provider")->GetString());
|
|
||||||
std::vector<std::string> list = GetClasses("ffmpegsource");
|
|
||||||
|
|
||||||
if (list.empty()) throw AudioOpenError("No audio providers are available.");
|
|
||||||
|
|
||||||
for (unsigned int i=0;i<list.size();i++) {
|
|
||||||
try {
|
|
||||||
provider = Create(list[i], filename);
|
|
||||||
if (provider) break;
|
|
||||||
}
|
|
||||||
catch (agi::FileNotFoundError const& err) {
|
|
||||||
msg += list[i] + ": " + err.GetMessage() + " not found.\n";
|
|
||||||
}
|
|
||||||
catch (AudioOpenError const& err) {
|
|
||||||
found = true;
|
|
||||||
msg += list[i] + ": " + err.GetMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!provider) {
|
|
||||||
if (found) {
|
|
||||||
throw AudioOpenError(msg);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw agi::FileNotFoundError(filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool needsCache = provider->NeedsCache();
|
|
||||||
|
|
||||||
// Give it a converter if needed
|
|
||||||
if (provider->GetBytesPerSample() != 2 || provider->GetSampleRate() < 32000 || provider->GetChannels() != 1)
|
|
||||||
provider = CreateConvertAudioProvider(provider);
|
|
||||||
|
|
||||||
// Change provider to RAM/HD cache if needed
|
|
||||||
//XXX if (cache == -1) cache = OPT_GET("Audio/Cache/Type")->GetInt();
|
|
||||||
if (cache == -1) cache = 1;
|
|
||||||
if (!cache || !needsCache) {
|
|
||||||
return provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to RAM
|
|
||||||
if (cache == 1) return new RAMAudioProvider(provider);
|
|
||||||
|
|
||||||
// Convert to HD
|
|
||||||
if (cache == 2) return new HDAudioProvider(provider, progress_factory);
|
|
||||||
|
|
||||||
throw AudioOpenError("Unknown caching method");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Register all providers
|
|
||||||
///
|
|
||||||
void AudioProviderFactory::RegisterProviders() {
|
|
||||||
#ifdef WITH_AVISYNTH
|
|
||||||
Register<AvisynthAudioProvider>("Avisynth");
|
|
||||||
#endif
|
|
||||||
#ifdef WITH_FFMPEGSOURCE
|
|
||||||
Register<ffms::FFmpegSourceAudioProvider>("FFmpegSource");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> AudioProviderFactory::map *FactoryBase<AudioProvider *(*)(std::string)>::classes = NULL;
|
|
||||||
|
|
||||||
} // namespace media
|
|
|
@ -1,499 +0,0 @@
|
||||||
// Copyright (c) 2005, Niels Martin Hansen, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file colorspace.cpp
|
|
||||||
/// @brief Functions for converting colours between different representations
|
|
||||||
/// @ingroup utility
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "libmedia/colourspace.h"
|
|
||||||
//#include "utils.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @brief matrix from http://forum.doom9.org/showthread.php?p=684080#post684080
|
|
||||||
/// @param Y
|
|
||||||
/// @param U
|
|
||||||
/// @param V
|
|
||||||
/// @param R
|
|
||||||
/// @param G
|
|
||||||
/// @param B
|
|
||||||
///
|
|
||||||
void yuv_to_rgb(int Y, int U, int V, unsigned char *R, unsigned char *G, unsigned char *B)
|
|
||||||
{
|
|
||||||
U = U - 128;
|
|
||||||
V = V - 128;
|
|
||||||
*R = clip_colorval((Y*65536 + int(1.140*65536) * V) / 65536);
|
|
||||||
*G = clip_colorval((Y*65536 - int(0.395*65536) * U - int(0.581*65536) * V) / 65536);
|
|
||||||
*B = clip_colorval((Y*65536 + int(2.032*65536) * U ) / 65536);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief making every value into 0..255 range though algorithm from http://130.113.54.154/~monger/hsl-rgb.html
|
|
||||||
/// @param H
|
|
||||||
/// @param S
|
|
||||||
/// @param L
|
|
||||||
/// @param R
|
|
||||||
/// @param G
|
|
||||||
/// @param B
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
void hsl_to_rgb(int H, int S, int L, unsigned char *R, unsigned char *G, unsigned char *B)
|
|
||||||
{
|
|
||||||
if (S == 0) {
|
|
||||||
*R = L;
|
|
||||||
*G = L;
|
|
||||||
*B = L;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (L == 128 && S == 255) {
|
|
||||||
switch (H) {
|
|
||||||
case 0:
|
|
||||||
case 255: // actually this is wrong, since this is more like 359 degrees... but it's what you'd expect (sadly :)
|
|
||||||
*R = 255;
|
|
||||||
*G = 0;
|
|
||||||
*B = 0;
|
|
||||||
return;
|
|
||||||
case 43:
|
|
||||||
*R = 255;
|
|
||||||
*G = 255;
|
|
||||||
*B = 0;
|
|
||||||
return;
|
|
||||||
case 85:
|
|
||||||
*R = 0;
|
|
||||||
*G = 255;
|
|
||||||
*B = 0;
|
|
||||||
return;
|
|
||||||
case 128:
|
|
||||||
*R = 0;
|
|
||||||
*G = 255;
|
|
||||||
*B = 255;
|
|
||||||
return;
|
|
||||||
case 171:
|
|
||||||
*R = 0;
|
|
||||||
*G = 0;
|
|
||||||
*B = 255;
|
|
||||||
return;
|
|
||||||
case 213:
|
|
||||||
*R = 255;
|
|
||||||
*G = 0;
|
|
||||||
*B = 255;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float h, s, l, r, g, b;
|
|
||||||
h = H / 255.f;
|
|
||||||
s = S / 255.f;
|
|
||||||
l = L / 255.f;
|
|
||||||
|
|
||||||
float temp2;
|
|
||||||
if (l < .5) {
|
|
||||||
temp2 = l * (1. + s);
|
|
||||||
} else {
|
|
||||||
temp2 = l + s - l*s;
|
|
||||||
}
|
|
||||||
|
|
||||||
float temp1 = 2.f * l - temp2;
|
|
||||||
|
|
||||||
// assume h is in range [0;1]
|
|
||||||
float temp3[3];
|
|
||||||
temp3[0] = h + 1.f/3.f;
|
|
||||||
if (temp3[0] > 1.f) temp3[0] -= 1.f;
|
|
||||||
temp3[1] = h;
|
|
||||||
temp3[2] = h - 1.f/3.f;
|
|
||||||
if (temp3[2] < 0.f) temp3[2] += 1.f;
|
|
||||||
|
|
||||||
if (6.f * temp3[0] < 1.f)
|
|
||||||
r = temp1 + (temp2 - temp1) * 6.f * temp3[0];
|
|
||||||
else if (2.f * temp3[0] < 1.f)
|
|
||||||
r = temp2;
|
|
||||||
else if (3.f * temp3[0] < 2.f)
|
|
||||||
r = temp1 + (temp2 - temp1) * ((2.f/3.f) - temp3[0]) * 6.f;
|
|
||||||
else
|
|
||||||
r = temp1;
|
|
||||||
|
|
||||||
if (6.f * temp3[1] < 1.f)
|
|
||||||
g = temp1 + (temp2 - temp1) * 6.f * temp3[1];
|
|
||||||
else if (2.f * temp3[1] < 1.f)
|
|
||||||
g = temp2;
|
|
||||||
else if (3.f * temp3[1] < 2.f)
|
|
||||||
g = temp1 + (temp2 - temp1) * ((2.f/3.f) - temp3[1]) * 6.f;
|
|
||||||
else
|
|
||||||
g = temp1;
|
|
||||||
|
|
||||||
if (6.f * temp3[2] < 1.f)
|
|
||||||
b = temp1 + (temp2 - temp1) * 6.f * temp3[2];
|
|
||||||
else if (2.f * temp3[2] < 1.f)
|
|
||||||
b = temp2;
|
|
||||||
else if (3.f * temp3[2] < 2.f)
|
|
||||||
b = temp1 + (temp2 - temp1) * ((2.f/3.f) - temp3[2]) * 6.f;
|
|
||||||
else
|
|
||||||
b = temp1;
|
|
||||||
|
|
||||||
*R = clip_colorval((int)(r*255));
|
|
||||||
*G = clip_colorval((int)(g*255));
|
|
||||||
*B = clip_colorval((int)(b*255));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief the range for H is 0..255 instead of 0..359, so 60 degrees has been translated to 256/6 here formulas taken from wikipedia: http://en.wikipedia.org/wiki/HSV_color_space
|
|
||||||
/// @param H
|
|
||||||
/// @param S
|
|
||||||
/// @param V
|
|
||||||
/// @param R
|
|
||||||
/// @param G
|
|
||||||
/// @param B
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
void hsv_to_rgb(int H, int S, int V, unsigned char *R, unsigned char *G, unsigned char *B)
|
|
||||||
{
|
|
||||||
*R = *G = *B = 0;
|
|
||||||
|
|
||||||
// some special cases... oh yeah baby!
|
|
||||||
if (S == 255) {
|
|
||||||
switch (H) {
|
|
||||||
case 0:
|
|
||||||
case 255: // actually this is wrong, since this is more like 359 degrees... but it's what you'd expect (sadly :)
|
|
||||||
*R = V;
|
|
||||||
*G = 0;
|
|
||||||
*B = 0;
|
|
||||||
return;
|
|
||||||
case 43:
|
|
||||||
*R = V;
|
|
||||||
*G = V;
|
|
||||||
*B = 0;
|
|
||||||
return;
|
|
||||||
case 85:
|
|
||||||
*R = 0;
|
|
||||||
*G = V;
|
|
||||||
*B = 0;
|
|
||||||
return;
|
|
||||||
case 128:
|
|
||||||
*R = 0;
|
|
||||||
*G = V;
|
|
||||||
*B = V;
|
|
||||||
return;
|
|
||||||
case 171:
|
|
||||||
*R = 0;
|
|
||||||
*G = 0;
|
|
||||||
*B = V;
|
|
||||||
return;
|
|
||||||
case 213:
|
|
||||||
*R = V;
|
|
||||||
*G = 0;
|
|
||||||
*B = V;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cap values
|
|
||||||
unsigned int h = H * 360;
|
|
||||||
unsigned int s = clip_colorval(S)*256;
|
|
||||||
unsigned int v = clip_colorval(V)*256;
|
|
||||||
|
|
||||||
// Saturation is zero, make grey
|
|
||||||
if (S == 0) {
|
|
||||||
*R = V;
|
|
||||||
*G = V;
|
|
||||||
*B = V;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int r, g, b;
|
|
||||||
|
|
||||||
// Else, calculate color
|
|
||||||
unsigned int Hi = h / 60 / 256;
|
|
||||||
unsigned int f = h / 60 - Hi * 256;
|
|
||||||
unsigned int p = v * (65535 - s) / 65536;
|
|
||||||
unsigned int q = v * (65535 - (f * s)/256) / 65536;
|
|
||||||
unsigned int t = v * (65535 - ((255 - f) * s)/256) / 65536;
|
|
||||||
switch (Hi) {
|
|
||||||
case 0:
|
|
||||||
r = v;
|
|
||||||
g = t;
|
|
||||||
b = p;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
r = q;
|
|
||||||
g = v;
|
|
||||||
b = p;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
r = p;
|
|
||||||
g = v;
|
|
||||||
b = t;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
r = p;
|
|
||||||
g = q;
|
|
||||||
b = v;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
r = t;
|
|
||||||
g = p;
|
|
||||||
b = v;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
default:
|
|
||||||
r = v;
|
|
||||||
g = p;
|
|
||||||
b = q;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
*R = clip_colorval(r/256);
|
|
||||||
*G = clip_colorval(g/256);
|
|
||||||
*B = clip_colorval(b/256);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief matrix from http://forum.doom9.org/showthread.php?p=684080#post684080
|
|
||||||
/// @param R
|
|
||||||
/// @param G
|
|
||||||
/// @param B
|
|
||||||
/// @param Y
|
|
||||||
/// @param U
|
|
||||||
/// @param V
|
|
||||||
///
|
|
||||||
void rgb_to_yuv(int R, int G, int B, unsigned char *Y, unsigned char *U, unsigned char *V)
|
|
||||||
{
|
|
||||||
*Y = clip_colorval(( int(0.299*65536) * R + int(0.587*65536) * G + int(0.114*65536) * B) / 65536);
|
|
||||||
*U = clip_colorval((-int(0.147*65536) * R - int(0.289*65536) * G + int(0.436*65536) * B) / 65536 + 128);
|
|
||||||
*V = clip_colorval(( int(0.615*65536) * R - int(0.515*65536) * G - int(0.100*65536) * B) / 65536 + 128);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief still keeping everything integer also from http://130.113.54.154/~monger/hsl-rgb.html
|
|
||||||
/// @param R
|
|
||||||
/// @param G
|
|
||||||
/// @param B
|
|
||||||
/// @param H
|
|
||||||
/// @param S
|
|
||||||
/// @param L
|
|
||||||
///
|
|
||||||
void rgb_to_hsl(int R, int G, int B, unsigned char *H, unsigned char *S, unsigned char *L)
|
|
||||||
{
|
|
||||||
float r = R/255.f, g = G/255.f, b = B/255.f;
|
|
||||||
float h, s, l;
|
|
||||||
|
|
||||||
float maxrgb = std::max(r, std::max(g, b)), minrgb = std::min(r, std::min(g, b));
|
|
||||||
|
|
||||||
l = (minrgb + maxrgb) / 2;
|
|
||||||
|
|
||||||
if (minrgb == maxrgb) {
|
|
||||||
h = 0;
|
|
||||||
s = 0;
|
|
||||||
} else {
|
|
||||||
if (l < 0.5) {
|
|
||||||
s = (maxrgb - minrgb) / (maxrgb + minrgb);
|
|
||||||
} else {
|
|
||||||
s = (maxrgb - minrgb) / (2.f - maxrgb - minrgb);
|
|
||||||
}
|
|
||||||
if (r == maxrgb) {
|
|
||||||
h = (g-b) / (maxrgb-minrgb) + 0;
|
|
||||||
} else if (g == maxrgb) {
|
|
||||||
h = (b-r) / (maxrgb-minrgb) + 2;
|
|
||||||
} else { // if b == maxrgb
|
|
||||||
h = (r-g) / (maxrgb-minrgb) + 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (h < 0) h += 6;
|
|
||||||
if (h >= 6) h -= 6;
|
|
||||||
|
|
||||||
*H = clip_colorval(int(h*256/6));
|
|
||||||
*S = clip_colorval(int(s*255));
|
|
||||||
*L = clip_colorval(int(l*255));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief formulas from http://en.wikipedia.org/wiki/HSV_color_space
|
|
||||||
/// @param R
|
|
||||||
/// @param G
|
|
||||||
/// @param B
|
|
||||||
/// @param H
|
|
||||||
/// @param S
|
|
||||||
/// @param V
|
|
||||||
///
|
|
||||||
void rgb_to_hsv(int R, int G, int B, unsigned char *H, unsigned char *S, unsigned char *V)
|
|
||||||
{
|
|
||||||
float r = R/255.f, g = G/255.f, b = B/255.f;
|
|
||||||
float h, s, v;
|
|
||||||
|
|
||||||
float maxrgb = std::max(r, std::max(g, b)), minrgb = std::min(r, std::min(g, b));
|
|
||||||
|
|
||||||
v = maxrgb;
|
|
||||||
|
|
||||||
if (maxrgb < .001f) {
|
|
||||||
s = 1;
|
|
||||||
} else {
|
|
||||||
s = (maxrgb - minrgb) / maxrgb;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minrgb == maxrgb) {
|
|
||||||
h = 0;
|
|
||||||
} else if (maxrgb == r) {
|
|
||||||
h = (g-b) / (maxrgb-minrgb) + 0;
|
|
||||||
} else if (maxrgb == g) {
|
|
||||||
h = (b-r) / (maxrgb-minrgb) + 2;
|
|
||||||
} else { // if maxrgb == b
|
|
||||||
h = (r-g) / (maxrgb-minrgb) + 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (h < 0) h += 6;
|
|
||||||
if (h >= 6) h -= 6;
|
|
||||||
|
|
||||||
*H = clip_colorval(int(h*256/6));
|
|
||||||
*S = clip_colorval(int(s*255));
|
|
||||||
*V = clip_colorval(int(v*255));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param iH
|
|
||||||
/// @param iS
|
|
||||||
/// @param iV
|
|
||||||
/// @param oH
|
|
||||||
/// @param oS
|
|
||||||
/// @param oL
|
|
||||||
///
|
|
||||||
void hsv_to_hsl(int iH, int iS, int iV, unsigned char *oH, unsigned char *oS, unsigned char *oL)
|
|
||||||
{
|
|
||||||
int p = iV * (255 - iS);
|
|
||||||
*oH = iH;
|
|
||||||
*oL = clip_colorval((p + iV*255)/255/2);
|
|
||||||
if (*oL == 0) {
|
|
||||||
*oS = iS; // oS is actually undefined, so any value should work ;)
|
|
||||||
} else if (*oL <= 128) {
|
|
||||||
*oS = clip_colorval((iV*255 - p) / (2 * *oL));
|
|
||||||
} else {
|
|
||||||
*oS = clip_colorval((iV*255 - p) / (511 - 2 * *oL));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param iH
|
|
||||||
/// @param iS
|
|
||||||
/// @param iL
|
|
||||||
/// @param oH
|
|
||||||
/// @param oS
|
|
||||||
/// @param oV
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
void hsl_to_hsv(int iH, int iS, int iL, unsigned char *oH, unsigned char *oS, unsigned char *oV)
|
|
||||||
{
|
|
||||||
*oH = iH;
|
|
||||||
|
|
||||||
if (iS == 0) {
|
|
||||||
*oS = 0;
|
|
||||||
*oV = iL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iL < 128) {
|
|
||||||
*oV = iL * (255 + iS) / 255;
|
|
||||||
*oS = 2 * 255 * iS / (255 + iS);
|
|
||||||
} else {
|
|
||||||
*oV = (iL*255 + iS*255 - iL*iS)/255;
|
|
||||||
*oS = 2 * 255 * iS * (255 - iL) / (iL*255 + iS*255 - iL*iS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param color
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
std::string color_to_html(agi::Colour color)
|
|
||||||
{
|
|
||||||
// return wxString::Format(_T("#%02X%02X%02X"), color.Red(), color.Green(), color.Blue());
|
|
||||||
return "broken";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param html
|
|
||||||
///
|
|
||||||
std::string html_to_color(agi::Colour html)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
html.Trim(true);
|
|
||||||
html.Trim(false);
|
|
||||||
if (html.StartsWith(_T("#"))) {
|
|
||||||
html.Remove(0, 1);
|
|
||||||
}
|
|
||||||
if (html.size() == 6) {
|
|
||||||
// 8 bit per channel
|
|
||||||
long r, g, b;
|
|
||||||
wxString sr, sg, sb;
|
|
||||||
sr = html.Mid(0, 2);
|
|
||||||
sg = html.Mid(2, 2);
|
|
||||||
sb = html.Mid(4, 2);
|
|
||||||
if (sr.ToLong(&r, 16) && sg.ToLong(&g, 16) && sb.ToLong(&b, 16)) {
|
|
||||||
return wxColour(r, g, b);
|
|
||||||
} else {
|
|
||||||
return wxColour(*wxBLACK);
|
|
||||||
}
|
|
||||||
} else if (html.size() == 3) {
|
|
||||||
// 4 bit per channel
|
|
||||||
long r, g, b;
|
|
||||||
wxString sr, sg, sb;
|
|
||||||
sr = html.Mid(0, 1);
|
|
||||||
sg = html.Mid(1, 1);
|
|
||||||
sb = html.Mid(2, 1);
|
|
||||||
if (sr.ToLong(&r, 16) && sg.ToLong(&g, 16) && sb.ToLong(&b, 16)) {
|
|
||||||
return wxColour(r*16+r, g*16+g, b*16+b);
|
|
||||||
} else {
|
|
||||||
return wxColour(*wxBLACK);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// only care about valid colors
|
|
||||||
return wxColour(*wxBLACK);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return "XXX: fixme";
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace media
|
|
|
@ -1,270 +0,0 @@
|
||||||
// Copyright (c) 2008-2009, Karl Blomster
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file ffmpegsource_common.cpp
|
|
||||||
/// @brief Shared code for ffms video and audio providers
|
|
||||||
/// @ingroup video_input audio_input ffms
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifdef WITH_FFMPEGSOURCE
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
//#include <wx/dir.h>
|
|
||||||
//#include <wx/choicdlg.h> // Keep this last so wxUSE_CHOICEDLG is set.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libaegisub/log.h>
|
|
||||||
|
|
||||||
#include "ffms_common.h"
|
|
||||||
|
|
||||||
//#include "compat.h"
|
|
||||||
//#include "ffmpegsource_common.h"
|
|
||||||
//#include "frame_main.h"
|
|
||||||
//#include "main.h"
|
|
||||||
//#include "md5.h"
|
|
||||||
//#include "standard_paths.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
namespace ffms {
|
|
||||||
|
|
||||||
/// @brief Callback function that updates the indexing progress dialog
|
|
||||||
/// @param Current The current file positition in bytes
|
|
||||||
/// @param Total The total file size in bytes
|
|
||||||
/// @param Private A pointer to the progress dialog box to update
|
|
||||||
/// @return Returns non-0 if indexing is cancelled, 0 otherwise.
|
|
||||||
///
|
|
||||||
int FFMS_CC FFmpegSourceProvider::UpdateIndexingProgress(int64_t Current, int64_t Total, void *Private) {
|
|
||||||
IndexingProgressDialog *Progress = (IndexingProgressDialog *)Private;
|
|
||||||
|
|
||||||
if (Progress->IndexingCanceled)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
// no one cares about a little bit of a rounding error here anyway
|
|
||||||
// Progress->ProgressDialog->SetProgress(((int64_t)1000*Current)/Total, 1000);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Does indexing of a source file
|
|
||||||
/// @param Indexer A pointer to the indexer object representing the file to be indexed
|
|
||||||
/// @param CacheName The filename of the output index file
|
|
||||||
/// @param Trackmask A binary mask of the track numbers to index
|
|
||||||
/// @param IgnoreDecodeErrors True if audio decoding errors will be tolerated, false otherwise
|
|
||||||
/// @return Returns the index object on success, NULL otherwise
|
|
||||||
///
|
|
||||||
FFMS_Index *FFmpegSourceProvider::DoIndexing(FFMS_Indexer *Indexer, const std::string &CacheName, int Trackmask, FFMS_IndexErrorHandling IndexEH) {
|
|
||||||
char FFMSErrMsg[1024];
|
|
||||||
FFMS_ErrorInfo ErrInfo;
|
|
||||||
ErrInfo.Buffer = FFMSErrMsg;
|
|
||||||
ErrInfo.BufferSize = sizeof(FFMSErrMsg);
|
|
||||||
ErrInfo.ErrorType = FFMS_ERROR_SUCCESS;
|
|
||||||
ErrInfo.SubType = FFMS_ERROR_SUCCESS;
|
|
||||||
std::string MsgString;
|
|
||||||
/*
|
|
||||||
// set up progress dialog callback
|
|
||||||
IndexingProgressDialog Progress;
|
|
||||||
Progress.IndexingCanceled = false;
|
|
||||||
Progress.ProgressDialog = new DialogProgress(AegisubApp::Get()->frame,
|
|
||||||
_("Indexing"), &Progress.IndexingCanceled,
|
|
||||||
_("Reading timecodes and frame/sample data"), 0, 1);
|
|
||||||
Progress.ProgressDialog->Show();
|
|
||||||
Progress.ProgressDialog->SetProgress(0,1);
|
|
||||||
|
|
||||||
// index all audio tracks
|
|
||||||
FFMS_Index *Index = FFMS_DoIndexing(Indexer, Trackmask, FFMS_TRACKMASK_NONE, NULL, NULL, IndexEH,
|
|
||||||
FFmpegSourceProvider::UpdateIndexingProgress, &Progress, &ErrInfo);
|
|
||||||
Progress.ProgressDialog->Destroy();
|
|
||||||
if (Progress.IndexingCanceled) {
|
|
||||||
throw agi::UserCancelException("indexing cancelled by user");
|
|
||||||
}
|
|
||||||
if (Index == NULL) {
|
|
||||||
MsgString.append("Failed to index: ").append(ErrInfo.Buffer);
|
|
||||||
throw MsgString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write index to disk for later use
|
|
||||||
// ignore write errors for now
|
|
||||||
FFMS_WriteIndex(CacheName.c_str(), Index, &ErrInfo);
|
|
||||||
// if (FFMS_WriteIndex(CacheName.char_str(), Index, FFMSErrMsg, MsgSize)) {
|
|
||||||
// wxString temp(FFMSErrMsg, wxConvUTF8);
|
|
||||||
// MsgString << _T("Failed to write index: ") << temp;
|
|
||||||
// throw MsgString;
|
|
||||||
// }
|
|
||||||
*/
|
|
||||||
FFMS_Index *Index;
|
|
||||||
return Index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Finds all tracks of the given type and return their track numbers and respective codec names
|
|
||||||
/// @param Indexer The indexer object representing the source file
|
|
||||||
/// @param Type The track type to look for
|
|
||||||
/// @return Returns a std::map with the track numbers as keys and the codec names as values.
|
|
||||||
std::map<int,std::string> FFmpegSourceProvider::GetTracksOfType(FFMS_Indexer *Indexer, FFMS_TrackType Type) {
|
|
||||||
std::map<int,std::string> TrackList;
|
|
||||||
int NumTracks = FFMS_GetNumTracksI(Indexer);
|
|
||||||
|
|
||||||
for (int i=0; i<NumTracks; i++) {
|
|
||||||
if (FFMS_GetTrackTypeI(Indexer, i) == Type) {
|
|
||||||
std::string CodecName(FFMS_GetCodecNameI(Indexer, i));
|
|
||||||
TrackList.insert(std::pair<int,std::string>(i, CodecName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TrackList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Ask user for which track he wants to load
|
|
||||||
/// @param TrackList A std::map with the track numbers as keys and codec names as values
|
|
||||||
/// @param Type The track type to ask about
|
|
||||||
/// @return Returns the track number chosen (an integer >= 0) on success, or a negative integer if the user cancelled.
|
|
||||||
int FFmpegSourceProvider::AskForTrackSelection(const std::map<int,std::string> &TrackList, FFMS_TrackType Type) {
|
|
||||||
/*
|
|
||||||
std::vector<int> TrackNumbers;
|
|
||||||
wxArrayString Choices;
|
|
||||||
std::string TypeName = "";
|
|
||||||
if (Type == FFMS_TYPE_VIDEO)
|
|
||||||
TypeName = _("video");
|
|
||||||
else if (Type == FFMS_TYPE_AUDIO)
|
|
||||||
TypeName = _("audio");
|
|
||||||
|
|
||||||
for (std::map<int,std::string>::const_iterator i = TrackList.begin(); i != TrackList.end(); i++) {
|
|
||||||
istream str;
|
|
||||||
//XXX Choices.Add(ng::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];
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Set ffms2 log level according to setting in config.dat
|
|
||||||
void FFmpegSourceProvider::SetLogLevel() {
|
|
||||||
/*
|
|
||||||
wxString LogLevel = lagi_wxString(OPT_GET("Provider/FFmpegSource/Log Level")->GetString());
|
|
||||||
if (!LogLevel.CmpNoCase("panic"))
|
|
||||||
FFMS_SetLogLevel(FFMS_LOG_PANIC);
|
|
||||||
else if (!LogLevel.CmpNoCase("fatal"))
|
|
||||||
FFMS_SetLogLevel(FFMS_LOG_FATAL);
|
|
||||||
else if (!LogLevel.CmpNoCase("error"))
|
|
||||||
FFMS_SetLogLevel(FFMS_LOG_ERROR);
|
|
||||||
else if (!LogLevel.CmpNoCase("warning"))
|
|
||||||
FFMS_SetLogLevel(FFMS_LOG_WARNING);
|
|
||||||
else if (!LogLevel.CmpNoCase("info"))
|
|
||||||
FFMS_SetLogLevel(FFMS_LOG_INFO);
|
|
||||||
else if (!LogLevel.CmpNoCase("verbose"))
|
|
||||||
FFMS_SetLogLevel(FFMS_LOG_VERBOSE);
|
|
||||||
else if (!LogLevel.CmpNoCase("debug"))
|
|
||||||
FFMS_SetLogLevel(FFMS_LOG_DEBUG);
|
|
||||||
else */
|
|
||||||
FFMS_SetLogLevel(FFMS_LOG_QUIET);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FFMS_IndexErrorHandling FFmpegSourceProvider::GetErrorHandlingMode() {
|
|
||||||
// std::string Mode = lagi_wxString(OPT_GET("Provider/Audio/FFmpegSource/Decode Error Handling")->GetString());
|
|
||||||
std::string mode = "ignore";
|
|
||||||
|
|
||||||
if (mode == "ignore")
|
|
||||||
return FFMS_IEH_IGNORE;
|
|
||||||
else if (mode == "clear")
|
|
||||||
return FFMS_IEH_CLEAR_TRACK;
|
|
||||||
else if (mode == "stop")
|
|
||||||
return FFMS_IEH_STOP_TRACK;
|
|
||||||
else if (mode == "abort")
|
|
||||||
return FFMS_IEH_ABORT;
|
|
||||||
else
|
|
||||||
return FFMS_IEH_STOP_TRACK; // questionable default?
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
/// @brief Generates an unique name for the ffms2 index file and prepares the cache folder if it doesn't exist
|
|
||||||
/// @param filename The name of the source file
|
|
||||||
/// @return Returns the generated filename.
|
|
||||||
std::string FFmpegSourceProvider::GetCacheFilename(const std::string& _filename)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
// Get the size of the file to be hashed
|
|
||||||
wxFileOffset len = 0;
|
|
||||||
{
|
|
||||||
wxFile file(_filename,wxFile::read);
|
|
||||||
if (file.IsOpened()) len = file.Length();
|
|
||||||
}
|
|
||||||
|
|
||||||
wxFileName filename(_filename);
|
|
||||||
|
|
||||||
// Generate string to be hashed
|
|
||||||
// std::string toHash = wxString::Format("%s %" PRId64 " %" PRId64, filename.GetFullName(), len, (int64_t)filename.GetModificationTime().GetTicks());
|
|
||||||
std::string toHash = "XXX";
|
|
||||||
|
|
||||||
// Get the MD5 digest of the string
|
|
||||||
const wchar_t *tmp = toHash.wc_str();
|
|
||||||
md5_state_t state;
|
|
||||||
md5_byte_t digest[16];
|
|
||||||
md5_init(&state);
|
|
||||||
md5_append(&state,(md5_byte_t*)tmp,toHash.Length()*sizeof(wchar_t));
|
|
||||||
md5_finish(&state,digest);
|
|
||||||
|
|
||||||
// Generate the filename
|
|
||||||
unsigned int *md5 = (unsigned int*) digest;
|
|
||||||
wxString result = wxString::Format("?user/ffms2cache/%08X%08X%08X%08X.ffindex",md5[0],md5[1],md5[2],md5[3]);
|
|
||||||
result = StandardPaths::DecodePath(result);
|
|
||||||
|
|
||||||
// Ensure that folder exists
|
|
||||||
wxFileName fn(result);
|
|
||||||
wxString dir = fn.GetPath();
|
|
||||||
if (!wxFileName::DirExists(dir)) {
|
|
||||||
wxFileName::Mkdir(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
wxFileName dirfn(dir);
|
|
||||||
return dirfn.GetShortPath() + "/" + fn.GetFullName();
|
|
||||||
*/
|
|
||||||
return "XXX";
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // WITH_FFMPEGSOURCE
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace ffms
|
|
||||||
} // namespace media
|
|
|
@ -1,96 +0,0 @@
|
||||||
// Copyright (c) 2008-2009, Karl Blomster
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file ffmpegsource_common.h
|
|
||||||
/// @see ffmpegsource_common.cpp
|
|
||||||
/// @ingroup video_input audio_input ffms
|
|
||||||
///
|
|
||||||
|
|
||||||
#ifdef WITH_FFMPEGSOURCE
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
//#include <wx/filename.h>
|
|
||||||
//#include <wx/thread.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "../../libffms/include/ffms.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
namespace ffms {
|
|
||||||
|
|
||||||
//#include "dialog_progress.h"
|
|
||||||
|
|
||||||
/// Index all tracks
|
|
||||||
#define FFMS_TRACKMASK_ALL -1
|
|
||||||
/// Index no tracks
|
|
||||||
#define FFMS_TRACKMASK_NONE 0
|
|
||||||
|
|
||||||
/// @class FFmpegSourceProvider
|
|
||||||
/// @brief Base class for FFMS2 source providers; contains common functions etc
|
|
||||||
class FFmpegSourceProvider {
|
|
||||||
public:
|
|
||||||
/// Logging level constants from avutil/log.h
|
|
||||||
enum FFMS_LogLevel {
|
|
||||||
/// nothing printed
|
|
||||||
FFMS_LOG_QUIET = -8,
|
|
||||||
FFMS_LOG_PANIC = 0,
|
|
||||||
FFMS_LOG_FATAL = 8,
|
|
||||||
FFMS_LOG_ERROR = 16,
|
|
||||||
FFMS_LOG_WARNING = 24,
|
|
||||||
FFMS_LOG_INFO = 32,
|
|
||||||
FFMS_LOG_VERBOSE = 40,
|
|
||||||
FFMS_LOG_DEBUG = 48,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Indexing progress report dialog
|
|
||||||
struct IndexingProgressDialog {
|
|
||||||
volatile bool IndexingCanceled;
|
|
||||||
// DialogProgress *ProgressDialog;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int FFMS_CC UpdateIndexingProgress(int64_t Current, int64_t Total, void *Private);
|
|
||||||
|
|
||||||
FFMS_Index *DoIndexing(FFMS_Indexer *Indexer, const std::string& Cachename, int Trackmask, FFMS_IndexErrorHandling IndexEH);
|
|
||||||
std::map<int,std::string> GetTracksOfType(FFMS_Indexer *Indexer, FFMS_TrackType Type);
|
|
||||||
int AskForTrackSelection(const std::map<int,std::string>& TrackList, FFMS_TrackType Type);
|
|
||||||
std::string GetCacheFilename(const std::string& filename);
|
|
||||||
void SetLogLevel();
|
|
||||||
FFMS_IndexErrorHandling GetErrorHandlingMode();
|
|
||||||
|
|
||||||
virtual ~FFmpegSourceProvider() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* WITH_FFMPEGSOURCE */
|
|
||||||
|
|
||||||
} // namespace ffms
|
|
||||||
} // namespace media
|
|
|
@ -1,90 +0,0 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file subtitles_provider.cpp
|
|
||||||
/// @brief Base class for subtitle renderers
|
|
||||||
/// @ingroup subtitle_rendering
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "compat.h"
|
|
||||||
#include "main.h"
|
|
||||||
#ifdef WITH_CSRI
|
|
||||||
#include "subtitles_provider_csri.h"
|
|
||||||
#endif
|
|
||||||
#ifdef WITH_LIBASS
|
|
||||||
#include "subtitles_provider_libass.h"
|
|
||||||
#endif
|
|
||||||
#if !defined(WITH_CSRI) && !defined(WITH_LIBASS)
|
|
||||||
#include "include/aegisub/subtitles_provider.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// @brief Get provider
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
SubtitlesProvider* SubtitlesProviderFactory::GetProvider() {
|
|
||||||
std::vector<std::string> list = GetClasses(OPT_GET("Subtitle/Provider")->GetString());
|
|
||||||
if (list.empty()) throw _T("No subtitle providers are available.");
|
|
||||||
|
|
||||||
// Get provider
|
|
||||||
wxString error;
|
|
||||||
for (unsigned int i=0;i<list.size();i++) {
|
|
||||||
try {
|
|
||||||
size_t pos = list[i].find('/');
|
|
||||||
std::string name = list[i].substr(0, pos);
|
|
||||||
std::string subType = pos < list[i].size() - 1 ? list[i].substr(pos + 1) : "";
|
|
||||||
SubtitlesProvider *provider = Create(list[i], subType);
|
|
||||||
if (provider) return provider;
|
|
||||||
}
|
|
||||||
catch (agi::UserCancelException const&) { throw; }
|
|
||||||
catch (wxString err) { error += list[i] + _T(" factory: ") + err + _T("\n"); }
|
|
||||||
catch (const wxChar *err) { error += list[i] + _T(" factory: ") + wxString(err) + _T("\n"); }
|
|
||||||
catch (...) { error += list[i] + _T(" factory: Unknown error\n"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failed
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Register providers
|
|
||||||
///
|
|
||||||
void SubtitlesProviderFactory::RegisterProviders() {
|
|
||||||
#ifdef WITH_CSRI
|
|
||||||
Register<CSRISubtitlesProvider>("CSRI", false, CSRISubtitlesProvider::GetSubTypes());
|
|
||||||
#endif
|
|
||||||
#ifdef WITH_LIBASS
|
|
||||||
Register<LibassSubtitlesProvider>("libass");
|
|
||||||
LibassSubtitlesProvider::CacheFonts();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> SubtitlesProviderFactory::map *FactoryBase<SubtitlesProvider *(*)(std::string)>::classes = NULL;
|
|
|
@ -1,116 +0,0 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro <amz@aegisub.org>
|
|
||||||
//
|
|
||||||
// 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 video_frame.cpp
|
|
||||||
/// @brief Wrapper around a frame of video data
|
|
||||||
/// @ingroup video
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "libmedia/video_frame.h"
|
|
||||||
|
|
||||||
#ifndef LAGI_PRE
|
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
void AegiVideoFrame::Reset() {
|
|
||||||
// Zero variables
|
|
||||||
data = 0;
|
|
||||||
pitch = 0;
|
|
||||||
memSize = 0;
|
|
||||||
w = 0;
|
|
||||||
h = 0;
|
|
||||||
|
|
||||||
// Set properties
|
|
||||||
flipped = false;
|
|
||||||
invertChannels = true;
|
|
||||||
ownMem = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AegiVideoFrame::AegiVideoFrame() {
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Create a solid black frame of the request size and format
|
|
||||||
/// @param width
|
|
||||||
/// @param height
|
|
||||||
AegiVideoFrame::AegiVideoFrame(unsigned int width, unsigned int height) {
|
|
||||||
assert(width > 0 && width < 10000);
|
|
||||||
assert(height > 0 && height < 10000);
|
|
||||||
|
|
||||||
Reset();
|
|
||||||
|
|
||||||
// Set format
|
|
||||||
w = width;
|
|
||||||
h = height;
|
|
||||||
pitch = w * GetBpp();
|
|
||||||
|
|
||||||
Allocate();
|
|
||||||
memset(data, 0, pitch * height);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AegiVideoFrame::Allocate() {
|
|
||||||
assert(pitch > 0 && pitch < 10000);
|
|
||||||
assert(w > 0 && w < 10000);
|
|
||||||
assert(h > 0 && h < 10000);
|
|
||||||
|
|
||||||
unsigned int size = pitch * h;
|
|
||||||
|
|
||||||
// Reallocate, if necessary
|
|
||||||
if (memSize != size || !ownMem) {
|
|
||||||
if (ownMem) {
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
data = new unsigned char[size];
|
|
||||||
memSize = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
ownMem = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AegiVideoFrame::Clear() {
|
|
||||||
if (ownMem) delete[] data;
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AegiVideoFrame::CopyFrom(const AegiVideoFrame &source) {
|
|
||||||
w = source.w;
|
|
||||||
h = source.h;
|
|
||||||
pitch = source.pitch;
|
|
||||||
Allocate();
|
|
||||||
memcpy(data, source.data, memSize);
|
|
||||||
flipped = source.flipped;
|
|
||||||
invertChannels = source.invertChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AegiVideoFrame::SetTo(const unsigned char *source, unsigned int width, unsigned int height, unsigned int pitch) {
|
|
||||||
assert(pitch > 0 && pitch < 10000);
|
|
||||||
assert(width > 0 && width < 10000);
|
|
||||||
assert(height > 0 && height < 10000);
|
|
||||||
|
|
||||||
ownMem = false;
|
|
||||||
w = width;
|
|
||||||
h = height;
|
|
||||||
// Note that despite this cast, the contents of data should still never be modified
|
|
||||||
data = const_cast<unsigned char*>(source);
|
|
||||||
this->pitch = pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace media
|
|
|
@ -1,129 +0,0 @@
|
||||||
// Copyright (c) 2006, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file video_provider_manager.cpp
|
|
||||||
/// @brief Keep track of installed video providers
|
|
||||||
/// @ingroup video_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifndef MAGI_PRE
|
|
||||||
#include <string>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libaegisub/log.h>
|
|
||||||
|
|
||||||
#include "libmedia/video.h"
|
|
||||||
|
|
||||||
#ifdef WITH_AVISYNTH
|
|
||||||
#include "../video/avs_video.h"
|
|
||||||
#endif
|
|
||||||
#include "../cache/video_cache.h"
|
|
||||||
//XXX: needs fixing. #include "../video/dummy_video.h"
|
|
||||||
#ifdef WITH_FFMPEGSOURCE
|
|
||||||
#include "../video/ffms_video.h"
|
|
||||||
#endif
|
|
||||||
//XXX: needs fixing. #include "../video/yuv4mpeg.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @brief Get provider
|
|
||||||
/// @param video
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
VideoProvider *VideoProviderFactory::GetProvider(std::string video) {
|
|
||||||
//XXX std::vector<std::string> list = GetClasses(OPT_GET("Video/Provider")->GetString());
|
|
||||||
std::vector<std::string> list = GetClasses("ffmpegsource");
|
|
||||||
if (video.find("?dummy") == 0) list.insert(list.begin(), "Dummy");
|
|
||||||
list.insert(list.begin(), "YUV4MPEG");
|
|
||||||
|
|
||||||
bool fileFound = false;
|
|
||||||
bool fileSupported = false;
|
|
||||||
std::string errors;
|
|
||||||
errors.reserve(1024);
|
|
||||||
for (int i = 0; i < (signed)list.size(); ++i) {
|
|
||||||
std::string err;
|
|
||||||
try {
|
|
||||||
VideoProvider *provider = Create(list[i], video);
|
|
||||||
LOG_I("manager/video/provider") << list[i] << ": opened " << video;
|
|
||||||
if (provider->WantsCaching()) {
|
|
||||||
return new VideoProviderCache(provider);
|
|
||||||
}
|
|
||||||
return provider;
|
|
||||||
}
|
|
||||||
catch (agi::FileNotFoundError const&) {
|
|
||||||
err = list[i] + ": file not found.";
|
|
||||||
// Keep trying other providers as this one may just not be able to
|
|
||||||
// open a valid path
|
|
||||||
}
|
|
||||||
catch (VideoNotSupported const&) {
|
|
||||||
fileFound = true;
|
|
||||||
err = list[i] + ": video is not in a supported format.";
|
|
||||||
}
|
|
||||||
catch (VideoOpenError const& ex) {
|
|
||||||
fileSupported = true;
|
|
||||||
err = list[i] + ": " + ex.GetMessage();
|
|
||||||
}
|
|
||||||
catch (agi::vfr::Error const& ex) {
|
|
||||||
fileSupported = true;
|
|
||||||
err = list[i] + ": " + ex.GetMessage();
|
|
||||||
}
|
|
||||||
errors += err;
|
|
||||||
errors += "\n";
|
|
||||||
LOG_D("manager/video/provider") << err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No provider could open the file
|
|
||||||
LOG_E("manager/video/provider") << "Could not open " << video;
|
|
||||||
std::string msg = "Could not open " + video + ":\n" + errors;
|
|
||||||
|
|
||||||
if (!fileFound) throw agi::FileNotFoundError(video);
|
|
||||||
if (!fileSupported) throw VideoNotSupported(msg);
|
|
||||||
throw VideoOpenError(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Register all providers
|
|
||||||
///
|
|
||||||
void VideoProviderFactory::RegisterProviders() {
|
|
||||||
#ifdef WITH_AVISYNTH
|
|
||||||
Register<AvisynthVideoProvider>("Avisynth");
|
|
||||||
#endif
|
|
||||||
#ifdef WITH_FFMPEGSOURCE
|
|
||||||
Register<ffms::FFmpegSourceVideoProvider>("FFmpegSource");
|
|
||||||
#endif
|
|
||||||
//XXX Register<DummyVideoProvider>("Dummy", true);
|
|
||||||
//XXX Register<YUV4MPEGVideoProvider>("YUV4MPEG", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> VideoProviderFactory::map *FactoryBase<VideoProvider *(*)(std::string)>::classes = NULL;
|
|
||||||
|
|
||||||
} // namespace media
|
|
|
@ -1,44 +0,0 @@
|
||||||
// Copyright (c) 2006-2008, Rodrigo Braz Monteiro, Fredrik Mellbin
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file video_provider_manager.h
|
|
||||||
/// @see video_provider_manager.cpp
|
|
||||||
/// @ingroup video_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "factory_manager.h"
|
|
||||||
#include "include/aegisub/video_provider.h"
|
|
||||||
|
|
||||||
class VideoProviderFactory : public Factory1<VideoProvider, wxString> {
|
|
||||||
public:
|
|
||||||
static VideoProvider *GetProvider(wxString video);
|
|
||||||
static void RegisterProviders();
|
|
||||||
};
|
|
|
@ -1,111 +0,0 @@
|
||||||
// Copyright (c) 2006, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file audio_provider.h
|
|
||||||
/// @brief Declaration of base-class for audio providers
|
|
||||||
/// @ingroup main_headers audio_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <libaegisub/exception.h>
|
|
||||||
#include <libaegisub/progress.h>
|
|
||||||
#include <libmedia/factory_manager.h>
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @class AudioProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class AudioProvider {
|
|
||||||
private:
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
char *raw;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int raw_len;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int channels;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int64_t num_samples; // for one channel, ie. number of PCM frames
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int sample_rate;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int bytes_per_sample;
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
std::string filename;
|
|
||||||
|
|
||||||
public:
|
|
||||||
AudioProvider();
|
|
||||||
virtual ~AudioProvider();
|
|
||||||
|
|
||||||
virtual std::string GetFilename() const { return filename; };
|
|
||||||
virtual void GetAudio(void *buf, int64_t start, int64_t count) const = 0;
|
|
||||||
void GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const;
|
|
||||||
|
|
||||||
int64_t GetNumSamples() const { return num_samples; }
|
|
||||||
int GetSampleRate() const { return sample_rate; }
|
|
||||||
int GetBytesPerSample() const { return bytes_per_sample; }
|
|
||||||
int GetChannels() const { return channels; }
|
|
||||||
virtual bool AreSamplesNativeEndian() const = 0;
|
|
||||||
|
|
||||||
/// @brief Does this provider benefit from external caching?
|
|
||||||
virtual bool NeedsCache() const { return false; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class AudioProviderFactory
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class AudioProviderFactory : public Factory1<AudioProvider, std::string> {
|
|
||||||
public:
|
|
||||||
static void RegisterProviders();
|
|
||||||
static AudioProvider *GetProvider(std::string filename, agi::ProgressSinkFactory *progress_factory, int cache=-1);
|
|
||||||
};
|
|
||||||
|
|
||||||
DEFINE_BASE_EXCEPTION_NOINNER(AudioProviderError, agi::Exception);
|
|
||||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(AudioOpenError, AudioProviderError, "audio/open/failed");
|
|
||||||
|
|
||||||
/// Error of some sort occurred while decoding a frame
|
|
||||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(AudioDecodeError, AudioProviderError, "audio/error");
|
|
||||||
|
|
||||||
} // namespace media
|
|
|
@ -1,84 +0,0 @@
|
||||||
// Copyright (c) 2005, Niels Martin Hansen
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file colorspace.h
|
|
||||||
/// @see colorspace.cpp
|
|
||||||
/// @ingroup utility
|
|
||||||
///
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <libaegisub/colour.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @brief DOCME
|
|
||||||
/// @param val
|
|
||||||
///
|
|
||||||
inline unsigned int clip_colorval(int val)
|
|
||||||
{
|
|
||||||
if (val < 0) return 0;
|
|
||||||
if (val > 255) return 255;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Convert an YUV color to RGB; all values are expected to be in range 0..255
|
|
||||||
void yuv_to_rgb(int Y, int U, int V, unsigned char *R, unsigned char *G, unsigned char *B);
|
|
||||||
|
|
||||||
// Convert a HSL color to RGB; all values are expected to be in range 0..255
|
|
||||||
void hsl_to_rgb(int H, int S, int L, unsigned char *R, unsigned char *G, unsigned char *B);
|
|
||||||
|
|
||||||
// Convert a HSV color to RGB; all values are expected to be in range 0..255
|
|
||||||
void hsv_to_rgb(int H, int S, int V, unsigned char *R, unsigned char *G, unsigned char *B);
|
|
||||||
|
|
||||||
// Convert an RGB color to YUV; all values are expected to be in range 0..255
|
|
||||||
void rgb_to_yuv(int R, int G, int B, unsigned char *Y, unsigned char *U, unsigned char *V);
|
|
||||||
|
|
||||||
// Convert an RGB color to HSL; all values are expected to be in range 0..255
|
|
||||||
void rgb_to_hsl(int R, int G, int B, unsigned char *H, unsigned char *S, unsigned char *L);
|
|
||||||
|
|
||||||
// Convert an RGB color to HSV; all values are expected to be in range 0..255
|
|
||||||
void rgb_to_hsv(int R, int G, int B, unsigned char *H, unsigned char *S, unsigned char *V);
|
|
||||||
|
|
||||||
void hsv_to_hsl(int iH, int iS, int iV, unsigned char *oH, unsigned char *oS, unsigned char *oL);
|
|
||||||
|
|
||||||
void hsl_to_hsv(int iH, int iS, int iL, unsigned char *oH, unsigned char *oS, unsigned char *oV);
|
|
||||||
|
|
||||||
|
|
||||||
// Convert a wxColour to a HTML hex string
|
|
||||||
std::string color_to_html(agi::Colour color);
|
|
||||||
|
|
||||||
// Convert a HTML hex string to a wxColour
|
|
||||||
std::string html_to_color(agi::Colour html);
|
|
||||||
|
|
||||||
} // namespace media
|
|
|
@ -1,147 +0,0 @@
|
||||||
// Copyright (c) 2010, Thomas Goyne <plorkyeran@aegisub.org>
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file factory_manager.h
|
|
||||||
/// @brief Template/base-class for factory classes
|
|
||||||
/// @ingroup utility
|
|
||||||
///
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef MAGI_PRE
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cctype>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
template <class func>
|
|
||||||
class FactoryBase {
|
|
||||||
protected:
|
|
||||||
typedef std::map<std::string, std::pair<bool, func> > map;
|
|
||||||
typedef typename map::iterator iterator;
|
|
||||||
|
|
||||||
static map *classes;
|
|
||||||
|
|
||||||
static void DoRegister(func function, std::string name, bool hide, std::vector<std::string> &subtypes) {
|
|
||||||
if (!classes) classes = new map;
|
|
||||||
|
|
||||||
if (subtypes.empty()) {
|
|
||||||
classes->insert(std::make_pair(name, std::make_pair(hide, function)));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (size_t i = 0; i < subtypes.size(); i++) {
|
|
||||||
classes->insert(std::make_pair(name + '/' + subtypes[i], std::make_pair(hide, function)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func Find(std::string name) {
|
|
||||||
if (!classes) return NULL;
|
|
||||||
|
|
||||||
iterator factory = classes->find(name);
|
|
||||||
if (factory != classes->end()) return factory->second.second;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void Clear() {
|
|
||||||
delete classes;
|
|
||||||
}
|
|
||||||
static std::vector<std::string> GetClasses(std::string favourite="") {
|
|
||||||
std::vector<std::string> list;
|
|
||||||
if (!classes) return list;
|
|
||||||
std::string cmp;
|
|
||||||
std::transform(favourite.begin(), favourite.end(), favourite.begin(), ::tolower);
|
|
||||||
for (iterator cur=classes->begin();cur!=classes->end();cur++) {
|
|
||||||
cmp.clear();
|
|
||||||
std::transform(cur->first.begin(), cur->first.end(), std::back_inserter(cmp), ::tolower);
|
|
||||||
if (cmp == favourite) list.insert(list.begin(), cur->first);
|
|
||||||
else if (!cur->second.first) list.push_back(cur->first);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
virtual ~FactoryBase() {
|
|
||||||
delete classes;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Base>
|
|
||||||
class Factory0 : public FactoryBase<Base *(*)()> {
|
|
||||||
typedef Base *(*func)();
|
|
||||||
template<class T>
|
|
||||||
static Base* create() {
|
|
||||||
return new T;
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
static Base* Create(std::string name) {
|
|
||||||
func factory = FactoryBase<func>::Find(name);
|
|
||||||
if (factory) {
|
|
||||||
return factory();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
static void Register(std::string name, bool hide = false, std::vector<std::string> subTypes = std::vector<std::string>()) {
|
|
||||||
DoRegister(&Factory0<Base>::template create<T>, name, hide, subTypes);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Base, class Arg1>
|
|
||||||
class Factory1 : public FactoryBase<Base *(*)(Arg1)> {
|
|
||||||
typedef Base *(*func)(Arg1);
|
|
||||||
template<class T>
|
|
||||||
static Base* create(Arg1 a1) {
|
|
||||||
return new T(a1);
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
static Base* Create(std::string name, Arg1 a1) {
|
|
||||||
func factory = FactoryBase<func>::Find(name);
|
|
||||||
if (factory) {
|
|
||||||
return factory(a1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
static void Register(std::string name, bool hide = false, std::vector<std::string> subTypes = std::vector<std::string>()) {
|
|
||||||
DoRegister(&Factory1<Base, Arg1>::template create<T>, name, hide, subTypes);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace media
|
|
|
@ -1,101 +0,0 @@
|
||||||
// Copyright (c) 2006-2008, Rodrigo Braz Monteiro, Fredrik Mellbin
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file video_provider.h
|
|
||||||
/// @brief Declaration of base-class for video providers
|
|
||||||
/// @ingroup main_headers video_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef MAGI_PRE
|
|
||||||
#include <string>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libmedia/factory_manager.h>
|
|
||||||
#include <libmedia/video_frame.h>
|
|
||||||
#include <libaegisub/exception.h>
|
|
||||||
#include <libaegisub/vfr.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @class VideoProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class VideoProvider {
|
|
||||||
public:
|
|
||||||
virtual ~VideoProvider() {}
|
|
||||||
|
|
||||||
// Override this method to actually get frames
|
|
||||||
virtual const AegiVideoFrame GetFrame(int n)=0;
|
|
||||||
|
|
||||||
// Override the following methods to get video information:
|
|
||||||
virtual int GetPosition() const=0; ///< Get the number of the last frame loaded
|
|
||||||
virtual int GetFrameCount() const=0; ///< Get total number of frames
|
|
||||||
virtual int GetWidth() const=0; ///< Returns the video width in pixels
|
|
||||||
virtual int GetHeight() const=0; ///< Returns the video height in pixels
|
|
||||||
virtual agi::vfr::Framerate GetFPS() const=0; ///< Get frame rate
|
|
||||||
virtual std::vector<int> GetKeyFrames() const=0;///< Returns list of keyframes
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Use this to set any post-loading warnings, such as "being loaded with unreliable seeking"
|
|
||||||
virtual std::string GetWarning() const { return ""; }
|
|
||||||
|
|
||||||
/// @brief Name of decoder, e.g. "Avisynth/FFMpegSource"
|
|
||||||
virtual std::string GetDecoderName() const = 0;
|
|
||||||
|
|
||||||
/// @brief Does this provider want Aegisub to cache video frames?
|
|
||||||
/// @return Returns true if caching is desired, false otherwise.
|
|
||||||
virtual bool WantsCaching() const { return false; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class VideoProviderFactory : public Factory1<VideoProvider, std::string> {
|
|
||||||
public:
|
|
||||||
static VideoProvider *GetProvider(std::string video);
|
|
||||||
static void RegisterProviders();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
DEFINE_BASE_EXCEPTION_NOINNER(VideoProviderError, agi::Exception);
|
|
||||||
/// File could be opened, but is not a supported format
|
|
||||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(VideoNotSupported, VideoProviderError, "video/open/notsupported");
|
|
||||||
/// File appears to be a supported format, but could not be opened
|
|
||||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(VideoOpenError, VideoProviderError, "video/open/failed");
|
|
||||||
|
|
||||||
/// Error of some sort occurred while decoding a frame
|
|
||||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(VideoDecodeError, VideoProviderError, "video/error");
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace media
|
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro <amz@aegisub.org>
|
|
||||||
//
|
|
||||||
// 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 media_video_frame.h
|
|
||||||
/// @see media_video_frame.cpp
|
|
||||||
/// @ingroup video
|
|
||||||
///
|
|
||||||
|
|
||||||
#ifndef LAGI_PRE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class AegiVideoFrame
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class AegiVideoFrame {
|
|
||||||
/// Whether the object owns its buffer. If this is false, **data should never be modified
|
|
||||||
bool ownMem;
|
|
||||||
/// @brief Reset values to the defaults
|
|
||||||
///
|
|
||||||
/// Note that this function DOES NOT deallocate memory.
|
|
||||||
/// Use Clear() for that
|
|
||||||
void Reset();
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// @brief Allocate memory if needed
|
|
||||||
void Allocate();
|
|
||||||
|
|
||||||
/// The size in bytes of the frame buffer
|
|
||||||
unsigned int memSize;
|
|
||||||
|
|
||||||
/// Pointer to the data planes
|
|
||||||
unsigned char *data;
|
|
||||||
|
|
||||||
/// Width in pixels
|
|
||||||
unsigned int w;
|
|
||||||
|
|
||||||
/// Height in pixels
|
|
||||||
unsigned int h;
|
|
||||||
|
|
||||||
// Pitch, that is, the number of bytes used by each row.
|
|
||||||
unsigned int pitch;
|
|
||||||
|
|
||||||
/// First row is actually the bottom one
|
|
||||||
bool flipped;
|
|
||||||
|
|
||||||
/// Swap Red and Blue channels (controls RGB versus BGR ordering etc)
|
|
||||||
bool invertChannels;
|
|
||||||
|
|
||||||
AegiVideoFrame();
|
|
||||||
AegiVideoFrame(unsigned int width, unsigned int height);
|
|
||||||
|
|
||||||
// @brief Clear this frame, freeing its memory if nessesary
|
|
||||||
void Clear();
|
|
||||||
|
|
||||||
/// @brief Copy from an AegiVideoFrame
|
|
||||||
/// @param source The frame to copy from
|
|
||||||
void CopyFrom(const AegiVideoFrame &source);
|
|
||||||
|
|
||||||
/// @brief Set the frame to an externally allocated block of memory
|
|
||||||
/// @param source Target frame data
|
|
||||||
/// @param width The frame width in pixels
|
|
||||||
/// @param height The frame height in pixels
|
|
||||||
/// @param pitch The frame's pitch
|
|
||||||
/// @param format The frame's format
|
|
||||||
void SetTo(const unsigned char *source, unsigned int width, unsigned int height, unsigned int pitch);
|
|
||||||
|
|
||||||
int GetBpp() const { return 4; };
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace media
|
|
|
@ -1,57 +0,0 @@
|
||||||
#define LAGI_PRE
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
// Common C
|
|
||||||
#include <errno.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#ifdef HAVE_SYS_TIME_H
|
|
||||||
# include <sys/time.h>
|
|
||||||
#else
|
|
||||||
# include <time.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Windows C
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
#include <io.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Common C++
|
|
||||||
#include <deque>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <iterator>
|
|
||||||
#include <numeric>
|
|
||||||
#include <map>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#else
|
|
||||||
#include <tr1/functional>
|
|
||||||
#include <tr1/memory>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __DEPRECATED // Dodge GCC warnings
|
|
||||||
# undef __DEPRECATED
|
|
||||||
# include <strstream>
|
|
||||||
# define __DEPRECATED
|
|
||||||
#else
|
|
||||||
# include <strstream>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Cajun
|
|
||||||
#include "libaegisub/cajun/elements.h"
|
|
||||||
#include "libaegisub/cajun/reader.h"
|
|
||||||
#include "libaegisub/cajun/visitor.h"
|
|
||||||
#include "libaegisub/cajun/writer.h"
|
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file subtitles_provider_csri.cpp
|
|
||||||
/// @brief Wrapper for CSRI-based subtitle renderers
|
|
||||||
/// @ingroup subtitle_rendering
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifdef WITH_CSRI
|
|
||||||
|
|
||||||
#include "ass_file.h"
|
|
||||||
#include "subtitles_provider_csri.h"
|
|
||||||
#include "text_file_writer.h"
|
|
||||||
#include "video_context.h"
|
|
||||||
#include "video_frame.h"
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param type
|
|
||||||
///
|
|
||||||
CSRISubtitlesProvider::CSRISubtitlesProvider(std::string type) : subType(type) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Destructor
|
|
||||||
///
|
|
||||||
CSRISubtitlesProvider::~CSRISubtitlesProvider() {
|
|
||||||
if (!tempfile.empty()) wxRemoveFile(tempfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Load subtitles
|
|
||||||
/// @param subs
|
|
||||||
///
|
|
||||||
void CSRISubtitlesProvider::LoadSubtitles(AssFile *subs) {
|
|
||||||
// CSRI variables
|
|
||||||
csri_rend *cur,*renderer=NULL;
|
|
||||||
|
|
||||||
// Select renderer
|
|
||||||
bool canOpenMem = true;
|
|
||||||
for (cur = csri_renderer_default();cur;cur=csri_renderer_next(cur)) {
|
|
||||||
std::string name(csri_renderer_info(cur)->name);
|
|
||||||
if (name == subType) {
|
|
||||||
renderer = cur;
|
|
||||||
if (name.find("vsfilter") != name.npos) canOpenMem = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matching renderer not found, fallback to default
|
|
||||||
if (!renderer) {
|
|
||||||
renderer = csri_renderer_default();
|
|
||||||
if (!renderer) {
|
|
||||||
throw _T("No CSRI renderer available, cannot show subtitles. Try installing one or switch to another subtitle provider.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open from memory
|
|
||||||
if (canOpenMem) {
|
|
||||||
std::vector<char> data;
|
|
||||||
subs->SaveMemory(data,wxSTRING_ENCODING);
|
|
||||||
instance.reset(csri_open_mem(renderer,&data[0],data.size(),NULL), &csri_close);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open from disk
|
|
||||||
else {
|
|
||||||
if (tempfile.empty()) {
|
|
||||||
tempfile = wxFileName::CreateTempFileName(_T("aegisub"));
|
|
||||||
wxRemoveFile(tempfile);
|
|
||||||
tempfile += L".ass";
|
|
||||||
}
|
|
||||||
subs->Save(tempfile,false,false,wxSTRING_ENCODING);
|
|
||||||
instance.reset(csri_open_file(renderer,tempfile.utf8_str(),NULL), &csri_close);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Draw subtitles
|
|
||||||
/// @param dst
|
|
||||||
/// @param time
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
void CSRISubtitlesProvider::DrawSubtitles(AegiVideoFrame &dst,double time) {
|
|
||||||
// Check if CSRI loaded properly
|
|
||||||
if (!instance.get()) return;
|
|
||||||
|
|
||||||
// Load data into frame
|
|
||||||
csri_frame frame;
|
|
||||||
if (dst.flipped) {
|
|
||||||
frame.planes[0] = dst.data + (dst.h-1) * dst.pitch;
|
|
||||||
frame.strides[0] = -(signed)dst.pitch;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
frame.planes[0] = dst.data;
|
|
||||||
frame.strides[0] = dst.pitch;
|
|
||||||
}
|
|
||||||
frame.pixfmt = CSRI_F_BGR_;
|
|
||||||
|
|
||||||
// Set format
|
|
||||||
csri_fmt format;
|
|
||||||
format.width = dst.w;
|
|
||||||
format.height = dst.h;
|
|
||||||
format.pixfmt = frame.pixfmt;
|
|
||||||
int error = csri_request_fmt(instance.get(),&format);
|
|
||||||
if (error) return;
|
|
||||||
|
|
||||||
// Render
|
|
||||||
csri_render(instance.get(),&frame,time);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get CSRI subtypes
|
|
||||||
///
|
|
||||||
std::vector<std::string> CSRISubtitlesProvider::GetSubTypes() {
|
|
||||||
std::vector<std::string> final;
|
|
||||||
for (csri_rend *cur = csri_renderer_default();cur;cur = csri_renderer_next(cur)) {
|
|
||||||
final.push_back(csri_renderer_info(cur)->name);
|
|
||||||
}
|
|
||||||
return final;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // WITH_CSRI
|
|
|
@ -1,74 +0,0 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file subtitles_provider_csri.h
|
|
||||||
/// @see subtitles_provider_csri.cpp
|
|
||||||
/// @ingroup subtitle_rendering
|
|
||||||
///
|
|
||||||
|
|
||||||
#ifdef WITH_CSRI
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <tr1/memory>
|
|
||||||
#include <vector>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "include/aegisub/subtitles_provider.h"
|
|
||||||
#ifdef WIN32
|
|
||||||
#define CSRIAPI
|
|
||||||
#include "../../contrib/csri/include/csri/csri.h"
|
|
||||||
#else
|
|
||||||
#include <csri/csri.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class CSRISubtitlesProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class CSRISubtitlesProvider : public SubtitlesProvider {
|
|
||||||
/// DOCME
|
|
||||||
std::string subType;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
std::tr1::shared_ptr<csri_inst> instance;
|
|
||||||
|
|
||||||
wxString tempfile;
|
|
||||||
public:
|
|
||||||
CSRISubtitlesProvider(std::string subType);
|
|
||||||
~CSRISubtitlesProvider();
|
|
||||||
|
|
||||||
void LoadSubtitles(AssFile *subs);
|
|
||||||
void DrawSubtitles(AegiVideoFrame &dst,double time);
|
|
||||||
|
|
||||||
static std::vector<std::string> GetSubTypes();
|
|
||||||
};
|
|
||||||
#endif
|
|
|
@ -1,252 +0,0 @@
|
||||||
// Copyright (c) 2006-2007, Rodrigo Braz Monteiro, Evgeniy Stepanov
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file subtitles_provider_libass.cpp
|
|
||||||
/// @brief libass-based subtitle renderer
|
|
||||||
/// @ingroup subtitle_rendering
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifdef WITH_LIBASS
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <wx/filefn.h>
|
|
||||||
#include <wx/utils.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <libaegisub/util_osx.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libaegisub/log.h>
|
|
||||||
|
|
||||||
#include "ass_file.h"
|
|
||||||
#include "dialog_progress.h"
|
|
||||||
#include "frame_main.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "standard_paths.h"
|
|
||||||
#include "subtitles_provider_libass.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include "video_context.h"
|
|
||||||
#include "video_frame.h"
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Handle libass messages
|
|
||||||
///
|
|
||||||
static void msg_callback(int level, const char *fmt, va_list args, void *) {
|
|
||||||
if (level >= 7) return;
|
|
||||||
char buf[1024];
|
|
||||||
#ifdef _WIN32
|
|
||||||
vsprintf_s(buf, sizeof(buf), fmt, args);
|
|
||||||
#else
|
|
||||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (level < 2) // warning/error
|
|
||||||
LOG_I("subtitle/provider/libass") << buf;
|
|
||||||
else // verbose
|
|
||||||
LOG_D("subtitle/provider/libass") << buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
class FontConfigCacheThread : public wxThread {
|
|
||||||
ASS_Library *ass_library;
|
|
||||||
ASS_Renderer *ass_renderer;
|
|
||||||
FontConfigCacheThread** thisPtr;
|
|
||||||
ExitCode Entry() {
|
|
||||||
#ifdef __APPLE__
|
|
||||||
char config_path[MAXPATHLEN];
|
|
||||||
char *config_dir;
|
|
||||||
|
|
||||||
config_dir = agi::util::OSX_GetBundleResourcesDirectory();
|
|
||||||
snprintf(config_path, MAXPATHLEN, "%s/etc/fonts/fonts.conf", config_dir);
|
|
||||||
free(config_dir);
|
|
||||||
#else
|
|
||||||
const char *config_path = NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ass_library) ass_renderer = ass_renderer_init(ass_library);
|
|
||||||
ass_set_fonts(ass_renderer, NULL, "Sans", 1, config_path, true);
|
|
||||||
if (ass_library) ass_renderer_done(ass_renderer);
|
|
||||||
*thisPtr = NULL;
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
FontConfigCacheThread(ASS_Library *ass_library, FontConfigCacheThread **thisPtr)
|
|
||||||
: ass_library(ass_library)
|
|
||||||
, ass_renderer(NULL)
|
|
||||||
, thisPtr(thisPtr)
|
|
||||||
{
|
|
||||||
*thisPtr = this;
|
|
||||||
Create();
|
|
||||||
Run();
|
|
||||||
}
|
|
||||||
FontConfigCacheThread(ASS_Renderer *ass_renderer, FontConfigCacheThread **thisPtr)
|
|
||||||
: ass_library(NULL)
|
|
||||||
, ass_renderer(ass_renderer)
|
|
||||||
, thisPtr(thisPtr)
|
|
||||||
{
|
|
||||||
*thisPtr = this;
|
|
||||||
Create();
|
|
||||||
Run();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static void wait_for_cache_thread(FontConfigCacheThread const * const * const cache_worker) {
|
|
||||||
if (!*cache_worker) return;
|
|
||||||
|
|
||||||
bool canceled;
|
|
||||||
DialogProgress *progress = new DialogProgress(AegisubApp::Get()->frame, L"", &canceled, L"Caching fonts", 0, 1);
|
|
||||||
progress->Show();
|
|
||||||
while (*cache_worker) {
|
|
||||||
if (canceled) throw agi::UserCancelException("Font caching cancelled");
|
|
||||||
progress->Pulse();
|
|
||||||
wxYield();
|
|
||||||
wxMilliSleep(100);
|
|
||||||
}
|
|
||||||
progress->Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
///
|
|
||||||
LibassSubtitlesProvider::LibassSubtitlesProvider(std::string) {
|
|
||||||
wait_for_cache_thread(&cache_worker);
|
|
||||||
|
|
||||||
// Initialize renderer
|
|
||||||
ass_track = NULL;
|
|
||||||
ass_renderer = ass_renderer_init(ass_library);
|
|
||||||
if (!ass_renderer) throw _T("ass_renderer_init failed");
|
|
||||||
ass_set_font_scale(ass_renderer, 1.);
|
|
||||||
new FontConfigCacheThread(ass_renderer, &cache_worker);
|
|
||||||
wait_for_cache_thread(&cache_worker);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Destructor
|
|
||||||
///
|
|
||||||
LibassSubtitlesProvider::~LibassSubtitlesProvider() {
|
|
||||||
if (ass_track) ass_free_track(ass_track);
|
|
||||||
if (ass_renderer) ass_renderer_done(ass_renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Load subtitles
|
|
||||||
/// @param subs
|
|
||||||
///
|
|
||||||
void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) {
|
|
||||||
// Prepare subtitles
|
|
||||||
std::vector<char> data;
|
|
||||||
subs->SaveMemory(data,_T("UTF-8"));
|
|
||||||
|
|
||||||
// Load file
|
|
||||||
if (ass_track) ass_free_track(ass_track);
|
|
||||||
ass_track = ass_read_memory(ass_library, &data[0], data.size(),(char *)"UTF-8");
|
|
||||||
if (!ass_track) throw _T("libass failed to load subtitles.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
#define _r(c) ((c)>>24)
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
#define _g(c) (((c)>>16)&0xFF)
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
#define _b(c) (((c)>>8)&0xFF)
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
#define _a(c) ((c)&0xFF)
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Draw subtitles
|
|
||||||
/// @param frame
|
|
||||||
/// @param time
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
void LibassSubtitlesProvider::DrawSubtitles(AegiVideoFrame &frame,double time) {
|
|
||||||
// Set size
|
|
||||||
ass_set_frame_size(ass_renderer, frame.w, frame.h);
|
|
||||||
|
|
||||||
// Get frame
|
|
||||||
ASS_Image* img = ass_render_frame(ass_renderer, ass_track, int(time * 1000), NULL);
|
|
||||||
|
|
||||||
// libass actually returns several alpha-masked monochrome images.
|
|
||||||
// Here, we loop through their linked list, get the colour of the current, and blend into the frame.
|
|
||||||
// This is repeated for all of them.
|
|
||||||
while (img) {
|
|
||||||
// Get colours
|
|
||||||
unsigned int opacity = 255 - ((unsigned int)_a(img->color));
|
|
||||||
unsigned int r = (unsigned int)_r(img->color);
|
|
||||||
unsigned int g = (unsigned int)_g(img->color);
|
|
||||||
unsigned int b = (unsigned int) _b(img->color);
|
|
||||||
|
|
||||||
// Prepare copy
|
|
||||||
int src_stride = img->stride;
|
|
||||||
int dst_stride = frame.pitch;
|
|
||||||
int dst_delta = dst_stride - img->w*4;
|
|
||||||
//int stride = std::min(src_stride,dst_stride);
|
|
||||||
const unsigned char *src = img->bitmap;
|
|
||||||
unsigned char *dst = frame.data + (img->dst_y * dst_stride + img->dst_x * 4);
|
|
||||||
unsigned int k,ck,t;
|
|
||||||
|
|
||||||
// Copy image to destination frame
|
|
||||||
for (int y=0;y<img->h;y++) {
|
|
||||||
//memcpy(dst,src,stride);
|
|
||||||
for (int x = 0; x < img->w; ++x) {
|
|
||||||
k = ((unsigned)src[x]) * opacity / 255;
|
|
||||||
ck = 255 - k;
|
|
||||||
t = *dst;
|
|
||||||
*dst++ = (k*b + ck*t) / 255;
|
|
||||||
t = *dst;
|
|
||||||
*dst++ = (k*g + ck*t) / 255;
|
|
||||||
t = *dst;
|
|
||||||
*dst++ = (k*r + ck*t) / 255;
|
|
||||||
dst++;
|
|
||||||
}
|
|
||||||
|
|
||||||
dst += dst_delta;
|
|
||||||
src += src_stride;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next image
|
|
||||||
img = img->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibassSubtitlesProvider::CacheFonts() {
|
|
||||||
ass_library = ass_library_init();
|
|
||||||
ass_set_message_cb(ass_library, msg_callback, NULL);
|
|
||||||
new FontConfigCacheThread(ass_library, &cache_worker);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
ASS_Library* LibassSubtitlesProvider::ass_library;
|
|
||||||
FontConfigCacheThread* LibassSubtitlesProvider::cache_worker = NULL;
|
|
||||||
|
|
||||||
#endif // WITH_LIBASS
|
|
|
@ -1,76 +0,0 @@
|
||||||
// Copyright (c) 2006-2007, Rodrigo Braz Monteiro, Evgeniy Stepanov
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file subtitles_provider_libass.h
|
|
||||||
/// @see subtitles_provider_libass.cpp
|
|
||||||
/// @ingroup subtitle_rendering
|
|
||||||
///
|
|
||||||
|
|
||||||
#ifdef WITH_LIBASS
|
|
||||||
|
|
||||||
#include "include/aegisub/subtitles_provider.h"
|
|
||||||
extern "C" {
|
|
||||||
#ifdef __VISUALC__
|
|
||||||
#include "stdint.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "../libass/ass.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
class FontConfigCacheThread;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class LibassSubtitlesProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class LibassSubtitlesProvider : public SubtitlesProvider {
|
|
||||||
/// DOCME
|
|
||||||
static ASS_Library* ass_library;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
ASS_Renderer* ass_renderer;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
ASS_Track* ass_track;
|
|
||||||
|
|
||||||
static FontConfigCacheThread *cache_worker;
|
|
||||||
|
|
||||||
public:
|
|
||||||
LibassSubtitlesProvider(std::string);
|
|
||||||
~LibassSubtitlesProvider();
|
|
||||||
|
|
||||||
void LoadSubtitles(AssFile *subs);
|
|
||||||
void DrawSubtitles(AegiVideoFrame &dst,double time);
|
|
||||||
|
|
||||||
static void CacheFonts();
|
|
||||||
};
|
|
||||||
#endif
|
|
|
@ -1,295 +0,0 @@
|
||||||
// Copyright (c) 2006, Fredrik Mellbin
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file video_provider_avs.cpp
|
|
||||||
/// @brief Avisynth-based video provider
|
|
||||||
/// @ingroup video_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifdef WITH_AVISYNTH
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <wx/filename.h>
|
|
||||||
#include <wx/msw/registry.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "charset_conv.h"
|
|
||||||
#include "compat.h"
|
|
||||||
#include "gl_wrap.h"
|
|
||||||
#include <libaegisub/log.h>
|
|
||||||
#include "mkv_wrap.h"
|
|
||||||
#include "standard_paths.h"
|
|
||||||
#include "vfw_wrap.h"
|
|
||||||
#include "video_context.h"
|
|
||||||
#include "video_provider_avs.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param _filename
|
|
||||||
///
|
|
||||||
AvisynthVideoProvider::AvisynthVideoProvider(wxString filename) try
|
|
||||||
: usedDirectShow(false)
|
|
||||||
, decoderName(_("Unknown"))
|
|
||||||
, num_frames(0)
|
|
||||||
, last_fnum(-1)
|
|
||||||
, RGB32Video(NULL)
|
|
||||||
{
|
|
||||||
RGB32Video = OpenVideo(filename);
|
|
||||||
|
|
||||||
vi = RGB32Video->GetVideoInfo();
|
|
||||||
}
|
|
||||||
catch (AvisynthError const& err) {
|
|
||||||
throw VideoOpenError("Avisynth error: " + std::string(err.msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Destructor
|
|
||||||
AvisynthVideoProvider::~AvisynthVideoProvider() {
|
|
||||||
iframe.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& extension) {
|
|
||||||
char *videoFilename = env->SaveString(fname.GetShortPath().mb_str(csConvLocal));
|
|
||||||
|
|
||||||
// Avisynth file, just import it
|
|
||||||
if (extension == L".avs") {
|
|
||||||
LOG_I("avisynth/video") << "Opening .avs file with Import";
|
|
||||||
decoderName = L"Import";
|
|
||||||
return env->Invoke("Import", videoFilename);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open avi file with AviSource
|
|
||||||
if (extension == L".avi") {
|
|
||||||
LOG_I("avisynth/video") << "Opening .avi file with AviSource";
|
|
||||||
try {
|
|
||||||
const char *argnames[2] = { 0, "audio" };
|
|
||||||
AVSValue args[2] = { videoFilename, false };
|
|
||||||
decoderName = L"AviSource";
|
|
||||||
return env->Invoke("AviSource", AVSValue(args,2), argnames);
|
|
||||||
}
|
|
||||||
|
|
||||||
// On Failure, fallback to DSS
|
|
||||||
catch (AvisynthError &) {
|
|
||||||
LOG_I("avisynth/video") << "Failed to open .avi file with AviSource, switching to DirectShowSource";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open d2v with mpeg2dec3
|
|
||||||
if (extension == L".d2v" && env->FunctionExists("Mpeg2Dec3_Mpeg2Source")) {
|
|
||||||
LOG_I("avisynth/video") << "Opening .d2v file with Mpeg2Dec3_Mpeg2Source";
|
|
||||||
AVSValue script = env->Invoke("Mpeg2Dec3_Mpeg2Source", videoFilename);
|
|
||||||
decoderName = L"Mpeg2Dec3_Mpeg2Source";
|
|
||||||
|
|
||||||
//if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this
|
|
||||||
if (env->FunctionExists("SetPlanarLegacyAlignment")) {
|
|
||||||
AVSValue args[2] = { script, true };
|
|
||||||
script = env->Invoke("SetPlanarLegacyAlignment", AVSValue(args,2));
|
|
||||||
}
|
|
||||||
return script;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If that fails, try opening it with DGDecode
|
|
||||||
if (extension == L".d2v" && env->FunctionExists("DGDecode_Mpeg2Source")) {
|
|
||||||
LOG_I("avisynth/video") << "Opening .d2v file with DGDecode_Mpeg2Source";
|
|
||||||
decoderName = L"DGDecode_Mpeg2Source";
|
|
||||||
return env->Invoke("Mpeg2Source", videoFilename);
|
|
||||||
|
|
||||||
//note that DGDecode will also have issues like if the version is too ancient but no sane person
|
|
||||||
//would use that anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extension == L".d2v" && env->FunctionExists("Mpeg2Source")) {
|
|
||||||
LOG_I("avisynth/video") << "Opening .d2v file with other Mpeg2Source";
|
|
||||||
AVSValue script = env->Invoke("Mpeg2Source", videoFilename);
|
|
||||||
decoderName = L"Mpeg2Source";
|
|
||||||
|
|
||||||
//if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this
|
|
||||||
if (env->FunctionExists("SetPlanarLegacyAlignment"))
|
|
||||||
script = env->Invoke("SetPlanarLegacyAlignment", script);
|
|
||||||
|
|
||||||
return script;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try loading DirectShowSource2
|
|
||||||
if (!env->FunctionExists("dss2")) {
|
|
||||||
wxFileName dss2path(StandardPaths::DecodePath(_T("?data/avss.dll")));
|
|
||||||
if (dss2path.FileExists()) {
|
|
||||||
env->Invoke("LoadPlugin",env->SaveString(dss2path.GetFullPath().mb_str(csConvLocal)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If DSS2 loaded properly, try using it
|
|
||||||
if (env->FunctionExists("dss2")) {
|
|
||||||
LOG_I("avisynth/video") << "Opening file with DSS2";
|
|
||||||
decoderName = L"DSS2";
|
|
||||||
return env->Invoke("DSS2", videoFilename);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try DirectShowSource
|
|
||||||
// Load DirectShowSource.dll from app dir if it exists
|
|
||||||
wxFileName dsspath(StandardPaths::DecodePath(_T("?data/DirectShowSource.dll")));
|
|
||||||
if (dsspath.FileExists()) {
|
|
||||||
env->Invoke("LoadPlugin",env->SaveString(dsspath.GetFullPath().mb_str(csConvLocal)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then try using DSS
|
|
||||||
if (env->FunctionExists("DirectShowSource")) {
|
|
||||||
const char *argnames[3] = { 0, "video", "audio" };
|
|
||||||
AVSValue args[3] = { videoFilename, true, false };
|
|
||||||
usedDirectShow = true;
|
|
||||||
decoderName = L"DirectShowSource";
|
|
||||||
LOG_I("avisynth/video") << "Opening file with DirectShowSource";
|
|
||||||
return env->Invoke("DirectShowSource", AVSValue(args,3), argnames);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failed to find a suitable function
|
|
||||||
LOG_E("avisynth/video") << "DSS function not found";
|
|
||||||
throw VideoNotSupported("No function suitable for opening the video found");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Actually open the video into Avisynth
|
|
||||||
/// @param _filename
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
PClip AvisynthVideoProvider::OpenVideo(wxString filename) {
|
|
||||||
wxMutexLocker lock(AviSynthMutex);
|
|
||||||
|
|
||||||
wxFileName fname(filename);
|
|
||||||
if (!fname.FileExists())
|
|
||||||
throw agi::FileNotFoundError(STD_STR(filename));
|
|
||||||
|
|
||||||
AVSValue script;
|
|
||||||
wxString extension = filename.Right(4).Lower();
|
|
||||||
try {
|
|
||||||
script = Open(fname, extension);
|
|
||||||
}
|
|
||||||
catch (AvisynthError const& err) {
|
|
||||||
throw VideoOpenError("Avisynth error: " + std::string(err.msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if video was loaded properly
|
|
||||||
if (!script.IsClip() || !script.AsClip()->GetVideoInfo().HasVideo()) {
|
|
||||||
throw VideoNotSupported("No usable video found");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read keyframes and timecodes from MKV file
|
|
||||||
bool mkvOpen = MatroskaWrapper::wrapper.IsOpen();
|
|
||||||
KeyFrames.clear();
|
|
||||||
if (extension == L".mkv" || mkvOpen) {
|
|
||||||
// Parse mkv
|
|
||||||
if (!mkvOpen) MatroskaWrapper::wrapper.Open(filename);
|
|
||||||
|
|
||||||
// Get keyframes
|
|
||||||
KeyFrames = MatroskaWrapper::wrapper.GetKeyFrames();
|
|
||||||
|
|
||||||
MatroskaWrapper::wrapper.SetToTimecodes(vfr_fps);
|
|
||||||
|
|
||||||
// Close mkv
|
|
||||||
MatroskaWrapper::wrapper.Close();
|
|
||||||
}
|
|
||||||
// check if we have windows, if so we can load keyframes from AVI files using VFW
|
|
||||||
#ifdef __WINDOWS__
|
|
||||||
else if (extension == L".avi") {
|
|
||||||
KeyFrames.clear();
|
|
||||||
KeyFrames = VFWWrapper::GetKeyFrames(filename);
|
|
||||||
}
|
|
||||||
#endif /* __WINDOWS__ */
|
|
||||||
|
|
||||||
// Check if the file is all keyframes
|
|
||||||
bool isAllKeyFrames = true;
|
|
||||||
for (unsigned int i=1; i<KeyFrames.size(); i++) {
|
|
||||||
// Is the last keyframe not this keyframe -1?
|
|
||||||
if (KeyFrames[i-1] != (int)(i-1)) {
|
|
||||||
// It's not all keyframes, go ahead
|
|
||||||
isAllKeyFrames = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it is all keyframes, discard the keyframe info as it is useless
|
|
||||||
if (isAllKeyFrames) {
|
|
||||||
KeyFrames.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
real_fps = (double)vi.fps_numerator / vi.fps_denominator;
|
|
||||||
|
|
||||||
// Convert to RGB32
|
|
||||||
script = env->Invoke("ConvertToRGB32", script);
|
|
||||||
|
|
||||||
// Cache
|
|
||||||
return (env->Invoke("Cache", script)).AsClip();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Actually get a frame
|
|
||||||
/// @param _n
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
const AegiVideoFrame AvisynthVideoProvider::GetFrame(int n) {
|
|
||||||
if (vfr_fps.IsLoaded()) {
|
|
||||||
n = real_fps.FrameAtTime(vfr_fps.TimeAtFrame(n));
|
|
||||||
}
|
|
||||||
// Get avs frame
|
|
||||||
wxMutexLocker lock(AviSynthMutex);
|
|
||||||
PVideoFrame frame = RGB32Video->GetFrame(n,env);
|
|
||||||
int Bpp = vi.BitsPerPixel() / 8;
|
|
||||||
|
|
||||||
// Aegisub's video frame
|
|
||||||
AegiVideoFrame &final = iframe;
|
|
||||||
final.flipped = true;
|
|
||||||
final.invertChannels = true;
|
|
||||||
|
|
||||||
// Set size properties
|
|
||||||
final.pitch = frame->GetPitch();
|
|
||||||
final.w = frame->GetRowSize() / Bpp;
|
|
||||||
final.h = frame->GetHeight();
|
|
||||||
|
|
||||||
// Allocate
|
|
||||||
final.Allocate();
|
|
||||||
|
|
||||||
// Copy
|
|
||||||
memcpy(final.data,frame->GetReadPtr(),final.pitch * final.h);
|
|
||||||
|
|
||||||
// Set last number
|
|
||||||
last_fnum = n;
|
|
||||||
return final;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get warning
|
|
||||||
///
|
|
||||||
wxString AvisynthVideoProvider::GetWarning() const {
|
|
||||||
if (usedDirectShow) return L"Warning! The file is being opened using Avisynth's DirectShowSource, which has unreliable seeking. Frame numbers might not match the real number. PROCEED AT YOUR OWN RISK!";
|
|
||||||
else return L"";
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace media
|
|
|
@ -1,104 +0,0 @@
|
||||||
// Copyright (c) 2006, Fredrik Mellbin
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file video_provider_avs.h
|
|
||||||
/// @see video_provider_avs.cpp
|
|
||||||
/// @ingroup video_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#ifdef WITH_AVISYNTH
|
|
||||||
#include "avisynth_wrap.h"
|
|
||||||
#include "include/aegisub/video_provider.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class AvisynthVideoProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class AvisynthVideoProvider: public VideoProvider, AviSynthWrapper {
|
|
||||||
/// DOCME
|
|
||||||
VideoInfo vi;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
AegiVideoFrame iframe;
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
bool usedDirectShow;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
wxString rendererCallString;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
wxString decoderName;
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int num_frames;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int last_fnum;
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
agi::vfr::Framerate real_fps;
|
|
||||||
agi::vfr::Framerate vfr_fps;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
std::vector<int> KeyFrames;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
PClip RGB32Video;
|
|
||||||
|
|
||||||
PClip OpenVideo(wxString filename);
|
|
||||||
AVSValue Open(wxFileName const& fname, wxString const& extension);
|
|
||||||
|
|
||||||
public:
|
|
||||||
AvisynthVideoProvider(wxString filename);
|
|
||||||
~AvisynthVideoProvider();
|
|
||||||
|
|
||||||
const AegiVideoFrame GetFrame(int n);
|
|
||||||
|
|
||||||
int GetPosition() const { return last_fnum; };
|
|
||||||
int GetFrameCount() const { return num_frames? num_frames: vi.num_frames; };
|
|
||||||
agi::vfr::Framerate GetFPS() const { return vfr_fps.IsLoaded() ? vfr_fps : real_fps; };
|
|
||||||
int GetWidth() const { return vi.width; };
|
|
||||||
int GetHeight() const { return vi.height; };
|
|
||||||
std::vector<int> GetKeyFrames() const { return KeyFrames; };
|
|
||||||
wxString GetWarning() const;
|
|
||||||
wxString GetDecoderName() const { return wxString(L"Avisynth/") + decoderName; }
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace media
|
|
||||||
|
|
|
@ -1,223 +0,0 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file video_provider_dummy.cpp
|
|
||||||
/// @brief Video provider returning a constant frame
|
|
||||||
/// @ingroup video_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <wx/tokenzr.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "colorspace.h"
|
|
||||||
#include "video_provider_dummy.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param _fps
|
|
||||||
/// @param frames
|
|
||||||
/// @param _width
|
|
||||||
/// @param _height
|
|
||||||
/// @param colour
|
|
||||||
/// @param pattern
|
|
||||||
///
|
|
||||||
void DummyVideoProvider::Create(double _fps, int frames, int _width, int _height, const wxColour &colour, bool pattern) {
|
|
||||||
lastFrame = -1;
|
|
||||||
framecount = frames;
|
|
||||||
fps = _fps;
|
|
||||||
width = _width;
|
|
||||||
height = _height;
|
|
||||||
|
|
||||||
frame = AegiVideoFrame(width,height);
|
|
||||||
unsigned char *dst = frame.data;
|
|
||||||
unsigned char r = colour.Red(), g = colour.Green(), b = colour.Blue();
|
|
||||||
|
|
||||||
unsigned char h, s, l, lr, lg, lb; // light variants
|
|
||||||
rgb_to_hsl(r, g, b, &h, &s, &l);
|
|
||||||
l += 24;
|
|
||||||
if (l < 24) l -= 48;
|
|
||||||
hsl_to_rgb(h, s, l, &lr, &lg, &lb);
|
|
||||||
|
|
||||||
if (pattern) {
|
|
||||||
int ppitch = frame.pitch / frame.GetBpp();
|
|
||||||
for (unsigned int y = 0; y < frame.h; ++y) {
|
|
||||||
if ((y / 8) & 1) {
|
|
||||||
for (int x = 0; x < ppitch; ++x) {
|
|
||||||
if ((x / 8) & 1) {
|
|
||||||
*dst++ = b;
|
|
||||||
*dst++ = g;
|
|
||||||
*dst++ = r;
|
|
||||||
*dst++ = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*dst++ = lb;
|
|
||||||
*dst++ = lg;
|
|
||||||
*dst++ = lr;
|
|
||||||
*dst++ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (int x = 0; x < ppitch; ++x) {
|
|
||||||
if ((x / 8) & 1) {
|
|
||||||
*dst++ = lb;
|
|
||||||
*dst++ = lg;
|
|
||||||
*dst++ = lr;
|
|
||||||
*dst++ = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*dst++ = b;
|
|
||||||
*dst++ = g;
|
|
||||||
*dst++ = r;
|
|
||||||
*dst++ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (int i=frame.pitch*frame.h/frame.GetBpp();--i>=0;) {
|
|
||||||
*dst++ = b;
|
|
||||||
*dst++ = g;
|
|
||||||
*dst++ = r;
|
|
||||||
*dst++ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Parsing constructor
|
|
||||||
/// @param filename
|
|
||||||
///
|
|
||||||
DummyVideoProvider::DummyVideoProvider(wxString filename)
|
|
||||||
{
|
|
||||||
wxString params;
|
|
||||||
if (!filename.StartsWith(_T("?dummy:"), ¶ms)) {
|
|
||||||
throw agi::FileNotFoundError("Attempted creating dummy video provider with non-dummy filename");
|
|
||||||
}
|
|
||||||
|
|
||||||
wxStringTokenizer t(params, _T(":"));
|
|
||||||
if (t.CountTokens() < 7) {
|
|
||||||
throw VideoOpenError("Too few fields in dummy video parameter list");
|
|
||||||
}
|
|
||||||
|
|
||||||
double fps;
|
|
||||||
long _frames, _width, _height, red, green, blue;
|
|
||||||
bool pattern = false;
|
|
||||||
|
|
||||||
wxString field = t.GetNextToken();
|
|
||||||
if (!field.ToDouble(&fps)) {
|
|
||||||
throw VideoOpenError("Unable to parse fps field in dummy video parameter list");
|
|
||||||
}
|
|
||||||
|
|
||||||
field = t.GetNextToken();
|
|
||||||
if (!field.ToLong(&_frames)) {
|
|
||||||
throw VideoOpenError("Unable to parse framecount field in dummy video parameter list");
|
|
||||||
}
|
|
||||||
|
|
||||||
field = t.GetNextToken();
|
|
||||||
if (!field.ToLong(&_width)) {
|
|
||||||
throw VideoOpenError("Unable to parse width field in dummy video parameter list");
|
|
||||||
}
|
|
||||||
|
|
||||||
field = t.GetNextToken();
|
|
||||||
if (!field.ToLong(&_height)) {
|
|
||||||
throw VideoOpenError("Unable to parse height field in dummy video parameter list");
|
|
||||||
}
|
|
||||||
|
|
||||||
field = t.GetNextToken();
|
|
||||||
if (!field.ToLong(&red)) {
|
|
||||||
throw VideoOpenError("Unable to parse red colour field in dummy video parameter list");
|
|
||||||
}
|
|
||||||
|
|
||||||
field = t.GetNextToken();
|
|
||||||
if (!field.ToLong(&green)) {
|
|
||||||
throw VideoOpenError("Unable to parse green colour field in dummy video parameter list");
|
|
||||||
}
|
|
||||||
|
|
||||||
field = t.GetNextToken();
|
|
||||||
if (!field.ToLong(&blue)) {
|
|
||||||
throw VideoOpenError("Unable to parse blue colour field in dummy video parameter list");
|
|
||||||
}
|
|
||||||
|
|
||||||
field = t.GetNextToken();
|
|
||||||
if (field == _T("c")) {
|
|
||||||
pattern = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Create(fps, _frames, _width, _height, wxColour(red, green, blue), pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Direct constructor
|
|
||||||
/// @param _fps
|
|
||||||
/// @param frames
|
|
||||||
/// @param _width
|
|
||||||
/// @param _height
|
|
||||||
/// @param colour
|
|
||||||
/// @param pattern
|
|
||||||
///
|
|
||||||
DummyVideoProvider::DummyVideoProvider(double _fps, int frames, int _width, int _height, const wxColour &colour, bool pattern) {
|
|
||||||
Create(_fps, frames, _width, _height, colour, pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Destructor
|
|
||||||
///
|
|
||||||
DummyVideoProvider::~DummyVideoProvider() {
|
|
||||||
frame.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Construct a fake filename describing the video
|
|
||||||
/// @param fps
|
|
||||||
/// @param frames
|
|
||||||
/// @param _width
|
|
||||||
/// @param _height
|
|
||||||
/// @param colour
|
|
||||||
/// @param pattern
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
wxString DummyVideoProvider::MakeFilename(double fps, int frames, int _width, int _height, const wxColour &colour, bool pattern) {
|
|
||||||
return wxString::Format(_T("?dummy:%f:%d:%d:%d:%d:%d:%d:%s"), fps, frames, _width, _height, colour.Red(), colour.Green(), colour.Blue(), pattern?_T("c"):_T(""));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get frame
|
|
||||||
/// @param n
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
const AegiVideoFrame DummyVideoProvider::GetFrame(int n) {
|
|
||||||
lastFrame = n;
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace media
|
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
// Copyright (c) 2007, Niels Martin Hansen
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file video_provider_dummy.h
|
|
||||||
/// @see video_provider_dummy.cpp
|
|
||||||
/// @ingroup video_input
|
|
||||||
///
|
|
||||||
|
|
||||||
// The dummy video provider needs a header, since it needs to be created directly as a special case
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <wx/colour.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "include/aegisub/video_provider.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class DummyVideoProvider
|
|
||||||
/// @brief DOCME
|
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class DummyVideoProvider : public VideoProvider {
|
|
||||||
/// DOCME
|
|
||||||
int lastFrame;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int framecount;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
agi::vfr::Framerate fps;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int width;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int height;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
AegiVideoFrame frame;
|
|
||||||
|
|
||||||
void Create(double fps, int frames, int _width, int _height, const wxColour &colour, bool pattern);
|
|
||||||
|
|
||||||
public:
|
|
||||||
DummyVideoProvider(wxString filename);
|
|
||||||
DummyVideoProvider(double fps, int frames, int _width, int _height, const wxColour &colour, bool pattern);
|
|
||||||
~DummyVideoProvider();
|
|
||||||
|
|
||||||
const AegiVideoFrame GetFrame(int n);
|
|
||||||
static wxString MakeFilename(double fps, int frames, int _width, int _height, const wxColour &colour, bool pattern);
|
|
||||||
|
|
||||||
int GetPosition() const { return lastFrame; }
|
|
||||||
int GetFrameCount() const { return framecount; }
|
|
||||||
int GetWidth() const { return width; }
|
|
||||||
int GetHeight() const { return height; }
|
|
||||||
agi::vfr::Framerate GetFPS() const { return fps; }
|
|
||||||
std::vector<int> GetKeyFrames() const { return std::vector<int>(); };
|
|
||||||
wxString GetDecoderName() const { return L"Dummy Video Provider"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace media
|
|
||||||
|
|
|
@ -1,277 +0,0 @@
|
||||||
// Copyright (c) 2008-2009, Karl Blomster <thefluff@aegisub.org>
|
|
||||||
//
|
|
||||||
// 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_video.h
|
|
||||||
/// @brief FFmpegSource Video support.
|
|
||||||
/// @ingroup fmms video
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifdef WITH_FFMPEGSOURCE
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#ifdef __WINDOWS__
|
|
||||||
#include <objbase.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
//#include <wx/choicdlg.h>
|
|
||||||
//#include <wx/msgdlg.h>
|
|
||||||
//#include <wx/utils.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "ffms_video.h"
|
|
||||||
#include "libaegisub/util.h"
|
|
||||||
|
|
||||||
//#include "aegisub_endian.h"
|
|
||||||
//#include "compat.h"
|
|
||||||
//#include "main.h"
|
|
||||||
//#include "utils.h"
|
|
||||||
//#include "video_context.h"
|
|
||||||
//#include "video_provider_ffmpegsource.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
namespace ffms {
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param filename The filename to open
|
|
||||||
FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(std::string filename)
|
|
||||||
: VideoSource(NULL)
|
|
||||||
, VideoInfo(NULL)
|
|
||||||
, Width(-1)
|
|
||||||
, Height(-1)
|
|
||||||
, FrameNumber(-1)
|
|
||||||
, COMInited(false)
|
|
||||||
{
|
|
||||||
#ifdef WIN32
|
|
||||||
HRESULT res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
||||||
if (SUCCEEDED(res))
|
|
||||||
COMInited = true;
|
|
||||||
else if (res != RPC_E_CHANGED_MODE)
|
|
||||||
throw VideoOpenError("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();
|
|
||||||
|
|
||||||
// and here we go
|
|
||||||
try {
|
|
||||||
LoadVideo(filename);
|
|
||||||
}
|
|
||||||
catch (std::string const& err) {
|
|
||||||
Close();
|
|
||||||
throw VideoOpenError(err);
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
Close();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Destructor
|
|
||||||
FFmpegSourceVideoProvider::~FFmpegSourceVideoProvider() {
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Opens video
|
|
||||||
/// @param filename The filename to open
|
|
||||||
void FFmpegSourceVideoProvider::LoadVideo(std::string filename) {
|
|
||||||
|
|
||||||
FFMS_Indexer *Indexer = FFMS_CreateIndexer(filename.c_str(), &ErrInfo);
|
|
||||||
if (Indexer == NULL) {
|
|
||||||
throw agi::FileNotFoundError(ErrInfo.Buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<int,std::string> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_VIDEO);
|
|
||||||
if (TrackList.size() <= 0)
|
|
||||||
throw VideoNotSupported("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 agi::UserCancelException("video 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// time to examine the index and check if the track we want is indexed
|
|
||||||
// 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) {
|
|
||||||
FFMS_Track *TempTrackData = FFMS_GetTrackFromIndex(Index, TrackNumber);
|
|
||||||
if (FFMS_GetNumFrames(TempTrackData) <= 0) {
|
|
||||||
IndexIsValid = false;
|
|
||||||
FFMS_DestroyIndex(Index);
|
|
||||||
Index = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// moment of truth
|
|
||||||
if (!IndexIsValid) {
|
|
||||||
// int TrackMask = OPT_GET("Provider/FFmpegSource/Index All Tracks")->GetBool() ? FFMS_TRACKMASK_ALL : FFMS_TRACKMASK_NONE;
|
|
||||||
int TrackMask = 1 ? FFMS_TRACKMASK_ALL : FFMS_TRACKMASK_NONE;
|
|
||||||
try {
|
|
||||||
// ignore audio decoding errors here, we don't care right now
|
|
||||||
Index = DoIndexing(Indexer, CacheName, TrackMask, FFMS_IEH_IGNORE);
|
|
||||||
}
|
|
||||||
catch (std::string err) {
|
|
||||||
throw VideoOpenError(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update access time of index file so it won't get cleaned away
|
|
||||||
//XXX: wxFileName(CacheName).Touch();
|
|
||||||
|
|
||||||
// track number still not set?
|
|
||||||
if (TrackNumber < 0) {
|
|
||||||
// just grab the first track
|
|
||||||
TrackNumber = FFMS_GetFirstIndexedTrackOfType(Index, FFMS_TYPE_VIDEO, &ErrInfo);
|
|
||||||
if (TrackNumber < 0) {
|
|
||||||
FFMS_DestroyIndex(Index);
|
|
||||||
Index = NULL;
|
|
||||||
throw VideoNotSupported(std::string("Couldn't find any video tracks: ") + ErrInfo.Buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set thread count
|
|
||||||
// int Threads = OPT_GET("Provider/Video/FFmpegSource/Decoding Threads")->GetInt();
|
|
||||||
int Threads = 1;
|
|
||||||
if (Threads < 1)
|
|
||||||
throw VideoOpenError("invalid decoding thread count");
|
|
||||||
|
|
||||||
// set seekmode
|
|
||||||
// TODO: give this its own option?
|
|
||||||
int SeekMode;
|
|
||||||
// if (OPT_GET("Provider/Video/FFmpegSource/Unsafe Seeking")->GetBool())
|
|
||||||
// SeekMode = FFMS_SEEK_UNSAFE;
|
|
||||||
// else
|
|
||||||
SeekMode = FFMS_SEEK_NORMAL;
|
|
||||||
|
|
||||||
VideoSource = FFMS_CreateVideoSource(filename.c_str(), TrackNumber, Index, Threads, SeekMode, &ErrInfo);
|
|
||||||
FFMS_DestroyIndex(Index);
|
|
||||||
Index = NULL;
|
|
||||||
if (VideoSource == NULL) {
|
|
||||||
throw VideoOpenError(std::string("Failed to open video track: ") + ErrInfo.Buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// load video properties
|
|
||||||
VideoInfo = FFMS_GetVideoProperties(VideoSource);
|
|
||||||
|
|
||||||
const FFMS_Frame *TempFrame = FFMS_GetFrame(VideoSource, 0, &ErrInfo);
|
|
||||||
if (TempFrame == NULL) {
|
|
||||||
throw VideoOpenError(std::string("Failed to decode first frame: ") + ErrInfo.Buffer);
|
|
||||||
}
|
|
||||||
Width = TempFrame->EncodedWidth;
|
|
||||||
Height = TempFrame->EncodedHeight;
|
|
||||||
|
|
||||||
if (FFMS_SetOutputFormatV(VideoSource, 1LL << FFMS_GetPixFmt("bgra"), Width, Height, FFMS_RESIZER_BICUBIC, &ErrInfo)) {
|
|
||||||
throw VideoOpenError(std::string("Failed to set output format: ") + ErrInfo.Buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get frame info data
|
|
||||||
FFMS_Track *FrameData = FFMS_GetTrackFromVideo(VideoSource);
|
|
||||||
if (FrameData == NULL)
|
|
||||||
throw VideoOpenError("failed to get frame data");
|
|
||||||
const FFMS_TrackTimeBase *TimeBase = FFMS_GetTimeBase(FrameData);
|
|
||||||
if (TimeBase == NULL)
|
|
||||||
throw VideoOpenError("failed to get track time base");
|
|
||||||
|
|
||||||
const FFMS_FrameInfo *CurFrameData;
|
|
||||||
|
|
||||||
// build list of keyframes and timecodes
|
|
||||||
std::vector<int> TimecodesVector;
|
|
||||||
for (int CurFrameNum = 0; CurFrameNum < VideoInfo->NumFrames; CurFrameNum++) {
|
|
||||||
CurFrameData = FFMS_GetFrameInfo(FrameData, CurFrameNum);
|
|
||||||
if (CurFrameData == NULL) {
|
|
||||||
//XXX throw VideoOpenError(STD_STR(wxString::Format(L"Couldn't get info about frame %d", CurFrameNum)));
|
|
||||||
throw VideoOpenError("Couldn't get info about frame %d");
|
|
||||||
}
|
|
||||||
|
|
||||||
// keyframe?
|
|
||||||
if (CurFrameData->KeyFrame)
|
|
||||||
KeyFramesList.push_back(CurFrameNum);
|
|
||||||
|
|
||||||
// calculate timestamp and add to timecodes vector
|
|
||||||
int Timestamp = (int)((CurFrameData->PTS * TimeBase->Num) / TimeBase->Den);
|
|
||||||
TimecodesVector.push_back(Timestamp);
|
|
||||||
}
|
|
||||||
Timecodes = agi::vfr::Framerate(TimecodesVector);
|
|
||||||
|
|
||||||
FrameNumber = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Close video
|
|
||||||
///
|
|
||||||
void FFmpegSourceVideoProvider::Close() {
|
|
||||||
if (VideoSource) FFMS_DestroyVideoSource(VideoSource);
|
|
||||||
#ifdef WIN32
|
|
||||||
if (COMInited)
|
|
||||||
CoUninitialize();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Get frame
|
|
||||||
/// @param _n
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
const media::AegiVideoFrame FFmpegSourceVideoProvider::GetFrame(int n) {
|
|
||||||
FrameNumber = agi::util::mid(0, n, GetFrameCount() - 1);
|
|
||||||
|
|
||||||
// decode frame
|
|
||||||
const FFMS_Frame *SrcFrame = FFMS_GetFrame(VideoSource, FrameNumber, &ErrInfo);
|
|
||||||
if (SrcFrame == NULL) {
|
|
||||||
throw VideoDecodeError(std::string("Failed to retrieve frame:") + ErrInfo.Buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
CurFrame.SetTo(SrcFrame->Data[0], Width, Height, SrcFrame->Linesize[0]);
|
|
||||||
return CurFrame;
|
|
||||||
}
|
|
||||||
#endif /* WITH_FFMPEGSOURCE */
|
|
||||||
|
|
||||||
} // namespace ffms
|
|
||||||
} // namespace media
|
|
|
@ -1,80 +0,0 @@
|
||||||
// Copyright (c) 2008-2009, Karl Blomster <thefluff@aegisub.org>
|
|
||||||
//
|
|
||||||
// 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_video.h
|
|
||||||
/// @brief FFmpegSource Video support.
|
|
||||||
/// @ingroup fmms video
|
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <vector>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libaegisub/exception.h>
|
|
||||||
|
|
||||||
#include "../../libffms/include/ffms.h"
|
|
||||||
#include "libaegisub/vfr.h"
|
|
||||||
#include "../common/ffms_common.h"
|
|
||||||
#include "libmedia/video.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
namespace ffms {
|
|
||||||
|
|
||||||
/// @class FFmpegSourceVideoProvider
|
|
||||||
/// @brief Implements video loading through the FFMS library.
|
|
||||||
class FFmpegSourceVideoProvider : public VideoProvider, FFmpegSourceProvider {
|
|
||||||
private:
|
|
||||||
FFMS_VideoSource *VideoSource; /// video source object
|
|
||||||
const FFMS_VideoProperties *VideoInfo; /// video properties
|
|
||||||
|
|
||||||
int Width; /// width in pixels
|
|
||||||
int Height; /// height in pixels
|
|
||||||
int FrameNumber; /// current framenumber
|
|
||||||
std::vector<int> KeyFramesList; /// list of keyframes
|
|
||||||
agi::vfr::Framerate Timecodes; /// vfr object
|
|
||||||
bool COMInited; /// COM initialization state
|
|
||||||
|
|
||||||
media::AegiVideoFrame CurFrame; /// current video frame
|
|
||||||
|
|
||||||
char FFMSErrMsg[1024]; /// FFMS error message
|
|
||||||
FFMS_ErrorInfo ErrInfo; /// FFMS error codes/messages
|
|
||||||
|
|
||||||
void LoadVideo(std::string filename);
|
|
||||||
void Close();
|
|
||||||
|
|
||||||
public:
|
|
||||||
FFmpegSourceVideoProvider(std::string filename);
|
|
||||||
~FFmpegSourceVideoProvider();
|
|
||||||
|
|
||||||
const media::AegiVideoFrame GetFrame(int n);
|
|
||||||
|
|
||||||
int GetPosition() const { return FrameNumber; }
|
|
||||||
int GetFrameCount() const { return VideoInfo->NumFrames; }
|
|
||||||
int GetWidth() const { return Width; }
|
|
||||||
int GetHeight() const { return Height; }
|
|
||||||
agi::vfr::Framerate GetFPS() const { return Timecodes; }
|
|
||||||
|
|
||||||
/// @brief Gets a list of keyframes
|
|
||||||
/// @return Returns a vector<int> of keyframes.
|
|
||||||
std::vector<int> GetKeyFrames() const { return KeyFramesList; };
|
|
||||||
std::string GetDecoderName() const { return "FFmpegSource"; }
|
|
||||||
/// @brief Gets the desired cache behavior.
|
|
||||||
/// @return Returns true.
|
|
||||||
bool WantsCaching() const { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace ffms
|
|
||||||
} // namespace media
|
|
|
@ -1,433 +0,0 @@
|
||||||
// Copyright (c) 2009, Karl Blomster
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file video_provider_yuv4mpeg.cpp
|
|
||||||
/// @brief Video provider reading YUV4MPEG files directly without depending on external libraries
|
|
||||||
/// @ingroup video_input
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <libaegisub/log.h>
|
|
||||||
#include <libaegisub/util.h>
|
|
||||||
|
|
||||||
#include "yuv4mpeg.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
// All of this cstdio bogus is because of one reason and one reason only:
|
|
||||||
// MICROSOFT'S IMPLEMENTATION OF STD::FSTREAM DOES NOT SUPPORT FILES LARGER THAN 2 GB.
|
|
||||||
// (yes, really)
|
|
||||||
// With cstdio it's at least possible to work around the problem...
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define fseeko _fseeki64
|
|
||||||
#define ftello _ftelli64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
/// @param filename The filename to open
|
|
||||||
YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(std::string filename)
|
|
||||||
: sf(NULL)
|
|
||||||
, inited(false)
|
|
||||||
, w (0)
|
|
||||||
, h (0)
|
|
||||||
, num_frames(-1)
|
|
||||||
, cur_fn(-1)
|
|
||||||
, pixfmt(Y4M_PIXFMT_NONE)
|
|
||||||
, imode(Y4M_ILACE_NOTSET)
|
|
||||||
{
|
|
||||||
fps_rat.num = -1;
|
|
||||||
fps_rat.den = 1;
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
sf = _wfopen(filename.c_str(), L"rb");
|
|
||||||
#else
|
|
||||||
sf = fopen(filename.c_str(), "rb");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (sf == NULL) throw agi::FileNotFoundError(filename);
|
|
||||||
|
|
||||||
CheckFileFormat();
|
|
||||||
|
|
||||||
ParseFileHeader(ReadHeader(0, false));
|
|
||||||
|
|
||||||
if (w <= 0 || h <= 0)
|
|
||||||
throw VideoOpenError("Invalid resolution");
|
|
||||||
if (fps_rat.num <= 0 || fps_rat.den <= 0) {
|
|
||||||
fps_rat.num = 25;
|
|
||||||
fps_rat.den = 1;
|
|
||||||
LOG_D("provider/video/yuv4mpeg") << "framerate info unavailable, assuming 25fps";
|
|
||||||
}
|
|
||||||
if (pixfmt == Y4M_PIXFMT_NONE)
|
|
||||||
pixfmt = Y4M_PIXFMT_420JPEG;
|
|
||||||
if (imode == Y4M_ILACE_NOTSET)
|
|
||||||
imode = Y4M_ILACE_UNKNOWN;
|
|
||||||
|
|
||||||
luma_sz = w * h;
|
|
||||||
switch (pixfmt) {
|
|
||||||
case Y4M_PIXFMT_420JPEG:
|
|
||||||
case Y4M_PIXFMT_420MPEG2:
|
|
||||||
case Y4M_PIXFMT_420PALDV:
|
|
||||||
chroma_sz = (w * h) >> 2; break;
|
|
||||||
case Y4M_PIXFMT_422:
|
|
||||||
chroma_sz = (w * h) >> 1; break;
|
|
||||||
/// @todo add support for more pixel formats
|
|
||||||
default:
|
|
||||||
throw VideoOpenError("Unsupported pixel format");
|
|
||||||
}
|
|
||||||
frame_sz = luma_sz + chroma_sz*2;
|
|
||||||
|
|
||||||
num_frames = IndexFile();
|
|
||||||
if (num_frames <= 0 || seek_table.empty())
|
|
||||||
throw VideoOpenError("Unable to determine file length");
|
|
||||||
cur_fn = 0;
|
|
||||||
|
|
||||||
fseeko(sf, 0, SEEK_SET);
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
if (sf) fclose(sf);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Destructor
|
|
||||||
YUV4MPEGVideoProvider::~YUV4MPEGVideoProvider() {
|
|
||||||
if (sf) fclose(sf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Checks if the file is an YUV4MPEG file or not
|
|
||||||
/// Note that it reports the error by throwing an exception,
|
|
||||||
/// not by returning a false value.
|
|
||||||
void YUV4MPEGVideoProvider::CheckFileFormat() {
|
|
||||||
char buf[10];
|
|
||||||
if (fread(buf, 10, 1, sf) != 1)
|
|
||||||
throw VideoNotSupported("CheckFileFormat: Failed reading header");
|
|
||||||
if (strncmp("YUV4MPEG2 ", buf, 10))
|
|
||||||
throw VideoNotSupported("CheckFileFormat: File is not a YUV4MPEG file (bad magic)");
|
|
||||||
|
|
||||||
fseeko(sf, 0, SEEK_SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Read a frame or file header at a given file position
|
|
||||||
/// @param startpos The byte offset at where to start reading
|
|
||||||
/// @param reset_pos If true, the function will reset the file position to what it was before the function call before returning
|
|
||||||
/// @return A list of parameters
|
|
||||||
std::vector<std::string> YUV4MPEGVideoProvider::ReadHeader(int64_t startpos, bool reset_pos) {
|
|
||||||
int64_t oldpos = ftello(sf);
|
|
||||||
std::vector<std::string> tags;
|
|
||||||
std::string curtag;
|
|
||||||
int bytesread = 0;
|
|
||||||
int buf;
|
|
||||||
|
|
||||||
if (fseeko(sf, startpos, SEEK_SET))
|
|
||||||
throw VideoOpenError("YUV4MPEG video provider: ReadHeader: failed seeking to position %d"); //XXX:, startpos)));
|
|
||||||
|
|
||||||
// read header until terminating newline (0x0A) is found
|
|
||||||
while ((buf = fgetc(sf)) != 0x0A) {
|
|
||||||
if (ferror(sf))
|
|
||||||
throw VideoOpenError("ReadHeader: Failed to read from file");
|
|
||||||
if (feof(sf)) {
|
|
||||||
// you know, this is one of the places where it would be really nice
|
|
||||||
// to be able to throw an exception object that tells the caller that EOF was reached
|
|
||||||
LOG_D("provider/video/yuv4mpeg") << "ReadHeader: Reached EOF, returning";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// some basic low-effort sanity checking
|
|
||||||
if (buf == 0x00)
|
|
||||||
throw VideoOpenError("ReadHeader: Malformed header (unexpected NUL)");
|
|
||||||
if (++bytesread >= YUV4MPEG_HEADER_MAXLEN)
|
|
||||||
throw VideoOpenError("ReadHeader: Malformed header (no terminating newline found)");
|
|
||||||
|
|
||||||
// found a new tag
|
|
||||||
if (buf == 0x20) {
|
|
||||||
tags.push_back(curtag);
|
|
||||||
curtag.clear();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
curtag.append((const char*)buf);
|
|
||||||
}
|
|
||||||
// if only one tag with no trailing space was found (possible in the
|
|
||||||
// FRAME header case), make sure we get it
|
|
||||||
if (!curtag.empty()) {
|
|
||||||
tags.push_back(curtag);
|
|
||||||
curtag.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reset_pos)
|
|
||||||
fseeko(sf, oldpos, SEEK_SET);
|
|
||||||
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Parses a list of parameters and sets reader state accordingly
|
|
||||||
/// @param tags The list of parameters to parse
|
|
||||||
void YUV4MPEGVideoProvider::ParseFileHeader(const std::vector<std::string>& tags) {
|
|
||||||
if (tags.size() <= 1)
|
|
||||||
throw VideoOpenError("ParseFileHeader: contentless header");
|
|
||||||
if (tags.front() == "YUV4MPEG2")
|
|
||||||
throw VideoOpenError("ParseFileHeader: malformed header (bad magic)");
|
|
||||||
|
|
||||||
// temporary stuff
|
|
||||||
int t_w = -1;
|
|
||||||
int t_h = -1;
|
|
||||||
int t_fps_num = -1;
|
|
||||||
int t_fps_den = -1;
|
|
||||||
Y4M_InterlacingMode t_imode = Y4M_ILACE_NOTSET;
|
|
||||||
Y4M_PixelFormat t_pixfmt = Y4M_PIXFMT_NONE;
|
|
||||||
|
|
||||||
for (unsigned i = 1; i < tags.size(); i++) {
|
|
||||||
std::string tag;
|
|
||||||
tag = tags[i];
|
|
||||||
|
|
||||||
if (tag.find("W") == 0) {
|
|
||||||
tag.erase(0,1);
|
|
||||||
t_w = agi::util::strtoi(tag);
|
|
||||||
if (t_w == 0)
|
|
||||||
throw VideoOpenError("ParseFileHeader: invalid width");
|
|
||||||
}
|
|
||||||
else if (tag.find("H") == 0) {
|
|
||||||
tag.erase(0,1);
|
|
||||||
t_h = agi::util::strtoi(tag);
|
|
||||||
if (t_h == 0)
|
|
||||||
throw VideoOpenError("ParseFileHeader: invalid height");
|
|
||||||
}
|
|
||||||
else if (tag.find("F") == 0) {
|
|
||||||
tag.erase(0,1);
|
|
||||||
int i = tag.find(":");
|
|
||||||
std::string num(tag.substr(0,i));
|
|
||||||
std::string den(tag.substr(i+1,den.size()));
|
|
||||||
t_fps_num = agi::util::strtoi(num);
|
|
||||||
t_fps_den = agi::util::strtoi(den);
|
|
||||||
if ((t_fps_num == 0) || (t_fps_den == 0))
|
|
||||||
throw VideoOpenError("ParseFileHeader: invalid framerate");
|
|
||||||
}
|
|
||||||
else if (tag.find("C") == 0) {
|
|
||||||
tag.erase(0,1);
|
|
||||||
// technically this should probably be case sensitive,
|
|
||||||
// but being liberal in what you accept doesn't hurt
|
|
||||||
agi::util::str_lower(tag);
|
|
||||||
if (tag == "420") t_pixfmt = Y4M_PIXFMT_420JPEG; // is this really correct?
|
|
||||||
else if (tag == "420jpeg") t_pixfmt = Y4M_PIXFMT_420JPEG;
|
|
||||||
else if (tag == "420mpeg2") t_pixfmt = Y4M_PIXFMT_420MPEG2;
|
|
||||||
else if (tag == "420paldv") t_pixfmt = Y4M_PIXFMT_420PALDV;
|
|
||||||
else if (tag == "411") t_pixfmt = Y4M_PIXFMT_411;
|
|
||||||
else if (tag == "422") t_pixfmt = Y4M_PIXFMT_422;
|
|
||||||
else if (tag == "444") t_pixfmt = Y4M_PIXFMT_444;
|
|
||||||
else if (tag == "444alpha") t_pixfmt = Y4M_PIXFMT_444ALPHA;
|
|
||||||
else if (tag == "mono") t_pixfmt = Y4M_PIXFMT_MONO;
|
|
||||||
else
|
|
||||||
throw VideoOpenError("ParseFileHeader: invalid or unknown colorspace");
|
|
||||||
}
|
|
||||||
else if (tag.find("I") == 0) {
|
|
||||||
tag.erase(0,1);
|
|
||||||
agi::util::str_lower(tag);
|
|
||||||
if (tag == "p") t_imode = Y4M_ILACE_PROGRESSIVE;
|
|
||||||
else if (tag == "t") t_imode = Y4M_ILACE_TFF;
|
|
||||||
else if (tag == "b") t_imode = Y4M_ILACE_BFF;
|
|
||||||
else if (tag == "m") t_imode = Y4M_ILACE_MIXED;
|
|
||||||
else if (tag == "?") t_imode = Y4M_ILACE_UNKNOWN;
|
|
||||||
else
|
|
||||||
throw VideoOpenError("ParseFileHeader: invalid or unknown interlacing mode");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LOG_D("provider/video/yuv4mpeg") << "Unparsed tag: " << tags[i].c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The point of all this is to allow multiple YUV4MPEG2 headers in a single file
|
|
||||||
// (can happen if you concat several files) as long as they have identical
|
|
||||||
// header flags. The spec doesn't explicitly say you have to allow this,
|
|
||||||
// but the "reference implementation" (mjpegtools) does, so I'm doing it too.
|
|
||||||
if (inited) {
|
|
||||||
if (t_w > 0 && t_w != w)
|
|
||||||
throw VideoOpenError("ParseFileHeader: illegal width change");
|
|
||||||
if (t_h > 0 && t_h != h)
|
|
||||||
throw VideoOpenError("ParseFileHeader: illegal height change");
|
|
||||||
if ((t_fps_num > 0 && t_fps_den > 0) && (t_fps_num != fps_rat.num || t_fps_den != fps_rat.den))
|
|
||||||
throw VideoOpenError("ParseFileHeader: illegal framerate change");
|
|
||||||
if (t_pixfmt != Y4M_PIXFMT_NONE && t_pixfmt != pixfmt)
|
|
||||||
throw VideoOpenError("ParseFileHeader: illegal colorspace change");
|
|
||||||
if (t_imode != Y4M_ILACE_NOTSET && t_imode != imode)
|
|
||||||
throw VideoOpenError("ParseFileHeader: illegal interlacing mode change");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
w = t_w;
|
|
||||||
h = t_h;
|
|
||||||
fps_rat.num = t_fps_num;
|
|
||||||
fps_rat.den = t_fps_den;
|
|
||||||
pixfmt = t_pixfmt != Y4M_PIXFMT_NONE ? t_pixfmt : Y4M_PIXFMT_420JPEG;
|
|
||||||
imode = t_imode != Y4M_ILACE_NOTSET ? t_imode : Y4M_ILACE_UNKNOWN;
|
|
||||||
fps = double(fps_rat.num) / fps_rat.den;
|
|
||||||
inited = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Parses a frame header
|
|
||||||
/// @param tags The list of parameters to parse
|
|
||||||
/// @return The flags set, as a binary mask
|
|
||||||
/// This function is currently unimplemented (it will always return Y4M_FFLAG_NONE).
|
|
||||||
YUV4MPEGVideoProvider::Y4M_FrameFlags YUV4MPEGVideoProvider::ParseFrameHeader(const std::vector<std::string>& tags) {
|
|
||||||
if (tags.front() == "FRAME")
|
|
||||||
throw VideoOpenError("ParseFrameHeader: malformed frame header (bad magic)");
|
|
||||||
|
|
||||||
/// @todo implement parsing of frame flags
|
|
||||||
|
|
||||||
return Y4M_FFLAG_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Indexes the file
|
|
||||||
/// @return The number of frames found in the file
|
|
||||||
/// This function goes through the file, finds and parses all file and frame headers,
|
|
||||||
/// and creates a seek table that lists the byte positions of all frames so seeking
|
|
||||||
/// can easily be done.
|
|
||||||
int YUV4MPEGVideoProvider::IndexFile() {
|
|
||||||
int framecount = 0;
|
|
||||||
int64_t curpos = ftello(sf);
|
|
||||||
|
|
||||||
// the ParseFileHeader() call in LoadVideo() will already have read
|
|
||||||
// the file header for us and set the seek position correctly
|
|
||||||
while (true) {
|
|
||||||
curpos = ftello(sf); // update position
|
|
||||||
// continue reading headers until no more are found
|
|
||||||
std::vector<std::string> tags = ReadHeader(curpos, false);
|
|
||||||
curpos = ftello(sf);
|
|
||||||
|
|
||||||
if (tags.empty())
|
|
||||||
break; // no more headers
|
|
||||||
|
|
||||||
Y4M_FrameFlags flags = Y4M_FFLAG_NOTSET;
|
|
||||||
if (tags.front() != "YUV4MPEG2") {
|
|
||||||
ParseFileHeader(tags);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (tags.front() != "FRAME")
|
|
||||||
flags = ParseFrameHeader(tags);
|
|
||||||
|
|
||||||
if (flags == Y4M_FFLAG_NONE) {
|
|
||||||
framecount++;
|
|
||||||
seek_table.push_back(curpos);
|
|
||||||
// seek to next frame header start position
|
|
||||||
if (fseeko(sf, frame_sz, SEEK_CUR))
|
|
||||||
throw VideoOpenError("IndexFile: failed seeking to position %d"); //XXX: , curpos + frame_sz)));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/// @todo implement rff flags etc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return framecount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://bob.allegronetwork.com/prog/tricks.html#clamp
|
|
||||||
static inline int clamp(int x) {
|
|
||||||
x &= (~x) >> 31;
|
|
||||||
x -= 255;
|
|
||||||
x &= x >> 31;
|
|
||||||
x += 255;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Gets a given frame
|
|
||||||
/// @param n The frame number to return
|
|
||||||
/// @return The video frame
|
|
||||||
const AegiVideoFrame YUV4MPEGVideoProvider::GetFrame(int n) {
|
|
||||||
cur_fn = agi::util::mid(0, n, num_frames - 1);
|
|
||||||
|
|
||||||
int uv_width = w / 2;
|
|
||||||
switch (pixfmt) {
|
|
||||||
case Y4M_PIXFMT_420JPEG:
|
|
||||||
case Y4M_PIXFMT_420MPEG2:
|
|
||||||
case Y4M_PIXFMT_420PALDV:
|
|
||||||
break;
|
|
||||||
/// @todo add support for more pixel formats
|
|
||||||
default:
|
|
||||||
throw "YUV4MPEG video provider: GetFrame: Unsupported source colorspace";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> planes[3];
|
|
||||||
planes[0].resize(luma_sz);
|
|
||||||
planes[1].resize(chroma_sz);
|
|
||||||
planes[2].resize(chroma_sz);
|
|
||||||
|
|
||||||
fseeko(sf, seek_table[n], SEEK_SET);
|
|
||||||
size_t ret;
|
|
||||||
ret = fread(&planes[0][0], luma_sz, 1, sf);
|
|
||||||
if (ret != 1 || feof(sf) || ferror(sf))
|
|
||||||
throw "YUV4MPEG video provider: GetFrame: failed to read luma plane";
|
|
||||||
for (int i = 1; i <= 2; i++) {
|
|
||||||
ret = fread(&planes[i][0], chroma_sz, 1, sf);
|
|
||||||
if (ret != 1 || feof(sf) || ferror(sf))
|
|
||||||
throw "YUV4MPEG video provider: GetFrame: failed to read chroma planes";
|
|
||||||
}
|
|
||||||
|
|
||||||
AegiVideoFrame dst_frame;
|
|
||||||
dst_frame.invertChannels = true;
|
|
||||||
dst_frame.w = w;
|
|
||||||
dst_frame.h = h;
|
|
||||||
dst_frame.pitch = w * 4;
|
|
||||||
dst_frame.Allocate();
|
|
||||||
|
|
||||||
const unsigned char *src_y = &planes[0][0];
|
|
||||||
const unsigned char *src_u = &planes[1][0];
|
|
||||||
const unsigned char *src_v = &planes[2][0];
|
|
||||||
unsigned char *dst = dst_frame.data;
|
|
||||||
|
|
||||||
for (int py = 0; py < h; ++py) {
|
|
||||||
for (int px = 0; px < w / 2; ++px) {
|
|
||||||
const int u = *src_u++ - 128;
|
|
||||||
const int v = *src_v++ - 128;
|
|
||||||
for (unsigned int i = 0; i < 2; ++i) {
|
|
||||||
const int y = (*src_y++ - 16) * 298;
|
|
||||||
|
|
||||||
*dst++ = clamp((y + 516 * u + 128) >> 8); // Blue
|
|
||||||
*dst++ = clamp((y - 100 * u - 208 * v + 128) >> 8); // Green
|
|
||||||
*dst++ = clamp((y + 409 * v + 128) >> 8); // Red
|
|
||||||
*dst++ = 0; // Alpha
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Roll back u/v on even lines
|
|
||||||
if (!(py & 1)) {
|
|
||||||
src_u -= uv_width;
|
|
||||||
src_v -= uv_width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dst_frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace media
|
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
// Copyright (c) 2009, Karl Blomster
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
|
||||||
// and/or other materials provided with the distribution.
|
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
||||||
// may be used to endorse or promote products derived from this software
|
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Aegisub Project http://www.aegisub.org/
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
/// @file video_provider_yuv4mpeg.h
|
|
||||||
/// @see video_provider_yuv4mpeg.cpp
|
|
||||||
/// @ingroup video_input
|
|
||||||
///
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef MAGI_PRE
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "libmedia/video.h"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
/// the maximum allowed header length, in bytes
|
|
||||||
#define YUV4MPEG_HEADER_MAXLEN 128
|
|
||||||
|
|
||||||
/// @class YUV4MPEGVideoProvider
|
|
||||||
/// @brief Implements reading of YUV4MPEG uncompressed video files
|
|
||||||
class YUV4MPEGVideoProvider : public VideoProvider {
|
|
||||||
/// Pixel formats
|
|
||||||
enum Y4M_PixelFormat {
|
|
||||||
Y4M_PIXFMT_NONE = -1, /// not set/unknown
|
|
||||||
|
|
||||||
/// 4:2:0 sampling variants.
|
|
||||||
/// afaict the only difference between these three
|
|
||||||
/// is the chroma sample location, and nobody cares about that.
|
|
||||||
Y4M_PIXFMT_420JPEG, /// 4:2:0, H/V centered, for JPEG/MPEG-1
|
|
||||||
Y4M_PIXFMT_420MPEG2, /// 4:2:0, H cosited, for MPEG-2
|
|
||||||
Y4M_PIXFMT_420PALDV, /// 4:2:0, alternating Cb/Cr, for PAL-DV
|
|
||||||
|
|
||||||
Y4M_PIXFMT_411, /// 4:1:1, H cosited
|
|
||||||
Y4M_PIXFMT_422, /// 4:2:2, H cosited
|
|
||||||
Y4M_PIXFMT_444, /// 4:4:4, i.e. no chroma subsampling
|
|
||||||
Y4M_PIXFMT_444ALPHA, /// 4:4:4 plus alpha channel
|
|
||||||
|
|
||||||
Y4M_PIXFMT_MONO, /// luma only (grayscale)
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// Interlacing mode for an entire stream
|
|
||||||
enum Y4M_InterlacingMode {
|
|
||||||
Y4M_ILACE_NOTSET = -1, /// undefined
|
|
||||||
Y4M_ILACE_PROGRESSIVE, /// progressive (no interlacing)
|
|
||||||
|
|
||||||
Y4M_ILACE_TFF, /// interlaced, top field first
|
|
||||||
Y4M_ILACE_BFF, /// interlaced, bottom field first
|
|
||||||
|
|
||||||
Y4M_ILACE_MIXED, /// mixed interlaced/progressive, possibly with RFF flags
|
|
||||||
Y4M_ILACE_UNKNOWN, /// unknown interlacing mode (not the same as undefined)
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// Frame information flags
|
|
||||||
enum Y4M_FrameFlags {
|
|
||||||
Y4M_FFLAG_NOTSET = -1, /// undefined
|
|
||||||
Y4M_FFLAG_NONE = 0x0000, /// no flags set
|
|
||||||
|
|
||||||
/// field order/repeat field flags
|
|
||||||
Y4M_FFLAG_R_TFF = 0x0001, /// top field first
|
|
||||||
Y4M_FFLAG_R_TFF_R = 0x0002, /// top field first, and repeat that field
|
|
||||||
Y4M_FFLAG_R_BFF = 0x0004, /// bottom field first
|
|
||||||
Y4M_FFLAG_R_BFF_R = 0x0008, /// bottom field first, and repeat that field
|
|
||||||
Y4M_FFLAG_R_P = 0x0010, /// progressive
|
|
||||||
Y4M_FFLAG_R_P_R = 0x0020, /// progressive, and repeat frame once
|
|
||||||
Y4M_FFLAG_R_P_RR = 0x0040, /// progressive, and repeat frame twice
|
|
||||||
|
|
||||||
/// temporal sampling flags
|
|
||||||
Y4M_FFLAG_T_P = 0x0080, /// progressive (fields sampled at the same time)
|
|
||||||
Y4M_FFLAG_T_I = 0x0100, /// interlaced (fields sampled at different times)
|
|
||||||
|
|
||||||
/// chroma subsampling flags
|
|
||||||
Y4M_FFLAG_C_P = 0x0200, /// progressive (whole frame subsampled)
|
|
||||||
Y4M_FFLAG_C_I = 0x0400, /// interlaced (fields subsampled independently)
|
|
||||||
Y4M_FFLAG_C_UNKNOWN = 0x0800, /// unknown (only allowed for non-4:2:0 sampling)
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
FILE *sf; /// source file
|
|
||||||
bool inited; /// initialization state
|
|
||||||
|
|
||||||
int w, h; /// frame width/height
|
|
||||||
int num_frames; /// length of file in frames
|
|
||||||
int frame_sz; /// size of each frame in bytes
|
|
||||||
int luma_sz; /// size of the luma plane of each frame, in bytes
|
|
||||||
int chroma_sz; /// size of one of the two chroma planes of each frame, in bytes
|
|
||||||
int cur_fn; /// current frame number
|
|
||||||
|
|
||||||
Y4M_PixelFormat pixfmt; /// colorspace/pixel format
|
|
||||||
Y4M_InterlacingMode imode; /// interlacing mode (for the entire stream)
|
|
||||||
struct {
|
|
||||||
int num; /// numerator
|
|
||||||
int den; /// denominator
|
|
||||||
} fps_rat; /// framerate
|
|
||||||
|
|
||||||
agi::vfr::Framerate fps;
|
|
||||||
|
|
||||||
/// a list of byte positions detailing where in the file
|
|
||||||
/// each frame header can be found
|
|
||||||
std::vector<int64_t> seek_table;
|
|
||||||
|
|
||||||
void CheckFileFormat();
|
|
||||||
void ParseFileHeader(const std::vector<std::string>& tags);
|
|
||||||
Y4M_FrameFlags ParseFrameHeader(const std::vector<std::string>& tags);
|
|
||||||
std::vector<std::string> ReadHeader(int64_t startpos, bool reset_pos=false);
|
|
||||||
int IndexFile();
|
|
||||||
|
|
||||||
public:
|
|
||||||
YUV4MPEGVideoProvider(std::string filename);
|
|
||||||
~YUV4MPEGVideoProvider();
|
|
||||||
|
|
||||||
const AegiVideoFrame GetFrame(int n);
|
|
||||||
|
|
||||||
int GetPosition() const { return cur_fn; }
|
|
||||||
int GetFrameCount() const { return num_frames; }
|
|
||||||
int GetWidth() const { return w; }
|
|
||||||
int GetHeight() const { return h; }
|
|
||||||
agi::vfr::Framerate GetFPS() const { return fps; }
|
|
||||||
std::vector<int> GetKeyFrames() const { return std::vector<int>(); };
|
|
||||||
std::string GetDecoderName() const { return "YU4MPEG"; };
|
|
||||||
bool WantsCaching() const { return true; };
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace media
|
|
||||||
|
|
Loading…
Reference in a new issue