Copy and rename files from src/ into libmedia/. A lot of these headers will have to go into include/*. At the moment there's no point sorting that out until we see the final relation and external api requirements. I'll first unhook all of this code from the UI to figure out what common changes are required then design the final changes for an api that the UI can use sanely.

Originally committed to SVN as r5290.
This commit is contained in:
Amar Takhar 2011-02-05 23:34:12 +00:00
parent 7508420000
commit 72211ad685
30 changed files with 4540 additions and 0 deletions

View File

@ -0,0 +1,170 @@
// 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"
/// @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

View File

@ -0,0 +1,68 @@
// 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"
/// 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

View File

@ -0,0 +1,220 @@
// 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 "audio_provider_convert.h"
#include "audio_provider_downmix.h"
/// @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;
}

View File

@ -0,0 +1,70 @@
// 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 "include/aegisub/audio_provider.h"
#ifndef AGI_PRE
#include <tr1/memory>
#endif
/// 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;
wxString GetFilename() const { return source->GetFilename(); }
};
AudioProvider *CreateConvertAudioProvider(AudioProvider *source_provider);

View File

@ -0,0 +1,125 @@
// 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 "audio_provider_downmix.h"
/// @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;
}

View File

@ -0,0 +1,61 @@
// 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 "include/aegisub/audio_provider.h"
#ifndef AGI_PRE
#include <tr1/memory>
#endif
/// 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;
};

View File

@ -0,0 +1,54 @@
// 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 "include/aegisub/audio_provider.h"
/// 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;
};

View File

@ -0,0 +1,76 @@
// 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 "audio_provider_dummy.h"
#include "utils.h"
/// @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;
}
}

View File

@ -0,0 +1,631 @@
// 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
#include <wx/file.h>
#include <wx/filename.h>
#include <wx/log.h>
#endif
#include <libaegisub/log.h>
#include "aegisub_endian.h"
#include "audio_provider_pcm.h"
#include "compat.h"
#include "utils.h"
/// @brief DOCME
/// @param filename
///
PCMAudioProvider::PCMAudioProvider(const wxString &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.mb_str(*wxConvFileName), O_RDONLY);
if (file_handle == -1) {
throw agi::FileNotFoundError(STD_STR(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 wxString &_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 wxString &_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 wxString &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);
}
}

View File

@ -0,0 +1,115 @@
// 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>
#include <wx/file.h>
#include <wx/thread.h>
#endif
#ifdef _WIN32
#include <windows.h>
#endif
#include "include/aegisub/audio_provider.h"
/// 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 wxString &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 wxString &filename);

172
aegisub/libmedia/cache/audio_hd.cpp vendored Normal file
View File

@ -0,0 +1,172 @@
// 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
#include <wx/filefn.h>
#include <wx/filename.h>
#endif
#include "audio_provider_hd.h"
#include "compat.h"
#include "dialog_progress.h"
#include "frame_main.h"
#include "main.h"
#include "standard_paths.h"
#include "utils.h"
/// @brief Constructor
/// @param source
///
HDAudioProvider::HDAudioProvider(AudioProvider *src) {
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
wxLongLong freespace;
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);
file_cache.Open(diskCacheFilename,wxFile::read_write);
if (!file_cache.IsOpened()) throw AudioOpenError("Unable to write to audio disk cache.");
// Start progress
volatile bool canceled = false;
DialogProgress *progress = new DialogProgress(AegisubApp::Get()->frame,_T("Load audio"),&canceled,_T("Reading to Hard Disk cache"),0,num_samples);
progress->Show();
// 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->SetProgress(i,num_samples);
}
file_cache.Seek(0);
// Finish
if (canceled) {
file_cache.Close();
delete[] data;
throw agi::UserCancelException("Audio loading cancelled by user");
}
progress->Destroy();
}
/// @brief Destructor
///
HDAudioProvider::~HDAudioProvider() {
file_cache.Close();
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) {
wxMutexLocker disklock(diskmutex);
file_cache.Seek(start*bytes_per_sample);
file_cache.Read((char*)buf,count*bytes_per_sample*channels);
}
}
/// @brief Get disk cache path
/// @return
///
wxString HDAudioProvider::DiskCachePath() {
// Default
wxString 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/")));
}
/// @brief Get disk cache filename
///
wxString HDAudioProvider::DiskCacheName() {
// Get pattern
wxString 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 L"";
}

75
aegisub/libmedia/cache/audio_hd.h vendored Normal file
View File

@ -0,0 +1,75 @@
// 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
#include <wx/file.h>
#include <wx/thread.h>
#endif
#include "include/aegisub/audio_provider.h"
/// DOCME
/// @class HDAudioProvider
/// @brief DOCME
///
/// DOCME
class HDAudioProvider : public AudioProvider {
/// DOCME
mutable wxMutex diskmutex;
/// DOCME
mutable wxFile file_cache;
/// DOCME
wxString diskCacheFilename;
/// DOCME
bool samples_native_endian;
/// DOCME
char *data;
static wxString DiskCachePath();
static wxString DiskCacheName();
public:
HDAudioProvider(AudioProvider *source);
~HDAudioProvider();
bool AreSamplesNativeEndian() const { return samples_native_endian; }
void GetAudio(void *buf, int64_t start, int64_t count) const;
};

175
aegisub/libmedia/cache/audio_ram.cpp vendored Normal file
View File

@ -0,0 +1,175 @@
// 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_provider_ram.h"
#include "dialog_progress.h"
#include "frame_main.h"
#include "main.h"
#include "utils.h"
/// 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
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;
}
}
}

62
aegisub/libmedia/cache/audio_ram.h vendored Normal file
View File

@ -0,0 +1,62 @@
// 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 "include/aegisub/audio_provider.h"
/// 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;
};

125
aegisub/libmedia/cache/video_cache.cpp vendored Normal file
View File

@ -0,0 +1,125 @@
// 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_provider_cache.h"
/// DOCME
/// @class CachedFrame
/// @brief DOCME
///
/// DOCME
struct CachedFrame {
/// DOCME
AegiVideoFrame frame;
/// DOCME
int n;
};
/// @brief Constructor
/// @param parent
///
VideoProviderCache::VideoProviderCache(VideoProvider *parent)
: master(parent)
, 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;
}

82
aegisub/libmedia/cache/video_cache.h vendored Normal file
View File

@ -0,0 +1,82 @@
// 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 "include/aegisub/video_provider.h"
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 wxString GetWarning() const { return master->GetWarning(); }
virtual wxString GetDecoderName() const { return master->GetDecoderName(); }
};

View File

@ -0,0 +1,184 @@
// 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
#include <wx/thread.h>
#endif
#ifdef WITH_AVISYNTH
#include "audio_provider_avs.h"
#endif
#include "audio_provider_convert.h"
#ifdef WITH_FFMPEGSOURCE
#include "audio_provider_ffmpegsource.h"
#endif
#include "audio_provider_hd.h"
#include "audio_provider_pcm.h"
#include "audio_provider_ram.h"
#include "compat.h"
#include "main.h"
/// @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(wxString filename, int cache) {
AudioProvider *provider = NULL;
bool found = false;
std::string msg;
if (!OPT_GET("Provider/Audio/PCM/Disable")->GetBool()) {
// 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) {
std::vector<std::string> list = GetClasses(OPT_GET("Audio/Provider")->GetString());
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(STD_STR(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
if (cache == -1) cache = OPT_GET("Audio/Cache/Type")->GetInt();
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);
throw AudioOpenError("Unknown caching method");
}
/// @brief Register all providers
///
void AudioProviderFactory::RegisterProviders() {
#ifdef WITH_AVISYNTH
Register<AvisynthAudioProvider>("Avisynth");
#endif
#ifdef WITH_FFMPEGSOURCE
Register<FFmpegSourceAudioProvider>("FFmpegSource");
#endif
}
template<> AudioProviderFactory::map *FactoryBase<AudioProvider *(*)(wxString)>::classes = NULL;

View File

@ -0,0 +1,90 @@
// 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;

View File

@ -0,0 +1,123 @@
// 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"
#include <libaegisub/log.h>
#include "compat.h"
#include "main.h"
#ifdef WITH_AVISYNTH
#include "video_provider_avs.h"
#endif
#include "video_provider_cache.h"
#include "video_provider_dummy.h"
#ifdef WITH_FFMPEGSOURCE
#include "video_provider_ffmpegsource.h"
#endif
#include "video_provider_manager.h"
#include "video_provider_yuv4mpeg.h"
/// @brief Get provider
/// @param video
/// @return
///
VideoProvider *VideoProviderFactory::GetProvider(wxString video) {
std::vector<std::string> list = GetClasses(OPT_GET("Video/Provider")->GetString());
if (video.StartsWith("?dummy")) 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 " << STD_STR(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 " << STD_STR(video);
std::string msg = "Could not open " + STD_STR(video) + ":\n" + errors;
if (!fileFound) throw agi::FileNotFoundError(STD_STR(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<FFmpegSourceVideoProvider>("FFmpegSource");
#endif
Register<DummyVideoProvider>("Dummy", true);
Register<YUV4MPEGVideoProvider>("YUV4MPEG", true);
}
template<> VideoProviderFactory::map *FactoryBase<VideoProvider *(*)(wxString)>::classes = NULL;

View File

@ -0,0 +1,44 @@
// 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();
};

View File

@ -0,0 +1,147 @@
// 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

View File

@ -0,0 +1,74 @@
// 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

View File

@ -0,0 +1,252 @@
// 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

View File

@ -0,0 +1,76 @@
// 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

View File

@ -0,0 +1,291 @@
// 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"
/// @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

View File

@ -0,0 +1,99 @@
// 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"
/// 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

View File

@ -0,0 +1,218 @@
// 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"
/// @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:"), &params)) {
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;
}

View File

@ -0,0 +1,86 @@
// 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"
/// 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"; }
};

View File

@ -0,0 +1,421 @@
// 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 "compat.h"
#include "utils.h"
#include "video_provider_yuv4mpeg.h"
// 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(wxString fname)
: 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 {
wxString filename = wxFileName(fname).GetShortPath();
#ifdef WIN32
sf = _wfopen(filename.wc_str(), L"rb");
#else
sf = fopen(filename.utf8_str(), "rb");
#endif
if (sf == NULL) throw agi::FileNotFoundError(STD_STR(fname));
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<wxString> YUV4MPEGVideoProvider::ReadHeader(int64_t startpos, bool reset_pos) {
int64_t oldpos = ftello(sf);
std::vector<wxString> tags;
wxString curtag;
int bytesread = 0;
int buf;
if (fseeko(sf, startpos, SEEK_SET))
throw VideoOpenError(STD_STR(wxString::Format(L"YUV4MPEG video provider: ReadHeader: failed seeking to position %d", 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(static_cast<wxChar>(buf));
}
// if only one tag with no trailing space was found (possible in the
// FRAME header case), make sure we get it
if (!curtag.IsEmpty()) {
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<wxString>& tags) {
if (tags.size() <= 1)
throw VideoOpenError("ParseFileHeader: contentless header");
if (tags.front().Cmp("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++) {
wxString tag;
long tmp_long1 = 0;
long tmp_long2 = 0;
if (tags[i].StartsWith("W", &tag)) {
if (!tag.ToLong(&tmp_long1))
throw VideoOpenError("ParseFileHeader: invalid width");
t_w = (int)tmp_long1;
}
else if (tags[i].StartsWith("H", &tag)) {
if (!tag.ToLong(&tmp_long1))
throw VideoOpenError("ParseFileHeader: invalid height");
t_h = (int)tmp_long1;
}
else if (tags[i].StartsWith("F", &tag)) {
if (!(tag.BeforeFirst(':')).ToLong(&tmp_long1) && tag.AfterFirst(':').ToLong(&tmp_long2))
throw VideoOpenError("ParseFileHeader: invalid framerate");
t_fps_num = (int)tmp_long1;
t_fps_den = (int)tmp_long2;
}
else if (tags[i].StartsWith("C", &tag)) {
// technically this should probably be case sensitive,
// but being liberal in what you accept doesn't hurt
tag.MakeLower();
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 (tags[i].StartsWith("I", &tag)) {
tag.MakeLower();
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<wxString>& tags) {
if (tags.front().Cmp(_("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<wxString> tags = ReadHeader(curpos, false);
curpos = ftello(sf);
if (tags.empty())
break; // no more headers
Y4M_FrameFlags flags = Y4M_FFLAG_NOTSET;
if (!tags.front().Cmp("YUV4MPEG2")) {
ParseFileHeader(tags);
continue;
}
else if (!tags.front().Cmp("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(STD_STR(wxString::Format("IndexFile: failed seeking to position %d", curpos + frame_sz)));
}
else {
/// @todo implement rff flags etc
}
}
return framecount;
}
// http://bob.allegronetwork.com/prog/tricks.html#clamp
static FORCEINLINE 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 = 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;
}

View File

@ -0,0 +1,154 @@
// 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
///
#include "include/aegisub/video_provider.h"
#ifndef AGI_PRE
#include <stdio.h>
#include <vector>
#include <wx/filename.h>
#include <wx/log.h>
#endif
/// 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<wxString>& tags);
Y4M_FrameFlags ParseFrameHeader(const std::vector<wxString>& tags);
std::vector<wxString> ReadHeader(int64_t startpos, bool reset_pos=false);
int IndexFile();
public:
YUV4MPEGVideoProvider(wxString 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>(); };
wxString GetDecoderName() const { return L"YU4MPEG"; };
bool WantsCaching() const { return true; };
};