Переглянути джерело

Major refactoring of movie code.

Josh Yelon 18 роки тому
батько
коміт
1e02c06517
54 змінених файлів з 4699 додано та 1151 видалено
  1. 7 7
      direct/src/showbase/Audio3DManager.py
  2. 1 1
      doc/makepanda/config.in
  3. 24 1
      doc/makepanda/makepanda.py
  4. 0 1
      dtool/src/parser-inc/avio.h
  5. 27 14
      panda/src/audio/config_audio.cxx
  6. 6 0
      panda/src/audio/config_audio.h
  7. 20 0
      panda/src/audiotraits/Sources.pp
  8. 64 0
      panda/src/audiotraits/config_openalAudio.cxx
  9. 35 0
      panda/src/audiotraits/config_openalAudio.h
  10. 3 7
      panda/src/audiotraits/fmodAudioSound.cxx
  11. 1049 0
      panda/src/audiotraits/openalAudioManager.cxx
  12. 227 0
      panda/src/audiotraits/openalAudioManager.h
  13. 23 0
      panda/src/audiotraits/openalAudioSound.I
  14. 730 0
      panda/src/audiotraits/openalAudioSound.cxx
  15. 194 0
      panda/src/audiotraits/openalAudioSound.h
  16. 1 0
      panda/src/audiotraits/openal_audio_composite.cxx
  17. 5 0
      panda/src/audiotraits/openal_audio_composite1.cxx
  18. 15 15
      panda/src/grutil/movieTexture.cxx
  19. 3 3
      panda/src/grutil/movieTexture.h
  20. 15 0
      panda/src/movies/Sources.pp
  21. 5 0
      panda/src/movies/config_movies.cxx
  22. 10 0
      panda/src/movies/config_movies.h
  23. 7 32
      panda/src/movies/ffmpegAudio.cxx
  24. 7 10
      panda/src/movies/ffmpegAudio.h
  25. 18 0
      panda/src/movies/ffmpegAudioCursor.I
  26. 264 0
      panda/src/movies/ffmpegAudioCursor.cxx
  27. 88 0
      panda/src/movies/ffmpegAudioCursor.h
  28. 7 259
      panda/src/movies/ffmpegVideo.cxx
  29. 6 33
      panda/src/movies/ffmpegVideo.h
  30. 18 0
      panda/src/movies/ffmpegVideoCursor.I
  31. 306 0
      panda/src/movies/ffmpegVideoCursor.cxx
  32. 86 0
      panda/src/movies/ffmpegVideoCursor.h
  33. 17 0
      panda/src/movies/ffmpegVirtualFileCursor.I
  34. 155 0
      panda/src/movies/ffmpegVirtualFileCursor.cxx
  35. 46 0
      panda/src/movies/ffmpegVirtualFileCursor.h
  36. 10 119
      panda/src/movies/inkblotVideo.cxx
  37. 10 13
      panda/src/movies/inkblotVideo.h
  38. 18 0
      panda/src/movies/inkblotVideoCursor.I
  39. 151 0
      panda/src/movies/inkblotVideoCursor.cxx
  40. 67 0
      panda/src/movies/inkblotVideoCursor.h
  41. 0 120
      panda/src/movies/movieAudio.I
  42. 11 66
      panda/src/movies/movieAudio.cxx
  43. 10 29
      panda/src/movies/movieAudio.h
  44. 147 0
      panda/src/movies/movieAudioCursor.I
  45. 104 0
      panda/src/movies/movieAudioCursor.cxx
  46. 93 0
      panda/src/movies/movieAudioCursor.h
  47. 0 136
      panda/src/movies/movieVideo.I
  48. 10 246
      panda/src/movies/movieVideo.cxx
  49. 11 39
      panda/src/movies/movieVideo.h
  50. 164 0
      panda/src/movies/movieVideoCursor.I
  51. 281 0
      panda/src/movies/movieVideoCursor.cxx
  52. 101 0
      panda/src/movies/movieVideoCursor.h
  53. 11 0
      panda/src/movies/movies_composite1.cxx
  54. 11 0
      panda/src/pandabase/pandasymbols.h

+ 7 - 7
direct/src/showbase/Audio3DManager.py

@@ -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

+ 1 - 1
doc/makepanda/config.in

@@ -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
 

+ 24 - 1
doc/makepanda/makepanda.py

@@ -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']

+ 0 - 1
dtool/src/parser-inc/avio.h

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

+ 27 - 14
panda/src/audio/config_audio.cxx

@@ -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);
 

+ 6 - 0
panda/src/audio/config_audio.h

@@ -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;

+ 20 - 0
panda/src/audiotraits/Sources.pp

@@ -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

+ 64 - 0
panda/src/audiotraits/config_openalAudio.cxx

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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 //]

+ 35 - 0
panda/src/audiotraits/config_openalAudio.h

@@ -0,0 +1,35 @@
+// Filename: config_openalAudio.h
+// Created by:  Ben Buchwald <[email protected]>
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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

+ 3 - 7
panda/src/audiotraits/fmodAudioSound.cxx

@@ -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;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1049 - 0
panda/src/audiotraits/openalAudioManager.cxx

@@ -0,0 +1,1049 @@
+// Filename: openalAudioManager.cxx
+// Created by:  Ben Buchwald <[email protected]>
+//
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandabase.h"
+
+#ifdef HAVE_OPENAL //[
+
+//Panda headers.
+#include "config_audio.h"
+#include "config_util.h"
+#include "config_express.h"
+#include "openalAudioManager.h"
+#include "openalAudioSound.h"
+#include "virtualFileSystem.h"
+#include "movieAudio.h"
+
+#include <algorithm>
+
+//OpenAL Headers.
+#include <al.h>
+#include <alc.h>
+
+TypeHandle OpenALAudioManager::_type_handle;
+
+int OpenALAudioManager::_active_managers = 0;
+bool OpenALAudioManager::_openal_active = false;
+ALCdevice* OpenALAudioManager::_device = NULL;
+ALCcontext* OpenALAudioManager::_context = NULL;
+
+// This is the list of all OpenALAudioManager objects in the world.  It
+// must be a pointer rather than a concrete object, so it won't be
+// destructed at exit time before we're done removing things from it.
+OpenALAudioManager::Managers *OpenALAudioManager::_managers = NULL;
+
+OpenALAudioManager::SourceCache *OpenALAudioManager::_al_sources = NULL;
+
+
+////////////////////////////////////////////////////////////////////
+// Central dispatcher for audio errors.
+////////////////////////////////////////////////////////////////////
+void al_audio_errcheck(const char *context) {
+  ALenum result = alGetError();
+  if (result != AL_NO_ERROR) {
+    audio_error(context << ": " << alGetString(result) );
+  }
+}
+
+void alc_audio_errcheck(const char *context,ALCdevice* device) {
+  ALCenum result = alcGetError(device);
+  if (result != ALC_NO_ERROR) {
+    audio_error(context << ": " << alcGetString(device,result) );
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Create_AudioManager
+//       Access: Private
+//  Description: Factory Function
+////////////////////////////////////////////////////////////////////
+PT(AudioManager) Create_AudioManager() {
+  audio_debug("Create_AudioManager() OpenAL.");
+  return new OpenALAudioManager;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::OpenALAudioManager()
+//       Access: Public
+//  Description: Constructor
+////////////////////////////////////////////////////////////////////
+OpenALAudioManager::
+OpenALAudioManager() {
+  audio_debug("OpenALAudioManager::OpenALAudioManager(), this = " 
+              << (void *)this);
+  if (_managers == (Managers *)NULL) {
+    _managers = new Managers;
+    _al_sources = new SourceCache;
+  }
+
+  _managers->insert(this);
+
+  audio_debug("  audio_active="<<audio_active);
+  audio_debug("  audio_volume="<<audio_volume);
+
+  _cleanup_required = true;
+  _active = audio_active;
+  _volume = audio_volume;
+  _play_rate = 1.0f;
+  
+  _cache_limit = audio_cache_limit;
+
+  _concurrent_sound_limit = 0;
+  _is_valid = true;
+
+  //Init 3D attributes
+  _distance_factor = 3.28;
+  _drop_off_factor = 1;
+
+  _position[0] = 0;
+  _position[1] = 0;
+  _position[2] = 0;
+
+  _velocity[0] = 0;
+  _velocity[1] = 0;
+  _velocity[2] = 0;
+
+  _forward_up[0] = 0;
+  _forward_up[1] = 0;
+  _forward_up[2] = 0;
+  _forward_up[3] = 0;
+  _forward_up[4] = 0;
+  _forward_up[5] = 0;
+
+  // Initialization
+  if (_active_managers==0 || !_openal_active) {
+    _device = alcOpenDevice(NULL); // select the "preferred device"
+    if (!_device) {
+      // this is a unique kind of error
+      audio_error("OpenALAudioManager: alcOpenDevice(NULL): ALC couldn't open device");
+    } else {
+      alcGetError(_device); // clear errors
+      _context=alcCreateContext(_device,NULL);
+      alc_audio_errcheck("alcCreateContext(_device,NULL)",_device);
+      if (_context!=NULL) {
+        _openal_active = true;
+      }
+    }
+  }
+  // We increment _active_managers regardless of possible errors above.
+  // The shutdown call will do the right thing when it's called,
+  // either way.
+  ++_active_managers;
+  audio_debug("  _active_managers="<<_active_managers);
+  nassertv(_active_managers>0);
+
+  if (!_device || !_context) {
+    audio_error("OpenALAudioManager: No open device or context");
+    _is_valid = false;
+  } else {
+    make_current();	
+
+    // set 3D sound characteristics as they are given in the configrc
+    audio_3d_set_doppler_factor(audio_doppler_factor);
+    audio_3d_set_distance_factor(audio_distance_factor);
+    audio_3d_set_drop_off_factor(audio_drop_off_factor);
+  }
+  
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::~OpenALAudioManager
+//       Access: Public
+//  Description: DESTRCUTOR !!!
+////////////////////////////////////////////////////////////////////
+OpenALAudioManager::
+~OpenALAudioManager() {
+  audio_debug("OpenALAudioManager::~OpenALAudioManager(), this = " 
+              << (void *)this);
+  nassertv(_managers != (Managers *)NULL);
+  Managers::iterator mi = _managers->find(this);
+  nassertv(mi != _managers->end());
+  _managers->erase(mi);
+
+  cleanup();
+  audio_debug("OpenALAudioManager::~OpenALAudioManager() finished");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::shutdown
+//       Access: Published, Virtual
+//  Description: Call this at exit time to shut down the audio system.
+//               This will invalidate all currently-active
+//               AudioManagers and AudioSounds in the system.  If you
+//               change your mind and want to play sounds again, you
+//               will have to recreate all of these objects.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+shutdown() {
+  audio_debug("shutdown(), _openal_active = " << _openal_active);
+  if (_managers != (Managers *)NULL) {
+    Managers::iterator mi;
+    for (mi = _managers->begin(); mi != _managers->end(); ++mi) {
+      (*mi)->cleanup();
+    }
+  }
+
+  nassertv(_active_managers == 0);
+  audio_debug("shutdown() finished");
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::is_valid
+//       Access:
+//  Description: This is mostly for debugging, but it it could be
+//               used to detect errors in a release build if you
+//               don't mind the cpu cost.
+////////////////////////////////////////////////////////////////////
+bool OpenALAudioManager::
+is_valid() {
+  bool check=true;
+  if (_sounds.size() != _lru.size()) {
+    audio_debug("-- Error _sounds.size() != _lru.size() --");
+    check=false;
+  } else {
+    LRU::const_iterator i=_lru.begin();
+    for (; i != _lru.end(); ++i) {
+      SoundMap::const_iterator smi=_sounds.find(**i);
+      if (smi == _sounds.end()) {
+        audio_debug("-- "<<**i<<" in _lru and not in _sounds --");
+        check=false;
+        break;
+      }
+    }
+  }
+  return _is_valid && check;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::make_current
+//       Access: Private
+//  Description: This makes this manager's OpenAL context the 
+//         current context. Needed before any parameter sets.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+make_current() const {
+  alcGetError(_device); // clear errors
+  alcMakeContextCurrent(_context);
+  alc_audio_errcheck("alcMakeContextCurrent(_context)",_device);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::load
+//       Access: Private
+//  Description: Reads a sound file and allocates a SoundData pointer
+//               for it.  Returns NULL if the sound file cannot be
+//               loaded.
+////////////////////////////////////////////////////////////////////
+PT(OpenALAudioManager::SoundData) OpenALAudioManager::
+load(Filename file_name) {
+  string raw_data;
+  PT(SoundData) sd = new SoundData(this);
+
+  sd->_basename = file_name.get_basename();
+
+  make_current();
+  
+  PT(MovieAudioCursor) source = MovieAudio::get(file_name)->open();
+  if (source == 0) {
+    audio_error("Could not load audio file "<<file_name);
+    return NULL;
+  }
+
+  int channels = source->audio_channels();
+  if ((channels != 1)&&(channels != 2)) {
+    audio_error("Currently, only mono and stereo are supported.");
+    return NULL;
+  }    
+  int samples = (int)(source->length() * source->audio_rate());
+  if (samples > 10000000) {
+    audio_error("Sound is too long for loading into RAM.");
+    return NULL;
+  }
+
+  alGetError(); // clear errors
+  sd->_buffer = 0;
+  alGenBuffers(1, &sd->_buffer);
+  al_audio_errcheck("alGenBuffers");
+  if (sd->_buffer == 0) {
+    audio_error("Could not create an OpenAL buffer object");
+    return NULL;
+  }
+  
+  PN_int16 *data = new PN_int16[samples * channels];
+  source->read_samples(samples, data);
+  alBufferData(sd->_buffer,
+               (channels>1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
+               data, samples * channels * 2, source->audio_rate());
+  int err = alGetError();
+  if (err != AL_NO_ERROR) {
+    audio_error("alBufferData: " << alGetString(err));
+    alDeleteBuffers(1, &sd->_buffer);
+    return NULL;
+  }
+  audio_debug("Loaded "<<file_name<<" Src Len "<<source->length());
+  double len = sd->get_length();
+  return sd;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::get_sound()
+//       Access: Public
+//  Description: This is what creates a sound instance.
+////////////////////////////////////////////////////////////////////
+PT(AudioSound) OpenALAudioManager::
+get_sound(const string &file_name, bool positional) {
+  audio_debug("OpenALAudioManager::get_sound(file_name=\""<<file_name<<"\")");
+
+  if(!is_valid()) {
+     audio_debug("invalid OpenALAudioManager returning NullSound");
+     return get_null_sound();
+  }
+
+  assert(is_valid());
+  Filename path = file_name;
+
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  vfs->resolve_filename(path, get_sound_path()) ||
+    vfs->resolve_filename(path, get_model_path());
+  audio_debug("  resolved file_name is '"<<path<<"'");
+
+  PT(SoundData) sd;
+  // Get the sound, either from the cache or from a loader:
+  SoundMap::const_iterator si=_sounds.find(path);
+  if (si != _sounds.end()) {
+    // ...found the sound in the cache.
+    sd = (*si).second;
+    audio_debug("  sound found in pool 0x" << (void*)sd);
+  } else {
+    // ...the sound was not found in the cache/pool.
+    sd = load(path);
+    if (sd != (SoundData *)NULL) {
+      while (_sounds.size() >= (unsigned int)_cache_limit) {
+        uncache_a_sound();
+      }
+      // Put it in the pool:
+      // The following is roughly like: _sounds[path] = sd;
+      // But, it gives us an iterator into the map.
+      pair<SoundMap::const_iterator, bool> ib
+          =_sounds.insert(SoundMap::value_type(path, sd));
+      if (!ib.second) {
+        // The insert failed.
+        audio_debug("  failed map insert of "<<path);
+        assert(is_valid());
+        return get_null_sound();
+      }
+      // Set si, so that we can get a reference to the path
+      // for the OpenALAudioSound.
+      si=ib.first;
+    }
+  }
+  // Create an AudioSound from the sound:
+  PT(AudioSound) audioSound = 0;
+  if (sd != (SoundData *)NULL) {
+    most_recently_used((*si).first);
+    PT(OpenALAudioSound) openalAudioSound
+        =new OpenALAudioSound(this, sd, (*si).first, positional);
+    nassertr(openalAudioSound, 0);
+    openalAudioSound->set_active(_active);
+    bool inserted = _sounds_on_loan.insert(openalAudioSound).second;
+    nassertr(inserted, openalAudioSound.p());
+    audioSound=openalAudioSound;
+  }
+
+  audio_debug("  returning 0x" << (void*)audioSound);
+  assert(is_valid());
+  return audioSound;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::uncache_sound
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+uncache_sound(const string& file_name) {
+  audio_debug("OpenALAudioManager::uncache_sound(file_name=\""
+      <<file_name<<"\")");
+  assert(is_valid());
+  Filename path = file_name;
+
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  vfs->resolve_filename(path, get_sound_path()) ||
+    vfs->resolve_filename(path, get_model_path());
+
+  audio_debug("  path=\""<<path<<"\"");
+  SoundMap::iterator i=_sounds.find(path);
+  if (i != _sounds.end()) {
+    assert(_lru.size()>0);
+    LRU::iterator lru_i=find(_lru.begin(), _lru.end(), &(i->first));
+    assert(lru_i != _lru.end());
+    _lru.erase(lru_i);
+    _sounds.erase(i);
+  }
+  assert(is_valid());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::uncache_a_sound
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+uncache_a_sound() {
+  audio_debug("OpenALAudioManager::uncache_a_sound()");
+  assert(is_valid());
+  // uncache least recently used:
+  assert(_lru.size()>0);
+  LRU::reference path=_lru.front();
+  SoundMap::iterator i = _sounds.find(*path);
+  assert(i != _sounds.end());
+  _lru.pop_front();
+
+  if (i != _sounds.end()) {
+    audio_debug("  uncaching \""<<i->first<<"\"");
+    _sounds.erase(i);
+  }
+  assert(is_valid());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::most_recently_used
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+most_recently_used(const string& path) {
+  audio_debug("OpenALAudioManager::most_recently_used(path=\""
+      <<path<<"\")");
+  LRU::iterator i=find(_lru.begin(), _lru.end(), &path);
+  if (i != _lru.end()) {
+    _lru.erase(i);
+  }
+  // At this point, path should not exist in the _lru:
+  assert(find(_lru.begin(), _lru.end(), &path) == _lru.end());
+  _lru.push_back(&path);
+  assert(is_valid());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::clear_cache
+//       Access: Public
+//  Description: Clear out the sound cache.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+clear_cache() {
+  audio_debug("OpenALAudioManager::clear_cache()");
+  if (_is_valid) { assert(is_valid()); }
+  _sounds.clear();
+  _lru.clear();
+  if (_is_valid) { assert(is_valid()); }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::set_cache_limit
+//       Access: Public
+//  Description: Set the number of sounds that the cache can hold.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+set_cache_limit(unsigned int count) {
+  audio_debug("OpenALAudioManager::set_cache_limit(count="<<count<<")");
+  assert(is_valid());
+  while (_lru.size() > count) {
+    uncache_a_sound();
+  }
+  _cache_limit=count;
+  assert(is_valid());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::get_cache_limit
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+unsigned int OpenALAudioManager::
+get_cache_limit() const {
+  audio_debug("OpenALAudioManager::get_cache_limit() returning "
+      <<_cache_limit);
+  return _cache_limit;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::release_sound
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+release_sound(OpenALAudioSound* audioSound) {
+  audio_debug("OpenALAudioManager::release_sound(audioSound=\""
+              <<audioSound->get_name()<<"\"), this = " << (void *)this);
+  AudioSet::iterator ai = _sounds_on_loan.find(audioSound);
+  nassertv(ai != _sounds_on_loan.end());
+  _sounds_on_loan.erase(ai);
+
+  audio_debug("OpenALAudioManager::release_sound() finished");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::set_volume(float volume)
+//       Access: Public
+//  Description: 
+//        Sets listener gain
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::set_volume(float volume) {
+  audio_debug("OpenALAudioManager::set_volume(volume="<<volume<<")");
+  if (_volume!=volume) {
+    _volume = volume;
+
+    // Tell our AudioSounds to adjust:
+    AudioSet::iterator i=_sounds_on_loan.begin();
+    for (; i!=_sounds_on_loan.end(); ++i) {
+      (**i).set_volume((**i).get_volume());
+    }
+
+    /* 
+    // this was neat alternative to the above look
+    // when we had a seperate context for each manager
+    make_current();
+
+    alGetError(); // clear errors
+    alListenerf(AL_GAIN,(ALfloat)_volume);
+    al_audio_errcheck("alListerf(AL_GAIN)");*/
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::get_volume()
+//       Access: Public
+//  Description: 
+//        Gets listener gain
+////////////////////////////////////////////////////////////////////
+float OpenALAudioManager::
+get_volume() const {
+  audio_debug("OpenALAudioManager::get_volume() returning "<<_volume);
+  return _volume;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::set_play_rate
+//       Access: Public
+//  Description: set the overall play rate
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+set_play_rate(float play_rate) {
+  audio_debug("OpenALAudioManager::set_play_rate(play_rate="<<play_rate<<")");
+  if (_play_rate!=play_rate) {
+    _play_rate = play_rate;
+    // Tell our AudioSounds to adjust:
+    AudioSet::iterator i=_sounds_on_loan.begin();
+    for (; i!=_sounds_on_loan.end(); ++i) {
+      (**i).set_play_rate((**i).get_play_rate());
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::get_play_rate
+//       Access: Public
+//  Description: get the overall speed/pitch/play rate
+////////////////////////////////////////////////////////////////////
+float OpenALAudioManager::
+get_play_rate() const {
+  audio_debug("OpenALAudioManager::get_play_rate() returning "<<_play_rate);
+  return _play_rate;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::set_active(bool active)
+//       Access: Public
+//  Description: Turn on/off
+//               Warning: not implemented.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+set_active(bool active) {
+  audio_debug("OpenALAudioManager::set_active(flag="<<active<<")");
+  if (_active!=active) {
+    _active=active;
+    // Tell our AudioSounds to adjust:
+    AudioSet::iterator i=_sounds_on_loan.begin();
+    for (; i!=_sounds_on_loan.end(); ++i) {
+      (**i).set_active(_active);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::get_active()
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool OpenALAudioManager::
+get_active() const {
+  audio_debug("OpenALAudioManager::get_active() returning "<<_active);
+  return _active;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_set_listener_attributes
+//       Access: Public
+//  Description: Set position of the "ear" that picks up 3d sounds
+//        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 OpenALAudioManager::
+audio_3d_set_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) {
+  _position[0] = px;
+  _position[1] = pz;
+  _position[2] = -py; 
+
+  _velocity[0] = vx;
+  _velocity[1] = vz;
+  _velocity[2] = -vy;
+
+  _forward_up[0] = fx;
+  _forward_up[1] = fz;
+  _forward_up[2] = -fy;
+
+  _forward_up[3] = ux;
+  _forward_up[4] = uz;
+  _forward_up[5] = -uy;
+    
+  
+  make_current();
+
+  alGetError(); // clear errors
+  alListenerfv(AL_POSITION,_position);
+  al_audio_errcheck("alListerfv(AL_POSITION)");
+  alListenerfv(AL_VELOCITY,_velocity);
+  al_audio_errcheck("alListerfv(AL_VELOCITY)");
+  alListenerfv(AL_ORIENTATION,_forward_up);
+  al_audio_errcheck("alListerfv(AL_ORIENTATION)");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_get_listener_attributes
+//       Access: Public
+//  Description: Get position of the "ear" that picks up 3d sounds
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+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) {
+  *px = _position[0];
+  *py = -_position[2];
+  *pz = _position[1];
+
+  *vx = _velocity[0];
+  *vy = -_velocity[2];
+  *vz = _velocity[1];
+
+  *fx = _forward_up[0];
+  *fy = -_forward_up[2];
+  *fz = _forward_up[1];
+  
+  *ux = _forward_up[3];
+  *uy = -_forward_up[5];
+  *uz = _forward_up[4];
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_set_distance_factor
+//       Access: Public
+//  Description: Set units per foot
+//               WARNING: 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.
+//                        OpenAL's default speed of sound is 343.3 m/s == 1126.3 ft/s
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+audio_3d_set_distance_factor(float factor) {
+  _distance_factor = factor;
+
+  make_current();
+
+  alGetError(); // clear errors
+
+  if (_distance_factor>0) {
+    alSpeedOfSound(1126.3*_distance_factor);
+    al_audio_errcheck("alSpeedOfSound()");
+    // resets the doppler factor to the correct setting in case it was set to 0.0 by a distance_factor<=0.0
+    alDopplerFactor(_doppler_factor);
+    al_audio_errcheck("alDopplerFactor()");
+  } else {
+    audio_debug("can't set speed of sound if distance_factor <=0.0, setting doppler factor to 0.0 instead");
+    alDopplerFactor(0.0);
+    al_audio_errcheck("alDopplerFactor()");
+  }
+
+  AudioSet::iterator i=_sounds_on_loan.begin();
+  for (; i!=_sounds_on_loan.end(); ++i) {
+    (**i).set_3d_min_distance((**i).get_3d_min_distance());
+    (**i).set_3d_max_distance((**i).get_3d_max_distance());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_get_distance_factor
+//       Access: Public
+//  Description: Sets units per foot
+////////////////////////////////////////////////////////////////////
+float OpenALAudioManager::
+audio_3d_get_distance_factor() const {
+  return _distance_factor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_set_doppler_factor
+//       Access: Public
+//  Description: Exaggerates or diminishes the Doppler effect. 
+//               Defaults to 1.0
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+audio_3d_set_doppler_factor(float factor) {
+  _doppler_factor = factor;
+
+  make_current();
+  
+  alGetError(); // clear errors
+  alDopplerFactor(_doppler_factor);
+  al_audio_errcheck("alDopplerFactor()");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_get_doppler_factor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+float OpenALAudioManager::
+audio_3d_get_doppler_factor() const {
+  return _doppler_factor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_set_drop_off_factor
+//       Access: Public
+//  Description: Control the effect distance has on audability.
+//               Defaults to 1.0
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+audio_3d_set_drop_off_factor(float factor) {
+  _drop_off_factor = factor;
+
+  AudioSet::iterator i=_sounds_on_loan.begin();
+  for (; i!=_sounds_on_loan.end(); ++i) {
+    (**i).set_3d_drop_off_factor((**i).get_3d_drop_off_factor());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_get_drop_off_factor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+float OpenALAudioManager::
+audio_3d_get_drop_off_factor() const {
+  return _drop_off_factor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::starting_sound
+//       Access: 
+//  Description: Inform the manager that a sound is about to play.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+starting_sound(OpenALAudioSound* audio) {
+  ALuint source=0;
+
+  // first give all sounds that have finished a chance to stop, so that these get stopped first
+  update();
+
+  if (_concurrent_sound_limit) {
+    reduce_sounds_playing_to(_concurrent_sound_limit-1); // because we're about to add one
+  }
+  
+  // get a source from the source pool or create a new source
+  if (_al_sources->empty()) {
+    make_current();
+    alGetError(); // clear errors
+    alGenSources(1,&source);
+    ALenum result = alGetError();
+    if (result!=AL_NO_ERROR) {
+      audio_error("alGenSources(): " << alGetString(result) );
+      // if we can't create any more sources, set stop a sound to free a source
+      reduce_sounds_playing_to(_sounds_playing.size()-1);
+      source = 0;
+    }
+  }
+  // get a source from the source bool if we didn't just allocate one
+  if (!source && !_al_sources->empty()) {
+    source = *(_al_sources->begin());
+    _al_sources->erase(source);
+  }
+
+  assert(!audio->_source);
+  audio->_source = source;
+
+  if (source)
+    _sounds_playing.insert(audio);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::stopping_sound
+//       Access: 
+//  Description: Inform the manager that a sound is finished or 
+//               someone called stop on the sound (this should not
+//               be called if a sound is only paused).
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+stopping_sound(OpenALAudioSound* audio) {
+  _sounds_playing.erase(audio);
+  if (audio->_source) {
+    _al_sources->insert(audio->_source);
+    audio->_source = 0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::set_concurrent_sound_limit
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+set_concurrent_sound_limit(unsigned int limit) {
+  _concurrent_sound_limit = limit;
+  reduce_sounds_playing_to(_concurrent_sound_limit);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::get_concurrent_sound_limit
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+unsigned int OpenALAudioManager::
+get_concurrent_sound_limit() const {
+  return _concurrent_sound_limit;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::reduce_sounds_playing_to
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+reduce_sounds_playing_to(unsigned int count) {
+  // first give all sounds that have finished a chance to stop, so that these get stopped first
+  update();
+
+  int limit = _sounds_playing.size() - count;
+  while (limit-- > 0) {
+    SoundsPlaying::iterator sound = _sounds_playing.begin();
+    assert(sound != _sounds_playing.end());
+    (**sound).stop();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::stop_all_sounds()
+//       Access: Public
+//  Description: Stop playback on all sounds managed by this manager.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+stop_all_sounds() {
+  audio_debug("OpenALAudioManager::stop_all_sounds()");
+  reduce_sounds_playing_to(0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::update
+//       Access: Public
+//  Description: Perform all per-frame update functions.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+update() {
+  //audio_debug("OpenALAudioManager::update()");
+
+  // See if any of our playing sounds have ended
+  // we must first collect a seperate list of finished sounds and then
+  // iterated over those again calling their finished method. We 
+  // can't call finished() within a loop iterating over _sounds_playing
+  // since finished() modifies _sounds_playing
+  SoundsPlaying sounds_finished;
+
+  SoundsPlaying::iterator i=_sounds_playing.begin();
+  for (; i!=_sounds_playing.end(); ++i) {
+    if ((**i).status()!=AudioSound::PLAYING) {
+      sounds_finished.insert(*i);
+    }
+  }
+
+  i=sounds_finished.begin();
+  for (; i!=sounds_finished.end(); ++i) {
+    (**i).finished();
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::cleanup
+//       Access: Private
+//  Description: Shuts down the audio manager and releases any
+//               resources associated with it.  Also cleans up all
+//               AudioSounds created via the manager.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+cleanup() {
+  audio_debug("OpenALAudioManager::cleanup(), this = " << (void *)this
+              << ", _cleanup_required = " << _cleanup_required);
+  if (!_cleanup_required) {
+    return;
+  }
+
+  // Be sure to cleanup associated sounds before cleaning up the manager:
+  AudioSet::iterator ai;
+  for (ai = _sounds_on_loan.begin(); ai != _sounds_on_loan.end(); ++ai) {
+    (*ai)->cleanup();
+  }
+
+  clear_cache();
+
+  nassertv(_active_managers > 0);
+  --_active_managers;
+  audio_debug("  _active_managers="<<_active_managers);
+
+  if (_active_managers == 0) {
+    if (_openal_active) {
+      // empty the source cache
+      int i=0;
+      ALuint *sources;
+      sources = new ALuint[_al_sources->size()];
+      for (SourceCache::iterator si = _al_sources->begin(); si!=_al_sources->end(); ++si) {
+        sources[i++]=*si;
+      }
+      make_current();
+      alGetError(); // clear errors
+      alDeleteSources(_al_sources->size(),sources);
+      al_audio_errcheck("alDeleteSources()");
+      delete [] sources;
+      _al_sources->clear();
+
+      // make sure that the context is not current when it is destroyed
+      alcGetError(_device); // clear errors
+      alcMakeContextCurrent(NULL);
+      alc_audio_errcheck("alcMakeContextCurrent(NULL)",_device);
+      
+      alcDestroyContext(_context);
+      alc_audio_errcheck("alcDestroyContext(_context)",_device);
+      _context = NULL;
+
+      if (_device) {
+        alcCloseDevice(_device);
+        //alc_audio_errcheck("alcCloseDevice(_device)",_device);
+        _device = NULL;
+      }
+
+      _openal_active = false;
+    }
+    /*if (_managers) {
+      delete _managers;
+      _managers = NULL;
+      delete _al_sources;
+      _al_sources = NULL;
+    }*/
+  }
+  _cleanup_required = false;
+  audio_debug("OpenALAudioManager::cleanup() finished");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::SoundData::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+OpenALAudioManager::SoundData::
+SoundData(OpenALAudioManager* manager) :
+  _manager(manager),
+  _buffer(0),
+  _has_length(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::SoundData::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+OpenALAudioManager::SoundData::
+~SoundData() {
+  if (_buffer != 0) {
+    if (_manager->_is_valid) {
+      _manager->make_current();
+      alDeleteBuffers(1,&_buffer);
+    }
+    _buffer = 0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::SoundData::get_length
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+float OpenALAudioManager::SoundData::
+get_length() {
+  int freq,bits,channels,size;
+
+  if (!_has_length) {
+    // Time to determine the length of the file.
+
+    audio_debug("Computing length of " << _basename);
+
+    _manager->make_current();
+
+    alGetError(); // clear errors
+    alGetBufferi(_buffer,AL_FREQUENCY,&freq);
+    audio_debug("Frequency = "<<freq);
+    al_audio_errcheck("alGetBufferi(_buffer,AL_FREQUENCY)");
+    alGetBufferi(_buffer,AL_BITS,&bits);
+    audio_debug("Bits = "<<bits);
+    al_audio_errcheck("alGetBufferi(_buffer,AL_BITS)");
+    alGetBufferi(_buffer,AL_CHANNELS,&channels);
+    audio_debug("Channels = "<<channels);
+    al_audio_errcheck("alGetBufferi(_buffer,AL_CHANNELS)");
+    alGetBufferi(_buffer,AL_SIZE,&size);
+    audio_debug("Size = "<<size);
+    al_audio_errcheck("alGetBufferi(_buffer,AL_SIZE)");
+  
+    _length = ((float)size)/channels/((float)bits/8)/freq;
+    audio_debug("Length = "<<_length);
+    _has_length = true;
+  }
+
+  return _length;
+}
+
+#endif //]

+ 227 - 0
panda/src/audiotraits/openalAudioManager.h

@@ -0,0 +1,227 @@
+// Filename: openalAudioManager.h
+// Created by:  Ben Buchwald <[email protected]>
+//
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+#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__ */

+ 23 - 0
panda/src/audiotraits/openalAudioSound.I

@@ -0,0 +1,23 @@
+// Filename: openalAudioSound.I
+// Created by:  Ben Buchwald <[email protected]>
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+
+
+
+

+ 730 - 0
panda/src/audiotraits/openalAudioSound.cxx

@@ -0,0 +1,730 @@
+// Filename: openalAudioSound.cxx
+// Created by:  Ben Buchwald <[email protected]>
+//
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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 //]

+ 194 - 0
panda/src/audiotraits/openalAudioSound.h

@@ -0,0 +1,194 @@
+// Filename: openalAudioSound.h
+// Created by:  Ben Buchwald <[email protected]>
+//
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+
+#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__ */
+
+
+
+
+

+ 1 - 0
panda/src/audiotraits/openal_audio_composite.cxx

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

+ 5 - 0
panda/src/audiotraits/openal_audio_composite1.cxx

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

+ 15 - 15
panda/src/grutil/movieTexture.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 &copy) :
   // Since 'make_copy' can be a slow operation, 
   // I release the read lock before calling make_copy.
   
-  pvector<MovieVideo *> color;
-  pvector<MovieVideo *> alpha;
+  pvector<MovieVideoCursor *> color;
+  pvector<MovieVideoCursor *> alpha;
   {
     CDReader copy_cdata(copy._cycler);
     color.resize(copy_cdata->_pages.size());
@@ -129,10 +128,10 @@ MovieTexture(const MovieTexture &copy) :
     cdata->_pages.resize(color.size());
     for (int i=0; i<(int)(color.size()); i++) {
       if (color[i]) {
-        cdata->_pages[i]._color = color[i]->make_copy();
+        cdata->_pages[i]._color = color[i]->get_source()->open();
       }
       if (alpha[i]) {
-        cdata->_pages[i]._alpha = color[i]->make_copy();
+        cdata->_pages[i]._alpha = alpha[i]->get_source()->open();
       }
     }
     recalculate_image_properties(cdata);
@@ -205,7 +204,7 @@ recalculate_image_properties(CDWriter &cdata) {
   double len = 0.0;
   
   for (int i=0; i<_z_size; i++) {
-    MovieVideo *t = cdata->_pages[i]._color;
+    MovieVideoCursor *t = cdata->_pages[i]._color;
     if (t) {
       if (t->size_x() > x_max) x_max = t->size_x();
       if (t->size_y() > y_max) y_max = t->size_y();
@@ -259,15 +258,15 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
     record->add_dependent_file(fullpath);
   }
 
-  PT(MovieVideo) color;
-  PT(MovieVideo) alpha;
+  PT(MovieVideoCursor) color;
+  PT(MovieVideoCursor) alpha;
   
-  color = MovieVideo::load(fullpath);
+  color = MovieVideo::get(fullpath)->open();
   if (color == 0) {
     return false;
   }
   if (!alpha_fullpath.empty()) {
-    alpha = MovieVideo::load(alpha_fullpath);
+    alpha = MovieVideo::get(alpha_fullpath)->open();
     if (alpha == 0) {
       return false;
     }
@@ -296,6 +295,7 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
   set_loaded_from_image();
   set_loop(true);
   play();
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -304,7 +304,7 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
 //  Description: Loads movie objects into the texture.
 ////////////////////////////////////////////////////////////////////
 bool MovieTexture::
-do_load_one(PT(MovieVideo) color, PT(MovieVideo) alpha, int z) {
+do_load_one(PT(MovieVideoCursor) color, PT(MovieVideoCursor) alpha, int z) {
   
   {
     CDWriter cdata(_cycler);
@@ -391,8 +391,8 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
   }
   
   for (int i=0; i<((int)(cdata->_pages.size())); i++) {
-    MovieVideo *color = cdata->_pages[i]._color;
-    MovieVideo *alpha = cdata->_pages[i]._alpha;
+    MovieVideoCursor *color = cdata->_pages[i]._color;
+    MovieVideoCursor *alpha = cdata->_pages[i]._alpha;
     if (color && alpha) {
       if ((offset >= color->next_start())||
           ((offset < color->last_start()) && (color->can_seek()))) {

+ 3 - 3
panda/src/grutil/movieTexture.h

@@ -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;

+ 15 - 0
panda/src/movies/Sources.pp

@@ -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
 

+ 5 - 0
panda/src/movies/config_movies.cxx

@@ -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

+ 10 - 0
panda/src/movies/config_movies.h

@@ -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);

+ 7 - 32
panda/src/movies/ffmpegAudio.cxx

@@ -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);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 7 - 10
panda/src/movies/ffmpegAudio.h

@@ -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() {

+ 18 - 0
panda/src/movies/ffmpegAudioCursor.I

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 264 - 0
panda/src/movies/ffmpegAudioCursor.cxx

@@ -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
+// [email protected] 
+//
+////////////////////////////////////////////////////////////////////
+
+#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

+ 88 - 0
panda/src/movies/ffmpegAudioCursor.h

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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

+ 7 - 259
panda/src/movies/ffmpegVideo.cxx

@@ -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

+ 6 - 33
panda/src/movies/ffmpegVideo.h

@@ -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() {

+ 18 - 0
panda/src/movies/ffmpegVideoCursor.I

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 306 - 0
panda/src/movies/ffmpegVideoCursor.cxx

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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

+ 86 - 0
panda/src/movies/ffmpegVideoCursor.h

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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

+ 17 - 0
panda/src/movies/ffmpegVirtualFileCursor.I

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////

+ 155 - 0
panda/src/movies/ffmpegVirtualFileCursor.cxx

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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

+ 46 - 0
panda/src/movies/ffmpegVirtualFileCursor.h

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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
+

+ 10 - 119
panda/src/movies/inkblotVideo.cxx

@@ -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
-//       Access: Published, Virtual
-//  Description: See MovieVideo::fetch_into_buffer.
-////////////////////////////////////////////////////////////////////
-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
+//     Function: InkblotVideo::open
 //       Access: Published, Virtual
-//  Description: Make a copy of this video with a separate cursor.
+//  Description: Open this video, returning a MovieVideoCursor.
 ////////////////////////////////////////////////////////////////////
-PT(MovieVideo) InkblotVideo::
-make_copy() const {
-  return new InkblotVideo(_size_x, _size_y, _fps);
+PT(MovieVideoCursor) InkblotVideo::
+open() {
+  return new InkblotVideoCursor(this);
 }

+ 10 - 13
panda/src/movies/inkblotVideo.h

@@ -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() {

+ 18 - 0
panda/src/movies/inkblotVideoCursor.I

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 151 - 0
panda/src/movies/inkblotVideoCursor.cxx

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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;
+      }
+    }
+  }
+}
+

+ 67 - 0
panda/src/movies/inkblotVideoCursor.h

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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

+ 0 - 120
panda/src/movies/movieAudio.I

@@ -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);
-}
- 

+ 11 - 66
panda/src/movies/movieAudio.cxx

@@ -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).
-////////////////////////////////////////////////////////////////////
-void MovieAudio::
-seek(int sr) {
-  _samples_read = sr;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::make_copy
-//       Access: Published, Virtual
-//  Description: Make a copy of this MovieAudio with its own cursor.
+//  Description: Open this audio, returning a MovieAudioCursor
 ////////////////////////////////////////////////////////////////////
-PT(MovieAudio) MovieAudio::
-make_copy() const {
-  return new MovieAudio();
+PT(MovieAudioCursor) MovieAudio::
+open() {
+  return new MovieAudioCursor(this);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     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
 }

+ 10 - 29
panda/src/movies/movieAudio.h

@@ -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() {

+ 147 - 0
panda/src/movies/movieAudioCursor.I

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     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);
+}
+ 

+ 104 - 0
panda/src/movies/movieAudioCursor.cxx

@@ -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
+// [email protected] 
+//
+////////////////////////////////////////////////////////////////////
+
+#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;
+}
+

+ 93 - 0
panda/src/movies/movieAudioCursor.h

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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

+ 0 - 136
panda/src/movies/movieVideo.I

@@ -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;
-}
-

+ 10 - 246
panda/src/movies/movieVideo.cxx

@@ -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
-//       Access: Published, Virtual
-//  Description: Discards the next video frame.  Still sets
-//               last_start and next_start.
-//
-//               See fetch_into_buffer for more details.
-////////////////////////////////////////////////////////////////////
-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);
-}
-
-////////////////////////////////////////////////////////////////////
-//     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
+//     Function: MovieVideo::open
 //       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.
+//  Description: Open this video, returning a MovieVideoCursor.
 ////////////////////////////////////////////////////////////////////
-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();
+PT(MovieVideoCursor) MovieVideo::
+open() {
+  return new MovieVideoCursor(this);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     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
 }
 

+ 11 - 39
panda/src/movies/movieVideo.h

@@ -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() {

+ 164 - 0
panda/src/movies/movieVideoCursor.I

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     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;
+}
+

+ 281 - 0
panda/src/movies/movieVideoCursor.cxx

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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;
+  }
+}
+

+ 101 - 0
panda/src/movies/movieVideoCursor.h

@@ -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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#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

+ 11 - 0
panda/src/movies/movies_composite1.cxx

@@ -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"

+ 11 - 0
panda/src/pandabase/pandasymbols.h

@@ -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