forked from mia/Aegisub
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:
parent
7508420000
commit
72211ad685
30 changed files with 4540 additions and 0 deletions
170
aegisub/libmedia/audio/avs_audio.cpp
Normal file
170
aegisub/libmedia/audio/avs_audio.cpp
Normal 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
|
68
aegisub/libmedia/audio/avs_audio.h
Normal file
68
aegisub/libmedia/audio/avs_audio.h
Normal 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
|
220
aegisub/libmedia/audio/convert.cpp
Normal file
220
aegisub/libmedia/audio/convert.cpp
Normal 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;
|
||||||
|
}
|
70
aegisub/libmedia/audio/convert.h
Normal file
70
aegisub/libmedia/audio/convert.h
Normal 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);
|
125
aegisub/libmedia/audio/downmix.cpp
Normal file
125
aegisub/libmedia/audio/downmix.cpp
Normal 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;
|
||||||
|
}
|
61
aegisub/libmedia/audio/downmix.h
Normal file
61
aegisub/libmedia/audio/downmix.h
Normal 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;
|
||||||
|
};
|
54
aegisub/libmedia/audio/dummy.h
Normal file
54
aegisub/libmedia/audio/dummy.h
Normal 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;
|
||||||
|
};
|
76
aegisub/libmedia/audio/dummy_audio.cpp
Normal file
76
aegisub/libmedia/audio/dummy_audio.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
631
aegisub/libmedia/audio/pcm.cpp
Normal file
631
aegisub/libmedia/audio/pcm.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
115
aegisub/libmedia/audio/pcm.h
Normal file
115
aegisub/libmedia/audio/pcm.h
Normal 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
172
aegisub/libmedia/cache/audio_hd.cpp
vendored
Normal 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
75
aegisub/libmedia/cache/audio_hd.h
vendored
Normal 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
175
aegisub/libmedia/cache/audio_ram.cpp
vendored
Normal 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
62
aegisub/libmedia/cache/audio_ram.h
vendored
Normal 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
125
aegisub/libmedia/cache/video_cache.cpp
vendored
Normal 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
82
aegisub/libmedia/cache/video_cache.h
vendored
Normal 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(); }
|
||||||
|
};
|
184
aegisub/libmedia/common/audio_manager.cpp
Normal file
184
aegisub/libmedia/common/audio_manager.cpp
Normal 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;
|
90
aegisub/libmedia/common/subtitle.cpp
Normal file
90
aegisub/libmedia/common/subtitle.cpp
Normal 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;
|
123
aegisub/libmedia/common/video_manager.cpp
Normal file
123
aegisub/libmedia/common/video_manager.cpp
Normal 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;
|
44
aegisub/libmedia/common/video_manager.h
Normal file
44
aegisub/libmedia/common/video_manager.h
Normal 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();
|
||||||
|
};
|
147
aegisub/libmedia/subtitle/csri.cpp
Normal file
147
aegisub/libmedia/subtitle/csri.cpp
Normal 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
|
74
aegisub/libmedia/subtitle/csri.h
Normal file
74
aegisub/libmedia/subtitle/csri.h
Normal 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
|
252
aegisub/libmedia/subtitle/libass.cpp
Normal file
252
aegisub/libmedia/subtitle/libass.cpp
Normal 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
|
76
aegisub/libmedia/subtitle/libass.h
Normal file
76
aegisub/libmedia/subtitle/libass.h
Normal 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
|
291
aegisub/libmedia/video/avs_video.cpp
Normal file
291
aegisub/libmedia/video/avs_video.cpp
Normal 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
|
99
aegisub/libmedia/video/avs_video.h
Normal file
99
aegisub/libmedia/video/avs_video.h
Normal 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
|
218
aegisub/libmedia/video/dummy_video.cpp
Normal file
218
aegisub/libmedia/video/dummy_video.cpp
Normal 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:"), ¶ms)) {
|
||||||
|
throw agi::FileNotFoundError("Attempted creating dummy video provider with non-dummy filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
wxStringTokenizer t(params, _T(":"));
|
||||||
|
if (t.CountTokens() < 7) {
|
||||||
|
throw VideoOpenError("Too few fields in dummy video parameter list");
|
||||||
|
}
|
||||||
|
|
||||||
|
double fps;
|
||||||
|
long _frames, _width, _height, red, green, blue;
|
||||||
|
bool pattern = false;
|
||||||
|
|
||||||
|
wxString field = t.GetNextToken();
|
||||||
|
if (!field.ToDouble(&fps)) {
|
||||||
|
throw VideoOpenError("Unable to parse fps field in dummy video parameter list");
|
||||||
|
}
|
||||||
|
|
||||||
|
field = t.GetNextToken();
|
||||||
|
if (!field.ToLong(&_frames)) {
|
||||||
|
throw VideoOpenError("Unable to parse framecount field in dummy video parameter list");
|
||||||
|
}
|
||||||
|
|
||||||
|
field = t.GetNextToken();
|
||||||
|
if (!field.ToLong(&_width)) {
|
||||||
|
throw VideoOpenError("Unable to parse width field in dummy video parameter list");
|
||||||
|
}
|
||||||
|
|
||||||
|
field = t.GetNextToken();
|
||||||
|
if (!field.ToLong(&_height)) {
|
||||||
|
throw VideoOpenError("Unable to parse height field in dummy video parameter list");
|
||||||
|
}
|
||||||
|
|
||||||
|
field = t.GetNextToken();
|
||||||
|
if (!field.ToLong(&red)) {
|
||||||
|
throw VideoOpenError("Unable to parse red colour field in dummy video parameter list");
|
||||||
|
}
|
||||||
|
|
||||||
|
field = t.GetNextToken();
|
||||||
|
if (!field.ToLong(&green)) {
|
||||||
|
throw VideoOpenError("Unable to parse green colour field in dummy video parameter list");
|
||||||
|
}
|
||||||
|
|
||||||
|
field = t.GetNextToken();
|
||||||
|
if (!field.ToLong(&blue)) {
|
||||||
|
throw VideoOpenError("Unable to parse blue colour field in dummy video parameter list");
|
||||||
|
}
|
||||||
|
|
||||||
|
field = t.GetNextToken();
|
||||||
|
if (field == _T("c")) {
|
||||||
|
pattern = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Create(fps, _frames, _width, _height, wxColour(red, green, blue), pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Direct constructor
|
||||||
|
/// @param _fps
|
||||||
|
/// @param frames
|
||||||
|
/// @param _width
|
||||||
|
/// @param _height
|
||||||
|
/// @param colour
|
||||||
|
/// @param pattern
|
||||||
|
///
|
||||||
|
DummyVideoProvider::DummyVideoProvider(double _fps, int frames, int _width, int _height, const wxColour &colour, bool pattern) {
|
||||||
|
Create(_fps, frames, _width, _height, colour, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Destructor
|
||||||
|
///
|
||||||
|
DummyVideoProvider::~DummyVideoProvider() {
|
||||||
|
frame.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Construct a fake filename describing the video
|
||||||
|
/// @param fps
|
||||||
|
/// @param frames
|
||||||
|
/// @param _width
|
||||||
|
/// @param _height
|
||||||
|
/// @param colour
|
||||||
|
/// @param pattern
|
||||||
|
/// @return
|
||||||
|
///
|
||||||
|
wxString DummyVideoProvider::MakeFilename(double fps, int frames, int _width, int _height, const wxColour &colour, bool pattern) {
|
||||||
|
return wxString::Format(_T("?dummy:%f:%d:%d:%d:%d:%d:%d:%s"), fps, frames, _width, _height, colour.Red(), colour.Green(), colour.Blue(), pattern?_T("c"):_T(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Get frame
|
||||||
|
/// @param n
|
||||||
|
/// @return
|
||||||
|
///
|
||||||
|
const AegiVideoFrame DummyVideoProvider::GetFrame(int n) {
|
||||||
|
lastFrame = n;
|
||||||
|
return frame;
|
||||||
|
}
|
86
aegisub/libmedia/video/dummy_video.h
Normal file
86
aegisub/libmedia/video/dummy_video.h
Normal 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"; }
|
||||||
|
};
|
421
aegisub/libmedia/video/yuv4mpeg.cpp
Normal file
421
aegisub/libmedia/video/yuv4mpeg.cpp
Normal 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;
|
||||||
|
}
|
154
aegisub/libmedia/video/yuv4mpeg.h
Normal file
154
aegisub/libmedia/video/yuv4mpeg.h
Normal 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; };
|
||||||
|
};
|
Loading…
Reference in a new issue