Major refactoring of movie code.
This commit is contained in:
parent
7eb74374ac
commit
1e02c06517
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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']
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#ifndef AVIO_H
|
||||
#define AVIO_H
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 //]
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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__ */
|
||||
|
|
@ -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 .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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 //]
|
||||
|
|
@ -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__ */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
#include "openal_audio_composite1.cxx"
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
#include "config_openalAudio.cxx"
|
||||
#include "openalAudioManager.cxx"
|
||||
#include "openalAudioSound.cxx"
|
||||
|
||||
|
|
@ -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 ©) :
|
|||
// 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 ©) :
|
|||
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()))) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue