Browse Source

Split vrpn out of panda into a libp3vrpn. Split ffmpeg out of movies into libp3ffmpeg. Add registry for MovieVideo and MovieAudio. Add a libvorbisfile-based Ogg Vorbis loader. Add a native .wav loader.

rdb 12 years ago
parent
commit
bca09667a6
53 changed files with 1943 additions and 239 deletions
  1. 1 1
      panda/metalibs/panda/Sources.pp
  2. 11 0
      panda/src/configfiles/panda.prc.pp
  3. 44 0
      panda/src/ffmpeg/Sources.pp
  4. 110 0
      panda/src/ffmpeg/config_ffmpeg.cxx
  5. 31 0
      panda/src/ffmpeg/config_ffmpeg.h
  6. 0 0
      panda/src/ffmpeg/ffmpegAudio.I
  7. 10 6
      panda/src/ffmpeg/ffmpegAudio.cxx
  8. 4 7
      panda/src/ffmpeg/ffmpegAudio.h
  9. 0 0
      panda/src/ffmpeg/ffmpegAudioCursor.I
  10. 5 11
      panda/src/ffmpeg/ffmpegAudioCursor.cxx
  11. 3 6
      panda/src/ffmpeg/ffmpegAudioCursor.h
  12. 0 0
      panda/src/ffmpeg/ffmpegVideo.I
  13. 12 8
      panda/src/ffmpeg/ffmpegVideo.cxx
  14. 4 6
      panda/src/ffmpeg/ffmpegVideo.h
  15. 0 0
      panda/src/ffmpeg/ffmpegVideoCursor.I
  16. 0 5
      panda/src/ffmpeg/ffmpegVideoCursor.cxx
  17. 2 6
      panda/src/ffmpeg/ffmpegVideoCursor.h
  18. 0 0
      panda/src/ffmpeg/ffmpegVirtualFile.I
  19. 0 4
      panda/src/ffmpeg/ffmpegVirtualFile.cxx
  20. 1 5
      panda/src/ffmpeg/ffmpegVirtualFile.h
  21. 1 0
      panda/src/ffmpeg/p3ffmpeg_composite1.cxx
  22. 0 2
      panda/src/grutil/Sources.pp
  23. 1 7
      panda/src/grutil/config_grutil.cxx
  24. 1 0
      panda/src/grutil/movieTexture.h
  25. 38 39
      panda/src/movies/Sources.pp
  26. 45 74
      panda/src/movies/config_movies.cxx
  27. 8 12
      panda/src/movies/config_movies.h
  28. 1 0
      panda/src/movies/inkblotVideoCursor.h
  29. 1 1
      panda/src/movies/microphoneAudio.h
  30. 5 8
      panda/src/movies/movieAudio.cxx
  31. 2 6
      panda/src/movies/movieAudio.h
  32. 29 0
      panda/src/movies/movieTypeRegistry.I
  33. 290 0
      panda/src/movies/movieTypeRegistry.cxx
  34. 57 0
      panda/src/movies/movieTypeRegistry.h
  35. 5 9
      panda/src/movies/movieVideo.cxx
  36. 0 4
      panda/src/movies/movieVideo.h
  37. 12 7
      panda/src/movies/p3movies_composite1.cxx
  38. 14 0
      panda/src/movies/vorbisAudio.I
  39. 77 0
      panda/src/movies/vorbisAudio.cxx
  40. 63 0
      panda/src/movies/vorbisAudio.h
  41. 14 0
      panda/src/movies/vorbisAudioCursor.I
  42. 235 0
      panda/src/movies/vorbisAudioCursor.cxx
  43. 88 0
      panda/src/movies/vorbisAudioCursor.h
  44. 14 0
      panda/src/movies/wavAudio.I
  45. 73 0
      panda/src/movies/wavAudio.cxx
  46. 59 0
      panda/src/movies/wavAudio.h
  47. 14 0
      panda/src/movies/wavAudioCursor.I
  48. 438 0
      panda/src/movies/wavAudioCursor.cxx
  49. 83 0
      panda/src/movies/wavAudioCursor.h
  50. 33 3
      panda/src/pandabase/pandasymbols.h
  51. 2 0
      panda/src/vrpn/Sources.pp
  52. 1 1
      panda/src/vrpn/config_vrpn.h
  53. 1 1
      panda/src/vrpn/vrpnClient.h

+ 1 - 1
panda/metalibs/panda/Sources.pp

@@ -10,7 +10,7 @@
 
 #define COMPONENT_LIBS \
     p3recorder p3pgraph p3pgraphnodes p3pipeline \
-    p3vrpn p3grutil p3chan p3pstatclient \
+    p3grutil p3chan p3pstatclient \
     p3char p3collide p3cull p3device \
     p3dgraph p3display p3event p3gobj p3gsgbase \
     p3linmath p3mathutil p3movies p3net p3nativenet \

+ 11 - 0
panda/src/configfiles/panda.prc.pp

@@ -59,6 +59,17 @@ audio-library-name p3openal_audio
 load-file-type egg pandaegg
 
 
+# These entries work very similar to load-file-type, except they are
+# used by the MovieVideo and MovieAudio code to determine which module
+# should be loaded in order to decode files of the given extension.
+
+# ffmpeg is added by default because it used to be compiled in.
+# The * is a special catch-all extension that is consulted unless a
+# loader has been defined with an explicit extension.
+
+load-audio-type * p3ffmpeg
+load-video-type * p3ffmpeg
+
 
 # The following lines define some handy object types to use within the
 # egg syntax.  This remaps <ObjectType> { name } into whatever egg

+ 44 - 0
panda/src/ffmpeg/Sources.pp

@@ -0,0 +1,44 @@
+#define BUILD_DIRECTORY $[HAVE_FFMPEG]
+
+#define OTHER_LIBS p3interrogatedb:c p3dconfig:c p3dtoolconfig:m \
+                   p3dtoolutil:c p3dtoolbase:c p3dtool:m p3prc:c
+
+#define BUILDING_DLL BUILDING_FFMPEG
+
+#define USE_PACKAGES ffmpeg
+
+#begin lib_target
+  #define TARGET p3ffmpeg
+
+  #define LOCAL_LIBS p3movies
+
+  #define COMBINED_SOURCES \
+    $[TARGET]_composite1.cxx
+
+  #define SOURCES \
+    config_ffmpeg.h \
+    ffmpegVideo.h ffmpegVideo.I \
+    ffmpegVideoCursor.h ffmpegVideoCursor.I \
+    ffmpegAudio.h ffmpegAudio.I \
+    ffmpegAudioCursor.h ffmpegAudioCursor.I \
+    ffmpegVirtualFile.h ffmpegVirtualFile.I
+    
+  #define INCLUDED_SOURCES \
+    config_ffmpeg.cxx \
+    ffmpegVideo.cxx \
+    ffmpegVideoCursor.cxx \
+    ffmpegAudio.cxx \
+    ffmpegAudioCursor.cxx \
+    ffmpegVirtualFile.cxx
+    
+  #define INSTALL_HEADERS \
+    config_ffmpeg.h \
+    ffmpegVideo.h ffmpegVideo.I \
+    ffmpegVideoCursor.h ffmpegVideoCursor.I \
+    ffmpegAudio.h ffmpegAudio.I \
+    ffmpegAudioCursor.h ffmpegAudioCursor.I \
+    ffmpegVirtualFile.h ffmpegVirtualFile.I
+
+  #define IGATESCAN all
+
+#end lib_target

+ 110 - 0
panda/src/ffmpeg/config_ffmpeg.cxx

@@ -0,0 +1,110 @@
+// Filename: config_ffmpeg.cxx
+// Created by:  rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_ffmpeg.h"
+#include "dconfig.h"
+#include "ffmpegVideo.h"
+#include "ffmpegVideoCursor.h"
+#include "ffmpegAudio.h"
+#include "ffmpegAudioCursor.h"
+
+#include "movieTypeRegistry.h"
+
+extern "C" {
+  #include "libavcodec/avcodec.h"
+}
+
+ConfigureDef(config_ffmpeg);
+NotifyCategoryDef(ffmpeg, "");
+
+ConfigureFn(config_ffmpeg) {
+  init_libffmpeg();
+}
+
+ConfigVariableInt ffmpeg_max_readahead_frames
+("ffmpeg-max-readahead-frames", 2,
+ PRC_DESC("The maximum number of frames ahead which an ffmpeg decoder thread "
+          "should read in advance of actual playback.  Set this to 0 to "
+          "decode ffmpeg videos in the main thread."));
+
+ConfigVariableBool ffmpeg_show_seek_frames
+("ffmpeg-show-seek-frames", true,
+ PRC_DESC("Set this true to allow showing the intermediate results of seeking "
+          "through the ffmpeg stream to a target frame, or false to hold the "
+          "current frame until the target frame is achieved.  This has the "
+          "biggest effect on videos that are too expensive to decode in real "
+          "time: when this is true, the video can be seen to animate at least "
+          "a little bit; when it is false, you may get long periods of one "
+          "held frame."));
+
+ConfigVariableBool ffmpeg_support_seek
+("ffmpeg-support-seek", true,
+ PRC_DESC("True to use the av_seek_frame() function to seek within ffmpeg "
+          "video files.  If this is false, Panda will only seek within a "
+          "file by reading it from the beginning until the desired point, "
+          "which can be much slower.  Set this false only if you suspect "
+          "a problem with av_seek_frame()."));
+
+ConfigVariableBool ffmpeg_global_lock
+("ffmpeg-global-lock", false,
+ PRC_DESC("Set this true to enable a single global mutex across *all* ffmpeg "
+          "operations.  Leave this false to use the mutex only for "
+          "the ffmpeg operations that are generally known to be "
+          "not thread-safe.  This will negatively affect ffmpeg performance, "
+          "especially when decoding multiple videos at once (including the "
+          "left and right channels of a stereo video).  Set this true only "
+          "if you suspect a problem with ffmpeg's own thread-safe nature."));
+
+ConfigVariableEnum<ThreadPriority> ffmpeg_thread_priority
+("ffmpeg-thread-priority", TP_normal,
+ PRC_DESC("The default thread priority at which to start ffmpeg decoder "
+          "threads."));
+
+ConfigVariableInt ffmpeg_read_buffer_size
+("ffmpeg-read-buffer-size", 4096,
+ PRC_DESC("The size in bytes of the buffer used when reading input files. "
+          "This is important for performance.  A typical size is that of a "
+          "cache page, e.g. 4kb."));
+
+////////////////////////////////////////////////////////////////////
+//     Function: init_libffmpeg
+//  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_libffmpeg() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+
+  FfmpegVirtualFile::register_protocol();
+
+  FfmpegVideo::init_type();
+  FfmpegVideoCursor::init_type();
+  FfmpegAudio::init_type();
+  FfmpegAudioCursor::init_type();
+
+  FfmpegVideo::register_with_read_factory();
+  FfmpegVideoCursor::register_with_read_factory();
+
+  // Register ffmpeg as catch-all audio/video type.
+  MovieTypeRegistry *reg = MovieTypeRegistry::get_global_ptr();
+  reg->register_audio_type(&FfmpegAudio::make, "*");
+  reg->register_video_type(&FfmpegVideo::make, "*");
+}

+ 31 - 0
panda/src/ffmpeg/config_ffmpeg.h

@@ -0,0 +1,31 @@
+// Filename: config_ffmpeg.h
+// Created by:  rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIG_FFMPEG_H
+#define CONFIG_FFMPEG_H
+
+#include "pandabase.h"
+#include "notifyCategoryProxy.h"
+#include "configVariableEnum.h"
+#include "configVariableInt.h"
+#include "configVariableBool.h"
+#include "threadPriority.h"
+#include "dconfig.h"
+
+ConfigureDecl(config_ffmpeg, EXPCL_FFMPEG, EXPTP_FFMPEG);
+NotifyCategoryDecl(ffmpeg, EXPCL_FFMPEG, EXPTP_FFMPEG);
+
+extern EXPCL_FFMPEG void init_libffmpeg();
+
+#endif /* CONFIG_FFMPEG_H */

+ 0 - 0
panda/src/movies/ffmpegAudio.I → panda/src/ffmpeg/ffmpegAudio.I


+ 10 - 6
panda/src/movies/ffmpegAudio.cxx → panda/src/ffmpeg/ffmpegAudio.cxx

@@ -13,10 +13,8 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "ffmpegAudio.h"
-
-#ifdef HAVE_FFMPEG
-
 #include "ffmpegAudioCursor.h"
+#include "dcast.h"
 
 TypeHandle FfmpegAudio::_type_handle;
 
@@ -50,7 +48,7 @@ PT(MovieAudioCursor) FfmpegAudio::
 open() {
   PT(FfmpegAudioCursor) result = new FfmpegAudioCursor(this);
   if (result->_format_ctx == 0) {
-    movies_cat.error() << "Could not open " << _filename << "\n";
+    ffmpeg_cat.error() << "Could not open " << _filename << "\n";
     return NULL;
   } else {
     return (MovieAudioCursor*)(FfmpegAudioCursor*)result;
@@ -58,5 +56,11 @@ open() {
 }
 
 ////////////////////////////////////////////////////////////////////
-
-#endif // HAVE_FFMPEG
+//     Function: FfmpegAudio::make
+//       Access: Published, Static
+//  Description: Obtains a MovieAudio that references a file.
+////////////////////////////////////////////////////////////////////
+PT(MovieAudio) FfmpegAudio::
+make(const Filename &name) {
+  return DCAST(MovieAudio, new FfmpegAudio(name));
+}

+ 4 - 7
panda/src/movies/ffmpegAudio.h → panda/src/ffmpeg/ffmpegAudio.h

@@ -16,9 +16,6 @@
 #define FFMPEGAUDIO_H
 
 #include "pandabase.h"
-
-#ifdef HAVE_FFMPEG
-
 #include "movieAudio.h"
 
 class FfmpegAudioCursor;
@@ -27,13 +24,14 @@ class FfmpegAudioCursor;
 //       Class : FfmpegAudio
 // Description : A stream that generates a sequence of audio samples.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_MOVIES FfmpegAudio : public MovieAudio {
-
+class EXPCL_FFMPEG FfmpegAudio : public MovieAudio {
 PUBLISHED:
   FfmpegAudio(const Filename &name);
   virtual ~FfmpegAudio();
   virtual PT(MovieAudioCursor) open();
 
+  static PT(MovieAudio) make(const Filename &name);
+
  private:
   friend class FfmpegAudioCursor;
   
@@ -57,5 +55,4 @@ private:
 
 #include "ffmpegAudio.I"
 
-#endif // HAVE_FFMPEG
-#endif // FFMPEG_AUDIO.H
+#endif // FFMPEGAUDIO_H

+ 0 - 0
panda/src/movies/ffmpegAudioCursor.I → panda/src/ffmpeg/ffmpegAudioCursor.I


+ 5 - 11
panda/src/movies/ffmpegAudioCursor.cxx → panda/src/ffmpeg/ffmpegAudioCursor.cxx

@@ -14,8 +14,6 @@
 
 #include "ffmpegAudioCursor.h"
 
-#ifdef HAVE_FFMPEG
-
 #include "ffmpegAudio.h"
 extern "C" {
   #include "libavutil/dict.h"
@@ -116,10 +114,10 @@ FfmpegAudioCursor(FfmpegAudio *src) :
   if (_audio_ctx->sample_fmt != AV_SAMPLE_FMT_S16) {
 #ifdef HAVE_SWRESAMPLE
 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 25, 0)
-    movies_cat.error()
+    ffmpeg_cat.error()
       << "Codec does not use signed 16-bit sample format.  Upgrade libavcodec to 53.25.0 or higher.\n";
 #else
-    movies_cat.debug()
+    ffmpeg_cat.debug()
       << "Codec does not use signed 16-bit sample format.  Setting up swresample context.\n";
 #endif
 
@@ -132,12 +130,12 @@ FfmpegAudioCursor(FfmpegAudio *src) :
     av_opt_set_sample_fmt(_resample_ctx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
 
     if (swr_init(_resample_ctx) != 0) {
-      movies_cat.error()
+      ffmpeg_cat.error()
         << "Failed to set up resample context.\n";
       _resample_ctx = NULL;
     }
 #else
-    movies_cat.error()
+    ffmpeg_cat.error()
       << "Codec does not use signed 16-bit sample format, but support for libswresample has not been enabled.\n";
 #endif
   }
@@ -354,7 +352,7 @@ seek(double t) {
     target_ts = _initial_dts;
   }
   if (av_seek_frame(_format_ctx, _audio_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
-    movies_cat.error() << "Seek failure. Shutting down movie.\n";
+    ffmpeg_cat.error() << "Seek failure. Shutting down movie.\n";
     cleanup();
     return;
   }
@@ -417,7 +415,3 @@ read_samples(int n, PN_int16 *data) {
   }
   _samples_read += n;
 }
-
-////////////////////////////////////////////////////////////////////
-
-#endif // HAVE_FFMPEG

+ 3 - 6
panda/src/movies/ffmpegAudioCursor.h → panda/src/ffmpeg/ffmpegAudioCursor.h

@@ -17,8 +17,6 @@
 
 #include "pandabase.h"
 
-#ifdef HAVE_FFMPEG
-
 #include "movieAudioCursor.h"
 #include "namable.h"
 #include "texture.h"
@@ -43,7 +41,7 @@ struct SwrContext;
 //       Class : FfmpegAudioCursor
 // Description : A stream that generates a sequence of audio samples.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_MOVIES FfmpegAudioCursor : public MovieAudioCursor {
+class EXPCL_FFMPEG FfmpegAudioCursor : public MovieAudioCursor {
   friend class FfmpegAudio;
 
 PUBLISHED:
@@ -85,7 +83,7 @@ public:
     return _type_handle;
   }
   static void init_type() {
-    TypedWritableReferenceCount::init_type();
+    MovieAudioCursor::init_type();
     register_type(_type_handle, "FfmpegAudioCursor",
                   MovieAudioCursor::get_class_type());
   }
@@ -100,5 +98,4 @@ private:
 
 #include "ffmpegAudioCursor.I"
 
-#endif // HAVE_FFMPEG
-#endif // FFMPEG_AUDIO.H
+#endif // FFMPEGAUDIOCURSOR_H

+ 0 - 0
panda/src/movies/ffmpegVideo.I → panda/src/ffmpeg/ffmpegVideo.I


+ 12 - 8
panda/src/movies/ffmpegVideo.cxx → panda/src/ffmpeg/ffmpegVideo.cxx

@@ -13,12 +13,10 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "ffmpegVideo.h"
-
-#ifdef HAVE_FFMPEG
-
 #include "ffmpegVideoCursor.h"
 #include "config_movies.h"
 #include "bamReader.h"
+#include "dcast.h"
 
 TypeHandle FfmpegVideo::_type_handle;
 
@@ -70,13 +68,23 @@ PT(MovieVideoCursor) FfmpegVideo::
 open() {
   PT(FfmpegVideoCursor) result = new FfmpegVideoCursor(this);
   if (result->_format_ctx == 0) {
-    movies_cat.error() << "Could not open " << _filename << "\n";
+    ffmpeg_cat.error() << "Could not open " << _filename << "\n";
     return NULL;
   } else {
     return result.p();
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideo::make
+//       Access: Published, Static
+//  Description: Obtains a MovieVideo that references a file.
+////////////////////////////////////////////////////////////////////
+PT(MovieVideo) FfmpegVideo::
+make(const Filename &name) {
+  return DCAST(MovieVideo, new FfmpegVideo(name));
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: FfmpegVideo::register_with_read_factory
 //       Access: Public, Static
@@ -130,7 +138,3 @@ void FfmpegVideo::
 fillin(DatagramIterator &scan, BamReader *manager) {
   MovieVideo::fillin(scan, manager);
 }
-
-////////////////////////////////////////////////////////////////////
-
-#endif // HAVE_FFMPEG

+ 4 - 6
panda/src/movies/ffmpegVideo.h → panda/src/ffmpeg/ffmpegVideo.h

@@ -17,8 +17,6 @@
 
 #include "pandabase.h"
 
-#ifdef HAVE_FFMPEG
-
 #include "movieVideo.h"
 
 class FfmpegVideoCursor;
@@ -30,15 +28,16 @@ class BamReader;
 //       Class : FfmpegVideo
 // Description : 
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_MOVIES FfmpegVideo : public MovieVideo {
-
+class EXPCL_FFMPEG FfmpegVideo : public MovieVideo {
 PUBLISHED:
   FfmpegVideo(const Filename &name);
   FfmpegVideo(const SubfileInfo &info);
 
   virtual ~FfmpegVideo();
   virtual PT(MovieVideoCursor) open();
-  
+
+  static PT(MovieVideo) make(const Filename &name);
+
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
@@ -69,5 +68,4 @@ private:
 
 #include "ffmpegVideo.I"
 
-#endif // HAVE_FFMPEG
 #endif // FFMPEGVIDEO_H

+ 0 - 0
panda/src/movies/ffmpegVideoCursor.I → panda/src/ffmpeg/ffmpegVideoCursor.I


+ 0 - 5
panda/src/movies/ffmpegVideoCursor.cxx → panda/src/ffmpeg/ffmpegVideoCursor.cxx

@@ -13,9 +13,6 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "ffmpegVideoCursor.h"
-
-#ifdef HAVE_FFMPEG
-
 #include "config_movies.h"
 #include "pStatCollector.h"
 #include "pStatTimer.h"
@@ -1299,5 +1296,3 @@ get_timestamp() const {
   int mid_frame = (_begin_frame + _end_frame - 1) / 2;
   return mid_frame * _video_timebase;
 }
-
-#endif // HAVE_FFMPEG

+ 2 - 6
panda/src/movies/ffmpegVideoCursor.h → panda/src/ffmpeg/ffmpegVideoCursor.h

@@ -16,9 +16,6 @@
 #define FFMPEGVIDEOCURSOR_H
 
 #include "pandabase.h"
-
-#ifdef HAVE_FFMPEG
-
 #include "movieVideoCursor.h"
 #include "texture.h"
 #include "pointerTo.h"
@@ -42,7 +39,7 @@ struct SwsContext;
 //       Class : FfmpegVideoCursor
 // Description : 
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_MOVIES FfmpegVideoCursor : public MovieVideoCursor {
+class EXPCL_FFMPEG FfmpegVideoCursor : public MovieVideoCursor {
 private:
   FfmpegVideoCursor();
   void init_from(FfmpegVideo *src);
@@ -67,7 +64,7 @@ public:
 
 public:
   // Nested class must be public for PT(FfmpegBuffer) to work correctly.
-  class EXPCL_PANDA_MOVIES FfmpegBuffer : public Buffer {
+  class EXPCL_FFMPEG FfmpegBuffer : public Buffer {
   public:
     ALLOC_DELETED_CHAIN(FfmpegBuffer);
     INLINE FfmpegBuffer(size_t block_size, double video_timebase);
@@ -215,5 +212,4 @@ private:
 
 #include "ffmpegVideoCursor.I"
 
-#endif // HAVE_FFMPEG
 #endif // FFMPEGVIDEO_H

+ 0 - 0
panda/src/movies/ffmpegVirtualFile.I → panda/src/ffmpeg/ffmpegVirtualFile.I


+ 0 - 4
panda/src/movies/ffmpegVirtualFile.cxx → panda/src/ffmpeg/ffmpegVirtualFile.cxx

@@ -14,8 +14,6 @@
 
 #include "pandabase.h"
 
-#ifdef HAVE_FFMPEG
-
 #include "config_movies.h"
 #include "ffmpegVirtualFile.h"
 #include "virtualFileSystem.h"
@@ -349,5 +347,3 @@ log_callback(void *ptr, int level, const char *fmt, va_list v1) {
       << buffer;
   }
 }
-
-#endif // HAVE_FFMPEG

+ 1 - 5
panda/src/movies/ffmpegVirtualFile.h → panda/src/ffmpeg/ffmpegVirtualFile.h

@@ -16,9 +16,6 @@
 #define FFMPEGVIRTUALFILE_H
 
 #include "pandabase.h"
-
-#ifdef HAVE_FFMPEG
-
 #include "config_movies.h"
 #include "filename.h"
 #include "subfileInfo.h"
@@ -37,7 +34,7 @@ struct AVFormatContext;
 //               instance of the FfmpegVirtualFile for each ffmpeg
 //               stream you wish to open.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_MOVIES FfmpegVirtualFile {
+class EXPCL_FFMPEG FfmpegVirtualFile {
 public:
   FfmpegVirtualFile();
   ~FfmpegVirtualFile();
@@ -76,5 +73,4 @@ private:
 
 #include "ffmpegVirtualFile.I"
 
-#endif // HAVE_FFMPEG
 #endif // FFMPEGVIRTUALFILE_H

+ 1 - 0
panda/src/movies/p3movies_composite2.cxx → panda/src/ffmpeg/p3ffmpeg_composite1.cxx

@@ -1,3 +1,4 @@
+#include "config_ffmpeg.cxx"
 #include "ffmpegAudio.cxx"
 #include "ffmpegVideo.cxx"
 #include "ffmpegVirtualFile.cxx"

+ 0 - 2
panda/src/grutil/Sources.pp

@@ -1,8 +1,6 @@
 #define OTHER_LIBS p3interrogatedb:c p3dconfig:c p3dtoolconfig:m \
                    p3dtoolutil:c p3dtoolbase:c p3dtool:m p3prc:c
 
-#define USE_PACKAGES ffmpeg
-
 #begin lib_target
   #define TARGET p3grutil
   #define LOCAL_LIBS \

+ 1 - 7
panda/src/grutil/config_grutil.cxx

@@ -118,14 +118,8 @@ init_libgrutil() {
 #ifdef HAVE_AUDIO
   MovieTexture::init_type();
   MovieTexture::register_with_read_factory();
-#endif  // HAVE_AUDIO
-
-#ifdef HAVE_FFMPEG
-  MovieTexture::init_type();
-  MovieTexture::register_with_read_factory();
 
   TexturePool *ts = TexturePool::get_global_ptr();
   ts->register_texture_type(MovieTexture::make_texture, "avi mov mpg mpeg mp4 wmv asf flv nut ogm mkv ogv");
-#endif  // HAVE_FFMPEG
+#endif
 }
-

+ 1 - 0
panda/src/grutil/movieTexture.h

@@ -20,6 +20,7 @@
 #ifdef HAVE_AUDIO
 
 #include "movieVideo.h"
+#include "movieVideoCursor.h"
 #include "audioSound.h"
 #include "pipelineCycler.h"
 #include "cycleData.h"

+ 38 - 39
panda/src/movies/Sources.pp

@@ -1,68 +1,67 @@
 #define OTHER_LIBS p3interrogatedb:c p3dconfig:c p3dtoolconfig:m \
                    p3dtoolutil:c p3dtoolbase:c p3dtool:m p3prc:c
 
-#define USE_PACKAGES ffmpeg dx9
-#define WIN_SYS_LIBS strmiids.lib
 
 #begin lib_target
   #define TARGET p3movies
-  #define LOCAL_LIBS \
-        p3gobj
-    
+  #define LOCAL_LIBS p3gobj
+
+  #define USE_PACKAGES dx9 vorbis
+  #define WIN_SYS_LIBS $[WIN_SYS_LIBS] strmiids.lib
+
   #define COMBINED_SOURCES \
-    $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx 
+    $[TARGET]_composite1.cxx
 
   #define SOURCES \
+    config_movies.h \
+    inkblotVideo.h inkblotVideo.I \
+    inkblotVideoCursor.h inkblotVideoCursor.I \
+    microphoneAudio.h microphoneAudio.I \
     movieAudio.h movieAudio.I \
     movieAudioCursor.h movieAudioCursor.I \
+    movieTypeRegistry.h movieTypeRegistry.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 \
     userDataAudio.h userDataAudio.I \
     userDataAudioCursor.h userDataAudioCursor.I \
-    microphoneAudio.h microphoneAudio.I \
-    config_movies.h
-    
+    vorbisAudio.h vorbisAudio.I \
+    vorbisAudioCursor.h vorbisAudioCursor.I \
+    wavAudio.h wavAudio.I \
+    wavAudioCursor.h wavAudioCursor.I
+
   #define INCLUDED_SOURCES \
-    movieVideo.cxx \
-    movieVideoCursor.cxx \
-    movieAudio.cxx \
-    movieAudioCursor.cxx \
+    config_movies.cxx \
     inkblotVideo.cxx \
     inkblotVideoCursor.cxx \
-    ffmpegVideo.cxx \
-    ffmpegVideoCursor.cxx \
-    ffmpegAudio.cxx \
-    ffmpegAudioCursor.cxx \
-    ffmpegVirtualFile.cxx \
-    userDataAudio.cxx \
-    userDataAudioCursor.cxx \
     microphoneAudio.cxx \
     microphoneAudioDS.cxx \
-    config_movies.cxx
-    
+    movieAudio.cxx \
+    movieAudioCursor.cxx \
+    movieTypeRegistry.cxx \
+    movieVideo.cxx \
+    movieVideoCursor.cxx \
+    userDataAudio.cxx \
+    userDataAudioCursor.cxx \
+    vorbisAudio.cxx \
+    vorbisAudioCursor.cxx \
+    wavAudio.cxx \
+    wavAudioCursor.cxx
+
   #define INSTALL_HEADERS \
+    config_movies.h \
+    inkblotVideo.h inkblotVideo.I \
+    inkblotVideoCursor.h inkblotVideoCursor.I \
+    microphoneAudio.h microphoneAudio.I \
     movieAudio.h movieAudio.I \
     movieAudioCursor.h movieAudioCursor.I \
+    movieTypeRegistry.h movieTypeRegistry.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 \
-    microphoneAudio.h microphoneAudio.I \
-    config_movies.h
+    vorbisAudio.h vorbisAudio.I \
+    vorbisAudioCursor.h vorbisAudioCursor.I \
+    wavAudio.h wavAudio.I \
+    wavAudioCursor.h wavAudioCursor.I
 
   #define IGATESCAN all
 
 #end lib_target
-

+ 45 - 74
panda/src/movies/config_movies.cxx

@@ -14,78 +14,48 @@
 
 #include "config_movies.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"
+#include "microphoneAudio.h"
+#include "movieAudio.h"
+#include "movieAudioCursor.h"
+#include "movieTypeRegistry.h"
+#include "movieVideo.h"
+#include "movieVideoCursor.h"
 #include "userDataAudio.h"
 #include "userDataAudioCursor.h"
-#include "microphoneAudio.h"
-
-#ifdef HAVE_FFMPEG
-extern "C" {
-  #include "libavcodec/avcodec.h"
-}
-#endif
+#include "vorbisAudio.h"
+#include "vorbisAudioCursor.h"
+#include "wavAudio.h"
+#include "wavAudioCursor.h"
 
 ConfigureDef(config_movies);
 NotifyCategoryDef(movies, "");
-NotifyCategoryDef(ffmpeg, "movies");
 
 ConfigureFn(config_movies) {
   init_libmovies();
 }
 
-ConfigVariableInt ffmpeg_max_readahead_frames
-("ffmpeg-max-readahead-frames", 2,
- PRC_DESC("The maximum number of frames ahead which an ffmpeg decoder thread "
-          "should read in advance of actual playback.  Set this to 0 to "
-          "decode ffmpeg videos in the main thread."));
-
-ConfigVariableBool ffmpeg_show_seek_frames
-("ffmpeg-show-seek-frames", true,
- PRC_DESC("Set this true to allow showing the intermediate results of seeking "
-          "through the ffmpeg stream to a target frame, or false to hold the "
-          "current frame until the target frame is achieved.  This has the "
-          "biggest effect on videos that are too expensive to decode in real "
-          "time: when this is true, the video can be seen to animate at least "
-          "a little bit; when it is false, you may get long periods of one "
-          "held frame."));
-
-ConfigVariableBool ffmpeg_support_seek
-("ffmpeg-support-seek", true,
- PRC_DESC("True to use the av_seek_frame() function to seek within ffmpeg "
-          "video files.  If this is false, Panda will only seek within a "
-          "file by reading it from the beginning until the desired point, "
-          "which can be much slower.  Set this false only if you suspect "
-          "a problem with av_seek_frame()."));
-
-ConfigVariableBool ffmpeg_global_lock
-("ffmpeg-global-lock", false,
- PRC_DESC("Set this true to enable a single global mutex across *all* ffmpeg "
-          "operations.  Leave this false to use the mutex only for "
-          "the ffmpeg operations that are generally known to be "
-          "not thread-safe.  This will negatively affect ffmpeg performance, "
-          "especially when decoding multiple videos at once (including the "
-          "left and right channels of a stereo video).  Set this true only "
-          "if you suspect a problem with ffmpeg's own thread-safe nature."));
-
-ConfigVariableEnum<ThreadPriority> ffmpeg_thread_priority
-("ffmpeg-thread-priority", TP_normal,
- PRC_DESC("The default thread priority at which to start ffmpeg decoder "
-          "threads."));
-
-ConfigVariableInt ffmpeg_read_buffer_size
-("ffmpeg-read-buffer-size", 4096,
- PRC_DESC("The size in bytes of the buffer used when reading input files. "
-          "This is important for performance.  A typical size is that of a "
-          "cache page, e.g. 4kb."));
+ConfigVariableList load_audio_type
+("load-audio-type",
+ PRC_DESC("List the audio loader modules that Panda will automatically "
+          "import when a new, unknown audio type is loaded.  This may be "
+          "either the name of a module, or a space-separate list of filename "
+          "extensions, followed by the name of the module."));
+
+ConfigVariableList load_video_type
+("load-video-type",
+ PRC_DESC("List the video loader modules that Panda will automatically "
+          "import when a new, unknown video type is loaded.  This may be "
+          "either the name of a module, or a space-separate list of filename "
+          "extensions, followed by the name of the module."));
+
+ConfigVariableBool vorbis_seek_lap
+("vorbis-seek-lap", true,
+ PRC_DESC("If this is set to true, the Ogg Vorbis decoder will automatically "
+          "crosslap the transition from the previous playback position into "
+          "the new playback position when seeking in order to eliminate "
+          "clicking and boundary discontinuities."));
 
 ////////////////////////////////////////////////////////////////////
 //     Function: init_libmovies
@@ -103,26 +73,27 @@ init_libmovies() {
   }
   initialized = true;
 
-  MovieVideo::init_type();
-  MovieVideoCursor::init_type();
-  MovieAudio::init_type();
-  MovieAudioCursor::init_type();
   InkblotVideo::init_type();
   InkblotVideoCursor::init_type();
+  MicrophoneAudio::init_type();
+  MovieAudio::init_type();
+  MovieAudioCursor::init_type();
+  MovieVideo::init_type();
+  MovieVideoCursor::init_type();
   UserDataAudio::init_type();
   UserDataAudioCursor::init_type();
-  MicrophoneAudio::init_type();
+  WavAudio::init_type();
+  WavAudioCursor::init_type();
 
-#ifdef HAVE_FFMPEG
-  FfmpegVirtualFile::register_protocol();
+#ifdef HAVE_VORBIS
+  VorbisAudio::init_type();
+  VorbisAudioCursor::init_type();
+#endif
 
-  FfmpegVideo::init_type();
-  FfmpegVideoCursor::init_type();
-  FfmpegAudio::init_type();
-  FfmpegAudioCursor::init_type();
+  MovieTypeRegistry *reg = MovieTypeRegistry::get_global_ptr();
+  reg->register_audio_type(&WavAudio::make, "wav wave");
 
-  FfmpegVideo::register_with_read_factory();
-  FfmpegVideoCursor::register_with_read_factory();
+#ifdef HAVE_VORBIS
+  reg->register_audio_type(&VorbisAudio::make, "ogg oga");
 #endif
 }
-

+ 8 - 12
panda/src/movies/config_movies.h

@@ -12,28 +12,24 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#ifndef __CONFIG_MOVIES_H__
-#define __CONFIG_MOVIES_H__
+#ifndef CONFIG_MOVIES_H
+#define CONFIG_MOVIES_H
 
 #include "pandabase.h"
 #include "notifyCategoryProxy.h"
-#include "configVariableEnum.h"
-#include "configVariableInt.h"
 #include "configVariableBool.h"
+#include "configVariableList.h"
 #include "threadPriority.h"
 #include "dconfig.h"
 
 ConfigureDecl(config_movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES);
 NotifyCategoryDecl(movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES);
-NotifyCategoryDecl(ffmpeg, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES);
 
-extern ConfigVariableInt ffmpeg_max_readahead_frames;
-extern ConfigVariableBool ffmpeg_show_seek_frames;
-extern ConfigVariableBool ffmpeg_support_seek;
-extern ConfigVariableBool ffmpeg_global_lock;
-extern ConfigVariableEnum<ThreadPriority> ffmpeg_thread_priority;
-extern ConfigVariableInt ffmpeg_read_buffer_size;
+extern ConfigVariableList load_audio_type;
+extern ConfigVariableList load_video_type;
+
+extern ConfigVariableBool vorbis_seek_lap;
 
 extern EXPCL_PANDA_MOVIES void init_libmovies();
 
-#endif /* __CONFIG_MOVIES_H__ */
+#endif /* CONFIG_MOVIES_H */

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

@@ -19,6 +19,7 @@
 #include "texture.h"
 #include "pointerTo.h"
 #include "inkblotVideo.h"
+#include "movieVideoCursor.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : InkblotVideoCursor

+ 1 - 1
panda/src/movies/microphoneAudio.h

@@ -52,7 +52,7 @@ protected:
     return _type_handle;
   }
   static void init_type() {
-    TypedWritableReferenceCount::init_type();
+    MovieAudio::init_type();
     register_type(_type_handle, "MicrophoneAudio",
                   MovieAudio::get_class_type());
   }

+ 5 - 8
panda/src/movies/movieAudio.cxx

@@ -14,7 +14,8 @@
 
 #include "movieAudio.h"
 #include "movieAudioCursor.h"
-#include "ffmpegAudio.h"
+#include "config_movies.h"
+#include "movieTypeRegistry.h"
 
 TypeHandle MovieAudio::_type_handle;
 
@@ -55,14 +56,10 @@ open() {
 //     Function: MovieAudio::get
 //       Access: Published, Static
 //  Description: Obtains a MovieAudio that references a file.
+//               Just calls MovieTypeRegistry::make_audio().
 ////////////////////////////////////////////////////////////////////
 PT(MovieAudio) MovieAudio::
 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 new MovieAudio("Load-Failure Stub");
-#endif
+  MovieTypeRegistry *reg = MovieTypeRegistry::get_global_ptr();
+  return reg->make_audio(name);
 }

+ 2 - 6
panda/src/movies/movieAudio.h

@@ -19,8 +19,8 @@
 #include "namable.h"
 #include "pointerTo.h"
 #include "typedWritableReferenceCount.h"
-class MovieAudioCursor;
 
+class MovieAudioCursor;
 
 #ifdef NOTIFY_DEBUG //[
   // Non-release build:
@@ -33,7 +33,6 @@ class MovieAudioCursor;
   #define movies_debug(msg) ((void)0);
 #endif //]
 
-
 ////////////////////////////////////////////////////////////////////
 //       Class : MovieAudio
 // Description : A MovieAudio is actually any source that provides
@@ -47,12 +46,12 @@ class MovieAudioCursor;
 //               is what allows access.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_MOVIES MovieAudio : public TypedWritableReferenceCount, public Namable {
-
  PUBLISHED:
   MovieAudio(const string &name = "Blank Audio");
   virtual ~MovieAudio();
   virtual PT(MovieAudioCursor) open();
   static PT(MovieAudio) get(const Filename &name);
+
   INLINE const Filename &get_filename() const;
 
  protected:
@@ -76,9 +75,6 @@ class EXPCL_PANDA_MOVIES MovieAudio : public TypedWritableReferenceCount, public
   static TypeHandle _type_handle;
 };
 
-/* okcircular */
-#include "movieAudioCursor.h"
-
 #include "movieAudio.I"
 
 #endif

+ 29 - 0
panda/src/movies/movieTypeRegistry.I

@@ -0,0 +1,29 @@
+// Filename: movieTypeRegistry.I
+// Created by:  rdb (24Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTypeRegistry::get_global_ptr
+//       Access: Published, Static
+//  Description: Returns a pointer to the global MovieTypeRegistry
+//               instance.
+////////////////////////////////////////////////////////////////////
+INLINE MovieTypeRegistry *MovieTypeRegistry::
+get_global_ptr() {
+  if (_global_ptr == NULL) {
+  	_global_ptr = new MovieTypeRegistry;
+  }
+
+  return _global_ptr;
+}

+ 290 - 0
panda/src/movies/movieTypeRegistry.cxx

@@ -0,0 +1,290 @@
+// Filename: movieTypeRegistry.cxx
+// Created by:  rdb (24Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "movieTypeRegistry.h"
+#include "config_movies.h"
+#include "config_util.h"
+#include "load_dso.h"
+
+MovieTypeRegistry *MovieTypeRegistry::_global_ptr = NULL;
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTypeRegistry::make_audio
+//       Access: Published, Static
+//  Description: Obtains a MovieVideo that references a file.
+////////////////////////////////////////////////////////////////////
+PT(MovieAudio) MovieTypeRegistry::
+make_audio(const Filename &name) {
+  string ext = downcase(name.get_extension());
+
+  // Make sure that the list of audio types has been read in.
+  load_audio_types();
+
+  // Maybe we need to load a module?
+  if (_deferred_audio_types.count(ext)) {
+    load_movie_library(_deferred_audio_types[ext]);
+    _deferred_audio_types.erase(ext);
+  }
+
+  // Explicit extension is preferred over catch-all.
+  if (_audio_type_registry.count(ext)) {
+    MakeAudioFunc func = _audio_type_registry[ext];
+    return (*func)(name);
+  }
+
+  // If we didn't find it, see if there was a type registered
+  // with '*' as extension.  This is a catch-all loader.
+  if (_deferred_audio_types.count("*")) {
+    load_movie_library(_deferred_audio_types["*"]);
+    _deferred_audio_types.erase("*");
+  }
+
+  if (_audio_type_registry.count("*")) {
+    MakeAudioFunc func = _audio_type_registry["*"];
+    return (*func)(name);
+  }
+
+  movies_cat.error()
+    << "Support for audio files with extension ." << ext << " was not enabled.\n";
+
+  return new MovieAudio("Load-Failure Stub");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTypeRegistry::register_audio_type
+//       Access: Published, Static
+//  Description: Registers a MovieAudio type, so that files with
+//               any of the given extensions will be loaded as this
+//               type.  You may use * as a catch-all extension.
+////////////////////////////////////////////////////////////////////
+void MovieTypeRegistry::
+register_audio_type(MakeAudioFunc func, const string &extensions) {
+  vector_string words;
+  extract_words(downcase(extensions), words);
+
+  vector_string::const_iterator wi;
+  for (wi = words.begin(); wi != words.end(); ++wi) {
+    if (_audio_type_registry.count(*wi)) {
+      movies_cat->warning()
+        << "Attempt to register multiple audio types with extension " << (*wi) << "\n";
+    } else {
+      movies_cat->debug()
+        << "Registered audio type with extension " << (*wi) << "\n";
+    }
+    _audio_type_registry[*wi] = func;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTypeRegistry::load_audio_types
+//       Access: Published, Static
+//  Description: Loads the list with audio types, if we haven't
+//               already.
+////////////////////////////////////////////////////////////////////
+void MovieTypeRegistry::
+load_audio_types() {
+  static bool audio_types_loaded = false;
+
+  if (!audio_types_loaded) {
+    int num_unique_values = load_audio_type.get_num_unique_values();
+
+    for (int i = 0; i < num_unique_values; i++) {
+      string param = load_audio_type.get_unique_value(i);
+
+      vector_string words;
+      extract_words(param, words);
+
+      if (words.size() == 1) {
+        // Exactly one word: load the named library immediately.
+        string name = words[0];
+        Filename dlname = Filename::dso_filename("lib" + name + ".so");
+        movies_cat.info()
+          << "loading audio type module: " << name << endl;
+        void *tmp = load_dso(get_plugin_path().get_value(), dlname);
+        if (tmp == (void *)NULL) {
+          movies_cat.warning()
+            << "Unable to load " << dlname.to_os_specific()
+            << ": " << load_dso_error() << endl;
+        } else if (movies_cat.is_debug()) {
+          movies_cat.debug()
+            << "done loading audio type module: " << name << endl;
+        }
+
+      } else if (words.size() > 1) {
+        // Multiple words: the first n words are filename extensions,
+        // and the last word is the name of the library to load should
+        // any of those filename extensions be encountered.
+        size_t num_extensions = words.size() - 1;
+        string library_name = words[num_extensions];
+
+        for (size_t i = 0; i < num_extensions; i++) {
+          string extension = downcase(words[i]);
+          if (extension[0] == '.') {
+            extension = extension.substr(1);
+          }
+
+          _deferred_audio_types[extension] = library_name;
+        }
+      }
+    }
+
+    audio_types_loaded = true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTypeRegistry::make_video
+//       Access: Published, Static
+//  Description: Obtains a MovieVideo that references a file.
+////////////////////////////////////////////////////////////////////
+PT(MovieVideo) MovieTypeRegistry::
+make_video(const Filename &name) {
+  string ext = downcase(name.get_extension());
+
+  // Make sure that the list of video types has been read in.
+  load_video_types();
+
+  // Maybe we need to load a module?
+  if (_deferred_video_types.count(ext)) {
+    load_movie_library(_deferred_video_types[ext]);
+    _deferred_video_types.erase(ext);
+  }
+
+  // Explicit extension is preferred over catch-all.
+  if (_video_type_registry.count(ext)) {
+    MakeVideoFunc func = _video_type_registry[ext];
+    return (*func)(name);
+  }
+
+  // If we didn't find it, see if there was a type registered
+  // with '*' as extension.  This is a catch-all loader.
+  if (_deferred_video_types.count("*")) {
+    load_movie_library(_deferred_video_types["*"]);
+    _deferred_video_types.erase("*");
+  }
+
+  if (_video_type_registry.count("*")) {
+    MakeVideoFunc func = _video_type_registry["*"];
+    return (*func)(name);
+  }
+
+  movies_cat.error()
+    << "Support for video files with extension ." << ext << " was not enabled.\n";
+
+  return new MovieVideo("Load-Failure Stub");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTypeRegistry::register_video_type
+//       Access: Published, Static
+//  Description: Registers a MovieVideo type, so that files with
+//               any of the given extensions will be loaded as this
+//               type.  You may use * as a catch-all extension.
+////////////////////////////////////////////////////////////////////
+void MovieTypeRegistry::
+register_video_type(MakeVideoFunc func, const string &extensions) {
+  vector_string words;
+  extract_words(downcase(extensions), words);
+
+  vector_string::const_iterator wi;
+  for (wi = words.begin(); wi != words.end(); ++wi) {
+    if (_video_type_registry.count(*wi)) {
+      movies_cat->warning()
+        << "Attempt to register multiple video types with extension " << (*wi) << "\n";
+    } else {
+      movies_cat->debug()
+        << "Registered video type with extension " << (*wi) << "\n";
+    }
+    _video_type_registry[*wi] = func;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTypeRegistry::load_video_types
+//       Access: Published, Static
+//  Description: Loads the list with video types, if we haven't
+//               already.
+////////////////////////////////////////////////////////////////////
+void MovieTypeRegistry::
+load_video_types() {
+  static bool video_types_loaded = false;
+
+  if (!video_types_loaded) {
+    int num_unique_values = load_video_type.get_num_unique_values();
+
+    for (int i = 0; i < num_unique_values; i++) {
+      string param = load_video_type.get_unique_value(i);
+
+      vector_string words;
+      extract_words(param, words);
+
+      if (words.size() == 1) {
+        // Exactly one word: load the named library immediately.
+        string name = words[0];
+        Filename dlname = Filename::dso_filename("lib" + name + ".so");
+        movies_cat.info()
+          << "loading video type module: " << name << endl;
+        void *tmp = load_dso(get_plugin_path().get_value(), dlname);
+        if (tmp == (void *)NULL) {
+          movies_cat.warning()
+            << "Unable to load " << dlname.to_os_specific()
+            << ": " << load_dso_error() << endl;
+        } else if (movies_cat.is_debug()) {
+          movies_cat.debug()
+            << "done loading video type module: " << name << endl;
+        }
+
+      } else if (words.size() > 1) {
+        // Multiple words: the first n words are filename extensions,
+        // and the last word is the name of the library to load should
+        // any of those filename extensions be encountered.
+        size_t num_extensions = words.size() - 1;
+        string library_name = words[num_extensions];
+
+        for (size_t i = 0; i < num_extensions; i++) {
+          string extension = downcase(words[i]);
+          if (extension[0] == '.') {
+            extension = extension.substr(1);
+          }
+
+          _deferred_video_types[extension] = library_name;
+        }
+      }
+    }
+
+    video_types_loaded = true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieTypeRegistry::load_library
+//       Access: Published, Static
+//  Description: Loads the module.
+////////////////////////////////////////////////////////////////////
+void MovieTypeRegistry::
+load_movie_library(const string &name) {
+  Filename dlname = Filename::dso_filename("lib" + name + ".so");
+  movies_cat.info()
+    << "loading video type module: " << name << endl;
+  void *tmp = load_dso(get_plugin_path().get_value(), dlname);
+
+  if (tmp == (void *)NULL) {
+    movies_cat.warning()
+      << "Unable to load " << dlname.to_os_specific()
+      << ": " << load_dso_error() << endl;
+  } else if (movies_cat.is_debug()) {
+    movies_cat.debug()
+      << "done loading video type module: " << name << endl;
+  }
+}

+ 57 - 0
panda/src/movies/movieTypeRegistry.h

@@ -0,0 +1,57 @@
+// Filename: movieTypeRegistry.h
+// Created by:  rdb (24Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef MOVIETYPEREGISTRY_H
+#define MOVIETYPEREGISTRY_H
+
+#include "pandabase.h"
+#include "movieAudio.h"
+#include "movieVideo.h"
+#include "filename.h"
+#include "pmap.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : MovieTypeRegistry
+// Description : This class records the different types of MovieAudio
+//               and MovieVideo that are available for loading.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES MovieTypeRegistry {
+public:
+  typedef PT(MovieAudio) (*MakeAudioFunc)(const Filename&);
+  PT(MovieAudio) make_audio(const Filename &name);
+  void register_audio_type(MakeAudioFunc func, const string &extensions);
+  void load_audio_types();
+
+  typedef PT(MovieVideo) (*MakeVideoFunc)(const Filename&);
+  PT(MovieVideo) make_video(const Filename &name);
+  void register_video_type(MakeVideoFunc func, const string &extensions);
+  void load_video_types();
+
+  void load_movie_library(const string &name);
+
+  INLINE static MovieTypeRegistry *get_global_ptr();
+  
+private:
+  static MovieTypeRegistry *_global_ptr;
+
+  pmap<string, MakeAudioFunc> _audio_type_registry;
+  pmap<string, string> _deferred_audio_types;
+
+  pmap<string, MakeVideoFunc> _video_type_registry;
+  pmap<string, string> _deferred_video_types;
+};
+
+#include "movieTypeRegistry.I"
+
+#endif

+ 5 - 9
panda/src/movies/movieVideo.cxx

@@ -13,8 +13,9 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "movieVideo.h"
+#include "movieVideoCursor.h"
 #include "config_movies.h"
-#include "ffmpegVideo.h"
+#include "movieTypeRegistry.h"
 #include "bamReader.h"
 #include "bamWriter.h"
 
@@ -58,19 +59,14 @@ open() {
 //     Function: MovieVideo::get
 //       Access: Published, Static
 //  Description: Obtains a MovieVideo that references a file.
+//               Just calls MovieTypeRegistry::make_video().
 ////////////////////////////////////////////////////////////////////
 PT(MovieVideo) MovieVideo::
 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 new MovieVideo("Load-Failure Stub");
-#endif
+  MovieTypeRegistry *reg = MovieTypeRegistry::get_global_ptr();
+  return reg->make_video(name);
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: MovieVideo::write_datagram
 //       Access: Public, Virtual

+ 0 - 4
panda/src/movies/movieVideo.h

@@ -40,7 +40,6 @@ class BamReader;
 //               is what allows access.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_MOVIES MovieVideo : public TypedWritableReferenceCount, public Namable {
-
 PUBLISHED:
   MovieVideo(const string &name = "Blank Video");
   virtual ~MovieVideo();
@@ -78,9 +77,6 @@ private:
   static TypeHandle _type_handle;
 };
 
-/* okcircular */
-#include "movieVideoCursor.h"
-
 #include "movieVideo.I"
 
 #endif

+ 12 - 7
panda/src/movies/p3movies_composite1.cxx

@@ -1,11 +1,16 @@
-#include "movieVideo.cxx"
-#include "movieVideoCursor.cxx"
-#include "movieAudio.cxx"
-#include "movieAudioCursor.cxx"
+#include "config_movies.cxx"
 #include "inkblotVideo.cxx"
 #include "inkblotVideoCursor.cxx"
-#include "userDataAudio.cxx"
-#include "userDataAudioCursor.cxx"
 #include "microphoneAudio.cxx"
 #include "microphoneAudioDS.cxx"
-#include "config_movies.cxx"
+#include "movieAudio.cxx"
+#include "movieAudioCursor.cxx"
+#include "movieTypeRegistry.cxx"
+#include "movieVideo.cxx"
+#include "movieVideoCursor.cxx"
+#include "userDataAudio.cxx"
+#include "userDataAudioCursor.cxx"
+#include "vorbisAudio.cxx"
+#include "vorbisAudioCursor.cxx"
+#include "wavAudio.cxx"
+#include "wavAudioCursor.cxx"

+ 14 - 0
panda/src/movies/vorbisAudio.I

@@ -0,0 +1,14 @@
+// Filename: wavAudio.I
+// Created by: rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 77 - 0
panda/src/movies/vorbisAudio.cxx

@@ -0,0 +1,77 @@
+// Filename: vorbisAudio.cxx
+// Created by: rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "vorbisAudio.h"
+#include "vorbisAudioCursor.h"
+#include "virtualFileSystem.h"
+#include "dcast.h"
+
+#ifdef HAVE_VORBIS
+
+TypeHandle VorbisAudio::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: VorbisAudio::Constructor
+//       Access: Protected
+//  Description: xxx
+////////////////////////////////////////////////////////////////////
+VorbisAudio::
+VorbisAudio(const Filename &name) :
+  MovieAudio(name)
+{
+  _filename = name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VorbisAudio::Destructor
+//       Access: Protected, Virtual
+//  Description: xxx
+////////////////////////////////////////////////////////////////////
+VorbisAudio::
+~VorbisAudio() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VorbisAudio::open
+//       Access: Published, Virtual
+//  Description: Open this audio, returning a MovieAudioCursor
+////////////////////////////////////////////////////////////////////
+PT(MovieAudioCursor) VorbisAudio::
+open() {
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  istream *stream = vfs->open_read_file(_filename, true);
+
+  if (stream == NULL) {
+    return NULL;
+  } else {
+    PT(VorbisAudioCursor) cursor = new VorbisAudioCursor(this, stream);
+    if (cursor == NULL || !cursor->_is_valid) {
+      return NULL;
+    } else {
+      return DCAST(MovieAudioCursor, cursor);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VorbisAudio::make
+//       Access: Published, Static
+//  Description: Obtains a MovieAudio that references a file.
+////////////////////////////////////////////////////////////////////
+PT(MovieAudio) VorbisAudio::
+make(const Filename &name) {
+  return DCAST(MovieAudio, new VorbisAudio(name));
+}
+
+#endif // HAVE_VORBIS

+ 63 - 0
panda/src/movies/vorbisAudio.h

@@ -0,0 +1,63 @@
+// Filename: vorbisAudio.h
+// Created by: rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef VORBISAUDIO_H
+#define VORBISAUDIO_H
+
+#include "pandabase.h"
+#include "movieAudio.h"
+
+#ifdef HAVE_VORBIS
+
+class VorbisAudioCursor;
+
+////////////////////////////////////////////////////////////////////
+//       Class : VorbisAudio
+// Description : Interfaces with the libvorbisfile library to
+//               implement decoding of Ogg Vorbis audio files.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES VorbisAudio : public MovieAudio {
+PUBLISHED:
+  VorbisAudio(const Filename &name);
+  virtual ~VorbisAudio();
+  virtual PT(MovieAudioCursor) open();
+
+  static PT(MovieAudio) make(const Filename &name);
+
+private:
+  friend class VorbisAudioCursor;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWritableReferenceCount::init_type();
+    register_type(_type_handle, "VorbisAudio",
+                  MovieAudio::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 "vorbisAudio.I"
+
+#endif // HAVE_VORBIS
+
+#endif // VORBISAUDIO_H

+ 14 - 0
panda/src/movies/vorbisAudioCursor.I

@@ -0,0 +1,14 @@
+// Filename: wavAudioCursor.I
+// Created by: rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 235 - 0
panda/src/movies/vorbisAudioCursor.cxx

@@ -0,0 +1,235 @@
+// Filename: vorbisAudioCursor.cxx
+// Created by: rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "vorbisAudioCursor.h"
+#include "virtualFileSystem.h"
+
+#ifdef HAVE_VORBIS
+
+TypeHandle VorbisAudioCursor::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: VorbisAudioCursor::Constructor
+//       Access: Protected
+//  Description: Reads the .wav header from the indicated stream.
+//               This leaves the read pointer positioned at the
+//               start of the data.
+////////////////////////////////////////////////////////////////////
+VorbisAudioCursor::
+VorbisAudioCursor(VorbisAudio *src, istream *stream) :
+  MovieAudioCursor(src),
+  _is_valid(false),
+  _bitstream(0)
+{
+  nassertv(stream != NULL);
+
+  // Set up the callbacks to read via the VFS.
+  ov_callbacks callbacks;
+  callbacks.read_func = &cb_read_func;
+  callbacks.seek_func = &cb_seek_func;
+  callbacks.close_func = &cb_close_func;
+  callbacks.tell_func = &cb_tell_func;
+
+  if (ov_open_callbacks((void*) stream, &_ov, NULL, 0, callbacks) != 0) {
+    movies_cat.error()
+      << "Failed to read Ogg Vorbis file.\n";
+    return;
+  }
+
+  _length = ov_time_total(&_ov, -1);
+
+  vorbis_info *vi = ov_info(&_ov, -1);
+  _audio_channels = vi->channels;
+  _audio_rate = vi->rate;
+
+  _can_seek = (ov_seekable(&_ov) != 0);
+  _can_seek_fast = _can_seek;
+
+  _is_valid = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VorbisAudioCursor::Destructor
+//       Access: Protected, Virtual
+//  Description: xxx
+////////////////////////////////////////////////////////////////////
+VorbisAudioCursor::
+~VorbisAudioCursor() {
+  ov_clear(&_ov);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VorbisAudioCursor::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 VorbisAudioCursor::
+seek(double t) {
+  t = max(t, 0.0);
+
+  // Use ov_time_seek_lap if cross-lapping is enabled.
+  if (vorbis_seek_lap) {
+    if (ov_time_seek_lap(&_ov, t) != 0) {
+      movies_cat.error()
+        << "Seek failed.  Ogg Vorbis stream may not be seekable.\n";
+      return;
+    }
+  } else {
+    if (ov_time_seek(&_ov, t) != 0) {
+      movies_cat.error()
+        << "Seek failed.  Ogg Vorbis stream may not be seekable.\n";
+      return;
+    }
+  }
+
+  _last_seek = ov_time_tell(&_ov);
+  _samples_read = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VorbisAudioCursor::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 VorbisAudioCursor::
+read_samples(int n, PN_int16 *data) {
+  int desired = n * _audio_channels;
+
+  char *buffer = (char*) data;
+  int length = desired * 2;
+
+  // Call ov_read repeatedly until the buffer is full.
+  while (length > 0) {
+    int bitstream;
+
+    // ov_read can give it to us in the exact format we need.  Nifty!
+    long read_bytes = ov_read(&_ov, buffer, length, 0, 2, 1, &bitstream);
+    if (read_bytes > 0) {
+      buffer += read_bytes;
+      length -= read_bytes;
+    } else {
+      break;
+    }
+
+    if (_bitstream != bitstream) {
+      // It is technically possible for it to change parameters from one
+      // bitstream to the next.  However, we don't offer this flexibility.
+      vorbis_info *vi = ov_info(&_ov, -1);
+      if (vi->channels != _audio_channels || vi->rate != _audio_rate) {
+        movies_cat.error()
+          << "Ogg Vorbis file has non-matching bitstreams!\n";
+
+        // We'll change it anyway.  Not sure what happens next.
+        _audio_channels = vi->channels;
+        _audio_rate = vi->rate;
+        break;
+      }
+
+      _bitstream = bitstream;
+    }
+  }
+
+  // Fill the rest of the buffer with silence.
+  if (length > 0) {
+    memset(buffer, 0, length);
+    n -= length / 2 / _audio_channels;
+  }
+
+  _samples_read += n;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VorbisAudioCursor::cb_read_func
+//       Access: Private, Static
+//  Description: Callback passed to libvorbisfile to implement
+//               file I/O via the VirtualFileSystem.
+////////////////////////////////////////////////////////////////////
+size_t VorbisAudioCursor::
+cb_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) {
+  istream *stream = (istream*) datasource;
+
+  stream->read((char *)ptr, size * nmemb);
+  return stream->gcount();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VorbisAudioCursor::cb_seek_func
+//       Access: Private, Static
+//  Description: Callback passed to libvorbisfile to implement
+//               file I/O via the VirtualFileSystem.
+////////////////////////////////////////////////////////////////////
+int VorbisAudioCursor::
+cb_seek_func(void *datasource, ogg_int64_t offset, int whence) {
+  istream *stream = (istream*) datasource;
+
+  switch (whence) {
+  case SEEK_SET:
+    stream->seekg(offset, ios::beg);
+    break;
+
+  case SEEK_CUR:
+    stream->seekg(offset, ios::cur);
+    break;
+
+  case SEEK_END:
+    stream->seekg(offset, ios::end);
+    break;
+
+  default:
+    movies_cat.error()
+      << "Illegal parameter to seek in VorbisAudioCursor::cb_seek_func\n";
+    return -1;
+  }
+
+  if (stream->fail()) {
+    return -1;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VorbisAudioCursor::cb_close_func
+//       Access: Private, Static
+//  Description: Callback passed to libvorbisfile to implement
+//               file I/O via the VirtualFileSystem.
+////////////////////////////////////////////////////////////////////
+int VorbisAudioCursor::
+cb_close_func(void *datasource) {
+  istream *stream = (istream*) datasource;
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  vfs->close_read_file(stream);
+
+  // Return value isn't checked, but let's be predictable
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VorbisAudioCursor::cb_tell_func
+//       Access: Private, Static
+//  Description: Callback passed to libvorbisfile to implement
+//               file I/O via the VirtualFileSystem.
+////////////////////////////////////////////////////////////////////
+long VorbisAudioCursor::
+cb_tell_func(void *datasource) {
+  istream *stream = (istream*) datasource;
+  return stream->tellg();
+}
+
+#endif // HAVE_VORBIS

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

@@ -0,0 +1,88 @@
+// Filename: vorbisAudioCursor.h
+// Created by: rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef VORBISAUDIOCURSOR_H
+#define VORBISAUDIOCURSOR_H
+
+#include "pandabase.h"
+#include "movieAudioCursor.h"
+
+#ifdef HAVE_VORBIS
+
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+
+class VorbisAudio;
+
+////////////////////////////////////////////////////////////////////
+//       Class : VorbisAudioCursor
+// Description : Interfaces with the libvorbisfile library to
+//               implement decoding of Ogg Vorbis audio files.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES VorbisAudioCursor : public MovieAudioCursor {
+PUBLISHED:
+  VorbisAudioCursor(VorbisAudio *src, istream *stream);
+  virtual ~VorbisAudioCursor();
+  virtual void seek(double offset);
+
+public:
+  virtual void read_samples(int n, PN_int16 *data);
+
+  bool _is_valid;
+
+private:
+  // Callbacks passed to libvorbisfile that read via VFS.
+  static size_t cb_read_func(void *ptr, size_t size, size_t nmemb, void *datasource);
+  static int cb_seek_func(void *datasource, ogg_int64_t offset, int whence);
+  static int cb_close_func(void *datasource);
+  static long cb_tell_func(void *datasource);
+
+protected:
+#ifndef CPPPARSER
+  OggVorbis_File _ov;
+#endif
+
+  int _bitstream;
+  double _byte_rate;
+  int _block_align;
+  int _bytes_per_sample;
+  bool _is_float;
+
+  streampos _data_start;
+  streampos _data_pos;
+  size_t _data_size;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    MovieAudioCursor::init_type();
+    register_type(_type_handle, "VorbisAudioCursor",
+                  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 "vorbisAudioCursor.I"
+
+#endif // HAVE_VORBIS
+
+#endif // VORBISAUDIOCURSOR_H

+ 14 - 0
panda/src/movies/wavAudio.I

@@ -0,0 +1,14 @@
+// Filename: wavAudio.I
+// Created by: rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 73 - 0
panda/src/movies/wavAudio.cxx

@@ -0,0 +1,73 @@
+// Filename: wavAudio.cxx
+// Created by: rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "wavAudio.h"
+#include "wavAudioCursor.h"
+#include "virtualFileSystem.h"
+#include "dcast.h"
+
+TypeHandle WavAudio::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: WavAudio::Constructor
+//       Access: Protected
+//  Description: xxx
+////////////////////////////////////////////////////////////////////
+WavAudio::
+WavAudio(const Filename &name) :
+  MovieAudio(name)
+{
+  _filename = name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WavAudio::Destructor
+//       Access: Protected, Virtual
+//  Description: xxx
+////////////////////////////////////////////////////////////////////
+WavAudio::
+~WavAudio() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WavAudio::open
+//       Access: Published, Virtual
+//  Description: Open this audio, returning a MovieAudioCursor
+////////////////////////////////////////////////////////////////////
+PT(MovieAudioCursor) WavAudio::
+open() {
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  istream *stream = vfs->open_read_file(_filename, true);
+
+  if (stream == NULL) {
+    return NULL;
+  } else {
+    PT(WavAudioCursor) cursor = new WavAudioCursor(this, stream);
+    if (cursor == NULL || !cursor->_is_valid) {
+      return NULL;
+    } else {
+      return DCAST(MovieAudioCursor, cursor);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WavAudio::make
+//       Access: Published, Static
+//  Description: Obtains a MovieAudio that references a file.
+////////////////////////////////////////////////////////////////////
+PT(MovieAudio) WavAudio::
+make(const Filename &name) {
+  return DCAST(MovieAudio, new WavAudio(name));
+}

+ 59 - 0
panda/src/movies/wavAudio.h

@@ -0,0 +1,59 @@
+// Filename: wavAudio.h
+// Created by: rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WAVAUDIO_H
+#define WAVAUDIO_H
+
+#include "pandabase.h"
+#include "movieAudio.h"
+
+class WavAudioCursor;
+
+////////////////////////////////////////////////////////////////////
+//       Class : WavAudio
+// Description : A native PCM .wav loader.  Supported formats
+//               are linear PCM, IEEE float, A-law and mu-law.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES WavAudio : public MovieAudio {
+PUBLISHED:
+  WavAudio(const Filename &name);
+  virtual ~WavAudio();
+  virtual PT(MovieAudioCursor) open();
+
+  static PT(MovieAudio) make(const Filename &name);
+
+private:
+  friend class WavAudioCursor;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWritableReferenceCount::init_type();
+    register_type(_type_handle, "WavAudio",
+                  MovieAudio::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 "wavAudio.I"
+
+#endif

+ 14 - 0
panda/src/movies/wavAudioCursor.I

@@ -0,0 +1,14 @@
+// Filename: wavAudioCursor.I
+// Created by: rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 438 - 0
panda/src/movies/wavAudioCursor.cxx

@@ -0,0 +1,438 @@
+// Filename: wavAudioCursor.cxx
+// Created by: rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "wavAudioCursor.h"
+#include "virtualFileSystem.h"
+
+// Tables for decompressing mu-law and A-law wav files.
+static PN_int16 mulaw_table[256] = {
+  -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
+  -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
+  -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
+  -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
+   -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
+   -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
+   -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+   -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
+   -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
+   -1372, -1308, -1244, -1180, -1116, -1052,  -988,  -924,
+    -876,  -844,  -812,  -780,  -748,  -716,  -684,  -652,
+    -620,  -588,  -556,  -524,  -492,  -460,  -428,  -396,
+    -372,  -356,  -340,  -324,  -308,  -292,  -276,  -260,
+    -244,  -228,  -212,  -196,  -180,  -164,  -148,  -132,
+    -120,  -112,  -104,   -96,   -88,   -80,   -72,   -64,
+     -56,   -48,   -40,   -32,   -24,   -16,    -8,    -1,
+   32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
+   23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
+   15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
+   11900, 11388, 10876, 10364,  9852,  9340,  8828,  8316,
+    7932,  7676,  7420,  7164,  6908,  6652,  6396,  6140,
+    5884,  5628,  5372,  5116,  4860,  4604,  4348,  4092,
+    3900,  3772,  3644,  3516,  3388,  3260,  3132,  3004,
+    2876,  2748,  2620,  2492,  2364,  2236,  2108,  1980,
+    1884,  1820,  1756,  1692,  1628,  1564,  1500,  1436,
+    1372,  1308,  1244,  1180,  1116,  1052,   988,   924,
+     876,   844,   812,   780,   748,   716,   684,   652,
+     620,   588,   556,   524,   492,   460,   428,   396,
+     372,   356,   340,   324,   308,   292,   276,   260,
+     244,   228,   212,   196,   180,   164,   148,   132,
+     120,   112,   104,    96,    88,    80,    72,    64,
+      56,    48,    40,    32,    24,    16,     8,     0
+};
+
+static PN_int16 alaw_table[256] = {
+  -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
+  -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
+  -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
+  -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
+  -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
+  -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
+  -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
+  -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
+  -344,  -328,  -376,  -360,  -280,  -264,  -312,  -296,
+  -472,  -456,  -504,  -488,  -408,  -392,  -440,  -424,
+  -88,   -72,   -120,  -104,  -24,   -8,    -56,   -40,
+  -216,  -200,  -248,  -232,  -152,  -136,  -184,  -168,
+  -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
+  -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
+  -688,  -656,  -752,  -720,  -560,  -528,  -624,  -592,
+  -944,  -912,  -1008, -976,  -816,  -784,  -880,  -848,
+   5504,  5248,  6016,  5760,  4480,  4224,  4992,  4736,
+   7552,  7296,  8064,  7808,  6528,  6272,  7040,  6784,
+   2752,  2624,  3008,  2880,  2240,  2112,  2496,  2368,
+   3776,  3648,  4032,  3904,  3264,  3136,  3520,  3392,
+   22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
+   30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
+   11008, 10496, 12032, 11520, 8960,  8448,  9984,  9472,
+   15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
+   344,   328,   376,   360,   280,   264,   312,   296,
+   472,   456,   504,   488,   408,   392,   440,   424,
+   88,    72,    120,   104,    24,     8,    56,    40,
+   216,   200,   248,   232,   152,   136,   184,   168,
+   1376,  1312,  1504,  1440,  1120,  1056,  1248,  1184,
+   1888,  1824,  2016,  1952,  1632,  1568,  1760,  1696,
+   688,   656,   752,   720,   560,   528,   624,   592,
+   944,   912,   1008,  976,   816,   784,   880,   848
+};
+
+TypeHandle WavAudioCursor::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: WavAudioCursor::Constructor
+//       Access: Protected
+//  Description: Reads the .wav header from the indicated stream.
+//               This leaves the read pointer positioned at the
+//               start of the data.
+////////////////////////////////////////////////////////////////////
+WavAudioCursor::
+WavAudioCursor(WavAudio *src, istream *stream) :
+  MovieAudioCursor(src),
+  _is_valid(false),
+  _stream(stream),
+  _reader(stream, false),
+  _format(F_pcm),
+  _data_pos(0),
+  _data_size(0)
+{
+  nassertv(stream != NULL);
+
+  // Beginning of "RIFF" chunk.
+  if (_reader.extract_bytes(4) != "RIFF") {
+    movies_cat.error()
+      << ".wav file is not a valid RIFF file.\n";
+    return;
+  }
+
+  unsigned int chunk_size = _reader.get_uint32();
+
+  if (_reader.extract_bytes(4) != "WAVE") {
+    movies_cat.error()
+      << ".wav file is a RIFF file but does not start with a WAVE chunk.\n";
+    return;
+  }
+
+  // Find a "fmt " subchunk followed by a "data" subchunk.
+  bool have_fmt = false, have_data = false;
+  unsigned int bytes_read = 4;
+
+  while ((!have_fmt || !have_data) && _stream->good() && (bytes_read + 8) < chunk_size) {
+
+    string subchunk_id = _reader.extract_bytes(4);
+    unsigned int subchunk_size = _reader.get_uint32();
+
+    if (subchunk_id == "fmt ") {
+      // The format chunk specifies information about the storage.
+      nassertv(subchunk_size >= 16);
+      have_fmt = true;
+
+      _format = (Format) _reader.get_uint16();
+
+      _audio_channels = _reader.get_uint16();
+      _audio_rate = _reader.get_uint32();
+
+      _byte_rate = (double) _reader.get_uint32();
+      _block_align = _reader.get_uint16();
+
+      // We can round up to next multiple of 8.
+      PN_uint16 bps = _reader.get_uint16();
+      bps = (bps + 7) & 0xfff8;
+
+      // How many bytes in this chunk we've read so far.
+      unsigned int read_bytes = 16;
+
+      // See if there is an extra header to read.
+      if (_format == F_extensible) {
+        unsigned short ext_size = _reader.get_uint16();
+        read_bytes += 2;
+
+        if (ext_size >= 8) {
+          /*n_valid_bits =*/ _reader.get_uint16();
+          /*speaker_mask =*/ _reader.get_uint32();
+          _format = (Format) _reader.get_uint16();
+
+          read_bytes += 8;
+        }
+      }
+
+      switch (_format) {
+      case F_pcm:
+        if (bps != 8 && bps != 16 && bps != 24 && bps != 32 && bps != 64) {
+          movies_cat.error()
+            << "Unsupported number of bits per sample for PCM storage: " << bps << "\n";
+          return;
+        }
+        break;
+
+      case F_float:
+        if (bps != 32 && bps != 64) {
+          movies_cat.error()
+            << "Unsupported number of bits per sample for float storage: " << bps << "\n";
+          return;
+        }
+        break;
+
+      case F_alaw:
+      case F_mulaw:
+        if (bps != 8) {
+          movies_cat.error()
+            << ".wav file with A-law or mu-law compression must specify 8 bits per sample, not " << bps << ".\n";
+          return;
+        }
+        break;
+
+      default:
+        movies_cat.error()
+          << "Unsupported .wav format " << _format << ".  Only PCM, float, A-law and mu-law encodings are supported.\n";
+        return;
+      }
+
+      _bytes_per_sample = bps / 8;
+
+      // Skip to the end of the chunk.
+      if (subchunk_size > read_bytes) {
+        _reader.skip_bytes(subchunk_size - read_bytes);
+      }
+
+    } else if (subchunk_id == "data") {
+      // The data chunk contains the actual sammples.
+      if (!have_fmt) {
+        movies_cat.error()
+          << ".wav file specifies 'data' chunk before 'fmt ' chunk.\n";
+        break;
+      }
+
+      // Excellent!  We've reached the beginning of the data.  Write down
+      // where we are and don't move until we want to start reading the data.
+      _data_start = stream->tellg();
+      _data_size = subchunk_size;
+      have_data = true;
+      break;
+
+    } else {
+      // A chunk we do not recognize.  Just skip it.
+      _reader.skip_bytes(subchunk_size);
+    }
+
+    bytes_read += subchunk_size + 8;
+  }
+
+  // If we bailed out prematurely, there must have been an error.
+  if (_stream->eof()) {
+    movies_cat.error()
+      << "Reached end of file while reading .wav file header.\n";
+
+  } else if (!_stream->good()) {
+    movies_cat.error()
+      << "Stream error while reading .wav file header.\n";
+    return;
+  }
+
+  if (!have_fmt) {
+    movies_cat.error()
+      << ".wav file did not specify a 'fmt ' chunk.\n";
+    return;
+  }
+
+  if (!have_data) {
+    movies_cat.error()
+      << ".wav file did not specify a 'data' chunk.\n";
+    return;
+  }
+
+  // We can always seek by skipping bytes, rereading if necessary.
+  _can_seek = true;
+
+  // How to tell if a stream is seekable?  We'll set it to true, and
+  // then change it to false as soon as we find out that we can't.
+  _can_seek_fast = true;
+
+  if (_block_align != _audio_channels * _bytes_per_sample) {
+    movies_cat.warning()
+      << ".wav file specified an unexpected block alignment of " << _block_align
+      << ", expected " << (_audio_channels * _bytes_per_sample) << ".\n";
+    _block_align = _audio_channels * _bytes_per_sample;
+  }
+
+  if (_byte_rate != _audio_rate * _block_align) {
+    movies_cat.warning()
+      << ".wav file specified an unexpected byte rate of " << _byte_rate
+      << ", expected " << (_audio_rate * _block_align) << ".\n";
+    _byte_rate = _audio_rate * _block_align;
+  }
+
+  _length = _data_size / _byte_rate;
+  _is_valid = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WavAudioCursor::Destructor
+//       Access: Protected, Virtual
+//  Description: xxx
+////////////////////////////////////////////////////////////////////
+WavAudioCursor::
+~WavAudioCursor() {
+  if (_stream != NULL) {
+    VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+    vfs->close_read_file(_stream);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WavAudioCursor::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 WavAudioCursor::
+seek(double t) {
+  t = max(t, 0.0);
+  streampos pos = _data_start + (streampos) min(t * _byte_rate, _data_size);
+
+  if (_can_seek_fast) {
+    _stream->seekg(pos);
+    if (_stream->tellg() != pos) {
+      // Clearly, we can't seek fast.
+      // Fall back to the case below.
+      _can_seek_fast = false;
+    }
+  }
+
+  if (!_can_seek_fast) {
+    streampos current = _stream->tellg();
+
+    if (pos > current) {
+      // It is ahead of our current position.  Skip ahead.
+      _reader.skip_bytes(pos - current);
+
+    } else if (pos < current) {
+      // We'll have to reopen the file.
+      //TODO
+    }
+  }
+
+  _last_seek = _stream->tellg() / _byte_rate;
+  _samples_read = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WavAudioCursor::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 WavAudioCursor::
+read_samples(int n, PN_int16 *data) {
+  int desired = n * _audio_channels;
+  int read_samples = min(desired, (_data_size - _data_pos) / _bytes_per_sample);
+
+  if (read_samples <= 0) {
+    return;
+  }
+
+  switch (_format) {
+  case F_pcm:
+    // Linear PCM storage.
+    switch (_bytes_per_sample) {
+    case 1:
+      // Upsample.  Also, 8-bits samples are stored unsigned.
+      for (int i = 0; i < read_samples; ++i) {
+        data[i] = (_reader.get_uint8() - 128) * 258/*ish*/;
+      }
+      break;
+
+    case 2:
+      // Our native format.  Read directly from file.
+      read_samples = _reader.extract_bytes((unsigned char*) data, read_samples * 2) / 2;
+      break;
+
+    case 3: {
+      // The scale factor happens to be 256 for 24-bit samples.
+      // That means we can just read the most significant bytes.
+      for (int i = 0; i < read_samples; ++i) {
+        _reader.skip_bytes(1);
+        data[i] = _reader.get_int16();
+      }
+      break;
+
+    } case 4: {
+      // Downsample.
+      const PN_int32 scale_factor = 0x7fffffff / 0x7fff;
+
+      for (int i = 0; i < read_samples; ++i) {
+        data[i] = _reader.get_int32() / scale_factor;
+      }
+      break;
+
+    } case 8: {
+      // Downsample.
+      const PN_int64 scale_factor = 0x7fffffffffffffff / 0x7fff;
+
+      for (int i = 0; i < read_samples; ++i) {
+        data[i] = _reader.get_int64() / scale_factor;
+      }
+      break;
+
+    } default:
+      // Huh?
+      read_samples = 0;
+    }
+    break;
+
+  case F_float:
+    // IEEE float storage.
+    switch (_bytes_per_sample) {
+    case 4:
+      for (int i = 0; i < read_samples; ++i) {
+        data[i] = (PN_int16) (_reader.get_float32() * 0x7fff);
+      }
+      break;
+
+    case 8:
+      for (int i = 0; i < read_samples; ++i) {
+        data[i] = (PN_int16) (_reader.get_float64() * 0x7fff);
+      }
+      break;
+
+    default:
+      read_samples = 0;
+    }
+    break;
+
+  case F_alaw:
+    for (int i = 0; i < read_samples; ++i) {
+      data[i] = alaw_table[_reader.get_uint8()];
+    }
+    break;
+
+  case F_mulaw:
+    for (int i = 0; i < read_samples; ++i) {
+      data[i] = mulaw_table[_reader.get_uint8()];
+    }
+    break;
+
+  default:
+    read_samples = 0;
+  }
+
+  // Fill the rest of the buffer with silence.
+  if (read_samples < desired) {
+    memset(data + read_samples, 0, (desired - read_samples) * 2);
+  }
+
+  _data_pos = _stream->tellg() - _data_start;
+  _samples_read += read_samples / _audio_channels;
+}

+ 83 - 0
panda/src/movies/wavAudioCursor.h

@@ -0,0 +1,83 @@
+// Filename: wavAudioCursor.h
+// Created by: rdb (23Aug13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WAVAUDIOCURSOR_H
+#define WAVAUDIOCURSOR_H
+
+#include "pandabase.h"
+#include "movieAudioCursor.h"
+#include "streamReader.h"
+
+class WavAudio;
+
+////////////////////////////////////////////////////////////////////
+//       Class : WavAudioCursor
+// Description : Used for reading PCM .wav files.  Supported formats
+//               are linear PCM, IEEE float, A-law and mu-law.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES WavAudioCursor : public MovieAudioCursor {
+PUBLISHED:
+  WavAudioCursor(WavAudio *src, istream *stream);
+  virtual ~WavAudioCursor();
+  virtual void seek(double offset);
+
+public:
+  virtual void read_samples(int n, PN_int16 *data);
+
+  bool _is_valid;
+
+protected:
+  // Format codes as used by wave files.
+  enum Format {
+    F_pcm = 0x0001,
+    F_float = 0x0003,
+    F_alaw = 0x0006,
+    F_mulaw = 0x0007,
+
+    F_extensible = 0xfffe,
+  };
+
+  istream *_stream;
+  StreamReader _reader;
+
+  Format _format;
+  double _byte_rate;
+  int _block_align;
+  int _bytes_per_sample;
+
+  streampos _data_start;
+  streampos _data_pos;
+  size_t _data_size;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    MovieAudioCursor::init_type();
+    register_type(_type_handle, "WavAudioCursor",
+                  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 "wavAudioCursor.I"
+
+#endif // WAVAUDIOCURSOR_H

+ 33 - 3
panda/src/pandabase/pandasymbols.h

@@ -40,6 +40,14 @@
   #define EXPTP_COLLADA extern
 #endif
 
+#ifdef BUILDING_FFMPEG
+  #define EXPCL_FFMPEG __declspec(dllexport)
+  #define EXPTP_FFMPEG
+#else
+  #define EXPCL_FFMPEG __declspec(dllimport)
+  #define EXPTP_FFMPEG extern
+#endif
+
 #ifdef BUILDING_FRAMEWORK
   #define EXPCL_FRAMEWORK __declspec(dllexport)
   #define EXPTP_FRAMEWORK
@@ -72,6 +80,14 @@
   #define EXPTP_FMOD_AUDIO extern
 #endif
 
+#ifdef BUILDING_OCULUSVR
+  #define EXPCL_OCULUSVR __declspec(dllexport)
+  #define EXPTP_OCULUSVR
+#else
+  #define EXPCL_OCULUSVR __declspec(dllimport)
+  #define EXPTP_OCULUSVR extern
+#endif
+
 #ifdef BUILDING_OPENAL_AUDIO
   #define EXPCL_OPENAL_AUDIO __declspec(dllexport)
   #define EXPTP_OPENAL_AUDIO
@@ -264,6 +280,14 @@
   #define EXPTP_VISION extern
 #endif
 
+#ifdef BUILDING_VRPN
+  #define EXPCL_VRPN __declspec(dllexport)
+  #define EXPTP_VRPN
+#else
+  #define EXPCL_VRPN __declspec(dllimport)
+  #define EXPTP_VRPN extern
+#endif
+
 #else   /* !WIN32_VC */
 
 #define EXPCL_CFTALK
@@ -272,6 +296,9 @@
 #define EXPCL_COLLADA
 #define EXPTP_COLLADA
 
+#define EXPCL_FFMPEG
+#define EXPTP_FFMPEG
+
 #define EXPCL_FRAMEWORK
 #define EXPTP_FRAMEWORK
 
@@ -284,6 +311,9 @@
 #define EXPCL_FMOD_AUDIO
 #define EXPTP_FMOD_AUDIO
 
+#define EXPCL_OCULUSVR
+#define EXPTP_OCULUSVR
+
 #define EXPCL_OPENAL_AUDIO
 #define EXPTP_OPENAL_AUDIO
 
@@ -359,6 +389,9 @@
 #define EXPCL_VISION
 #define EXPTP_VISION
 
+#define EXPCL_VRPN
+#define EXPTP_VRPN
+
 #endif  /* WIN32_VC */
 
 #if (defined(WIN32_VC) || defined(WIN64_VC)) && !defined(CPPPARSER)
@@ -429,9 +462,6 @@
 #define EXPCL_PANDA_PIPELINE EXPCL_PANDA
 #define EXPTP_PANDA_PIPELINE EXPTP_PANDA
 
-#define EXPCL_PANDA_VRPN EXPCL_PANDA
-#define EXPTP_PANDA_VRPN EXPTP_PANDA
-
 #define EXPCL_PANDA_GRUTIL EXPCL_PANDA
 #define EXPTP_PANDA_GRUTIL EXPTP_PANDA
 

+ 2 - 0
panda/src/vrpn/Sources.pp

@@ -3,6 +3,8 @@
 #define OTHER_LIBS p3interrogatedb:c p3dconfig:c p3dtoolconfig:m \
                    p3dtoolutil:c p3dtoolbase:c p3dtool:m
 
+#define BUILDING_DLL BUILDING_VRPN
+
 #begin lib_target
   #define TARGET p3vrpn
   #define LOCAL_LIBS \

+ 1 - 1
panda/src/vrpn/config_vrpn.h

@@ -18,6 +18,6 @@
 #include "pandabase.h"
 #include "notifyCategoryProxy.h"
 
-NotifyCategoryDecl(vrpn, EXPCL_PANDA_VRPN, EXPTP_PANDA_VRPN);
+NotifyCategoryDecl(vrpn, EXPCL_VRPN, EXPTP_VRPN);
 
 #endif

+ 1 - 1
panda/src/vrpn/vrpnClient.h

@@ -35,7 +35,7 @@ class VrpnDialDevice;
 //               and records information on the connected VRPN
 //               devices.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_VRPN VrpnClient : public ClientBase {
+class EXPCL_VRPN VrpnClient : public ClientBase {
 PUBLISHED:
   VrpnClient(const string &server_name);
   ~VrpnClient();