Major refactoring of movie code.

This commit is contained in:
Josh Yelon 2007-08-20 22:34:58 +00:00
parent 7eb74374ac
commit 1e02c06517
54 changed files with 4699 additions and 1151 deletions

View File

@ -36,6 +36,7 @@ class Audio3DManager:
"""
Control the scale that sets the distance units for 3D spacialized audio.
Default is 1.0 which is adjust in panda to be feet.
When you change this, don't forget that this effects the scale of setSoundMinDistance
"""
self.audio_manager.audio3dSetDistanceFactor(factor)
@ -84,9 +85,8 @@ class Audio3DManager:
"""
Controls the distance (in units) that this sound begins to fall off.
Also affects the rate it falls off.
Default is 1.0
Closer/Faster, <1.0
Farther/Slower, >1.0
Default is 3.28 (in feet, this is 1 meter)
Don't forget to change this when you change the DistanceFactor
"""
sound.set3dMinDistance(dist)
@ -94,9 +94,7 @@ class Audio3DManager:
"""
Controls the distance (in units) that this sound begins to fall off.
Also affects the rate it falls off.
Default is 1.0
Closer/Faster, <1.0
Farther/Slower, >1.0
Default is 3.28 (in feet, this is 1 meter)
"""
return sound.get3dMinDistance()
@ -267,8 +265,10 @@ class Audio3DManager:
# to which it is attached
if self.listener_target:
pos = self.listener_target.getPos(self.root)
forward = self.listener_target.getRelativeVector(self.root, VBase3(0,1,0))
up = self.listener_target.getRelativeVector(self.root, VBase3(0,0,1))
vel = self.getListenerVelocity()
self.audio_manager.audio3dSetListenerAttributes(pos[0], pos[1], pos[2], vel[0], vel[1], vel[2], 0, 1, 0, 0, 0, 1)
self.audio_manager.audio3dSetListenerAttributes(pos[0], pos[1], pos[2], vel[0], vel[1], vel[2], forward[0], forward[1], forward[2], up[0], up[1], up[2])
else:
self.audio_manager.audio3dSetListenerAttributes(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1)
return Task.cont

View File

@ -69,7 +69,7 @@ want-tk #f
want-pstats #f
show-frame-rate-meter #f
# Enable audio using the FMod audio library by default:
# Enable audio using the FmodEx audio library by default:
audio-library-name p3fmod_audio

View File

@ -35,7 +35,7 @@ GENMAN=0
VERSION=0
VERBOSE=1
COMPRESSOR="zlib"
PACKAGES=["PYTHON","ZLIB","PNG","JPEG","TIFF","VRPN","FMODEX","NVIDIACG",
PACKAGES=["PYTHON","ZLIB","PNG","JPEG","TIFF","VRPN","FMOD","FMODEX","OPENAL","NVIDIACG",
"OPENSSL","FREETYPE","FFTW","MILES",
"MAYA6","MAYA65","MAYA7","MAYA8","MAYA85","MAX6","MAX7","MAX8","MAX9",
"FFMPEG","PANDATOOL","PANDAAPP","DX8","DX9"]
@ -1077,7 +1077,9 @@ def CompileCxx(obj,src,ipath,opts):
if (OMIT.count("PYTHON")==0): cmd = cmd + ' -I"' + PYTHONSDK + '"'
if (PkgSelected(opts,"VRPN")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'vrpn/include'
if (PkgSelected(opts,"FFTW")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'fftw/include'
if (PkgSelected(opts,"FMOD")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'fmod/include'
if (PkgSelected(opts,"FMODEX")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'fmodex/include'
if (PkgSelected(opts,"OPENAL")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'openal/include'
if (PkgSelected(opts,"NVIDIACG")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'nvidiacg/include'
if (PkgSelected(opts,"FFMPEG")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'ffmpeg/include'
if (PkgSelected(opts,"FREETYPE")): cmd = cmd + ' -I/usr/include/freetype2'
@ -1354,8 +1356,13 @@ def CompileLink(dll, obj, opts, ldef):
if (PkgSelected(opts,"VRPN")):
cmd = cmd + ' ' + THIRDPARTYLIBS + 'vrpn/lib/vrpn.lib'
cmd = cmd + ' ' + THIRDPARTYLIBS + 'vrpn/lib/quat.lib'
if (PkgSelected(opts,"FMOD")):
cmd = cmd + ' ' + THIRDPARTYLIBS + 'fmod/lib/fmod.lib'
if (PkgSelected(opts,"FMODEX")):
cmd = cmd + ' ' + THIRDPARTYLIBS + 'fmodex/lib/fmodex_vc.lib'
if (PkgSelected(opts,"OPENAL")):
cmd = cmd + ' ' + THIRDPARTYLIBS + 'openal/lib/OpenAL32.lib'
cmd = cmd + ' ' + THIRDPARTYLIBS + 'openal/lib/alut.lib'
if (PkgSelected(opts,"MILES")):
cmd = cmd + ' ' + THIRDPARTYLIBS + 'miles/lib/mss32.lib'
if (PkgSelected(opts,"NVIDIACG")):
@ -1406,7 +1413,9 @@ def CompileLink(dll, obj, opts, ldef):
elif (suffix==".dll"): cmd = cmd + ' -l' + x[3:-4]
elif (suffix==".lib"): cmd = cmd + ' built/lib/' + x[:-4] + '.a'
elif (suffix==".ilb"): cmd = cmd + ' built/tmp/' + x[:-4] + '.a'
#if (PkgSelected(opts,"FMOD")): cmd = cmd + ' -L' + THIRDPARTYLIBS + 'fmod/lib -lfmod'
if (PkgSelected(opts,"FMODEX")): cmd = cmd + ' -L' + THIRDPARTYLIBS + 'fmodex/lib -lfmodex'
#if (PkgSelected(opts,"OPENAL")): cmd = cmd + ' -L' + THIRDPARTYLIBS + 'openal/lib -lopenal -lalut'
if (PkgSelected(opts,"NVIDIACG")):
cmd = cmd + ' -Lthirdparty/nvidiacg/lib '
if (opts.count("CGGL")): cmd = cmd + " -lCgGL"
@ -1659,7 +1668,9 @@ DTOOL_CONFIG=[
("HAVE_JPEG", 'UNDEF', 'UNDEF'),
("HAVE_TIFF", 'UNDEF', 'UNDEF'),
("HAVE_VRPN", 'UNDEF', 'UNDEF'),
("HAVE_FMOD", 'UNDEF', 'UNDEF'),
("HAVE_FMODEX", 'UNDEF', 'UNDEF'),
("HAVE_OPENAL", 'UNDEF', 'UNDEF'),
("HAVE_NVIDIACG", 'UNDEF', 'UNDEF'),
("HAVE_FREETYPE", 'UNDEF', 'UNDEF'),
("HAVE_FFTW", 'UNDEF', 'UNDEF'),
@ -2744,6 +2755,18 @@ if OMIT.count("FMODEX") == 0:
'libp3dtool.dll',
])
if OMIT.count("OPENAL") == 0:
IPATH=['panda/src/audiotraits']
OPTS=['BUILDING_OPENAL_AUDIO', 'OPENAL']
EnqueueCxx(ipath=IPATH, opts=OPTS, src='openal_audio_composite.cxx', obj='openal_audio_openal_audio_composite.obj')
EnqueueLink(opts=['ADVAPI', 'WINUSER', 'WINMM', 'OPENAL'], dll='libp3openal_audio.dll', obj=[
'openal_audio_openal_audio_composite.obj',
'libpanda.dll',
'libpandaexpress.dll',
'libp3dtoolconfig.dll',
'libp3dtool.dll',
])
if OMIT.count("MILES") == 0:
IPATH=['panda/src/audiotraits']
OPTS=['BUILDING_MILES_AUDIO', 'MILES']

View File

@ -1,4 +1,3 @@
#ifndef AVIO_H
#define AVIO_H
#endif

View File

@ -40,7 +40,11 @@ ConfigVariableString audio_library_name
#if defined(HAVE_RAD_MSS)
"miles_audio"
#elif defined(HAVE_FMODEX)
"fmodex_audio"
#elif defined(HAVE_FMOD)
"fmod_audio"
#elif defined(HAVE_OPENAL)
"openal_audio"
#else
""
#endif
@ -49,23 +53,24 @@ ConfigVariableString audio_library_name
ConfigVariableDouble audio_volume
("audio-volume", 1.0f);
ConfigVariableFilename audio_dls_file
("audio-dls-file", Filename(),
PRC_DESC("Specifies a DLS file that defines an instrument set to load "
"for MIDI file playback. If this is not specified, the sound "
"interface will try to use the system default DLS file, if "
"one is available; the likely success of this depends on the "
"operating system."));
// Config variables for Fmod3:
ConfigVariableDouble audio_doppler_factor
("audio-doppler-factor", 1.0f);
ConfigVariableDouble audio_distance_factor
("audio-distance-factor", 1.0f);
ConfigVariableDouble audio_drop_off_factor
("audio-drop-off-factor", 1.0f);
ConfigVariableInt audio_min_hw_channels
("audio-min-hw-channels", 15,
PRC_DESC("Guarantee this many channels on the local sound card, or just "
"play EVERYTHING in software."));
// Config variables for Fmod:
// At one time, the actual number of sound channels was twice this,
// because Panda creates two AudioManagers, and each AudioManager was
// initializing the sound hardware separately. But it turns out you
// can't do that reliably on all platforms, so now we only initialize
// the sound hardware once, and this is once again the overall limit.
// But with FMOD Ex there's not much reason to make this a small
// number.
ConfigVariableInt fmod_number_of_sound_channels
("fmod-number-of-sound-channels", 128,
PRC_DESC("Guarantee this many channels you will have with FMOD. AKA the max number of sounds you can play at one time.") );
@ -80,6 +85,14 @@ ConfigVariableBool fmod_use_surround_sound
ConfigVariableBool audio_software_midi
("audio-software-midi", true);
ConfigVariableFilename audio_dls_file
("audio-dls-file", Filename(),
PRC_DESC("Specifies a DLS file that defines an instrument set to load "
"for MIDI file playback. If this is not specified, the sound "
"interface will try to use the system default DLS file, if "
"one is available; the likely success of this depends on the "
"operating system."));
ConfigVariableBool audio_play_midi
("audio-play-midi", true);

View File

@ -46,6 +46,12 @@ extern EXPCL_PANDA_AUDIO ConfigVariableString audio_library_name;
extern EXPCL_PANDA_AUDIO ConfigVariableInt fmod_number_of_sound_channels;
extern EXPCL_PANDA_AUDIO ConfigVariableBool fmod_use_surround_sound;
// Config vars for OpenAL:
extern EXPCL_PANDA ConfigVariableDouble audio_doppler_factor;
extern EXPCL_PANDA ConfigVariableDouble audio_distance_factor;
extern EXPCL_PANDA ConfigVariableDouble audio_drop_off_factor;
// Config vars for Miles:
extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_software_midi;

View File

@ -49,6 +49,26 @@
#end lib_target
//#begin lib_target
// #define TARGET openal_audio
// #define BUILD_TARGET $[HAVE_OPENAL]
// #define USE_PACKAGES openal
// #define BUILDING_DLL BUILDING_OPENAL_AUDIO
// #define LOCAL_LIBS audio event
// #define WIN_SYS_LIBS $[WIN_SYS_LIBS] user32.lib advapi32.lib winmm.lib
//
// #define COMBINED_SOURCES $[TARGET]_composite1.cxx
//
// #define SOURCES \
// config_openalAudio.h \
// openalAudioManager.h \
// openalAudioSound.I openalAudioSound.h
//
// #define INCLUDED_SOURCES \
// config_openalAudio.cxx openalAudioManager.cxx openalAudioSound.cxx
//
//#end lib_target
//#begin lib_target
// #define TARGET audio_linux
// #define BUILDING_DLL BUILDING_MISC

View File

@ -0,0 +1,64 @@
// Filename: config_openalAudio.cxx
// Created by: cort
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "pandabase.h"
#ifdef HAVE_OPENAL //[
#include "config_openalAudio.h"
#include "openalAudioManager.h"
#include "openalAudioSound.h"
#include "pandaSystem.h"
#include "dconfig.h"
ConfigureDef(config_openalAudio);
NotifyCategoryDef(openalAudio, ":audio");
ConfigureFn(config_openalAudio) {
init_libOpenALAudio();
}
////////////////////////////////////////////////////////////////////
// Function: init_libOpenALAudio
// Description: Initializes the library. This must be called at
// least once before any of the functions or classes in
// this library can be used. Normally it will be
// called by the static initializers and need not be
// called explicitly, but special cases exist.
////////////////////////////////////////////////////////////////////
void
init_libOpenALAudio() {
static bool initialized = false;
if (initialized) {
return;
}
initialized = true;
AudioManager::register_AudioManager_creator(Create_AudioManager);
OpenALAudioManager::init_type();
OpenALAudioSound::init_type();
PandaSystem *ps = PandaSystem::get_global_ptr();
ps->add_system("OpenAL");
ps->add_system("audio");
ps->set_system_tag("audio", "implementation", "OpenAL");
}
#endif //]

View File

@ -0,0 +1,35 @@
// Filename: config_openalAudio.h
// Created by: Ben Buchwald <bb2@alumni.cmu.edu>
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef CONFIG_OPENALAUDIO_H
#define CONFIG_OPENALAUDIO_H
#include "pandabase.h"
#ifdef HAVE_OPENAL //[
#include "notifyCategoryProxy.h"
#include "dconfig.h"
ConfigureDecl(config_openalAudio, EXPCL_OPENAL_AUDIO, EXPTP_OPENAL_AUDIO);
NotifyCategoryDecl(openalAudio, EXPCL_OPENAL_AUDIO, EXPTP_OPENAL_AUDIO);
extern EXPCL_OPENAL_AUDIO void init_libOpenALAudio();
#endif //]
#endif // CONFIG_OPENALAUDIO_H

View File

@ -356,10 +356,8 @@ get_time() const {
return 0.0f;
}
fmod_audio_errcheck("_channel->getPosition()", result);
current_time = current_time / 1000;
return current_time;
return ((double)current_time) / 1000.0;
}
////////////////////////////////////////////////////////////////////
@ -496,9 +494,7 @@ length() const {
result = _sound->getLength( &length, FMOD_TIMEUNIT_MS );
fmod_audio_errcheck("_sound->getLength()", result);
length = length / 1000;
return length;
return ((double)length) / 1000.0;
}
////////////////////////////////////////////////////////////////////

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,227 @@
// Filename: openalAudioManager.h
// Created by: Ben Buchwald <bb2@alumni.cmu.edu>
//
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef __OPENAL_AUDIO_MANAGER_H__
#define __OPENAL_AUDIO_MANAGER_H__
#include "pandabase.h"
#ifdef HAVE_OPENAL //[
#include "audioManager.h"
#include "pset.h"
#include "pmap.h"
#include "pdeque.h"
//The Includes needed for OpenAL
#include <al.h>
#include <alc.h>
#include <alut.h>
class OpenALAudioSound;
extern void al_audio_errcheck(const char *context);
extern void alc_audio_errcheck(const char *context,ALCdevice* device);
extern void alut_audio_errcheck(const char *context);
class EXPCL_OPENAL_AUDIO OpenALAudioManager : public AudioManager {
class SoundData;
friend class OpenALAudioSound;
friend class OpenALSoundData;
public:
//Constructor and Destructor
OpenALAudioManager();
virtual ~OpenALAudioManager();
virtual void shutdown();
virtual bool is_valid();
virtual PT(AudioSound) get_sound(const string&, bool positional = false);
virtual void uncache_sound(const string&);
virtual void clear_cache();
virtual void set_cache_limit(unsigned int count);
virtual unsigned int get_cache_limit() const;
virtual void set_volume(float);
virtual float get_volume() const;
void set_play_rate(float play_rate);
float get_play_rate() const;
virtual void set_active(bool);
virtual bool get_active() const;
// This controls the "set of ears" that listens to 3D spacialized sound
// px, py, pz are position coordinates. Can be 0.0f to ignore.
// vx, vy, vz are a velocity vector in UNITS PER SECOND.
// fx, fy and fz are the respective components of a unit forward-vector
// ux, uy and uz are the respective components of a unit up-vector
// These changes will NOT be invoked until audio_3d_update() is called.
virtual void audio_3d_set_listener_attributes(float px, float py, float pz,
float vx, float xy, float xz,
float fx, float fy, float fz,
float ux, float uy, float uz);
virtual void audio_3d_get_listener_attributes(float *px, float *py, float *pz,
float *vx, float *vy, float *vz,
float *fx, float *fy, float *fz,
float *ux, float *uy, float *uz);
// Control the "relative distance factor" for 3D spacialized audio in units-per-foot. Default is 1.0
// OpenAL has no distance factor but we use this as a scale
// on the min/max distances of sounds to preserve FMOD compatibility.
// Also, adjusts the speed of sound to compensate for unit difference.
virtual void audio_3d_set_distance_factor(float factor);
virtual float audio_3d_get_distance_factor() const;
// Control the presence of the Doppler effect. Default is 1.0
// Exaggerated Doppler, use >1.0
// Diminshed Doppler, use <1.0
virtual void audio_3d_set_doppler_factor(float factor);
virtual float audio_3d_get_doppler_factor() const;
// Exaggerate or diminish the effect of distance on sound. Default is 1.0
// Faster drop off, use >1.0
// Slower drop off, use <1.0
virtual void audio_3d_set_drop_off_factor(float factor);
virtual float audio_3d_get_drop_off_factor() const;
virtual void set_concurrent_sound_limit(unsigned int limit = 0);
virtual unsigned int get_concurrent_sound_limit() const;
virtual void reduce_sounds_playing_to(unsigned int count);
virtual void stop_all_sounds();
virtual void update();
private:
void make_current() const;
PT(SoundData) load(Filename file_name);
// Tell the manager that the sound dtor was called.
void release_sound(OpenALAudioSound* audioSound);
void most_recently_used(const string& path);
void uncache_a_sound();
void starting_sound(OpenALAudioSound* audio);
void stopping_sound(OpenALAudioSound* audio);
void cleanup();
private:
// The sound cache:
class SoundData : public ReferenceCount {
public:
SoundData(OpenALAudioManager* manager);
~SoundData();
float get_length();
OpenALAudioManager* _manager;
string _basename;
ALuint _buffer;
bool _has_length;
float _length; // in seconds.
};
typedef pmap<string, PT(SoundData) > SoundMap;
SoundMap _sounds;
int _cache_limit;
typedef pset<OpenALAudioSound* > AudioSet;
// The offspring of this manager:
AudioSet _sounds_on_loan;
typedef pset<OpenALAudioSound* > SoundsPlaying;
// The sounds from this manager that are currently playing
SoundsPlaying _sounds_playing;
// The Least Recently Used mechanism:
typedef pdeque<const string* > LRU;
LRU _lru;
// State:
float _volume;
float _play_rate;
bool _active;
bool _cleanup_required;
// keep a count for startup and shutdown:
static int _active_managers;
static bool _openal_active;
unsigned int _concurrent_sound_limit;
bool _is_valid;
typedef pset<OpenALAudioManager *> Managers;
static Managers *_managers;
static ALCdevice* _device;
static ALCcontext* _context;
// cache of openal sources, use only for playing sounds
typedef pset<ALuint > SourceCache;
static SourceCache *_al_sources;
float _distance_factor;
float _doppler_factor;
float _drop_off_factor;
ALfloat _position[3];
ALfloat _velocity[3];
ALfloat _forward_up[6];
////////////////////////////////////////////////////////////
//These are needed for Panda's Pointer System. DO NOT ERASE!
////////////////////////////////////////////////////////////
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
AudioManager::init_type();
register_type(_type_handle, "OpenALAudioManager", AudioManager::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {
init_type();
return get_class_type();
}
private:
static TypeHandle _type_handle;
////////////////////////////////////////////////////////////
//DONE
////////////////////////////////////////////////////////////
};
EXPCL_OPENAL_AUDIO PT(AudioManager) Create_AudioManager();
#endif //]
#endif /* __OPENAL_AUDIO_MANAGER_H__ */

View File

@ -0,0 +1,23 @@
// Filename: openalAudioSound.I
// Created by: Ben Buchwald <bb2@alumni.cmu.edu>
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,730 @@
// Filename: openalAudioSound.cxx
// Created by: Ben Buchwald <bb2@alumni.cmu.edu>
//
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "pandabase.h"
#ifdef HAVE_OPENAL //[
//Panda Headers
#include "throw_event.h"
#include "openalAudioSound.h"
#include "openalAudioManager.h"
TypeHandle OpenALAudioSound::_type_handle;
#ifndef NDEBUG //[
#define openal_audio_debug(x) \
audio_debug("OpenALAudioSound \""<<get_name() \
<<"\" "<< x )
#else //][
#define openal_audio_debug(x) ((void)0)
#endif //]
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::OpenALAudioSound
// Access: public
// Description: Constructor
// All sound will DEFAULT load as a 2D sound unless
// otherwise specified.
////////////////////////////////////////////////////////////////////
OpenALAudioSound::
OpenALAudioSound(OpenALAudioManager* manager,
OpenALAudioManager::SoundData *sd, string file_name, bool positional)
: _sd(sd), _source(0), _manager(manager), _file_name(file_name),
_volume(1.0f), _balance(0), _play_rate(1.0),
_loop_count(1), _pause_time(0.0),
_active(true), _paused(false) {
nassertv(sd != NULL);
nassertv(!file_name.empty());
audio_debug("OpenALAudioSound(manager=0x"<<(void*)&manager
<<", sd=0x"<<(void*)sd<<", file_name="<<file_name<<")");
//Inits 3D Attributes
_location[0] = 0;
_location[1] = 0;
_location[2] = 0;
_velocity[0] = 0;
_velocity[1] = 0;
_velocity[2] = 0;
_min_dist = 3.28f; _max_dist = 1000000000.0f;
_drop_off_factor = 1.0f;
_buffer = _sd->_buffer;
// don't assign source until we play since sources are limited
/*
// Create a source to play the buffer
alGetError(); // clear errors
alGenSources(1,&_source);
al_audio_errcheck("alGenSources()");
// Assign the buffer to the source
alSourcei(_source,AL_BUFFER,_buffer);
al_audio_errcheck("alSourcei(_source,AL_BUFFER,_buffer)");
*/
// nonpositional sources are made relative to the listener so they don't move
_positional = positional;
if (_positional) {
int nChannels=1;
alGetBufferi(_buffer,AL_CHANNELS,&nChannels);
al_audio_errcheck("alGetBufferi(_buffer,AL_CHANNELS)");
if (nChannels>1)
audio_warning("OpenALAudioSound: Stereo sounds won't be spacialized: "<<file_name);
} else {
/*alSourcei(_source,AL_SOURCE_RELATIVE,AL_TRUE);
al_audio_errcheck("alSourcei(_source,AL_SOURCE_RELATIVE,AL_TRUE)");*/
}
/*
// set initial values since they are manager-relative
set_volume(_volume);
//set_balance(_balance);
set_play_rate(_play_rate);
set_3d_min_distance(_min_dist);
set_3d_max_distance(_max_dist);
set_3d_drop_off_factor(_drop_off_factor);
*/
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::~OpenALAudioSound
// Access: public
// Description: DESTRUCTOR!!!
////////////////////////////////////////////////////////////////////
OpenALAudioSound::
~OpenALAudioSound() {
openal_audio_debug("~OpenALAudioSound()");
cleanup();
_manager->release_sound(this);
openal_audio_debug("~OpenALAudioSound() done");
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound:: play
// Access: public
// Description: Plays a sound.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
play() {
float px,py,pz,vx,vy,vz;
openal_audio_debug("play()");
if (_active) {
if (status() == AudioSound::PLAYING) {
stop();
}
//nassertv(_source);
_manager->starting_sound(this);
if (_source) {
// Setup source
_manager->make_current();
alGetError(); // clear errors
// Assign the buffer to the source
alSourcei(_source,AL_BUFFER,_buffer);
ALenum result = alGetError();
if (result!=AL_NO_ERROR) {
audio_error("alSourcei(_source,AL_BUFFER,_buffer): " << alGetString(result) );
stop();
return;
}
// nonpositional sources are made relative to the listener so they don't move
alSourcei(_source,AL_SOURCE_RELATIVE,_positional?AL_FALSE:AL_TRUE);
al_audio_errcheck("alSourcei(_source,AL_SOURCE_RELATIVE)");
// set source properties that we have stored
set_volume(_volume);
//set_balance(_balance);
set_play_rate(_play_rate);
set_3d_min_distance(_min_dist);
set_3d_max_distance(_max_dist);
set_3d_drop_off_factor(_drop_off_factor);
get_3d_attributes(&px,&py,&pz,&vx,&vy,&vz);
set_3d_attributes(px, py, pz, vx, vy, vz);
set_loop_count(_loop_count);
if (_pause_time) {
set_time(_pause_time);
_pause_time = 0.0;
}
// Start playing:
alSourcePlay(_source);
al_audio_errcheck("alSourcePlay(_source)");
audio_debug(" started sound " << _file_name );
}
} else {
// In case _loop_count gets set to forever (zero):
audio_debug(" paused "<<_file_name );
_paused=true;
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::stop
// Access: public
// Description: Stop a sound
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
stop() {
openal_audio_debug("stop()");
//nassertv(_source);
if (_source) {
_manager->make_current();
alGetError(); // clear errors
alSourceStop(_source);
al_audio_errcheck("alSourceStop(_source)");
}
_pause_time = 0.0;
_manager->stopping_sound(this);
// The _paused flag should not be cleared here. _paused is not like
// the Pause button on a cd/dvd player. It is used as a flag to say
// that it was looping when it was set inactive. There is no need to
// make this symmetrical with play(). set_active() is the 'owner' of
// _paused. play() accesses _paused to help in the situation where
// someone calls play on an inactive sound().
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::finished
// Access:
// Description:
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
finished() {
openal_audio_debug("finished()");
_manager->stopping_sound(this);
if (!_finished_event.empty()) {
throw_event(_finished_event);
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_loop
// Access: public
// Description: Turns looping on and off
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_loop(bool loop) {
openal_audio_debug("set_loop(loop="<<loop<<")");
// loop count of 0 means always loop
set_loop_count((loop)?0:1);
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_loop
// Access: public
// Description: Returns whether looping is on or off
////////////////////////////////////////////////////////////////////
bool OpenALAudioSound::
get_loop() const {
openal_audio_debug("get_loop() returning "<<(_loop_count==0));
return (_loop_count == 0);
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_loop_count
// Access: public
// Description:
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_loop_count(unsigned long loop_count) {
openal_audio_debug("set_loop_count(loop_count="<<loop_count<<")");
if (loop_count>1) {
audio_error("OpenALAudioSound::set_loop_count() doesn't support looping a finite number of times, 0 (infinite) or 1 only");
loop_count = 1;
}
if (_loop_count!=loop_count) {
_loop_count=loop_count;
}
if (_source) {
_manager->make_current();
alGetError(); // clear errors
alSourcei(_source,AL_LOOPING,_loop_count==0?AL_TRUE:AL_FALSE);
al_audio_errcheck("alSourcei(_source,AL_LOOPING)");
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_loop_count
// Access: public
// Description: Return how many times a sound will loop.
////////////////////////////////////////////////////////////////////
unsigned long OpenALAudioSound::
get_loop_count() const {
openal_audio_debug("get_loop_count() returning "<<_loop_count);
return _loop_count;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_time
// Access: public
// Description: Sets the play position within the sound
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_time(float time) {
openal_audio_debug("set_time(time="<<time<<")");
//nassertv(_source);
// Ensure we don't inadvertently run off the end of the sound.
float max_time = length();
if (time > max_time) {
openalAudio_cat.warning()
<< "set_time(" << time << ") requested for sound of length "
<< max_time << "\n";
time = max_time;
}
if (_source) {
_manager->make_current();
alGetError(); // clear errors
alSourcef(_source,AL_SEC_OFFSET,time);
al_audio_errcheck("alSourcef(_source,AL_SEC_OFFSET)");
}
if (status()==PLAYING) {
_pause_time = 0.0;
} else {
_pause_time = time;
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_time
// Access: public
// Description: Gets the play position within the sound
////////////////////////////////////////////////////////////////////
float OpenALAudioSound::
get_time() const {
float time;
//nassertv(_source);
if (_source) {
_manager->make_current();
alGetError(); // clear errors
alGetSourcef(_source,AL_SEC_OFFSET,&time);
al_audio_errcheck("alGetSourcef(_source,AL_SEC_OFFSET)");
} else {
time = _pause_time;
}
openal_audio_debug("get_time() returning "<<time);
return time;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_volume(float vol)
// Access: public
// Description: 0.0 to 1.0 scale of volume converted to Fmod's
// internal 0.0 to 255.0 scale.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_volume(float volume) {
openal_audio_debug("set_volume(volume="<<volume<<")");
//nassertv(_source);
_volume=volume;
if (_source) {
volume*=_manager->get_volume();
_manager->make_current();
alGetError(); // clear errors
alSourcef(_source,AL_GAIN,volume);
al_audio_errcheck("alSourcef(_source,AL_GAIN)");
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_volume
// Access: public
// Description: Gets the current volume of a sound. 1 is Max. O is Min.
////////////////////////////////////////////////////////////////////
float OpenALAudioSound::
get_volume() const {
openal_audio_debug("get_volume() returning "<<_volume);
return _volume;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_balance(float bal)
// Access: public
// Description: -1.0 to 1.0 scale
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_balance(float balance_right) {
audio_debug("OpenALAudioSound::set_balance() not implemented");
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_balance
// Access: public
// Description: -1.0 to 1.0 scale
// -1 should be all the way left.
// 1 is all the way to the right.
////////////////////////////////////////////////////////////////////
float OpenALAudioSound::
get_balance() const {
audio_debug("OpenALAudioSound::get_balance() not implemented");
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_play_rate(float rate)
// Access: public
// Description: Sets the speed at which a sound plays back.
// The rate is a multiple of the sound, normal playback speed.
// IE 2 would play back 2 times fast, 3 would play 3 times, and so on.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_play_rate(float play_rate) {
openal_audio_debug("set_play_rate(play_rate="<<play_rate<<")");
//nassertv(_source);
_play_rate=play_rate;
if (_source) {
play_rate*=_manager->get_play_rate();
_manager->make_current();
alGetError(); // clear errors
alSourcef(_source,AL_PITCH,play_rate);
al_audio_errcheck("alSourcef(_source,AL_PITCH)");
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_play_rate
// Access: public
// Description:
////////////////////////////////////////////////////////////////////
float OpenALAudioSound::
get_play_rate() const {
openal_audio_debug("get_play_rate() returning "<<_play_rate);
return _play_rate;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::length
// Access: public
// Description: Get length
////////////////////////////////////////////////////////////////////
float OpenALAudioSound::
length() const {
return _sd->get_length();
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_3d_attributes
// Access: public
// Description: Set position and velocity of this sound
// NOW LISTEN UP!!! THIS IS IMPORTANT!
// Both Panda3D and OpenAL use a right handed coordinate system.
// But there is a major difference!
// In Panda3D the Y-Axis is going into the Screen and the Z-Axis is going up.
// In OpenAL the Y-Axis is going up and the Z-Axis is coming out of the screen.
// The solution is simple, we just flip the Y and Z axis and negate the Z, as we move coordinates
// from Panda to OpenAL and back.
// What does did mean to average Panda user? Nothing, they shouldn't notice anyway.
// But if you decide to do any 3D audio work in here you have to keep it in mind.
// I told you, so you can't say I didn't.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_3d_attributes(float px, float py, float pz, float vx, float vy, float vz) {
_location[0] = px;
_location[1] = pz;
_location[2] = -py;
_velocity[0] = vx;
_velocity[1] = vz;
_velocity[2] = -vy;
if (_source) {
_manager->make_current();
alGetError(); // clear errors
alSourcefv(_source,AL_POSITION,_location);
al_audio_errcheck("alSourcefv(_source,AL_POSITION)");
alSourcefv(_source,AL_VELOCITY,_velocity);
al_audio_errcheck("alSourcefv(_source,AL_VELOCITY)");
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_3d_attributes
// Access: public
// Description: Get position and velocity of this sound
// Currently unimplemented. Get the attributes of the attached object.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
get_3d_attributes(float *px, float *py, float *pz, float *vx, float *vy, float *vz) {
*px = _location[0];
*py = -_location[2];
*pz = _location[1];
*vx = _velocity[0];
*vy = -_velocity[2];
*vz = _velocity[1];
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_3d_min_distance
// Access: public
// Description: Set the distance that this sound begins to fall off. Also
// affects the rate it falls off.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_3d_min_distance(float dist) {
_min_dist = dist;
if (_source) {
_manager->make_current();
alGetError(); // clear errors
alSourcef(_source,AL_REFERENCE_DISTANCE,_min_dist*_manager->audio_3d_get_distance_factor());
al_audio_errcheck("alSourcefv(_source,AL_REFERENCE_DISTANCE)");
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_3d_min_distance
// Access: public
// Description: Get the distance that this sound begins to fall off
////////////////////////////////////////////////////////////////////
float OpenALAudioSound::
get_3d_min_distance() const {
return _min_dist;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_3d_max_distance
// Access: public
// Description: Set the distance that this sound stops falling off
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_3d_max_distance(float dist) {
_max_dist = dist;
if (_source) {
_manager->make_current();
alGetError(); // clear errors
alSourcef(_source,AL_MAX_DISTANCE,_max_dist*_manager->audio_3d_get_distance_factor());
al_audio_errcheck("alSourcefv(_source,AL_MAX_DISTANCE)");
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_3d_max_distance
// Access: public
// Description: Get the distance that this sound stops falling off
////////////////////////////////////////////////////////////////////
float OpenALAudioSound::
get_3d_max_distance() const {
return _max_dist;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_3d_drop_off_factor
// Access: public
// Description: Control the effect distance has on audability.
// Defaults to 1.0
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_3d_drop_off_factor(float factor) {
_drop_off_factor = factor;
if (_source) {
_manager->make_current();
alGetError(); // clear errors
alSourcef(_source,AL_ROLLOFF_FACTOR,_drop_off_factor*_manager->audio_3d_get_drop_off_factor());
al_audio_errcheck("alSourcefv(_source,AL_ROLLOFF_FACTOR)");
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_3d_drop_off_factor
// Access: public
// Description: Control the effect distance has on audability.
// Defaults to 1.0
////////////////////////////////////////////////////////////////////
float OpenALAudioSound::
get_3d_drop_off_factor() const {
return _drop_off_factor;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_active
// Access: public
// Description: Sets whether the sound is marked "active". By
// default, the active flag true for all sounds. If the
// active flag is set to false for any particular sound,
// the sound will not be heard.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_active(bool active) {
openal_audio_debug("set_active(active="<<active<<")");
if (_active!=active) {
_active=active;
if (_active) {
// ...activate the sound.
if (_paused
&&
_loop_count==0) {
// ...this sound was looping when it was paused.
_paused=false;
play();
}
} else {
// ...deactivate the sound.
if (status()==PLAYING) {
if (_loop_count==0) {
// ...we're pausing a looping sound.
_paused=true;
}
stop();
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_active
// Access: public
// Description: Returns whether the sound has been marked "active".
////////////////////////////////////////////////////////////////////
bool OpenALAudioSound::
get_active() const {
openal_audio_debug("get_active() returning "<<_active);
return _active;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_finished_event
// Access:
// Description:
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_finished_event(const string& event) {
openal_audio_debug("set_finished_event(event="<<event<<")");
_finished_event = event;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_finished_event
// Access:
// Description:
////////////////////////////////////////////////////////////////////
const string& OpenALAudioSound::
get_finished_event() const {
openal_audio_debug("get_finished_event() returning "<<_finished_event);
return _finished_event;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_name
// Access: public
// Description: Get name of sound file
////////////////////////////////////////////////////////////////////
const string& OpenALAudioSound::
get_name() const {
return _file_name;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::status
// Access: public
// Description: Get status of the sound.
////////////////////////////////////////////////////////////////////
AudioSound::SoundStatus OpenALAudioSound::
status() const {
ALenum status;
if (_source==0) {
//return AudioSound::BAD;
return AudioSound::READY;
}
_manager->make_current();
alGetError(); // clear errors
alGetSourcei(_source,AL_SOURCE_STATE,&status);
al_audio_errcheck("alGetSourcei(_source,AL_SOURCE_STATE)");
if (status == AL_PLAYING/* || status == AL_PAUSED*/) {
return AudioSound::PLAYING;
} else {
return AudioSound::READY;
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::cleanup
// Access: Private
// Description: Called to release any resources associated with the
// sound.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
cleanup() {
if (_source) {
stop();
/*
if (OpenALAudioManager::_openal_active) {
// delete the source
_manager->make_current();
alGetError(); // clear errors
alDeleteSources(1,&_source);
al_audio_errcheck("alDeleteSources()");
}
_source = 0;
*/
}
}
#endif //]

View File

@ -0,0 +1,194 @@
// Filename: openalAudioSound.h
// Created by: Ben Buchwald <bb2@alumni.cmu.edu>
//
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef __OPENAL_AUDIO_SOUND_H__
#define __OPENAL_AUDIO_SOUND_H__
#include "pandabase.h"
#ifdef HAVE_OPENAL //[
#include "audioSound.h"
#include <al.h>
#include <alc.h>
#include <alut.h>
class EXPCL_OPENAL_AUDIO OpenALAudioSound : public AudioSound {
friend class OpenALAudioManager;
public:
~OpenALAudioSound();
// For best compatability, set the loop_count, start_time,
// volume, and balance, prior to calling play(). You may
// set them while they're playing, but it's implementation
// specific whether you get the results.
void play();
void stop();
// loop: false = play once; true = play forever.
// inits to false.
void set_loop(bool loop=true);
bool get_loop() const;
// loop_count: 0 = forever; 1 = play once; n = play n times.
// inits to 1.
void set_loop_count(unsigned long loop_count=1);
unsigned long get_loop_count() const;
// 0 = begining; length() = end.
// inits to 0.0.
void set_time(float time=0.0);
float get_time() const;
// 0 = minimum; 1.0 = maximum.
// inits to 1.0.
void set_volume(float volume=1.0);
float get_volume() const;
// -1.0 is hard left
// 0.0 is centered
// 1.0 is hard right
// inits to 0.0.
void set_balance(float balance_right=0.0);
float get_balance() const;
// play_rate is any positive float value.
// inits to 1.0.
void set_play_rate(float play_rate=1.0f);
float get_play_rate() const;
// inits to manager's state.
void set_active(bool active=true);
bool get_active() const;
// This is the string that throw_event() will throw
// when the sound finishes playing. It is not triggered
// when the sound is stopped with stop().
void set_finished_event(const string& event);
const string& get_finished_event() const;
const string &get_name() const;
// return: playing time in seconds.
float length() const;
// Controls the position of this sound's emitter.
// pos is a pointer to an xyz triplet of the emitter's position.
// vel is a pointer to an xyz triplet of the emitter's velocity.
void set_3d_attributes(float px, float py, float pz, float vx, float vy, float vz);
void get_3d_attributes(float *px, float *py, float *pz, float *vx, float *vy, float *vz);
void set_3d_min_distance(float dist);
float get_3d_min_distance() const;
void set_3d_max_distance(float dist);
float get_3d_max_distance() const;
void set_3d_drop_off_factor(float factor);
float get_3d_drop_off_factor() const;
AudioSound::SoundStatus status() const;
void finished();
private:
OpenALAudioSound(OpenALAudioManager* manager, OpenALAudioManager::SoundData *sd,
string file_name, bool positional);
void cleanup();
private:
PT(OpenALAudioManager::SoundData) _sd;
ALuint _buffer;
ALuint _source;
PT(OpenALAudioManager) _manager;
float _volume; // 0..1.0
float _balance; // -1..1
float _play_rate; // 0..1.0
float _pause_time;
bool _positional;
ALfloat _location[3];
ALfloat _velocity[3];
float _min_dist;
float _max_dist;
float _drop_off_factor;
mutable float _length; // in seconds.
unsigned long _loop_count;
// This is the string that throw_event() will throw
// when the sound finishes playing. It is not triggered
// when the sound is stopped with stop().
string _finished_event;
string _file_name;
// _active is for things like a 'turn off sound effects' in
// a preferences pannel.
// _active is not about whether a sound is currently playing.
// Use status() for info on whether the sound is playing.
bool _active;
// _paused is not like the Pause button on a cd/dvd player.
// It is used as a flag to say that the sound was looping when
// itwas set inactive.
bool _paused;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
AudioSound::init_type();
register_type(_type_handle, "OpenALAudioSound", AudioSound::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {
init_type();
return get_class_type();
}
private:
static TypeHandle _type_handle;
////////////////////////////////////////////////////////////
//DONE
////////////////////////////////////////////////////////////
};
#include "openalAudioSound.I"
#endif //]
#endif /* __OPENAL_AUDIO_SOUND_H__ */

View File

@ -0,0 +1 @@
#include "openal_audio_composite1.cxx"

View File

@ -0,0 +1,5 @@
#include "config_openalAudio.cxx"
#include "openalAudioManager.cxx"
#include "openalAudioSound.cxx"

View File

@ -19,6 +19,7 @@
#include "pandabase.h"
#include "movieVideo.h"
#include "movieVideoCursor.h"
#include "movieTexture.h"
#include "clockObject.h"
#include "config_gobj.h"
@ -49,9 +50,7 @@ MovieTexture::
MovieTexture(PT(MovieVideo) video) :
Texture(video->get_name())
{
// It is necessary to copy the video, because
// the cull thread will be accessing it.
do_load_one(video->make_copy(), NULL, 0);
do_load_one(video->open(), NULL, 0);
}
////////////////////////////////////////////////////////////////////
@ -112,8 +111,8 @@ MovieTexture(const MovieTexture &copy) :
// Since 'make_copy' can be a slow operation,
// I release the read lock before calling make_copy.
pvector<MovieVideo *> color;
pvector<MovieVideo *> alpha;
pvector<MovieVideoCursor *> color;
pvector<MovieVideoCursor *> alpha;
{
CDReader copy_cdata(copy._cycler);
color.resize(copy_cdata->_pages.size());
@ -129,10 +128,10 @@ MovieTexture(const MovieTexture &copy) :
cdata->_pages.resize(color.size());
for (int i=0; i<(int)(color.size()); i++) {
if (color[i]) {
cdata->_pages[i]._color = color[i]->make_copy();
cdata->_pages[i]._color = color[i]->get_source()->open();
}
if (alpha[i]) {
cdata->_pages[i]._alpha = color[i]->make_copy();
cdata->_pages[i]._alpha = alpha[i]->get_source()->open();
}
}
recalculate_image_properties(cdata);
@ -205,7 +204,7 @@ recalculate_image_properties(CDWriter &cdata) {
double len = 0.0;
for (int i=0; i<_z_size; i++) {
MovieVideo *t = cdata->_pages[i]._color;
MovieVideoCursor *t = cdata->_pages[i]._color;
if (t) {
if (t->size_x() > x_max) x_max = t->size_x();
if (t->size_y() > y_max) y_max = t->size_y();
@ -259,15 +258,15 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
record->add_dependent_file(fullpath);
}
PT(MovieVideo) color;
PT(MovieVideo) alpha;
PT(MovieVideoCursor) color;
PT(MovieVideoCursor) alpha;
color = MovieVideo::load(fullpath);
color = MovieVideo::get(fullpath)->open();
if (color == 0) {
return false;
}
if (!alpha_fullpath.empty()) {
alpha = MovieVideo::load(alpha_fullpath);
alpha = MovieVideo::get(alpha_fullpath)->open();
if (alpha == 0) {
return false;
}
@ -296,6 +295,7 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
set_loaded_from_image();
set_loop(true);
play();
return true;
}
////////////////////////////////////////////////////////////////////
@ -304,7 +304,7 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
// Description: Loads movie objects into the texture.
////////////////////////////////////////////////////////////////////
bool MovieTexture::
do_load_one(PT(MovieVideo) color, PT(MovieVideo) alpha, int z) {
do_load_one(PT(MovieVideoCursor) color, PT(MovieVideoCursor) alpha, int z) {
{
CDWriter cdata(_cycler);
@ -391,8 +391,8 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
}
for (int i=0; i<((int)(cdata->_pages.size())); i++) {
MovieVideo *color = cdata->_pages[i]._color;
MovieVideo *alpha = cdata->_pages[i]._alpha;
MovieVideoCursor *color = cdata->_pages[i]._color;
MovieVideoCursor *alpha = cdata->_pages[i]._alpha;
if (color && alpha) {
if ((offset >= color->next_start())||
((offset < color->last_start()) && (color->can_seek()))) {

View File

@ -68,13 +68,13 @@ protected:
bool header_only, BamCacheRecord *record);
virtual bool do_load_one(const PNMImage &pnmimage, const string &name,
int z, int n);
bool do_load_one(PT(MovieVideo) color, PT(MovieVideo) alpha, int z);
bool do_load_one(PT(MovieVideoCursor) color, PT(MovieVideoCursor) alpha, int z);
class VideoPage {
public:
VideoPage();
PT(MovieVideo) _color;
PT(MovieVideo) _alpha;
PT(MovieVideoCursor) _color;
PT(MovieVideoCursor) _alpha;
};
typedef pvector<VideoPage> Pages;

View File

@ -12,28 +12,43 @@
#define SOURCES \
movieAudio.h movieAudio.I \
movieAudioCursor.h movieAudioCursor.I \
movieVideo.h movieVideo.I \
movieVideoCursor.h movieVideoCursor.I \
inkblotVideo.h inkblotVideo.I \
inkblotVideoCursor.h inkblotVideoCursor.I \
ffmpegVideo.h ffmpegVideo.I \
ffmpegVideoCursor.h ffmpegVideoCursor.I \
ffmpegAudio.h ffmpegAudio.I \
ffmpegAudioCursor.h ffmpegAudioCursor.I \
ffmpegVirtualFile.h ffmpegVirtualFile.I \
config_movies.h
#define INCLUDED_SOURCES \
movieVideo.cxx \
movieVideoCursor.cxx \
movieAudio.cxx \
movieAudioCursor.cxx \
inkblotVideo.cxx \
inkblotVideoCursor.cxx \
ffmpegVideo.cxx \
ffmpegVideoCursor.cxx \
ffmpegAudio.cxx \
ffmpegAudioCursor.cxx \
ffmpegVirtualFile.cxx \
config_movies.cxx
#define INSTALL_HEADERS \
movieAudio.h movieAudio.I \
movieAudioCursor.h movieAudioCursor.I \
movieVideo.h movieVideo.I \
movieVideoCursor.h movieVideoCursor.I \
inkblotVideo.h inkblotVideo.I \
inkblotVideoCursor.h inkblotVideoCursor.I \
ffmpegVideo.h ffmpegVideo.I \
ffmpegVideoCursor.h ffmpegVideoCursor.I \
ffmpegAudio.h ffmpegAudio.I \
ffmpegAudioCursor.h ffmpegAudioCursor.I \
ffmpegVirtualFile.h ffmpegVirtualFile.I \
config_movies.h

View File

@ -47,11 +47,16 @@ init_libmovies() {
initialized = true;
MovieVideo::init_type();
MovieVideoCursor::init_type();
MovieAudio::init_type();
MovieAudioCursor::init_type();
InkblotVideo::init_type();
InkblotVideoCursor::init_type();
#ifdef HAVE_FFMPEG
FfmpegVideo::init_type();
FfmpegVideoCursor::init_type();
FfmpegAudio::init_type();
FfmpegAudioCursor::init_type();
av_register_all();
FfmpegVirtualFile::register_protocol();
#endif

View File

@ -24,11 +24,21 @@
#include "configVariableEnum.h"
#include "configVariableDouble.h"
#include "dconfig.h"
#include "movieVideo.h"
#include "movieVideoCursor.h"
#include "movieAudio.h"
#include "movieAudioCursor.h"
#include "inkblotVideo.h"
#include "inkblotVideoCursor.h"
#include "ffmpegVideo.h"
#include "ffmpegVideoCursor.h"
#include "ffmpegAudio.h"
#include "ffmpegAudioCursor.h"
ConfigureDecl(config_movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES);
NotifyCategoryDecl(movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES);

View File

@ -19,8 +19,7 @@
#ifdef HAVE_FFMPEG
#include "ffmpegAudio.h"
#include "avcodec.h"
#include "avformat.h"
#include "ffmpegAudioCursor.h"
TypeHandle FfmpegAudio::_type_handle;
@ -32,7 +31,7 @@ TypeHandle FfmpegAudio::_type_handle;
FfmpegAudio::
FfmpegAudio(const Filename &name) :
MovieAudio(name),
_filename(name)
_specified_filename(name)
{
}
@ -46,37 +45,13 @@ FfmpegAudio::
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudio::read_samples
// Access: Public, Virtual
// Description: Read audio samples from the stream. N is the
// number of samples you wish to read. Your buffer
// must be equal in size to N * channels.
// Multiple-channel audio will be interleaved.
////////////////////////////////////////////////////////////////////
void FfmpegAudio::
read_samples(int n, PN_int16 *data) {
// This is the null implementation, which generates pure silence.
// Normally, this method will be overridden by a subclass.
if (n <= 0) {
return;
}
for (int i=0; i<n; i++) {
data[i] = 0;
}
_samples_read += n;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudio::make_copy
// Function: FfmpegAudio::open
// Access: Published, Virtual
// Description: Make a copy of this MovieAudio with its own cursor.
// Description: Open this audio, returning a MovieAudioCursor
////////////////////////////////////////////////////////////////////
PT(MovieAudio) FfmpegAudio::
make_copy() const {
return new FfmpegAudio(_filename);
PT(MovieAudioCursor) FfmpegAudio::
open() {
return new FfmpegAudioCursor(this);
}
////////////////////////////////////////////////////////////////////

View File

@ -20,10 +20,9 @@
#define FFMPEGAUDIO_H
#ifdef HAVE_FFMPEG
#include "pandabase.h"
#include "namable.h"
#include "texture.h"
#include "pointerTo.h"
#include "movieAudio.h"
class FfmpegAudioCursor;
////////////////////////////////////////////////////////////////////
// Class : FfmpegAudio
@ -34,13 +33,11 @@ class EXPCL_PANDA_MOVIES FfmpegAudio : public MovieAudio {
PUBLISHED:
FfmpegAudio(const Filename &name);
virtual ~FfmpegAudio();
virtual PT(MovieAudio) make_copy() const;
virtual PT(MovieAudioCursor) open();
public:
virtual void read_samples(int n, PN_int16 *data);
protected:
Filename _filename;
private:
Filename _specified_filename;
friend class FfmpegAudioCursor;
public:
static TypeHandle get_class_type() {

View File

@ -0,0 +1,18 @@
// Filename: ffmpegAudioCursor.I
// Created by: jyelon (01Aug2007)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,264 @@
// Filename: ffmpegAudioCursor.cxx
// Created by: jyelon (01Aug2007)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2007, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net
//
////////////////////////////////////////////////////////////////////
#ifdef HAVE_FFMPEG
#include "ffmpegAudioCursor.h"
#include "avcodec.h"
#include "avformat.h"
TypeHandle FfmpegAudioCursor::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::Constructor
// Access: Protected
// Description: xxx
////////////////////////////////////////////////////////////////////
FfmpegAudioCursor::
FfmpegAudioCursor(PT(FfmpegAudio) src) :
MovieAudioCursor((MovieAudio*)(FfmpegAudio*)src),
_filename(src->_specified_filename)
{
string url = "pandavfs:";
url += _filename;
if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
cleanup();
return;
}
if (av_find_stream_info(_format_ctx)<0) {
cleanup();
return;
}
// Find the audio stream
for(int i=0; i<_format_ctx->nb_streams; i++) {
if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO) {
_audio_index = i;
_audio_ctx = _format_ctx->streams[i]->codec;
_audio_timebase = av_q2d(_format_ctx->streams[i]->time_base);
_audio_rate = _audio_ctx->sample_rate;
_audio_channels = _audio_ctx->channels;
}
}
if (_audio_ctx == 0) {
cleanup();
return;
}
AVCodec *pAudioCodec=avcodec_find_decoder(_audio_ctx->codec_id);
if(pAudioCodec == 0) {
cleanup();
return;
}
if(avcodec_open(_audio_ctx, pAudioCodec)<0) {
cleanup();
return;
}
_length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
_can_seek = true;
_can_seek_fast = true;
_packet = new AVPacket;
_buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE / 2;
_buffer_alloc = new PN_int16[_buffer_size + 128];
if ((_packet == 0)||(_buffer_alloc == 0)) {
cleanup();
return;
}
memset(_packet, 0, sizeof(AVPacket));
// Align the buffer to a 16-byte boundary
// The ffmpeg codec likes this, because it uses SSE/SSE2.
_buffer = _buffer_alloc;
while (((int)_buffer) & 15) {
_buffer += 1;
}
fetch_packet();
_initial_dts = _packet->dts;
_last_seek = 0;
_samples_read = 0;
_buffer_head = 0;
_buffer_tail = 0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::Destructor
// Access: Protected, Virtual
// Description: xxx
////////////////////////////////////////////////////////////////////
FfmpegAudioCursor::
~FfmpegAudioCursor() {
cleanup();
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::cleanup
// Access: Public
// Description: Reset to a standard inactive state.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
cleanup() {
if (_packet) {
if (_packet->data) {
av_free_packet(_packet);
}
delete _packet;
_packet = 0;
}
if (_buffer_alloc) {
delete[] _buffer_alloc;
_buffer_alloc = 0;
_buffer = 0;
}
if (_audio_ctx) {
avcodec_close(_audio_ctx);
_audio_ctx = 0;
}
if (_format_ctx) {
av_close_input_file(_format_ctx);
_format_ctx = 0;
}
_audio_ctx = 0;
_audio_index = -1;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::fetch_packet
// Access: Protected
// Description: Fetches an audio packet and stores it in the
// packet buffer. Also sets packet_size and packet_data.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
fetch_packet() {
if (_packet->data) {
av_free_packet(_packet);
}
while (av_read_frame(_format_ctx, _packet) >= 0) {
if (_packet->stream_index == _audio_index) {
_packet_size = _packet->size;
_packet_data = _packet->data;
return;
}
av_free_packet(_packet);
}
_packet->data = 0;
_packet_size = 0;
_packet_data = 0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::reload_buffer
// Access: Protected
// Description: Reloads the audio buffer by decoding audio packets
// until one of those audio packets finally yields
// some samples. If we encounter the end of the
// stream, we synthesize silence.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
reload_buffer() {
while (_buffer_head == _buffer_tail) {
// If we're out of packets, generate silence.
if (_packet->data == 0) {
_buffer_head = 0;
_buffer_tail = _buffer_size;
memset(_buffer, 0, _buffer_size * 2);
return;
} else if (_packet_size > 0) {
int bufsize = _buffer_size * 2;
int len = avcodec_decode_audio(_audio_ctx, _buffer, &bufsize,
_packet_data, _packet_size);
if (len < 0) {
break;
}
_packet_data += len;
_packet_size -= len;
if (bufsize > 0) {
_buffer_head = 0;
_buffer_tail = (bufsize/2);
return;
}
} else {
fetch_packet();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::seek
// Access: Protected
// Description: Seeks to a target location. Afterward, the
// packet_time is guaranteed to be less than or
// equal to the specified time.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
seek(double t) {
movies_cat.error() << "Seek not implemented yet.\n";
// PN_int64 target_ts = (PN_int64)(t / _audio_timebase);
// if (target_ts < (PN_int64)(_initial_dts)) {
// // Attempts to seek before the first packet will fail.
// target_ts = _initial_dts;
// }
// if (av_seek_frame(_format_ctx, _audio_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
// if (t >= _packet_time) {
// return;
// }
// movies_cat.error() << "Seek failure. Shutting down movie.\n";
// cleanup();
// _packet_time = t;
// return;
// }
// fetch_packet(t);
// if (_packet_time > t) {
// _packet_time = t;
// }
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegAudioCursor::read_samples
// Access: Public, Virtual
// Description: Read audio samples from the stream. N is the
// number of samples you wish to read. Your buffer
// must be equal in size to N * channels.
// Multiple-channel audio will be interleaved.
////////////////////////////////////////////////////////////////////
void FfmpegAudioCursor::
read_samples(int n, PN_int16 *data) {
int desired = n * _audio_channels;
while (desired) {
if (_buffer_head == _buffer_tail) {
reload_buffer();
}
int available = _buffer_tail - _buffer_head;
int ncopy = (desired > available) ? available : desired;
if (ncopy) {
memcpy(data, _buffer + _buffer_head, ncopy * 2);
data += ncopy;
desired -= ncopy;
_buffer_head += ncopy;
}
}
_samples_read += n;
}
////////////////////////////////////////////////////////////////////
#endif // HAVE_FFMPEG

View File

@ -0,0 +1,88 @@
// Filename: ffmpegAudioCursor.h
// Created by: jyelon (01Aug2007)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef FFMPEGAUDIOCURSOR_H
#define FFMPEGAUDIOCURSOR_H
#ifdef HAVE_FFMPEG
#include "pandabase.h"
#include "namable.h"
#include "texture.h"
#include "pointerTo.h"
struct AVFormatContext;
struct AVCodecContext;
struct AVStream;
struct AVPacket;
////////////////////////////////////////////////////////////////////
// Class : FfmpegAudioCursor
// Description : A stream that generates a sequence of audio samples.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_MOVIES FfmpegAudioCursor : public MovieAudioCursor {
PUBLISHED:
FfmpegAudioCursor(PT(FfmpegAudio) src);
virtual ~FfmpegAudioCursor();
virtual void seek(double offset);
public:
virtual void read_samples(int n, PN_int16 *data);
protected:
void fetch_packet();
void reload_buffer();
void cleanup();
Filename _filename;
int _initial_dts;
AVPacket *_packet;
int _packet_size;
unsigned char *_packet_data;
AVFormatContext *_format_ctx;
AVCodecContext *_audio_ctx;
int _audio_index;
double _audio_timebase;
PN_int16 *_buffer;
int _buffer_size;
PN_int16 *_buffer_alloc;
int _buffer_head;
int _buffer_tail;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
TypedWritableReferenceCount::init_type();
register_type(_type_handle, "FfmpegAudioCursor",
MovieAudioCursor::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "ffmpegAudioCursor.I"
#endif // HAVE_FFMPEG
#endif // FFMPEG_AUDIO.H

View File

@ -20,18 +20,6 @@
#include "ffmpegVideo.h"
#include "config_movies.h"
#include "avcodec.h"
#include "avformat.h"
// Earlier versions of ffmpeg didn't define this symbol.
#ifndef PIX_FMT_BGRA
#ifdef WORDS_BIGENDIAN
#define PIX_FMT_BGRA PIX_FMT_BGR32_1
#else
#define PIX_FMT_BGRA PIX_FMT_RGBA32
#endif
#endif // PIX_FMT_BGRA
TypeHandle FfmpegVideo::_type_handle;
@ -43,71 +31,8 @@ TypeHandle FfmpegVideo::_type_handle;
FfmpegVideo::
FfmpegVideo(const Filename &name) :
MovieVideo(name),
_filename(name),
_format_ctx(0),
_video_ctx(0),
_video_index(-1),
_frame(0),
_frame_out(0),
_min_fseek(3.0)
_specified_filename(name)
{
string url = "pandavfs:";
url += name;
if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
cleanup();
return;
}
if (av_find_stream_info(_format_ctx)<0) {
cleanup();
return;
}
// Find the video stream
for(int i=0; i<_format_ctx->nb_streams; i++) {
if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
_video_index = i;
_video_ctx = _format_ctx->streams[i]->codec;
_video_timebase = av_q2d(_format_ctx->streams[i]->time_base);
}
}
if (_video_ctx == 0) {
cleanup();
return;
}
AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
if(pVideoCodec == 0) {
cleanup();
return;
}
if(avcodec_open(_video_ctx, pVideoCodec)<0) {
cleanup();
return;
}
_size_x = _video_ctx->width;
_size_y = _video_ctx->height;
_num_components = 3; // Don't know how to implement RGBA movies yet.
_length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
_can_seek = true;
_can_seek_fast = true;
_packet = new AVPacket;
_frame = avcodec_alloc_frame();
_frame_out = avcodec_alloc_frame();
if ((_packet == 0)||(_frame == 0)||(_frame_out == 0)) {
cleanup();
return;
}
memset(_packet, 0, sizeof(AVPacket));
fetch_packet(0.0);
_initial_dts = _packet->dts;
_packet_time = 0.0;
_last_start = -1.0;
_next_start = 0.0;
}
////////////////////////////////////////////////////////////////////
@ -117,196 +42,19 @@ FfmpegVideo(const Filename &name) :
////////////////////////////////////////////////////////////////////
FfmpegVideo::
~FfmpegVideo() {
cleanup();
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideo::cleanup
// Access: Public
// Description: Reset to a standard inactive state.
////////////////////////////////////////////////////////////////////
void FfmpegVideo::
cleanup() {
_frame_out->data[0] = 0;
if (_packet) {
if (_packet->data) {
av_free_packet(_packet);
}
delete _packet;
_packet = 0;
}
if (_format_ctx) {
av_close_input_file(_format_ctx);
_format_ctx = 0;
}
if (_frame) {
av_free(_frame);
_frame = 0;
}
if (_frame_out) {
av_free(_frame_out);
_frame_out = 0;
}
_video_ctx = 0;
_video_index = -1;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideo::export_frame
// Access: Public, Virtual
// Description: Exports the contents of the frame buffer into the
// user's target buffer.
////////////////////////////////////////////////////////////////////
void FfmpegVideo::
export_frame(unsigned char *data, bool bgra) {
if (bgra) {
_frame_out->data[0] = data + ((_size_y - 1) * _size_x * 4);
_frame_out->linesize[0] = _size_x * -4;
img_convert((AVPicture *)_frame_out, PIX_FMT_BGRA,
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
} else {
_frame_out->data[0] = data + ((_size_y - 1) * _size_x * 3);
_frame_out->linesize[0] = _size_x * -3;
img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideo::fetch_packet
// Access: Protected
// Description: Fetches a video packet and stores it in the
// packet buffer. Sets packet_time to the packet's
// timestamp. If a packet could not be read, the
// packet is cleared and the packet_time is set to
// the specified default value.
////////////////////////////////////////////////////////////////////
void FfmpegVideo::
fetch_packet(double default_time) {
if (_packet->data) {
av_free_packet(_packet);
}
while (av_read_frame(_format_ctx, _packet) >= 0) {
if (_packet->stream_index == _video_index) {
_packet_time = _packet->dts * _video_timebase;
return;
}
av_free_packet(_packet);
}
_packet->data = 0;
_packet_time = default_time;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideo::fetch_frame
// Access: Protected
// Description: Fetches a frame from the stream and stores it in
// the frame buffer. Sets last_start and next_start
// to indicate the extents of the frame.
////////////////////////////////////////////////////////////////////
void FfmpegVideo::
fetch_frame() {
int finished = 0;
_last_start = _packet_time;
while (!finished && _packet->data) {
avcodec_decode_video(_video_ctx, _frame,
&finished, _packet->data, _packet->size);
fetch_packet(_last_start + 1.0);
}
_next_start = _packet_time;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideo::seek
// Access: Protected
// Description: Seeks to a target location. Afterward, the
// packet_time is guaranteed to be less than or
// equal to the specified time.
////////////////////////////////////////////////////////////////////
void FfmpegVideo::
seek(double t) {
long long target_ts = (long long)(t / _video_timebase);
if (target_ts < (long long)(_initial_dts)) {
// Attempts to seek before the first packet will fail.
target_ts = _initial_dts;
}
if (av_seek_frame(_format_ctx, _video_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
if (t >= _packet_time) {
return;
}
movies_cat.error() << "Seek failure. Shutting down movie.\n";
cleanup();
_packet_time = t;
return;
}
fetch_packet(t);
if (_packet_time > t) {
_packet_time = t;
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideo::fetch_into_buffer
// Access: Public, Virtual
// Description: See MovieVideo::fetch_into_buffer.
////////////////////////////////////////////////////////////////////
void FfmpegVideo::
fetch_into_buffer(double time, unsigned char *data, bool bgra) {
// If there was an error at any point, synthesize black.
if (_format_ctx==0) {
if (data) {
memset(data,0,size_x()*size_y()*(bgra?4:3));
}
_last_start = time;
_next_start = time + 1.0;
return;
}
if (time < _last_start) {
// Time is in the past.
seek(time);
while (_packet_time <= time) {
fetch_frame();
}
} else if (time < _next_start) {
// Time is in the present: already have the frame.
} else if (time < _next_start + _min_fseek) {
// Time is in the near future.
while (_packet_time <= time) {
fetch_frame();
}
} else {
// Time is in the far future. Seek forward, then read.
// There's a danger here: because keyframes are spaced
// unpredictably, trying to seek forward could actually
// move us backward in the stream! This must be avoided.
// So the rule is, try the seek. If it hurts us by moving
// us backward, we increase the minimum threshold distance
// for forward-seeking in the future.
double base = _packet_time;
seek(time);
if (_packet_time < base) {
_min_fseek += (base - _packet_time);
}
while (_packet_time <= time) {
fetch_frame();
}
}
export_frame(data, bgra);
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideo::make_copy
// Function: FfmpegVideo::open
// Access: Published, Virtual
// Description: Make a copy of this MovieVideo with its own cursor.
// Description: Open this video, returning a MovieVideoCursor.
////////////////////////////////////////////////////////////////////
PT(MovieVideo) FfmpegVideo::
make_copy() const {
return new FfmpegVideo(_filename);
PT(MovieVideoCursor) FfmpegVideo::
open() {
return new FfmpegVideoCursor(this);
}
////////////////////////////////////////////////////////////////////
#endif // HAVE_FFMPEG

View File

@ -20,15 +20,9 @@
#define FFMPEGVIDEO_H
#ifdef HAVE_FFMPEG
#include "pandabase.h"
#include "texture.h"
#include "pointerTo.h"
#include "movieVideo.h"
struct AVFormatContext;
struct AVCodecContext;
struct AVStream;
struct AVPacket;
struct AVFrame;
class FfmpegVideoCursor;
////////////////////////////////////////////////////////////////////
// Class : FfmpegVideo
@ -39,32 +33,11 @@ class EXPCL_PANDA_MOVIES FfmpegVideo : public MovieVideo {
PUBLISHED:
FfmpegVideo(const Filename &name);
virtual ~FfmpegVideo();
virtual PT(MovieVideo) make_copy() const;
virtual PT(MovieVideoCursor) open();
public:
virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba);
protected:
INLINE double get_time_correction();
INLINE void update_time_correction(double diff);
void fetch_packet(double default_time);
void fetch_frame();
void seek(double t);
void export_frame(unsigned char *data, bool bgra);
void cleanup();
Filename _filename;
AVPacket *_packet;
double _packet_time;
AVFormatContext *_format_ctx;
AVCodecContext *_video_ctx;
AVCodecContext *_audio_ctx;
int _video_index;
double _video_timebase;
AVFrame *_frame;
AVFrame *_frame_out;
int _initial_dts;
double _min_fseek;
private:
Filename _specified_filename;
friend class FfmpegVideoCursor;
public:
static TypeHandle get_class_type() {

View File

@ -0,0 +1,18 @@
// Filename: ffmpegVideoCursor.I
// Created by: jyelon (01Aug2007)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,306 @@
// Filename: ffmpegVideoCursor.cxx
// Created by: jyelon (01Aug2007)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2007, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifdef HAVE_FFMPEG
#include "ffmpegVideoCursor.h"
#include "config_movies.h"
#include "avcodec.h"
#include "avformat.h"
// Earlier versions of ffmpeg didn't define this symbol.
#ifndef PIX_FMT_BGRA
#ifdef WORDS_BIGENDIAN
#define PIX_FMT_BGRA PIX_FMT_BGR32_1
#else
#define PIX_FMT_BGRA PIX_FMT_RGBA32
#endif
#endif // PIX_FMT_BGRA
TypeHandle FfmpegVideoCursor::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::Constructor
// Access: Public
// Description: xxx
////////////////////////////////////////////////////////////////////
FfmpegVideoCursor::
FfmpegVideoCursor(PT(FfmpegVideo) src) :
MovieVideoCursor((MovieVideo*)(FfmpegVideo*)src),
_filename(src->_specified_filename),
_format_ctx(0),
_video_index(-1),
_video_ctx(0),
_frame(0),
_frame_out(0),
_min_fseek(3.0)
{
string url = "pandavfs:";
url += _filename;
if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
cleanup();
return;
}
if (av_find_stream_info(_format_ctx)<0) {
cleanup();
return;
}
// Find the video stream
for(int i=0; i<_format_ctx->nb_streams; i++) {
if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
_video_index = i;
_video_ctx = _format_ctx->streams[i]->codec;
_video_timebase = av_q2d(_format_ctx->streams[i]->time_base);
}
}
if (_video_ctx == 0) {
cleanup();
return;
}
AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
if(pVideoCodec == 0) {
cleanup();
return;
}
if(avcodec_open(_video_ctx, pVideoCodec)<0) {
cleanup();
return;
}
_size_x = _video_ctx->width;
_size_y = _video_ctx->height;
_num_components = 3; // Don't know how to implement RGBA movies yet.
_length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
_can_seek = true;
_can_seek_fast = true;
_packet = new AVPacket;
_frame = avcodec_alloc_frame();
_frame_out = avcodec_alloc_frame();
if ((_packet == 0)||(_frame == 0)||(_frame_out == 0)) {
cleanup();
return;
}
memset(_packet, 0, sizeof(AVPacket));
fetch_packet(0.0);
_initial_dts = _packet->dts;
_packet_time = 0.0;
_last_start = -1.0;
_next_start = 0.0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::Destructor
// Access: Public
// Description: xxx
////////////////////////////////////////////////////////////////////
FfmpegVideoCursor::
~FfmpegVideoCursor() {
cleanup();
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::cleanup
// Access: Public
// Description: Reset to a standard inactive state.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
cleanup() {
_frame_out->data[0] = 0;
if (_packet) {
if (_packet->data) {
av_free_packet(_packet);
}
delete _packet;
_packet = 0;
}
if (_video_ctx) {
avcodec_close(_video_ctx);
_video_ctx = 0;
}
if (_format_ctx) {
av_close_input_file(_format_ctx);
_format_ctx = 0;
}
if (_frame) {
av_free(_frame);
_frame = 0;
}
if (_frame_out) {
av_free(_frame_out);
_frame_out = 0;
}
_video_ctx = 0;
_video_index = -1;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::export_frame
// Access: Public, Virtual
// Description: Exports the contents of the frame buffer into the
// user's target buffer.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
export_frame(unsigned char *data, bool bgra) {
if (bgra) {
_frame_out->data[0] = data + ((_size_y - 1) * _size_x * 4);
_frame_out->linesize[0] = _size_x * -4;
img_convert((AVPicture *)_frame_out, PIX_FMT_BGRA,
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
} else {
_frame_out->data[0] = data + ((_size_y - 1) * _size_x * 3);
_frame_out->linesize[0] = _size_x * -3;
img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_packet
// Access: Protected
// Description: Fetches a video packet and stores it in the
// packet buffer. Sets packet_time to the packet's
// timestamp. If a packet could not be read, the
// packet is cleared and the packet_time is set to
// the specified default value.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
fetch_packet(double default_time) {
if (_packet->data) {
av_free_packet(_packet);
}
while (av_read_frame(_format_ctx, _packet) >= 0) {
if (_packet->stream_index == _video_index) {
_packet_time = _packet->dts * _video_timebase;
return;
}
av_free_packet(_packet);
}
_packet->data = 0;
_packet_time = default_time;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_frame
// Access: Protected
// Description: Fetches a frame from the stream and stores it in
// the frame buffer. Sets last_start and next_start
// to indicate the extents of the frame.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
fetch_frame() {
int finished = 0;
_last_start = _packet_time;
while (!finished && _packet->data) {
avcodec_decode_video(_video_ctx, _frame,
&finished, _packet->data, _packet->size);
fetch_packet(_last_start + 1.0);
}
_next_start = _packet_time;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::seek
// Access: Protected
// Description: Seeks to a target location. Afterward, the
// packet_time is guaranteed to be less than or
// equal to the specified time.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
seek(double t) {
PN_int64 target_ts = (PN_int64)(t / _video_timebase);
if (target_ts < (PN_int64)(_initial_dts)) {
// Attempts to seek before the first packet will fail.
target_ts = _initial_dts;
}
if (av_seek_frame(_format_ctx, _video_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
if (t >= _packet_time) {
return;
}
movies_cat.error() << "Seek failure. Shutting down movie.\n";
cleanup();
_packet_time = t;
return;
}
fetch_packet(t);
if (_packet_time > t) {
_packet_time = t;
}
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVideoCursor::fetch_into_buffer
// Access: Public, Virtual
// Description: See MovieVideoCursor::fetch_into_buffer.
////////////////////////////////////////////////////////////////////
void FfmpegVideoCursor::
fetch_into_buffer(double time, unsigned char *data, bool bgra) {
// If there was an error at any point, synthesize black.
if (_format_ctx==0) {
if (data) {
memset(data,0,size_x()*size_y()*(bgra?4:3));
}
_last_start = time;
_next_start = time + 1.0;
return;
}
if (time < _last_start) {
// Time is in the past.
seek(time);
while (_packet_time <= time) {
fetch_frame();
}
} else if (time < _next_start) {
// Time is in the present: already have the frame.
} else if (time < _next_start + _min_fseek) {
// Time is in the near future.
while (_packet_time <= time) {
fetch_frame();
}
} else {
// Time is in the far future. Seek forward, then read.
// There's a danger here: because keyframes are spaced
// unpredictably, trying to seek forward could actually
// move us backward in the stream! This must be avoided.
// So the rule is, try the seek. If it hurts us by moving
// us backward, we increase the minimum threshold distance
// for forward-seeking in the future.
double base = _packet_time;
seek(time);
if (_packet_time < base) {
_min_fseek += (base - _packet_time);
}
while (_packet_time <= time) {
fetch_frame();
}
}
export_frame(data, bgra);
}
////////////////////////////////////////////////////////////////////
#endif // HAVE_FFMPEG

View File

@ -0,0 +1,86 @@
// Filename: ffmpegVideoCursor.h
// Created by: jyelon (01Aug2007)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef FFMPEGVIDEOCURSOR_H
#define FFMPEGVIDEOCURSOR_H
#ifdef HAVE_FFMPEG
#include "pandabase.h"
#include "texture.h"
#include "pointerTo.h"
struct AVFormatContext;
struct AVCodecContext;
struct AVStream;
struct AVPacket;
struct AVFrame;
////////////////////////////////////////////////////////////////////
// Class : FfmpegVideoCursor
// Description :
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_MOVIES FfmpegVideoCursor : public MovieVideoCursor {
PUBLISHED:
FfmpegVideoCursor(PT(FfmpegVideo) src);
virtual ~FfmpegVideoCursor();
public:
virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba);
protected:
void fetch_packet(double default_time);
void fetch_frame();
void seek(double t);
void export_frame(unsigned char *data, bool bgra);
void cleanup();
Filename _filename;
AVPacket *_packet;
double _packet_time;
AVFormatContext *_format_ctx;
AVCodecContext *_video_ctx;
int _video_index;
double _video_timebase;
AVFrame *_frame;
AVFrame *_frame_out;
int _initial_dts;
double _min_fseek;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
MovieVideoCursor::init_type();
register_type(_type_handle, "FfmpegVideoCursor",
MovieVideoCursor::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "ffmpegVideoCursor.I"
#endif // HAVE_FFMPEG
#endif // FFMPEGVIDEO_H

View File

@ -0,0 +1,17 @@
// Filename: ffmpegVirtualFile.I
// Created by: jyelon (01Aug2007)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2007, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,155 @@
// Filename: ffmpegVirtualFile.cxx
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2007, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifdef HAVE_FFMPEG
#include "pandabase.h"
#include "config_movies.h"
#include "ffmpegVirtualFile.h"
#include "virtualFileSystem.h"
#include "avio.h"
////////////////////////////////////////////////////////////////////
// These functions need to use C calling conventions.
////////////////////////////////////////////////////////////////////
extern "C" {
static int pandavfs_open(URLContext *h, const char *filename, int flags);
static int pandavfs_read(URLContext *h, unsigned char *buf, int size);
static int pandavfs_write(URLContext *h, unsigned char *buf, int size);
static PN_int64 pandavfs_seek(URLContext *h, PN_int64 pos, int whence);
static int pandavfs_close(URLContext *h);
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_open
// Access: Static Function
// Description: A hook to open a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_open(URLContext *h, const char *filename, int flags) {
if (flags != 0) {
movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
return -1;
}
filename += 9; // Skip over "pandavfs:"
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
istream *s = vfs->open_read_file(filename, true);
if (s == 0) {
return -1;
}
// Test whether seek works.
s->seekg(1, ios::beg);
int tel1 = s->tellg();
s->seekg(0, ios::beg);
int tel2 = s->tellg();
if (s->fail() || (tel1!=1) || (tel2!=0)) {
movies_cat.error() << "cannot play movie (not seekable): " << h->filename << "\n";
delete s;
return -1;
}
h->priv_data = s;
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_read
// Access: Static Function
// Description: A hook to read a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_read(URLContext *h, unsigned char *buf, int size) {
istream *s = (istream*)(h->priv_data);
s->read((char*)buf, size);
int gc = s->gcount();
s->clear();
return gc;
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_write
// Access: Static Function
// Description: A hook to write a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_write(URLContext *h, unsigned char *buf, int size) {
movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_seek
// Access: Static Function
// Description: A hook to seek a panda VFS file.
////////////////////////////////////////////////////////////////////
static PN_int64
pandavfs_seek(URLContext *h, PN_int64 pos, int whence) {
istream *s = (istream*)(h->priv_data);
switch(whence) {
case 0: s->seekg(pos, ios::beg); break;
case 1: s->seekg(pos, ios::cur); break;
case 2: s->seekg(pos, ios::end); break;
default:
movies_cat.error() << "Illegal parameter to seek in ffmpegVirtualFile\n";
return -1;
}
s->clear();
int tl = s->tellg();
return tl;
}
////////////////////////////////////////////////////////////////////
// Function: pandavfs_close
// Access: Static Function
// Description: A hook to close a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_close(URLContext *h) {
istream *s = (istream*)(h->priv_data);
delete s;
h->priv_data = 0;
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: FfmpegVirtualFile::register_protocol
// Access: Public, Static
// Description: Enables ffmpeg to access panda's VFS.
//
// After calling this method, ffmpeg will be
// able to open "URLs" that look like this:
//
// pandavfs:/c/mygame/foo.avi
//
////////////////////////////////////////////////////////////////////
void FfmpegVirtualFile::
register_protocol() {
static bool initialized = false;
if (initialized) {
return;
}
static URLProtocol protocol;
protocol.name = "pandavfs";
protocol.url_open = pandavfs_open;
protocol.url_read = pandavfs_read;
protocol.url_write = pandavfs_write;
protocol.url_seek = pandavfs_seek;
protocol.url_close = pandavfs_close;
::register_protocol(&protocol);
}
#endif // HAVE_FFMPEG

View File

@ -0,0 +1,46 @@
// Filename: ffmpegVirtualFile.h
// Created by: jyelon (01Aug2007)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2007, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef FFMPEGVIRTUALFILE_H
#define FFMPEGVIRTUALFILE_H
#ifdef HAVE_FFMPEG
#include "config_movies.h"
////////////////////////////////////////////////////////////////////
// Class : FfmpegVirtualFile
// Description : Enables ffmpeg to access panda's VFS.
//
// This class only has one public method,
// register_hooks. Once the hooks are registered,
// ffmpeg will be able to open "URLs" that look
// like this:
//
// pandavfs:/c/mygame/foo.avi
//
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_MOVIES FfmpegVirtualFile {
public:
static void register_protocol();
};
#include "ffmpegVirtualFile.I"
#endif // HAVE_FFMPEG
#endif // FFMPEGVIRTUALFILE_H

View File

@ -17,39 +17,10 @@
////////////////////////////////////////////////////////////////////
#include "inkblotVideo.h"
#include "config_movies.h"
#include "inkblotVideoCursor.h"
TypeHandle InkblotVideo::_type_handle;
////////////////////////////////////////////////////////////////////
//
// The Color-Map
//
////////////////////////////////////////////////////////////////////
struct color {
int r,g,b;
};
static color colormap[17] = {
{ 255,0,0 },
{ 255,255,0 },
{ 0,255,0 },
{ 0,255,255 },
{ 0,0,255 },
{ 0,0,0 },
{ 255,0,0 },
{ 255,255,0 },
{ 0,255,0 },
{ 0,255,255 },
{ 0,0,255 },
{ 0,0,0 },
{ 255,0,0 },
{ 255,255,0 },
{ 0,255,0 },
{ 0,255,255 },
{ 0,0,255 },
};
////////////////////////////////////////////////////////////////////
// Function: InkblotVideo::Constructor
// Access: Public
@ -57,24 +28,11 @@ static color colormap[17] = {
////////////////////////////////////////////////////////////////////
InkblotVideo::
InkblotVideo(int x, int y, int fps) :
MovieVideo("inkblot")
MovieVideo("inkblot"),
_specified_x(x),
_specified_y(y),
_specified_fps(y)
{
if (x < 1) x=1;
if (y < 1) y=1;
if (fps < 1) fps=1;
_size_x = x;
_size_y = y;
_fps = fps;
int padx = x + 2;
int pady = y + 2;
_cells = new unsigned char[padx * pady];
_cells2 = new unsigned char[padx * pady];
memset(_cells, 255, padx * pady);
memset(_cells2, 255, padx * pady);
_can_seek = true;
_can_seek_fast = false;
_frames_read = 0;
}
////////////////////////////////////////////////////////////////////
@ -84,81 +42,14 @@ InkblotVideo(int x, int y, int fps) :
////////////////////////////////////////////////////////////////////
InkblotVideo::
~InkblotVideo() {
delete[] _cells;
delete[] _cells2;
}
////////////////////////////////////////////////////////////////////
// Function: InkblotVideo::fetch_into_buffer
// Function: InkblotVideo::open
// Access: Published, Virtual
// Description: See MovieVideo::fetch_into_buffer.
// Description: Open this video, returning a MovieVideoCursor.
////////////////////////////////////////////////////////////////////
void InkblotVideo::
fetch_into_buffer(double time, unsigned char *data, bool bgra) {
int padx = size_x() + 2;
int pady = size_y() + 2;
if (time < _next_start) {
// Rewind to beginning.
memset(_cells, 255, padx * pady);
memset(_cells2, 255, padx * pady);
_last_start = -1.0;
_next_start = 0.0;
_frames_read = 0;
}
nassertv(time >= _next_start);
while (_next_start <= time) {
_last_start = (_frames_read * 1.0) / _fps;
_frames_read += 1;
_next_start = (_frames_read * 1.0) / _fps;
for (int y=1; y<pady-1; y++) {
for (int x=1; x<padx-1; x++) {
int tot =
_cells[(x+1)+(y+1)*padx] +
_cells[(x+1)+(y+0)*padx] +
_cells[(x+1)+(y-1)*padx] +
_cells[(x+0)+(y+1)*padx] +
_cells[(x+0)+(y+0)*padx] +
_cells[(x+0)+(y-1)*padx] +
_cells[(x-1)+(y+1)*padx] +
_cells[(x-1)+(y+0)*padx] +
_cells[(x-1)+(y-1)*padx];
_cells2[x + y*padx] = (tot/9)+3;
}
}
unsigned char *t = _cells;
_cells = _cells2;
_cells2 = t;
}
for (int y=1; y<pady - 1; y++) {
for (int x=1; x<padx - 1; x++) {
int val = _cells[x + y*padx];
color &c1 = colormap[(val>>4)+0];
color &c2 = colormap[(val>>4)+1];
int lerp = val & 15;
data[0] = (c1.b * (16-lerp) + c2.b * lerp) / 16;
data[1] = (c1.g * (16-lerp) + c2.g * lerp) / 16;
data[2] = (c1.r * (16-lerp) + c2.r * lerp) / 16;
if (bgra) {
data[3] = 255;
data += 4;
} else {
data += 3;
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: InkblotVideo::make_copy
// Access: Published, Virtual
// Description: Make a copy of this video with a separate cursor.
////////////////////////////////////////////////////////////////////
PT(MovieVideo) InkblotVideo::
make_copy() const {
return new InkblotVideo(_size_x, _size_y, _fps);
PT(MovieVideoCursor) InkblotVideo::
open() {
return new InkblotVideoCursor(this);
}

View File

@ -19,9 +19,9 @@
#ifndef INKBLOTVIDEO_H
#define INKBLOTVIDEO_H
#include "pandabase.h"
#include "texture.h"
#include "pointerTo.h"
#include "movieVideo.h"
class InkblotVideoCursor;
////////////////////////////////////////////////////////////////////
// Class : InkblotVideo
@ -33,16 +33,13 @@ class EXPCL_PANDA_MOVIES InkblotVideo : public MovieVideo {
PUBLISHED:
InkblotVideo(int x, int y, int fps);
virtual ~InkblotVideo();
virtual PT(MovieVideo) make_copy() const;
public:
virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba);
protected:
unsigned char *_cells;
unsigned char *_cells2;
int _fps;
int _frames_read;
virtual PT(MovieVideoCursor) open();
private:
int _specified_x;
int _specified_y;
int _specified_fps;
friend class InkblotVideoCursor;
public:
static TypeHandle get_class_type() {

View File

@ -0,0 +1,18 @@
// Filename: inkblotVideoCursor.I
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,151 @@
// Filename: inkblotVideoCursor.cxx
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2007, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "inkblotVideoCursor.h"
#include "config_movies.h"
TypeHandle InkblotVideoCursor::_type_handle;
////////////////////////////////////////////////////////////////////
//
// The Color-Map
//
////////////////////////////////////////////////////////////////////
struct color {
int r,g,b;
};
static color colormap[17] = {
{ 255,0,0 },
{ 255,255,0 },
{ 0,255,0 },
{ 0,255,255 },
{ 0,0,255 },
{ 0,0,0 },
{ 255,0,0 },
{ 255,255,0 },
{ 0,255,0 },
{ 0,255,255 },
{ 0,0,255 },
{ 0,0,0 },
{ 255,0,0 },
{ 255,255,0 },
{ 0,255,0 },
{ 0,255,255 },
{ 0,0,255 },
};
////////////////////////////////////////////////////////////////////
// Function: InkblotVideoCursor::Constructor
// Access: Public
// Description: xxx
////////////////////////////////////////////////////////////////////
InkblotVideoCursor::
InkblotVideoCursor(PT(InkblotVideo) src) :
MovieVideoCursor((MovieVideo*)(InkblotVideo*)src)
{
_size_x = src->_specified_x;
_size_y = src->_specified_y;
_fps = src->_specified_fps;
int padx = _size_x + 2;
int pady = _size_y + 2;
_cells = new unsigned char[padx * pady];
_cells2 = new unsigned char[padx * pady];
memset(_cells, 255, padx * pady);
memset(_cells2, 255, padx * pady);
_can_seek = true;
_can_seek_fast = false;
_frames_read = 0;
}
////////////////////////////////////////////////////////////////////
// Function: InkblotVideoCursor::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
InkblotVideoCursor::
~InkblotVideoCursor() {
delete[] _cells;
delete[] _cells2;
}
////////////////////////////////////////////////////////////////////
// Function: InkblotVideoCursor::fetch_into_buffer
// Access: Published, Virtual
// Description: See MovieVideoCursor::fetch_into_buffer.
////////////////////////////////////////////////////////////////////
void InkblotVideoCursor::
fetch_into_buffer(double time, unsigned char *data, bool bgra) {
int padx = size_x() + 2;
int pady = size_y() + 2;
if (time < _next_start) {
// Rewind to beginning.
memset(_cells, 255, padx * pady);
memset(_cells2, 255, padx * pady);
_last_start = -1.0;
_next_start = 0.0;
_frames_read = 0;
}
nassertv(time >= _next_start);
while (_next_start <= time) {
_last_start = (_frames_read * 1.0) / _fps;
_frames_read += 1;
_next_start = (_frames_read * 1.0) / _fps;
for (int y=1; y<pady-1; y++) {
for (int x=1; x<padx-1; x++) {
int tot =
_cells[(x+1)+(y+1)*padx] +
_cells[(x+1)+(y+0)*padx] +
_cells[(x+1)+(y-1)*padx] +
_cells[(x+0)+(y+1)*padx] +
_cells[(x+0)+(y+0)*padx] +
_cells[(x+0)+(y-1)*padx] +
_cells[(x-1)+(y+1)*padx] +
_cells[(x-1)+(y+0)*padx] +
_cells[(x-1)+(y-1)*padx];
_cells2[x + y*padx] = (tot/9)+3;
}
}
unsigned char *t = _cells;
_cells = _cells2;
_cells2 = t;
}
for (int y=1; y<pady - 1; y++) {
for (int x=1; x<padx - 1; x++) {
int val = _cells[x + y*padx];
color &c1 = colormap[(val>>4)+0];
color &c2 = colormap[(val>>4)+1];
int lerp = val & 15;
data[0] = (c1.b * (16-lerp) + c2.b * lerp) / 16;
data[1] = (c1.g * (16-lerp) + c2.g * lerp) / 16;
data[2] = (c1.r * (16-lerp) + c2.r * lerp) / 16;
if (bgra) {
data[3] = 255;
data += 4;
} else {
data += 3;
}
}
}
}

View File

@ -0,0 +1,67 @@
// Filename: inkblotVideoCursor.h
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef INKBLOTVIDEOCURSOR_H
#define INKBLOTVIDEOCURSOR_H
#include "pandabase.h"
#include "texture.h"
#include "pointerTo.h"
#include "inkblotVideo.h"
////////////////////////////////////////////////////////////////////
// Class : InkblotVideoCursor
// Description : A cellular automaton that generates an amusing
// pattern of swirling colors.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_MOVIES InkblotVideoCursor : public MovieVideoCursor {
PUBLISHED:
InkblotVideoCursor(PT(InkblotVideo) src);
virtual ~InkblotVideoCursor();
public:
virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba);
protected:
unsigned char *_cells;
unsigned char *_cells2;
int _fps;
int _frames_read;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
MovieVideoCursor::init_type();
register_type(_type_handle, "InkblotVideoCursor",
MovieVideoCursor::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "inkblotVideoCursor.I"
#endif

View File

@ -16,123 +16,3 @@
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: MovieAudio::audio_rate
// Access: Public
// Description: Returns the audio sample rate.
////////////////////////////////////////////////////////////////////
INLINE int MovieAudio::
audio_rate() const {
return _audio_rate;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudio::audio_channels
// Access: Public
// Description: Returns the number of audio channels (ie, two for
// stereo, one for mono).
////////////////////////////////////////////////////////////////////
INLINE int MovieAudio::
audio_channels() const {
return _audio_channels;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudio::length
// Access: Public
// Description: Returns the length of the movie.
//
// Some kinds of Movie, such as internet TV station,
// might not have a predictable length. In that case,
// the length will be set to a very large number: 1.0E10.
// If the internet TV station goes offline, the video
// or audio stream will set its abort flag. Reaching the
// end of the movie (ie, the specified length) normally
// does not cause the abort flag to be set.
//
// The video and audio streams produced by get_video and
// get_audio are always of unlimited duration - you can
// always read another video frame or another audio
// sample. This is true even if the specified length
// is reached, or an abort is flagged. If either stream
// runs out of data, it will synthesize blank video
// frames and silent audio samples as necessary to
// satisfy read requests.
//
// Some AVI files have incorrect length values encoded
// into them - usually, they're a second or two long or
// short. When playing such an AVI using the Movie class,
// you may see a slightly truncated video, or a slightly
// elongated video (padded with black frames). There are
// utilities out there to fix the length values in AVI
// files.
//
////////////////////////////////////////////////////////////////////
INLINE double MovieAudio::
length() const {
return _length;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudio::can_seek
// Access: Public
// Description: Returns true if the movie can seek. If this is
// true, seeking is still not guaranteed to be fast:
// for some movies, seeking is implemented by rewinding
// to the beginning and then fast-forwarding to the
// desired location. Even if the movie cannot seek,
// the seek method can still advance to an arbitrary
// location by reading samples and discarding them.
// However, to move backward, can_seek must return true.
////////////////////////////////////////////////////////////////////
INLINE bool MovieAudio::
can_seek() const {
return _can_seek;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudio::can_seek_fast
// Access: Public
// Description: Returns true if seek operations are constant time.
////////////////////////////////////////////////////////////////////
INLINE bool MovieAudio::
can_seek_fast() const {
return _can_seek_fast;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudio::aborted
// Access: Public
// Description: Returns true if the audio has aborted prematurely.
// For example, this could occur if the Movie was actually
// an internet TV station, and the connection was lost.
// Reaching the normal end of the audio does not
// constitute an 'abort' condition.
////////////////////////////////////////////////////////////////////
INLINE bool MovieAudio::
aborted() const {
return _aborted;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudio::samples_read
// Access: Public
// Description: Returns the number of audio samples you have read
// from the audio stream so far.
////////////////////////////////////////////////////////////////////
INLINE int MovieAudio::
samples_read() const {
return _samples_read;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudio::skip_samples
// Access: Published
// Description: Skip audio samples from the stream. This is mostly
// for debugging purposes.
////////////////////////////////////////////////////////////////////
INLINE void MovieAudio::
skip_samples(int n) {
read_samples(n, 0);
}

View File

@ -17,6 +17,7 @@
////////////////////////////////////////////////////////////////////
#include "movieAudio.h"
#include "movieAudioCursor.h"
TypeHandle MovieAudio::_type_handle;
@ -30,14 +31,7 @@ TypeHandle MovieAudio::_type_handle;
////////////////////////////////////////////////////////////////////
MovieAudio::
MovieAudio(const string &name) :
Namable(name),
_audio_rate(8000),
_audio_channels(1),
_length(1.0E10),
_can_seek(true),
_can_seek_fast(true),
_aborted(false),
_samples_read(0)
Namable(name)
{
}
@ -51,76 +45,27 @@ MovieAudio::
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudio::read_samples
// Access: Public, Virtual
// Description: Read audio samples from the stream. N is the
// number of samples you wish to read. Your buffer
// must be equal in size to N * channels.
// Multiple-channel audio will be interleaved.
////////////////////////////////////////////////////////////////////
void MovieAudio::
read_samples(int n, PN_int16 *data) {
// This is the null implementation, which generates pure silence.
// Normally, this method will be overridden by a subclass.
if (n <= 0) {
return;
}
for (int i=0; i<n; i++) {
data[i] = 0;
}
_samples_read += n;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudio::seek
// Function: MovieAudio::open
// Access: Published, Virtual
// Description: Skips to the specified sample number. After
// calling seek, samples_read will be equal to the
// specified value. If the movie reports that it
// cannot seek, then this method can still advance
// by reading samples and discarding them. However,
// to move backward, can_seek must be true.
//
// If the movie reports that it can_seek, it doesn't
// mean that it can do so quickly. It may have to
// rewind the movie and then fast forward to the
// desired location. Only if can_seek_fast returns
// true can seek operations be done in constant time.
//
// Seeking may not be precise, because AVI files
// often have inaccurate indices. However, it is
// usually pretty close (ie, 0.05 seconds).
// Description: Open this audio, returning a MovieAudioCursor
////////////////////////////////////////////////////////////////////
void MovieAudio::
seek(int sr) {
_samples_read = sr;
PT(MovieAudioCursor) MovieAudio::
open() {
return new MovieAudioCursor(this);
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudio::make_copy
// Access: Published, Virtual
// Description: Make a copy of this MovieAudio with its own cursor.
////////////////////////////////////////////////////////////////////
PT(MovieAudio) MovieAudio::
make_copy() const {
return new MovieAudio();
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudio::load
// Function: MovieAudio::get
// Access: Published, Static
// Description: Load a movie from a file.
// Description: Obtains a MovieAudio that references a file.
////////////////////////////////////////////////////////////////////
PT(MovieAudio) MovieAudio::
load(const Filename &name) {
get(const Filename &name) {
#ifdef HAVE_FFMPEG
// Someday, I'll probably put a dispatcher here.
// But for now, just hardwire it to go to FFMPEG.
return new FfmpegAudio(name);
#else
return NULL;
return new MovieAudio("Load-Failure Stub");
#endif
}

View File

@ -21,8 +21,10 @@
#include "pandabase.h"
#include "namable.h"
#include "texture.h"
#include "pointerTo.h"
#include "typedWritableReferenceCount.h"
class MovieAudio;
#include "movieAudioCursor.h"
////////////////////////////////////////////////////////////////////
// Class : MovieAudio
@ -30,40 +32,19 @@
// a sequence of audio samples. That could include an
// AVI file, a microphone, or an internet TV station.
//
// Thread safety: each individual MovieAudio
// must be owned and accessed by a single thread.
// It is OK for two different threads to open
// the same file at the same time, as long as they
// use separate MovieAudio objects.
// The difference between a MovieAudio and a
// MovieAudioCursor is like the difference between a
// filename and a file handle. The MovieAudio just
// indicates a particular movie. The MovieAudioCursor
// is what allows access.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_MOVIES MovieAudio : public TypedWritableReferenceCount, public Namable {
PUBLISHED:
MovieAudio(const string &name = "Blank Audio");
virtual ~MovieAudio();
INLINE int audio_rate() const;
INLINE int audio_channels() const;
INLINE double length() const;
INLINE bool can_seek() const;
INLINE bool can_seek_fast() const;
INLINE bool aborted() const;
INLINE int samples_read() const;
INLINE void skip_samples(int n);
virtual void seek(int sr);
virtual PT(MovieAudio) make_copy() const;
static PT(MovieAudio) load(const Filename &name);
public:
virtual void read_samples(int n, PN_int16 *data);
protected:
int _audio_rate;
int _audio_channels;
double _length;
bool _can_seek;
bool _can_seek_fast;
bool _aborted;
int _samples_read;
virtual PT(MovieAudioCursor) open();
static PT(MovieAudio) get(const Filename &name);
public:
static TypeHandle get_class_type() {

View File

@ -0,0 +1,147 @@
// Filename: movieAudioCursor.I
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::get_source
// Access: Public
// Description: Returns the MovieAudio which this cursor references.
////////////////////////////////////////////////////////////////////
INLINE PT(MovieAudio) MovieAudioCursor::
get_source() const {
return _source;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::audio_rate
// Access: Public
// Description: Returns the audio sample rate.
////////////////////////////////////////////////////////////////////
INLINE int MovieAudioCursor::
audio_rate() const {
return _audio_rate;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::audio_channels
// Access: Public
// Description: Returns the number of audio channels (ie, two for
// stereo, one for mono).
////////////////////////////////////////////////////////////////////
INLINE int MovieAudioCursor::
audio_channels() const {
return _audio_channels;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::length
// Access: Public
// Description: Returns the length of the movie.
//
// Some kinds of Movie, such as internet TV station,
// might not have a predictable length. In that case,
// the length will be set to a very large number: 1.0E10.
// If the internet TV station goes offline, the video
// or audio stream will set its abort flag. Reaching the
// end of the movie (ie, the specified length) normally
// does not cause the abort flag to be set.
//
// The video and audio streams produced by get_video and
// get_audio are always of unlimited duration - you can
// always read another video frame or another audio
// sample. This is true even if the specified length
// is reached, or an abort is flagged. If either stream
// runs out of data, it will synthesize blank video
// frames and silent audio samples as necessary to
// satisfy read requests.
//
// Some AVI files have incorrect length values encoded
// into them - usually, they're a second or two long or
// short. When playing such an AVI using the Movie class,
// you may see a slightly truncated video, or a slightly
// elongated video (padded with black frames). There are
// utilities out there to fix the length values in AVI
// files.
//
////////////////////////////////////////////////////////////////////
INLINE double MovieAudioCursor::
length() const {
return _length;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::can_seek
// Access: Public
// Description: Returns true if the movie can seek. If this is
// true, seeking is still not guaranteed to be fast:
// for some movies, seeking is implemented by rewinding
// to the beginning and then fast-forwarding to the
// desired location. Even if the movie cannot seek,
// the seek method can still advance to an arbitrary
// location by reading samples and discarding them.
// However, to move backward, can_seek must return true.
////////////////////////////////////////////////////////////////////
INLINE bool MovieAudioCursor::
can_seek() const {
return _can_seek;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::can_seek_fast
// Access: Public
// Description: Returns true if seek operations are constant time.
////////////////////////////////////////////////////////////////////
INLINE bool MovieAudioCursor::
can_seek_fast() const {
return _can_seek_fast;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::aborted
// Access: Public
// Description: Returns true if the audio has aborted prematurely.
// For example, this could occur if the Movie was actually
// an internet TV station, and the connection was lost.
// Reaching the normal end of the audio does not
// constitute an 'abort' condition.
////////////////////////////////////////////////////////////////////
INLINE bool MovieAudioCursor::
aborted() const {
return _aborted;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::tell
// Access: Public
// Description: Returns the current offset within the file.
////////////////////////////////////////////////////////////////////
INLINE double MovieAudioCursor::
tell() const {
return _last_seek + ((_samples_read * 1.0) / _audio_rate);
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::skip_samples
// Access: Published
// Description: Skip audio samples from the stream. This is mostly
// for debugging purposes.
////////////////////////////////////////////////////////////////////
INLINE void MovieAudioCursor::
skip_samples(int n) {
read_samples(n, 0);
}

View File

@ -0,0 +1,104 @@
// Filename: movieAudioCursor.cxx
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2007, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net
//
////////////////////////////////////////////////////////////////////
#include "movieAudioCursor.h"
TypeHandle MovieAudioCursor::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::Constructor
// Access: Public
// Description: This constructor returns a null audio stream --- a
// stream of total silence, at 8000 samples per second.
// To get more interesting audio, you need to construct
// a subclass of this class.
////////////////////////////////////////////////////////////////////
MovieAudioCursor::
MovieAudioCursor(PT(MovieAudio) src) :
_source(src),
_audio_rate(8000),
_audio_channels(1),
_length(1.0E10),
_can_seek(true),
_can_seek_fast(true),
_aborted(false),
_samples_read(0)
{
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
MovieAudioCursor::
~MovieAudioCursor() {
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::read_samples
// Access: Public, Virtual
// Description: Read audio samples from the stream. N is the
// number of samples you wish to read. Your buffer
// must be equal in size to N * channels.
// Multiple-channel audio will be interleaved.
////////////////////////////////////////////////////////////////////
void MovieAudioCursor::
read_samples(int n, PN_int16 *data) {
// This is the null implementation, which generates pure silence.
// Normally, this method will be overridden by a subclass.
if (n <= 0) {
return;
}
for (int i=0; i<n; i++) {
data[i] = 0;
}
_samples_read += n;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::seek
// Access: Published, Virtual
// Description: Skips to the specified offset within the file.
//
// If the movie reports that it cannot seek, then
// this method can still advance by reading samples
// and discarding them. However, to move backward,
// can_seek must be true.
//
// If the movie reports that it can_seek, it doesn't
// mean that it can do so quickly. It may have to
// rewind the movie and then fast forward to the
// desired location. Only if can_seek_fast returns
// true can seek operations be done in constant time.
//
// Seeking may not be precise, because AVI files
// often have inaccurate indices. After
// seeking, tell will indicate that the cursor is
// at the target location. However, in truth, the data
// you read may come from a slightly offset location.
////////////////////////////////////////////////////////////////////
void MovieAudioCursor::
seek(double offset) {
_last_seek = offset;
_samples_read = 0;
}

View File

@ -0,0 +1,93 @@
// Filename: movieAudioCursor.h
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef MOVIEAUDIOCURSOR_H
#define MOVIEAUDIOCURSOR_H
#include "pandabase.h"
#include "namable.h"
#include "texture.h"
#include "pointerTo.h"
class MovieAudioCursor;
#include "movieAudio.h"
////////////////////////////////////////////////////////////////////
// Class : MovieAudioCursor
// Description : A MovieAudio is actually any source that provides
// a sequence of audio samples. That could include an
// AVI file, a microphone, or an internet TV station.
// A MovieAudioCursor is a handle that lets you read
// data sequentially from a MovieAudio.
//
// Thread safety: each individual MovieAudioCursor
// must be owned and accessed by a single thread.
// It is OK for two different threads to open
// the same file at the same time, as long as they
// use separate MovieAudioCursor objects.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_MOVIES MovieAudioCursor : public TypedWritableReferenceCount {
PUBLISHED:
MovieAudioCursor(PT(MovieAudio) src);
virtual ~MovieAudioCursor();
INLINE PT(MovieAudio) get_source() const;
INLINE int audio_rate() const;
INLINE int audio_channels() const;
INLINE double length() const;
INLINE bool can_seek() const;
INLINE bool can_seek_fast() const;
INLINE bool aborted() const;
INLINE double tell() const;
INLINE void skip_samples(int n);
virtual void seek(double offset);
public:
virtual void read_samples(int n, PN_int16 *data);
protected:
PT(MovieAudio) _source;
int _audio_rate;
int _audio_channels;
double _length;
bool _can_seek;
bool _can_seek_fast;
bool _aborted;
double _last_seek;
PN_int64 _samples_read;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
TypedWritableReferenceCount::init_type();
register_type(_type_handle, "MovieAudioCursor",
TypedWritableReferenceCount::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "movieAudioCursor.I"
#endif

View File

@ -16,139 +16,3 @@
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::size_x
// Access: Published
// Description: Get the horizontal size of the movie.
////////////////////////////////////////////////////////////////////
INLINE int MovieVideo::
size_x() const {
return _size_x;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::size_y
// Access: Published
// Description: Get the vertical size of the movie.
////////////////////////////////////////////////////////////////////
INLINE int MovieVideo::
size_y() const {
return _size_y;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::get_num_components
// Access: Published
// Description: Returns 4 if the movie has an alpha
// channel, 3 otherwise.
////////////////////////////////////////////////////////////////////
INLINE int MovieVideo::
get_num_components() const {
return _num_components;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::length
// Access: Published
// Description: Returns the length of the movie.
//
// Some kinds of Movie, such as internet TV station,
// might not have a predictable length. In that case,
// the length will be set to a very large number: 1.0E10.
// If the internet TV station goes offline, the video
// or audio stream will set its abort flag. Reaching the
// end of the movie (ie, the specified length) normally
// does not cause the abort flag to be set.
//
// The video and audio streams produced by get_video and
// get_audio are always of unlimited duration - you can
// always read another video frame or another audio
// sample. This is true even if the specified length
// is reached, or an abort is flagged. If either stream
// runs out of data, it will synthesize blank video
// frames and silent audio samples as necessary to
// satisfy read requests.
//
// Some AVI files have incorrect length values encoded
// into them - usually, they're a second or two long or
// short. When playing such an AVI using the Movie class,
// you may see a slightly truncated video, or a slightly
// elongated video (padded with black frames). There are
// utilities out there to fix the length values in AVI
// files.
//
////////////////////////////////////////////////////////////////////
INLINE int MovieVideo::
length() const {
return _length;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::can_seek
// Access: Published
// Description: Returns true if the movie can seek. If this is
// true, seeking is still not guaranteed to be fast:
// for some movies, seeking is implemented by rewinding
// to the beginning and then fast-forwarding to the
// desired location. Even if the movie cannot seek,
// the fetch methods can still advance to an arbitrary
// location by reading frames and discarding them.
// However, to move backward, can_seek must return true.
////////////////////////////////////////////////////////////////////
INLINE bool MovieVideo::
can_seek() const {
return _can_seek;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::can_seek_fast
// Access: Published
// Description: Returns true if seek operations are constant time.
////////////////////////////////////////////////////////////////////
INLINE bool MovieVideo::
can_seek_fast() const {
return _can_seek_fast;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::aborted
// Access: Published
// Description: Returns true if the video has aborted prematurely.
// For example, this could occur if the Movie was actually
// an internet TV station, and the connection was lost.
// Reaching the normal end of the video does not
// constitute an 'abort' condition.
////////////////////////////////////////////////////////////////////
INLINE bool MovieVideo::
aborted() const {
return _aborted;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::last_start
// Access: Published
// Description: Returns the start time of the last frame you read.
//
// MovieVideo streams have variable frame rates. Each
// frame will specify how long it is to be displayed.
// These lengths may not be equal from frame to frame.
////////////////////////////////////////////////////////////////////
INLINE double MovieVideo::
last_start() const {
return _last_start;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::next_start
// Access: Published
// Description: Returns the start time of the next frame you can read.
//
// MovieVideo streams have variable frame rates. Each
// frame will specify how long it is to be displayed.
// These lengths may not be equal from frame to frame.
////////////////////////////////////////////////////////////////////
INLINE double MovieVideo::
next_start() const {
return _next_start;
}

View File

@ -17,7 +17,6 @@
////////////////////////////////////////////////////////////////////
#include "movieVideo.h"
#include "ffmpegVideo.h"
#include "config_movies.h"
TypeHandle MovieVideo::_type_handle;
@ -32,16 +31,7 @@ TypeHandle MovieVideo::_type_handle;
////////////////////////////////////////////////////////////////////
MovieVideo::
MovieVideo(const string &name) :
Namable(name),
_size_x(1),
_size_y(1),
_num_components(3),
_length(1.0E10),
_can_seek(true),
_can_seek_fast(true),
_aborted(false),
_last_start(-1.0),
_next_start(0.0)
Namable(name)
{
}
@ -52,257 +42,31 @@ MovieVideo(const string &name) :
////////////////////////////////////////////////////////////////////
MovieVideo::
~MovieVideo() {
if (_conversion_buffer != 0) {
delete[] _conversion_buffer;
}
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::allocate_conversion_buffer
// Access: Private
// Description: The generic implementations of fetch_into_texture
// and fetch_into_alpha require the use of a conversion
// buffer. This allocates the buffer.
////////////////////////////////////////////////////////////////////
void MovieVideo::
allocate_conversion_buffer() {
if (_conversion_buffer == 0) {
_conversion_buffer = new unsigned char[size_x() * size_y() * 4];
}
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::fetch_into_bitbucket
// Function: MovieVideo::open
// Access: Published, Virtual
// Description: Discards the next video frame. Still sets
// last_start and next_start.
//
// See fetch_into_buffer for more details.
// Description: Open this video, returning a MovieVideoCursor.
////////////////////////////////////////////////////////////////////
void MovieVideo::
fetch_into_bitbucket(double time) {
// This generic implementation is layered on fetch_into_buffer.
// It will work for any derived class, so it is never necessary to
// redefine this. It is probably possible to make a faster
// implementation, but since this function is rarely used, it
// probably isn't worth the trouble.
allocate_conversion_buffer();
fetch_into_buffer(time, _conversion_buffer, false);
PT(MovieVideoCursor) MovieVideo::
open() {
return new MovieVideoCursor(this);
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::fetch_into_texture
// Access: Published, Virtual
// Description: Reads the specified video frame into
// the specified texture.
//
// See fetch_into_buffer for more details.
////////////////////////////////////////////////////////////////////
void MovieVideo::
fetch_into_texture(double time, Texture *t, int page) {
// This generic implementation is layered on fetch_into_buffer.
// It will work for any derived class, so it is never necessary to
// redefine this. However, it may be possible to make a faster
// implementation that uses fewer intermediate copies, depending
// on the capabilities of the underlying codec software.
nassertv(t->get_x_size() >= size_x());
nassertv(t->get_y_size() >= size_y());
nassertv((t->get_num_components() == 3) || (t->get_num_components() == 4));
nassertv(t->get_component_width() == 1);
nassertv(page < t->get_z_size());
PTA_uchar img = t->modify_ram_image();
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
if (t->get_x_size() == size_x()) {
fetch_into_buffer(time, data, t->get_num_components() == 4);
} else {
allocate_conversion_buffer();
fetch_into_buffer(time, _conversion_buffer, t->get_num_components() == 4);
int src_stride = size_x() * t->get_num_components();
int dst_stride = t->get_x_size() * t->get_num_components();
unsigned char *p = _conversion_buffer;
for (int y=0; y<size_y(); y++) {
memcpy(data, p, src_stride);
data += dst_stride;
p += src_stride;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::fetch_into_texture_alpha
// Access: Published, Virtual
// Description: Reads the specified video frame into
// the alpha channel of the supplied texture. The
// RGB channels of the texture are not touched.
//
// See fetch_into_buffer for more details.
////////////////////////////////////////////////////////////////////
void MovieVideo::
fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src) {
// This generic implementation is layered on fetch_into_buffer.
// It will work for any derived class, so it is never necessary to
// redefine this. However, it may be possible to make a faster
// implementation that uses fewer intermediate copies, depending
// on the capabilities of the underlying codec software.
nassertv(t->get_x_size() >= size_x());
nassertv(t->get_y_size() >= size_y());
nassertv(t->get_num_components() == 4);
nassertv(t->get_component_width() == 1);
nassertv(page < t->get_z_size());
nassertv((alpha_src >= 0) && (alpha_src <= 4));
allocate_conversion_buffer();
fetch_into_buffer(time, _conversion_buffer, true);
PTA_uchar img = t->modify_ram_image();
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
int src_stride = size_x() * 4;
int dst_stride = t->get_x_size() * 4;
if (alpha_src == 0) {
unsigned char *p = _conversion_buffer;
for (int y=0; y<size_y(); y++) {
for (int x=0; x<size_x(); x++) {
data[x*4+3] = (p[x*4+0] + p[x*4+1] + p[x*4+2]) / 3;
}
data += dst_stride;
p += src_stride;
}
} else {
alpha_src -= 1;
unsigned char *p = _conversion_buffer;
for (int y=0; y<size_y(); y++) {
for (int x=0; x<size_x(); x++) {
data[x*4+3] = p[x*4+alpha_src];
}
data += dst_stride;
p += src_stride;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::fetch_into_texture_rgb
// Access: Published, Virtual
// Description: Reads the specified video frame into
// the RGB channels of the supplied texture. The alpha
// channel of the texture is not touched.
//
// See fetch_into_buffer for more details.
////////////////////////////////////////////////////////////////////
void MovieVideo::
fetch_into_texture_rgb(double time, Texture *t, int page) {
// This generic implementation is layered on fetch_into_buffer.
// It will work for any derived class, so it is never necessary to
// redefine this. However, it may be possible to make a faster
// implementation that uses fewer intermediate copies, depending
// on the capabilities of the underlying codec software.
nassertv(t->get_x_size() >= size_x());
nassertv(t->get_y_size() >= size_y());
nassertv(t->get_num_components() == 4);
nassertv(t->get_component_width() == 1);
nassertv(page < t->get_z_size());
allocate_conversion_buffer();
fetch_into_buffer(time, _conversion_buffer, true);
PTA_uchar img = t->modify_ram_image();
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
int src_stride = size_x() * 4;
int dst_stride = t->get_x_size() * 4;
unsigned char *p = _conversion_buffer;
for (int y=0; y<size_y(); y++) {
for (int x=0; x<size_x(); x++) {
data[x*4+0] = p[x*4+0];
data[x*4+1] = p[x*4+1];
data[x*4+2] = p[x*4+2];
}
data += dst_stride;
p += src_stride;
}
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::fetch_into_buffer
// Access: Published, Virtual
// Description: Reads the specified video frame into the supplied
// BGR or BGRA buffer. The frame's begin and end
// times are stored in last_start and next_start.
//
// If the movie reports that it can_seek, you may
// also specify a timestamp less than next_start.
// Otherwise, you may only specify a timestamp
// greater than or equal to next_start.
//
// If the movie reports that it can_seek, it doesn't
// mean that it can do so quickly. It may have to
// rewind the movie and then fast forward to the
// desired location. Only if can_seek_fast returns
// true can it seek rapidly.
////////////////////////////////////////////////////////////////////
void MovieVideo::
fetch_into_buffer(double time, unsigned char *data, bool bgra) {
// The following is the implementation of the null video stream, ie,
// a stream of blinking red and blue frames. This method must be
// overridden by the subclass.
_last_start = floor(time);
_next_start = _last_start + 1;
if (((int)_last_start) & 1) {
data[0] = 255;
data[1] = 128;
data[2] = 128;
} else {
data[0] = 128;
data[1] = 128;
data[2] = 255;
}
if (bgra) {
data[3] = 255;
}
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::make_copy
// Access: Published, Virtual
// Description: Make a copy of this video with a separate cursor.
////////////////////////////////////////////////////////////////////
PT(MovieVideo) MovieVideo::
make_copy() const {
return new MovieVideo();
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideo::load
// Function: MovieVideo::get
// Access: Published, Static
// Description: Load a movie from a file.
// Description: Obtains a MovieVideo that references a file.
////////////////////////////////////////////////////////////////////
PT(MovieVideo) MovieVideo::
load(const Filename &name) {
get(const Filename &name) {
#ifdef HAVE_FFMPEG
// Someday, I'll probably put a dispatcher here.
// But for now, just hardwire it to go to FFMPEG.
return new FfmpegVideo(name);
#else
return NULL;
return new MovieVideo("Load-Failure Stub");
#endif
}

View File

@ -20,8 +20,11 @@
#define MOVIEVIDEO_H
#include "pandabase.h"
#include "texture.h"
#include "namable.h"
#include "pointerTo.h"
#include "typedWritableReferenceCount.h"
class MovieVideo;
#include "movieVideoCursor.h"
////////////////////////////////////////////////////////////////////
// Class : MovieVideo
@ -29,50 +32,19 @@
// a sequence of video frames. That could include an
// AVI file, a digital camera, or an internet TV station.
//
// Thread safety: each individual MovieVideo or
// must be owned and accessed by a single thread.
// It is OK for two different threads to open
// the same file at the same time, as long as they
// use separate MovieVideo objects.
// The difference between a MovieVideo and a
// MovieVideoCursor is like the difference between a
// filename and a file handle. The MovieVideo just
// indicates a particular movie. The MovieVideoCursor
// is what allows access.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_MOVIES MovieVideo : public TypedWritableReferenceCount, public Namable {
PUBLISHED:
MovieVideo(const string &name = "Blank Video");
virtual ~MovieVideo();
INLINE int size_x() const;
INLINE int size_y() const;
INLINE int get_num_components() const;
INLINE int length() const;
INLINE bool can_seek() const;
INLINE bool can_seek_fast() const;
INLINE bool aborted() const;
INLINE double last_start() const;
INLINE double next_start() const;
virtual void fetch_into_bitbucket(double time);
virtual void fetch_into_texture(double time, Texture *t, int page);
virtual void fetch_into_texture_rgb(double time, Texture *t, int page);
virtual void fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src);
virtual PT(MovieVideo) make_copy() const;
static PT(MovieVideo) load(const Filename &name);
public:
virtual void fetch_into_buffer(double time, unsigned char *block, bool bgra);
private:
void allocate_conversion_buffer();
unsigned char *_conversion_buffer;
protected:
int _size_x;
int _size_y;
int _num_components;
double _length;
bool _can_seek;
bool _can_seek_fast;
bool _aborted;
double _last_start;
double _next_start;
virtual PT(MovieVideoCursor) open();
static PT(MovieVideo) get(const Filename &name);
public:
static TypeHandle get_class_type() {

View File

@ -0,0 +1,164 @@
// Filename: movieVideoCursor.I
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::get_source
// Access: Published
// Description: Get the MovieVideo which this cursor references.
////////////////////////////////////////////////////////////////////
INLINE PT(MovieVideo) MovieVideoCursor::
get_source() const {
return _source;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::size_x
// Access: Published
// Description: Get the horizontal size of the movie.
////////////////////////////////////////////////////////////////////
INLINE int MovieVideoCursor::
size_x() const {
return _size_x;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::size_y
// Access: Published
// Description: Get the vertical size of the movie.
////////////////////////////////////////////////////////////////////
INLINE int MovieVideoCursor::
size_y() const {
return _size_y;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::get_num_components
// Access: Published
// Description: Returns 4 if the movie has an alpha
// channel, 3 otherwise.
////////////////////////////////////////////////////////////////////
INLINE int MovieVideoCursor::
get_num_components() const {
return _num_components;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::length
// Access: Published
// Description: Returns the length of the movie.
//
// Some kinds of Movie, such as internet TV station,
// might not have a predictable length. In that case,
// the length will be set to a very large number: 1.0E10.
// If the internet TV station goes offline, the video
// or audio stream will set its abort flag. Reaching the
// end of the movie (ie, the specified length) normally
// does not cause the abort flag to be set.
//
// The video and audio streams produced by get_video and
// get_audio are always of unlimited duration - you can
// always read another video frame or another audio
// sample. This is true even if the specified length
// is reached, or an abort is flagged. If either stream
// runs out of data, it will synthesize blank video
// frames and silent audio samples as necessary to
// satisfy read requests.
//
// Some AVI files have incorrect length values encoded
// into them - usually, they're a second or two long or
// short. When playing such an AVI using the Movie class,
// you may see a slightly truncated video, or a slightly
// elongated video (padded with black frames). There are
// utilities out there to fix the length values in AVI
// files.
//
////////////////////////////////////////////////////////////////////
INLINE double MovieVideoCursor::
length() const {
return _length;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::can_seek
// Access: Published
// Description: Returns true if the movie can seek. If this is
// true, seeking is still not guaranteed to be fast:
// for some movies, seeking is implemented by rewinding
// to the beginning and then fast-forwarding to the
// desired location. Even if the movie cannot seek,
// the fetch methods can still advance to an arbitrary
// location by reading frames and discarding them.
// However, to move backward, can_seek must return true.
////////////////////////////////////////////////////////////////////
INLINE bool MovieVideoCursor::
can_seek() const {
return _can_seek;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::can_seek_fast
// Access: Published
// Description: Returns true if seek operations are constant time.
////////////////////////////////////////////////////////////////////
INLINE bool MovieVideoCursor::
can_seek_fast() const {
return _can_seek_fast;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::aborted
// Access: Published
// Description: Returns true if the video has aborted prematurely.
// For example, this could occur if the Movie was actually
// an internet TV station, and the connection was lost.
// Reaching the normal end of the video does not
// constitute an 'abort' condition.
////////////////////////////////////////////////////////////////////
INLINE bool MovieVideoCursor::
aborted() const {
return _aborted;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::last_start
// Access: Published
// Description: Returns the start time of the last frame you read.
//
// MovieVideoCursor streams have variable frame rates. Each
// frame will specify how long it is to be displayed.
// These lengths may not be equal from frame to frame.
////////////////////////////////////////////////////////////////////
INLINE double MovieVideoCursor::
last_start() const {
return _last_start;
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::next_start
// Access: Published
// Description: Returns the start time of the next frame you can read.
//
// MovieVideoCursor streams have variable frame rates. Each
// frame will specify how long it is to be displayed.
// These lengths may not be equal from frame to frame.
////////////////////////////////////////////////////////////////////
INLINE double MovieVideoCursor::
next_start() const {
return _next_start;
}

View File

@ -0,0 +1,281 @@
// Filename: movieVideo.cxx
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2007, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "movieVideoCursor.h"
#include "config_movies.h"
TypeHandle MovieVideoCursor::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::Constructor
// Access: Public
// Description: This constructor returns a null video stream --- a
// stream of plain blue and white frames that last one
// second each. To get more interesting video, you need
// to construct a subclass of this class.
////////////////////////////////////////////////////////////////////
MovieVideoCursor::
MovieVideoCursor(PT(MovieVideo) src) :
_source(src),
_size_x(1),
_size_y(1),
_num_components(3),
_length(1.0E10),
_can_seek(true),
_can_seek_fast(true),
_aborted(false),
_last_start(-1.0),
_next_start(0.0)
{
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
MovieVideoCursor::
~MovieVideoCursor() {
if (_conversion_buffer != 0) {
delete[] _conversion_buffer;
}
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::allocate_conversion_buffer
// Access: Private
// Description: The generic implementations of fetch_into_texture
// and fetch_into_alpha require the use of a conversion
// buffer. This allocates the buffer.
////////////////////////////////////////////////////////////////////
void MovieVideoCursor::
allocate_conversion_buffer() {
if (_conversion_buffer == 0) {
_conversion_buffer = new unsigned char[size_x() * size_y() * 4];
}
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::fetch_into_bitbucket
// Access: Published, Virtual
// Description: Discards the next video frame. Still sets
// last_start and next_start.
//
// See fetch_into_buffer for more details.
////////////////////////////////////////////////////////////////////
void MovieVideoCursor::
fetch_into_bitbucket(double time) {
// This generic implementation is layered on fetch_into_buffer.
// It will work for any derived class, so it is never necessary to
// redefine this. It is probably possible to make a faster
// implementation, but since this function is rarely used, it
// probably isn't worth the trouble.
allocate_conversion_buffer();
fetch_into_buffer(time, _conversion_buffer, false);
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::fetch_into_texture
// Access: Published, Virtual
// Description: Reads the specified video frame into
// the specified texture.
//
// See fetch_into_buffer for more details.
////////////////////////////////////////////////////////////////////
void MovieVideoCursor::
fetch_into_texture(double time, Texture *t, int page) {
// This generic implementation is layered on fetch_into_buffer.
// It will work for any derived class, so it is never necessary to
// redefine this. However, it may be possible to make a faster
// implementation that uses fewer intermediate copies, depending
// on the capabilities of the underlying codec software.
nassertv(t->get_x_size() >= size_x());
nassertv(t->get_y_size() >= size_y());
nassertv((t->get_num_components() == 3) || (t->get_num_components() == 4));
nassertv(t->get_component_width() == 1);
nassertv(page < t->get_z_size());
PTA_uchar img = t->modify_ram_image();
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
if (t->get_x_size() == size_x()) {
fetch_into_buffer(time, data, t->get_num_components() == 4);
} else {
allocate_conversion_buffer();
fetch_into_buffer(time, _conversion_buffer, t->get_num_components() == 4);
int src_stride = size_x() * t->get_num_components();
int dst_stride = t->get_x_size() * t->get_num_components();
unsigned char *p = _conversion_buffer;
for (int y=0; y<size_y(); y++) {
memcpy(data, p, src_stride);
data += dst_stride;
p += src_stride;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::fetch_into_texture_alpha
// Access: Published, Virtual
// Description: Reads the specified video frame into
// the alpha channel of the supplied texture. The
// RGB channels of the texture are not touched.
//
// See fetch_into_buffer for more details.
////////////////////////////////////////////////////////////////////
void MovieVideoCursor::
fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src) {
// This generic implementation is layered on fetch_into_buffer.
// It will work for any derived class, so it is never necessary to
// redefine this. However, it may be possible to make a faster
// implementation that uses fewer intermediate copies, depending
// on the capabilities of the underlying codec software.
nassertv(t->get_x_size() >= size_x());
nassertv(t->get_y_size() >= size_y());
nassertv(t->get_num_components() == 4);
nassertv(t->get_component_width() == 1);
nassertv(page < t->get_z_size());
nassertv((alpha_src >= 0) && (alpha_src <= 4));
allocate_conversion_buffer();
fetch_into_buffer(time, _conversion_buffer, true);
PTA_uchar img = t->modify_ram_image();
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
int src_stride = size_x() * 4;
int dst_stride = t->get_x_size() * 4;
if (alpha_src == 0) {
unsigned char *p = _conversion_buffer;
for (int y=0; y<size_y(); y++) {
for (int x=0; x<size_x(); x++) {
data[x*4+3] = (p[x*4+0] + p[x*4+1] + p[x*4+2]) / 3;
}
data += dst_stride;
p += src_stride;
}
} else {
alpha_src -= 1;
unsigned char *p = _conversion_buffer;
for (int y=0; y<size_y(); y++) {
for (int x=0; x<size_x(); x++) {
data[x*4+3] = p[x*4+alpha_src];
}
data += dst_stride;
p += src_stride;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::fetch_into_texture_rgb
// Access: Published, Virtual
// Description: Reads the specified video frame into
// the RGB channels of the supplied texture. The alpha
// channel of the texture is not touched.
//
// See fetch_into_buffer for more details.
////////////////////////////////////////////////////////////////////
void MovieVideoCursor::
fetch_into_texture_rgb(double time, Texture *t, int page) {
// This generic implementation is layered on fetch_into_buffer.
// It will work for any derived class, so it is never necessary to
// redefine this. However, it may be possible to make a faster
// implementation that uses fewer intermediate copies, depending
// on the capabilities of the underlying codec software.
nassertv(t->get_x_size() >= size_x());
nassertv(t->get_y_size() >= size_y());
nassertv(t->get_num_components() == 4);
nassertv(t->get_component_width() == 1);
nassertv(page < t->get_z_size());
allocate_conversion_buffer();
fetch_into_buffer(time, _conversion_buffer, true);
PTA_uchar img = t->modify_ram_image();
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
int src_stride = size_x() * 4;
int dst_stride = t->get_x_size() * 4;
unsigned char *p = _conversion_buffer;
for (int y=0; y<size_y(); y++) {
for (int x=0; x<size_x(); x++) {
data[x*4+0] = p[x*4+0];
data[x*4+1] = p[x*4+1];
data[x*4+2] = p[x*4+2];
}
data += dst_stride;
p += src_stride;
}
}
////////////////////////////////////////////////////////////////////
// Function: MovieVideoCursor::fetch_into_buffer
// Access: Published, Virtual
// Description: Reads the specified video frame into the supplied
// BGR or BGRA buffer. The frame's begin and end
// times are stored in last_start and next_start.
//
// If the movie reports that it can_seek, you may
// also specify a timestamp less than next_start.
// Otherwise, you may only specify a timestamp
// greater than or equal to next_start.
//
// If the movie reports that it can_seek, it doesn't
// mean that it can do so quickly. It may have to
// rewind the movie and then fast forward to the
// desired location. Only if can_seek_fast returns
// true can it seek rapidly.
////////////////////////////////////////////////////////////////////
void MovieVideoCursor::
fetch_into_buffer(double time, unsigned char *data, bool bgra) {
// The following is the implementation of the null video stream, ie,
// a stream of blinking red and blue frames. This method must be
// overridden by the subclass.
_last_start = floor(time);
_next_start = _last_start + 1;
if (((int)_last_start) & 1) {
data[0] = 255;
data[1] = 128;
data[2] = 128;
} else {
data[0] = 128;
data[1] = 128;
data[2] = 255;
}
if (bgra) {
data[3] = 255;
}
}

View File

@ -0,0 +1,101 @@
// Filename: movieVideoCursor.h
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef MOVIEVIDEOCURSOR_H
#define MOVIEVIDEOCURSOR_H
#include "pandabase.h"
#include "texture.h"
#include "pointerTo.h"
class MovieVideoCursor;
#include "movieVideo.h"
////////////////////////////////////////////////////////////////////
// Class : MovieVideoCursor
// Description : A MovieVideo is actually any source that provides
// a sequence of video frames. That could include an
// AVI file, a digital camera, or an internet TV station.
// A MovieVideoCursor is a handle that lets you read
// data sequentially from a MovieVideo.
//
// Thread safety: each individual MovieVideoCursor
// must be owned and accessed by a single thread.
// It is OK for two different threads to open
// the same file at the same time, as long as they
// use separate MovieVideoCursor objects.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_MOVIES MovieVideoCursor : public TypedWritableReferenceCount {
PUBLISHED:
MovieVideoCursor(PT(MovieVideo) src);
virtual ~MovieVideoCursor();
PT(MovieVideo) get_source() const;
INLINE int size_x() const;
INLINE int size_y() const;
INLINE int get_num_components() const;
INLINE double length() const;
INLINE bool can_seek() const;
INLINE bool can_seek_fast() const;
INLINE bool aborted() const;
INLINE double last_start() const;
INLINE double next_start() const;
virtual void fetch_into_bitbucket(double time);
virtual void fetch_into_texture(double time, Texture *t, int page);
virtual void fetch_into_texture_rgb(double time, Texture *t, int page);
virtual void fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src);
public:
virtual void fetch_into_buffer(double time, unsigned char *block, bool bgra);
private:
void allocate_conversion_buffer();
unsigned char *_conversion_buffer;
protected:
PT(MovieVideo) _source;
int _size_x;
int _size_y;
int _num_components;
double _length;
bool _can_seek;
bool _can_seek_fast;
bool _aborted;
double _last_start;
double _next_start;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
TypedWritableReferenceCount::init_type();
register_type(_type_handle, "MovieVideo",
TypedWritableReferenceCount::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "movieVideoCursor.I"
#endif

View File

@ -1,7 +1,18 @@
#include "movieVideo.cxx"
#include "movieVideoCursor.cxx"
#include "movieAudio.cxx"
#include "movieAudioCursor.cxx"
#include "inkblotVideo.cxx"
#include "inkblotVideoCursor.cxx"
#include "ffmpegAudio.cxx"
#include "ffmpegAudioCursor.cxx"
#include "ffmpegVideo.cxx"
#include "ffmpegVideoCursor.cxx"
#include "ffmpegVirtualFile.cxx"
#include "config_movies.cxx"

View File

@ -60,6 +60,14 @@
#define EXPTP_FMOD_AUDIO extern
#endif
#ifdef BUILDING_OPENAL_AUDIO
#define EXPCL_OPENAL_AUDIO __declspec(dllexport)
#define EXPTP_OPENAL_AUDIO
#else
#define EXPCL_OPENAL_AUDIO __declspec(dllimport)
#define EXPTP_OPENAL_AUDIO extern
#endif
#ifdef BUILDING_PANDA
#define EXPCL_PANDA __declspec(dllexport)
#define EXPTP_PANDA
@ -194,6 +202,9 @@
#define EXPCL_FMOD_AUDIO
#define EXPTP_FMOD_AUDIO
#define EXPCL_OPENAL_AUDIO
#define EXPTP_OPENAL_AUDIO
#define EXPCL_PANDA
#define EXPTP_PANDA