[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] gnash ChangeLog configure.ac gui/Makefile.am li... [relea
From: |
Sandro Santilli |
Subject: |
[Gnash-commit] gnash ChangeLog configure.ac gui/Makefile.am li... [release_0_8_2_rc1] |
Date: |
Fri, 22 Feb 2008 14:16:43 +0000 |
CVSROOT: /sources/gnash
Module name: gnash
Branch: release_0_8_2_rc1
Changes by: Sandro Santilli <strk> 08/02/22 14:16:42
Modified files:
. : ChangeLog configure.ac
gui : Makefile.am
libmedia : Makefile.am sound_handler.h
po : Makefile.am
pythonmodule : Makefile.am
server : Makefile.am
server/asobj : Makefile.am NetConnection.cpp NetConnection.h
NetStreamFfmpeg.cpp NetStreamFfmpeg.h
server/parser : Makefile.am video_stream_def.cpp
video_stream_def.h
server/vm : Makefile.am
utilities : Makefile.am
Added files:
libmedia/ffmpeg: AudioDecoderFfmpeg.cpp AudioDecoderFfmpeg.h
VideoDecoderFfmpeg.cpp VideoDecoderFfmpeg.h
ffmpegNetStreamUtil.cpp ffmpegNetStreamUtil.h
sound_handler_sdl.cpp sound_handler_sdl.h
Log message:
Re-enable ffmpeg backend option. Work of Hong Yu (many thanks)
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/gnash/ChangeLog?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.5711.2.10&r2=1.5711.2.11
http://cvs.savannah.gnu.org/viewcvs/gnash/configure.ac?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.479.2.2&r2=1.479.2.3
http://cvs.savannah.gnu.org/viewcvs/gnash/gui/Makefile.am?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.118&r2=1.118.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/Makefile.am?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.13&r2=1.13.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/sound_handler.h?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.10&r2=1.10.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/ffmpeg/AudioDecoderFfmpeg.cpp?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/ffmpeg/AudioDecoderFfmpeg.h?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/ffmpeg/VideoDecoderFfmpeg.cpp?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/ffmpeg/VideoDecoderFfmpeg.h?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/ffmpeg/ffmpegNetStreamUtil.cpp?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/ffmpeg/ffmpegNetStreamUtil.h?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/ffmpeg/sound_handler_sdl.cpp?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/ffmpeg/sound_handler_sdl.h?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/po/Makefile.am?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.15&r2=1.15.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/pythonmodule/Makefile.am?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.3&r2=1.3.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/server/Makefile.am?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.138&r2=1.138.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/Makefile.am?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.48&r2=1.48.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/NetConnection.cpp?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.57&r2=1.57.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/NetConnection.h?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.38&r2=1.38.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/NetStreamFfmpeg.cpp?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.105&r2=1.105.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/NetStreamFfmpeg.h?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.56&r2=1.56.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/server/parser/Makefile.am?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.42&r2=1.42.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/server/parser/video_stream_def.cpp?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.39&r2=1.39.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/server/parser/video_stream_def.h?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.22&r2=1.22.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/server/vm/Makefile.am?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.17&r2=1.17.2.1
http://cvs.savannah.gnu.org/viewcvs/gnash/utilities/Makefile.am?cvsroot=gnash&only_with_tag=release_0_8_2_rc1&r1=1.64&r2=1.64.2.1
Patches:
Index: ChangeLog
===================================================================
RCS file: /sources/gnash/gnash/ChangeLog,v
retrieving revision 1.5711.2.10
retrieving revision 1.5711.2.11
diff -u -b -r1.5711.2.10 -r1.5711.2.11
--- ChangeLog 22 Feb 2008 11:25:05 -0000 1.5711.2.10
+++ ChangeLog 22 Feb 2008 14:16:31 -0000 1.5711.2.11
@@ -1,3 +1,23 @@
+2008-02-22 Hong Yu <address@hidden>
+
+ * configure.ac, gui/Makefile.am, libmedia/Makefile.am,
+ libmedia/sound_handler.h, libmedia/ffmpeg/AudioDecoderFfmpeg.cpp,
+ libmedia/ffmpeg/AudioDecoderFfmpeg.h,
+ libmedia/ffmpeg/VideoDecoderFfmpeg.cpp,
+ libmedia/ffmpeg/VideoDecoderFfmpeg.h,
+ libmedia/ffmpeg/ffmpegNetStreamUtil.cpp,
+ libmedia/ffmpeg/ffmpegNetStreamUtil.h,
+ libmedia/ffmpeg/sound_handler_sdl.cpp,
+ libmedia/ffmpeg/sound_handler_sdl.h,
+ po/Makefile.am, pythonmodule/Makefile.am,
+ server/Makefile.am, server/asobj/Makefile.am,
+ server/asobj/NetConnection.cpp, server/asobj/NetConnection.h,
+ server/asobj/NetStreamFfmpeg.cpp, server/asobj/NetStreamFfmpeg.h,
+ server/parser/Makefile.am, server/parser/video_stream_def.cpp,
+ server/parser/video_stream_def.h, server/vm/Makefile.am,
+ utilities/Makefile.am:
+ Re-enable ffmpeg backend option.
+
2008-02-22 Sandro Santilli <address@hidden>
* cygnal/cygnal.cpp: don't call LogFile::openLog explicitly,
Index: configure.ac
===================================================================
RCS file: /sources/gnash/gnash/configure.ac,v
retrieving revision 1.479.2.2
retrieving revision 1.479.2.3
diff -u -b -r1.479.2.2 -r1.479.2.3
--- configure.ac 21 Feb 2008 22:32:15 -0000 1.479.2.2
+++ configure.ac 22 Feb 2008 14:16:32 -0000 1.479.2.3
@@ -580,12 +580,16 @@
media_handler_specified=false
AC_ARG_ENABLE(media,
- AC_HELP_STRING([--enable-media=handler], [Enable media handling support using
the specified handler: gst or none (no sound) [[none]] ]),
+ AC_HELP_STRING([--enable-media=handler], [Enable media handling support using
the specified handler: gst, ffmpeg or none (no sound) [[gst]] ]),
[case "${enableval}" in
GST|gst)
media_handler=gst
media_handler_specified=true
;;
+ ffmpeg|FFMPEG)
+ media_handler=ffmpeg
+ media_handler_specified=true
+ ;;
no|NO|none)
media_handler=none
media_handler_specified=true
@@ -1596,9 +1600,14 @@
case "${media_handler}" in
gst) AC_DEFINE([SOUND_GST], [1], [Use GSTREAMER for media handling]) ;;
+ ffmpeg) AC_DEFINE([SOUND_SDL], [1], [Use SDL for sound handling]) ;;
*)
esac
+case "${media_handler}" in
+ ffmpeg) AC_DEFINE([USE_FFMPEG], [1], [Use FFMPEG for media decoding]) ;;
+ *)
+esac
dnl I'm kinda lazy, get rid of this later... //Markus
AM_CONDITIONAL(HAVE_GST, test x$media_handler = xgst)
Index: gui/Makefile.am
===================================================================
RCS file: /sources/gnash/gnash/gui/Makefile.am,v
retrieving revision 1.118
retrieving revision 1.118.2.1
diff -u -b -r1.118 -r1.118.2.1
--- gui/Makefile.am 17 Feb 2008 02:04:26 -0000 1.118
+++ gui/Makefile.am 22 Feb 2008 14:16:32 -0000 1.118.2.1
@@ -42,11 +42,10 @@
-I$(top_srcdir)/backend \
-I$(top_srcdir)/libgeometry \
-I$(top_srcdir)/libmedia \
- -I$(top_srcdir)/libmedia/sdl \
+ -I$(top_srcdir)/libmedia/ffmpeg \
-I$(top_srcdir)/libmedia/gst \
-DLOCALEDIR=\"$(localedir)\" \
-DPLUGINSDIR=\"$(pluginsdir)\" \
- $(GLIB_CFLAGS) \
$(LIBXML_CFLAGS) \
$(DMALLOC_CFLAGS) \
$(LIRC_CFLAGS) \
@@ -79,7 +78,6 @@
AM_LDFLAGS = \
$(GLIB_LIBS) \
$(LIBLTDL) \
- $(FFMPEG_LIBS) \
$(CURL_LIBS) \
$(LIBXML_LIBS) \
$(INCLTDL) \
@@ -127,19 +125,19 @@
endif # HAVE_OPENGL
endif # BUILD_AQUA_GUI
-if USE_SOUND_SDL
if USE_FFMPEG_ENGINE
AM_LDFLAGS += $(FFMPEG_LIBS)
AM_CPPFLAGS += $(FFMPEG_CFLAGS)
endif
+
if USE_MAD_ENGINE
AM_LDFLAGS += $(MAD_LIBS)
AM_CPPFLAGS += $(MAD_CFLAGS)
endif
-endif
if USE_SOUND_GST
AM_CPPFLAGS += $(GSTREAMER_CFLAGS)
+AM_LDFLAGS += $(GSTREAMER_LIBS)
endif
# if USE_GUI_FLTK
@@ -165,8 +163,8 @@
bin_PROGRAMS =
gnash: gnash.in
- $(INSTALL_DATA) $? gnash
- chmod a+x gnash
+ cp $< $@
+ chmod +x $@
GUI_SRCS = gnash.cpp \
gui.cpp gui.h \
Index: libmedia/Makefile.am
===================================================================
RCS file: /sources/gnash/gnash/libmedia/Makefile.am,v
retrieving revision 1.13
retrieving revision 1.13.2.1
diff -u -b -r1.13 -r1.13.2.1
--- libmedia/Makefile.am 20 Feb 2008 19:03:58 -0000 1.13
+++ libmedia/Makefile.am 22 Feb 2008 14:16:32 -0000 1.13.2.1
@@ -42,12 +42,11 @@
-I$(top_srcdir)/server/parser \
-I$(top_srcdir)/libgeometry \
$(PTHREAD_CFLAGS) \
+ $(DMALLOC_CFLAGS) \
$(OPENGL_CFLAGS) \
$(LIBXML_CFLAGS) \
$(PNG_CFLAGS) \
- $(SDL_CFLAGS) \
$(GLIB_CFLAGS) \
- $(GSTREAMER_CFLAGS) \
$(CURL_CFLAGS) \
$(Z_CFLAGS) \
$(JPEG_CFLAGS) \
@@ -72,7 +71,6 @@
$(LIBLTDLHEAD) \
$(BOOST_LIBS) \
$(PTHREAD_LIBS) \
- $(SDL_LIBS) \
$(NULL)
libgnashmedia_la_SOURCES = \
@@ -134,36 +132,36 @@
if USE_FFMPEG_ENGINE
libgnashmedia_la_SOURCES += \
- sdl/MediaDecoderSdl.cpp \
- sdl/AudioDecoderFfmpeg.cpp \
- sdl/VideoDecoderFfmpeg.cpp \
- sdl/MediaParserFfmpeg.cpp \
- sdl/sound_handler_sdl.cpp
+ ffmpeg/AudioDecoderFfmpeg.cpp \
+ ffmpeg/VideoDecoderFfmpeg.cpp \
+ ffmpeg/sound_handler_sdl.cpp \
+ ffmpeg/ffmpegNetStreamUtil.cpp
noinst_HEADERS += \
- sdl/AudioDecoderFfmpeg.h \
- sdl/VideoDecoderFfmpeg.h \
- sdl/sound_handler_sdl.h \
- sdl/MediaDecoderSdl.h \
- sdl/MediaParserFfmpeg.h
+ ffmpeg/AudioDecoderFfmpeg.h \
+ ffmpeg/VideoDecoderFfmpeg.h \
+ ffmpeg/sound_handler_sdl.h \
+ ffmpeg/ffmpegNetStreamUtil.h
libgnashmedia_la_LIBADD += \
- $(FFMPEG_LIBS)
+ $(FFMPEG_LIBS) \
+ $(SDL_LIBS)
libgnashmedia_la_CPPFLAGS += \
- $(FFMPEG_CFLAGS)
+ $(FFMPEG_CFLAGS) \
+ $(SDL_CFLAGS)
endif
if USE_MAD_ENGINE
libgnashmedia_la_SOURCES += \
- sdl/MediaDecoderSdl.cpp \
- sdl/AudioDecoderMad.cpp \
- sdl/sound_handler_sdl.cpp
+ ffmpeg/MediaDecoderSdl.cpp \
+ ffmpeg/AudioDecoderMad.cpp \
+ ffmpeg/sound_handler_sdl.cpp
noinst_HEADERS += \
- sdl/MediaDecoderSdl.h \
- sdl/AudioDecoderMad.h \
- sdl/sound_handler_sdl.h
+ ffmpeg/MediaDecoderSdl.h \
+ ffmpeg/AudioDecoderMad.h \
+ ffmpeg/sound_handler_sdl.h
libgnashmedia_la_LIBADD += \
$(MAD_LIBS)
Index: libmedia/sound_handler.h
===================================================================
RCS file: /sources/gnash/gnash/libmedia/sound_handler.h,v
retrieving revision 1.10
retrieving revision 1.10.2.1
diff -u -b -r1.10 -r1.10.2.1
--- libmedia/sound_handler.h 9 Feb 2008 15:06:01 -0000 1.10
+++ libmedia/sound_handler.h 22 Feb 2008 14:16:33 -0000 1.10.2.1
@@ -15,7 +15,7 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-/* $Id: sound_handler.h,v 1.10 2008/02/09 15:06:01 bjacques Exp $ */
+/* $Id: sound_handler.h,v 1.10.2.1 2008/02/22 14:16:33 strk Exp $ */
/// \page sound_handler_intro Sound handler introduction
///
@@ -355,7 +355,41 @@
///
virtual bool is_muted() = 0;
-
+#ifdef USE_FFMPEG
+ /// This is called by AS classes NetStream or Sound to attach callback,
so
+ /// that audio from the classes will be played through the soundhandler.
+ //
+ /// This is actually only used by the SDL sound_handler. It uses these
"auxiliary"
+ /// streamers to fetch decoded audio data to mix and send to the output
channel.
+ ///
+ /// The "aux streamer" will be called with the 'udata' pointer as first
argument,
+ /// then will be passed a buffer pointer as second argument and it's
length
+ /// as third. The callbacks should fill the given buffer if possible.
+ /// The callback should return true if wants to remain attached, false
if wants
+ /// to be detached.
+ ///
+ /// @param ptr
+ /// The pointer to the callback function
+ ///
+ /// @param udata
+ /// User data pointer, passed as first argument to the registered
callback.
+ /// WARNING: this is currently also used to *identify* the callback
for later
+ /// removal, see detach_aux_streamer. TODO: stop using the data
pointer for
+ /// identification purposes and use the callback pointer directly
instead.
+ ///
+ virtual void attach_aux_streamer(aux_streamer_ptr ptr, void* owner)
= 0;
+
+ /// This is called by AS classes NetStream or Sound to dettach
callback, so
+ /// that audio from the classes no longer will be played through the
+ /// soundhandler.
+ //
+ /// @param udata
+ /// The key identifying the auxiliary streamer.
+ /// WARNING: this need currently be the 'udata' pointer passed to
attach_aux_streamer.
+ /// TODO: get the aux_streamer_ptr as key !!
+ ///
+ virtual void detach_aux_streamer(void* udata) = 0;
+#endif
virtual ~sound_handler() {};
Index: po/Makefile.am
===================================================================
RCS file: /sources/gnash/gnash/po/Makefile.am,v
retrieving revision 1.15
retrieving revision 1.15.2.1
diff -u -b -r1.15 -r1.15.2.1
--- po/Makefile.am 13 Jan 2008 10:31:52 -0000 1.15
+++ po/Makefile.am 22 Feb 2008 14:16:37 -0000 1.15.2.1
@@ -35,7 +35,7 @@
$(top_srcdir)/backend \
$(top_srcdir)/libamf \
$(top_srcdir)/libmedia \
- $(top_srcdir)/libmedia/sdl \
+ $(top_srcdir)/libmedia/ffmpeg \
$(top_srcdir)/libmedia/gst \
$(top_srcdir)/server \
$(top_srcdir)/server/vm \
Index: pythonmodule/Makefile.am
===================================================================
RCS file: /sources/gnash/gnash/pythonmodule/Makefile.am,v
retrieving revision 1.3
retrieving revision 1.3.2.1
diff -u -b -r1.3 -r1.3.2.1
--- pythonmodule/Makefile.am 10 Jan 2008 08:25:28 -0000 1.3
+++ pythonmodule/Makefile.am 22 Feb 2008 14:16:37 -0000 1.3.2.1
@@ -34,6 +34,7 @@
$(GLIB_LIBS) \
$(LIBLTDL) \
$(FFMPEG_LIBS) \
+ $(GSTREAMER_LIBS) \
$(CURL_LIBS) \
$(LIBXML_LIBS) \
$(INCLTDL) \
@@ -51,7 +52,7 @@
-I$(top_srcdir)/backend \
-I$(top_srcdir)/libgeometry \
-I$(top_srcdir)/libmedia \
- -I$(top_srcdir)/libmedia/sdl \
+ -I$(top_srcdir)/libmedia/ffmpeg \
-I$(top_srcdir)/libmedia/gst \
-DLOCALEDIR=\"$(localedir)\" \
$(BOOSTPYTHON_CFLAGS) \
@@ -84,7 +85,7 @@
-I$(top_srcdir)/backend \
-I$(top_srcdir)/libgeometry \
-I$(top_srcdir)/libmedia \
- -I$(top_srcdir)/libmedia/sdl \
+ -I$(top_srcdir)/libmedia/ffmpeg \
-I$(top_srcdir)/libmedia/gst \
-DLOCALEDIR=\"$(localedir)\" \
-DBOOST_PYTHON_DYNAMIC_LIB \
Index: server/Makefile.am
===================================================================
RCS file: /sources/gnash/gnash/server/Makefile.am,v
retrieving revision 1.138
retrieving revision 1.138.2.1
diff -u -b -r1.138 -r1.138.2.1
--- server/Makefile.am 8 Feb 2008 15:27:32 -0000 1.138
+++ server/Makefile.am 22 Feb 2008 14:16:38 -0000 1.138.2.1
@@ -37,13 +37,10 @@
-I$(top_srcdir)/libbase \
-I$(top_srcdir)/libgeometry \
-I$(top_srcdir)/libmedia \
- -I$(top_srcdir)/libmedia/sdl \
- -I$(top_srcdir)/libmedia/gst \
-I$(top_srcdir) \
$(PTHREAD_CFLAGS) \
$(DMALLOC_CFLAGS) \
$(GLIB_CFLAGS) \
- $(GSTREAMER_CFLAGS) \
$(BOOST_CFLAGS) \
$(LIBXML_CFLAGS) \
$(FREETYPE2_CFLAGS) \
@@ -202,10 +199,15 @@
$(NULL)
if USE_SOUND_GST
-AM_CPPFLAGS += $(GSTREAMER_CFLAGS)
+AM_CPPFLAGS += $(GSTREAMER_CFLAGS) \
+ -I$(top_srcdir)/libmedia/gst
libgnashserver_la_LIBADD += $(GSTREAMER_LIBS)
endif
+if USE_FFMPEG_ENGINE
+AM_CPPFLAGS += $(FFMPEG_CFLAGS) \
+ -I$(top_srcdir)/libmedia/ffmpeg
+endif
# Enable building the Flash debugger
if DEBUGGER
Index: server/asobj/Makefile.am
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/Makefile.am,v
retrieving revision 1.48
retrieving revision 1.48.2.1
diff -u -b -r1.48 -r1.48.2.1
--- server/asobj/Makefile.am 21 Jan 2008 20:55:56 -0000 1.48
+++ server/asobj/Makefile.am 22 Feb 2008 14:16:38 -0000 1.48.2.1
@@ -15,7 +15,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-# $Id: Makefile.am,v 1.48 2008/01/21 20:55:56 rsavoye Exp $
+# $Id: Makefile.am,v 1.48.2.1 2008/02/22 14:16:38 strk Exp $
AUTOMAKE_OPTIONS =
@@ -32,14 +32,10 @@
-I$(top_srcdir)/libamf \
-I$(top_srcdir)/libltdl \
-I$(top_srcdir)/libmedia \
- -I$(top_srcdir)/libmedia/gst \
- -I$(top_srcdir)/libmedia/sdl \
-I$(top_srcdir) \
$(PTHREAD_CFLAGS) \
$(DMALLOC_CFLAGS) \
$(GLIB_CFLAGS) \
- $(FFMPEG_CFLAGS) \
- $(GSTREAMER_CFLAGS) \
$(BOOST_CFLAGS) \
$(LIBXML_CFLAGS) \
$(NULL)
@@ -71,7 +67,7 @@
Math.cpp \
Microphone.cpp \
Mouse.cpp \
- NetConnection.cpp\
+ NetConnection.cpp \
NetStream.cpp \
Number.cpp \
Object.cpp \
@@ -145,14 +141,19 @@
$(LIBXML_LIBS)
if USE_FFMPEG_ENGINE
-libgnashasobjs_la_SOURCES += NetStreamFfmpeg.cpp SoundFfmpeg.cpp
-AM_CPPFLAGS += $(FFMPEG_CFLAGS)
-# libgnashasobjs_la_LIBADD += $(FFMPEG_LIBS)
+libgnashasobjs_la_SOURCES += NetStreamFfmpeg.cpp \
+ SoundFfmpeg.cpp
+AM_CPPFLAGS += $(FFMPEG_CFLAGS) \
+ $(SDL_CFLAGS) \
+ -I$(top_srcdir)/libmedia/ffmpeg
+libgnashasobjs_la_LIBADD += $(FFMPEG_LIBS) \
+ $(SDL_LIBS)
endif
if USE_SOUND_GST
libgnashasobjs_la_SOURCES += NetStreamGst.cpp SoundGst.cpp
-AM_CPPFLAGS += $(GSTREAMER_CFLAGS)
+AM_CPPFLAGS += $(GSTREAMER_CFLAGS) \
+ -I$(top_srcdir)/libmedia/gst
libgnashasobjs_la_LIBADD += $(GSTREAMER_LIBS)
endif
Index: server/asobj/NetConnection.cpp
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/NetConnection.cpp,v
retrieving revision 1.57
retrieving revision 1.57.2.1
diff -u -b -r1.57 -r1.57.2.1
--- server/asobj/NetConnection.cpp 19 Feb 2008 19:20:54 -0000 1.57
+++ server/asobj/NetConnection.cpp 22 Feb 2008 14:16:39 -0000 1.57.2.1
@@ -17,7 +17,7 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
-/* $Id: NetConnection.cpp,v 1.57 2008/02/19 19:20:54 bwy Exp $ */
+/* $Id: NetConnection.cpp,v 1.57.2.1 2008/02/22 14:16:39 strk Exp $ */
#ifdef HAVE_CONFIG_H
#include "gnashconfig.h"
@@ -50,15 +50,80 @@
NetConnection::NetConnection()
:
- as_object(getNetConnectionInterface())
+ as_object(getNetConnectionInterface()),
+ _loader()
{
attachProperties();
}
+
NetConnection::~NetConnection()
{
}
+
+/*public*/
+bool NetConnection::openConnection(const std::string& url)
+{
+ // if already running there is no need to setup things again
+ if ( _loader.get() ) {
+ log_debug("NetConnection::openConnection() called when already connected
to a stream. Checking if the existing connection can be used.");
+ std::string newurl;
+ if (_prefixUrl.size() > 0) {
+ newurl += _prefixUrl + "/" + url;
+ } else {
+ newurl += url;
+ }
+ if (newurl.compare(_completeUrl) == 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ if ( _prefixUrl.size() > 0 ) {
+ _completeUrl += _prefixUrl + "/" + url;
+ } else {
+ _completeUrl += url;
+ }
+
+ URL uri( _completeUrl, get_base_url() );
+
+ std::string uriStr( uri.str() );
+ assert( uriStr.find( "://" ) != std::string::npos );
+
+ // Check if we're allowed to open url
+ if ( ! URLAccessManager::allow( uri ) ) {
+ log_security( _("Gnash is not allowed to open this url: %s"),
uriStr.c_str() );
+ return false;
+ }
+
+ log_security( _("Connecting to movie: %s"), uriStr.c_str() );
+
+ _loader.reset( new LoadThread() );
+
+ if ( ! _loader->setStream(
std::auto_ptr<tu_file>(StreamProvider::getDefaultInstance().getStream( uri ) )
) ) {
+ log_error( _("Gnash could not open this url: %s"), uriStr.c_str() );
+ _loader.reset();
+
+ return false;
+ }
+
+ log_debug( _("Connection established to movie: %s"), uriStr.c_str() );
+
+ return true;
+}
+
+
+/*public*/
+bool
+NetConnection::eof()
+{
+ if (!_loader.get()) return true; // @@ correct ?
+ return _loader->eof();
+}
+
+
/*public*/
std::string NetConnection::validateURL(const std::string& url)
{
@@ -85,7 +150,7 @@
return uriStr;
}
-/*public*/
+/*private*/
void
NetConnection::addToURL(const std::string& url)
{
@@ -100,6 +165,82 @@
}
+/*public*/
+size_t
+NetConnection::read( void *dst, size_t bytes )
+{
+ if ( ! _loader.get() ) {
+ return 0;
+ }
+
+ return _loader->read( dst, bytes );
+}
+
+
+/*public*/
+bool
+NetConnection::seek( size_t pos )
+{
+ if ( ! _loader.get() ) {
+ return false;
+ }
+
+ return _loader->seek( pos );
+}
+
+
+/*public*/
+size_t
+NetConnection::tell()
+{
+ if (!_loader.get()) return 0; // @@ correct ?
+ return _loader->tell();
+}
+
+
+/*public*/
+long
+NetConnection::getBytesLoaded()
+{
+ if (!_loader.get()) return 0; // @@ correct ?
+ return _loader->getBytesLoaded();
+}
+
+
+/*public*/
+long
+NetConnection::getBytesTotal()
+{
+ if (!_loader.get()) return 0; // @@ correct ?
+ return _loader->getBytesTotal();
+}
+
+
+/*public*/
+bool
+NetConnection::loadCompleted()
+{
+ if ( ! _loader.get() ) {
+ return false;
+ }
+
+ return _loader->completed();
+}
+
+
+std::auto_ptr<FLVParser>
+NetConnection::getConnectedParser() const
+{
+ std::auto_ptr<FLVParser> ret;
+
+ if ( _loader.get() ) {
+ ret.reset( new FLVParser(*_loader) );
+ }
+
+ return ret;
+}
+
+
/// \brief callback to instantiate a new NetConnection object.
/// \param fn the parameters from the Flash movie
/// \return nothing from the function call.
Index: server/asobj/NetConnection.h
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/NetConnection.h,v
retrieving revision 1.38
retrieving revision 1.38.2.1
diff -u -b -r1.38 -r1.38.2.1
--- server/asobj/NetConnection.h 21 Jan 2008 20:55:56 -0000 1.38
+++ server/asobj/NetConnection.h 22 Feb 2008 14:16:39 -0000 1.38.2.1
@@ -15,7 +15,7 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-/* $Id: NetConnection.h,v 1.38 2008/01/21 20:55:56 rsavoye Exp $ */
+/* $Id: NetConnection.h,v 1.38.2.1 2008/02/22 14:16:39 strk Exp $ */
#ifndef __NETCONNECTION_H__
#define __NETCONNECTION_H__
@@ -80,6 +80,83 @@
/// Register the "NetConnection" constructor to the given global object
static void registerConstructor(as_object& global);
+ /// Open a connection to stream FLV files.
+ //
+ /// If already connected an error is raised and false
+ /// is returned. Otherwise, a connection is attempted
+ /// using a separate thread that starts loading data
+ /// caching it.
+ ///
+ /// @param url
+ /// An url portion to append to the base url (???)
+ ///
+ /// @return true on success, false on error.
+ ///
+ /// @note Older Flash movies can only take a NULL value as
+ /// the parameter, which therefor only connects to the localhost using
+ /// RTMP. Newer Flash movies have a parameter to connect which is a
+ /// URL string like rtmp://foobar.com/videos/bar.flv
+ ///
+ bool openConnection(const std::string& url);
+
+ /// Put read pointer at given position
+ //
+ /// If the position has not been loaded yet
+ /// this call blocks. If not connected false
+ /// is returned w/out blocking.
+ ///
+ bool seek(size_t pos);
+
+ /// Read 'bytes' bytes into the given buffer.
+ //
+ /// If not enough bytes have been loaded yet
+ /// this call blocks. If not connected false
+ /// is returned w/out blocking.
+ ///
+ /// Return number of actually read bytes
+ ///
+ size_t read(void *dst, size_t bytes);
+
+ /// Return true if EOF has been reached
+ //
+ /// This call never blocks.
+ /// If not connected, true is returned (is this correct behaviour?)
+ ///
+ bool eof();
+
+ /// Report global position within the file
+ //
+ /// This call never blocks.
+ /// If not connected, 0 is returned (is this correct behaviour?)
+ ///
+ size_t tell();
+
+ /// Returns the number of bytes cached
+ //
+ /// This call never blocks.
+ /// If not connected, 0 is returned (is this correct behaviour?)
+ ///
+ long getBytesLoaded();
+
+ /// Returns the total size of the file
+ //
+ /// This call never blocks.
+ /// If not connected, 0 is returned (is this correct behaviour?)
+ ///
+ long getBytesTotal();
+
+ /// Return an FLVParser using our LoadThread for input
+ //
+ /// If not connected, a NULL auto_ptr is returned.
+ ///
+ std::auto_ptr<FLVParser> getConnectedParser() const;
+
+ /// Returns whether the load is complete
+ //
+ /// This call never blocks.
+ ///
+ bool loadCompleted();
+
private:
/// Extend the URL to be used for playing
@@ -88,6 +165,12 @@
/// the url prefix optionally passed to connect()
std::string _prefixUrl;
+ /// the complete url of the file
+ std::string _completeUrl;
+
+ /// The file/stream loader thread and interface
+ std::auto_ptr<LoadThread> _loader;
+
/// Attach ActionScript instance properties
void attachProperties();
Index: server/asobj/NetStreamFfmpeg.cpp
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/NetStreamFfmpeg.cpp,v
retrieving revision 1.105
retrieving revision 1.105.2.1
diff -u -b -r1.105 -r1.105.2.1
--- server/asobj/NetStreamFfmpeg.cpp 19 Feb 2008 19:20:55 -0000 1.105
+++ server/asobj/NetStreamFfmpeg.cpp 22 Feb 2008 14:16:40 -0000
1.105.2.1
@@ -17,7 +17,7 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
-/* $Id: NetStreamFfmpeg.cpp,v 1.105 2008/02/19 19:20:55 bwy Exp $ */
+/* $Id: NetStreamFfmpeg.cpp,v 1.105.2.1 2008/02/22 14:16:40 strk Exp $ */
#ifdef HAVE_CONFIG_H
#include "gnashconfig.h"
@@ -31,7 +31,6 @@
#include "NetStream.h"
#include "render.h"
#include "movie_root.h"
-#include "NetConnection.h"
#include "sound_handler.h"
#include "VideoDecoderFfmpeg.h"
#include "tu_timer.h" // TODO: use the VirtualClock instead ?
@@ -88,26 +87,33 @@
close();
}
-void NetStreamFfmpeg::pause(int mode)
-{
- if (mode == -1)
- {
- if (m_pause) unpausePlayback();
- else pausePlayback();
+void NetStreamFfmpeg::pause( PauseMode mode )
+{
+ switch ( mode ) {
+ case pauseModeToggle:
+ if ( m_pause ) {
+ unpausePlayback();
+ } else {
+ pausePlayback();
}
- else
- {
- if (mode == 0) pausePlayback();
- else unpausePlayback();
+ break;
+ case pauseModePause:
+ pausePlayback();
+ break;
+ case pauseModeUnPause:
+ unpausePlayback();
+ break;
+ default:
+ break;
}
- if (!m_pause && !m_go)
- {
- setStatus(playStart);
+
+ if ( !m_pause && !m_go ) {
+ setStatus( playStart );
m_go = true;
- _decodeThread = new
boost::thread(boost::bind(NetStreamFfmpeg::av_streamer, this));
- }
+ _decodeThread = new boost::thread(
boost::bind(NetStreamFfmpeg::av_streamer, this) );
+ }
}
void NetStreamFfmpeg::close()
@@ -136,23 +142,13 @@
if (m_Frame) av_free(m_Frame);
m_Frame = NULL;
- if (m_VCodecCtx)
- {
- avcodec_close(m_VCodecCtx);
- if (m_isFLV)
- {
- av_free(m_VCodecCtx);
- }
+ if ( m_VCodecCtx ) {
+ avcodec_close( m_VCodecCtx );
}
m_VCodecCtx = NULL;
- if (m_ACodecCtx)
- {
- avcodec_close(m_ACodecCtx);
- if (m_isFLV)
- {
- av_free(m_ACodecCtx);
- }
+ if ( m_ACodecCtx ) {
+ avcodec_close( m_ACodecCtx );
}
m_ACodecCtx = NULL;
@@ -451,16 +447,13 @@
if (!m_VCodecCtx)
{
log_error(_("Failed to initialize FLV video codec"));
+ return false;
}
m_ACodecCtx = initFlvAudio(m_parser.get());
if (!m_ACodecCtx)
{
log_error(_("Failed to initialize FLV audio codec"));
- }
-
- if (!m_ACodecCtx && !m_VCodecCtx)
- {
return false;
}
@@ -625,24 +618,20 @@
/// of this structure must be initialized.
/// @param width the width, in bytes, of a row of video data.
static void
-rgbcopy(image::rgb* dst, raw_mediadata_t* src, int width)
+rgbcopy(image::rgb* dst, media::raw_mediadata_t* src, int width)
{
- assert( dst->size() >= src->m_size );
- assert( dst->pitch() >= width );
- dst->update(src->m_data);
+ assert( src->m_size <= static_cast<boost::uint32_t>(dst->width() *
dst->height() * 3) );
-#if 0
- boost::uint8_t* dstptr = dst->m_data;
+ boost::uint8_t* dstptr = dst->data();
boost::uint8_t* srcptr = src->m_data;
boost::uint8_t* srcend = src->m_data + src->m_size;
while (srcptr < srcend) {
memcpy(dstptr, srcptr, width);
- dstptr += dst->m_pitch;
+ dstptr += dst->pitch();
srcptr += width;
}
-#endif
}
// decoder thread
@@ -695,7 +684,7 @@
if (ns->m_isFLV)
{
// If queues are full then don't bother filling it
- if ((ns->m_VCodecCtx && ns->m_qvideo.size() < 20) ||
(ns->m_ACodecCtx && ns->m_qaudio.size() < 20))
+ if ( ns->m_qvideo.size() < 20 || ns->m_qaudio.size() <
20 )
{
// If we have problems with decoding - break
@@ -747,7 +736,7 @@
while (len > 0 && ns->m_qaudio.size() > 0)
{
- raw_mediadata_t* samples = ns->m_qaudio.front();
+ media::raw_mediadata_t* samples = ns->m_qaudio.front();
int n = imin(samples->m_size, len);
memcpy(stream, samples->m_ptr, n);
@@ -771,12 +760,10 @@
bool NetStreamFfmpeg::decodeFLVFrame()
{
FLVFrame* frame = NULL;
- if (m_qvideo.size() < m_qaudio.size() && m_VCodecCtx)
+ if ( m_qvideo.size() < m_qaudio.size() )
{
frame = m_parser->nextVideoFrame();
- }
- else if (m_ACodecCtx)
- {
+ } else {
frame = m_parser->nextAudioFrame();
}
@@ -788,7 +775,7 @@
log_debug("decodeFLVFrame: load completed, stopping");
#endif
// Stop!
- //m_go = false;
+ this->m_go = false;
}
else
{
@@ -799,28 +786,29 @@
return false;
}
- AvPkt packet;
- // TODO: move this logic in AvPkt itself ?
- packet->destruct = avpacket_destruct;
- packet->size = frame->dataSize;
- packet->data = frame->data;
+ AVPacket packet;
+
+ packet.destruct = avpacket_destruct;
+ packet.size = frame->dataSize;
+ packet.data = frame->data;
// FIXME: is this the right value for packet.dts?
- packet->pts = packet->dts =
static_cast<boost::int64_t>(frame->timestamp);
+ packet.pts = packet.dts = static_cast<boost::int64_t>(frame->timestamp);
if (frame->tag == 9)
{
- packet->stream_index = 0;
- return decodeVideo(packet);
+ packet.stream_index = 0;
+ return decodeVideo(&packet);
}
else
{
- packet->stream_index = 1;
- return decodeAudio(packet);
+ packet.stream_index = 1;
+ return decodeAudio(&packet);
}
}
-bool NetStreamFfmpeg::decodeAudio(AvPkt& packet)
+
+bool NetStreamFfmpeg::decodeAudio( AVPacket* packet )
{
if (!m_ACodecCtx) return false;
@@ -852,7 +840,7 @@
ptr = reinterpret_cast<boost::uint8_t*>(output);
}
- raw_mediadata_t* raw = new raw_mediadata_t();
+ media::raw_mediadata_t* raw = new media::raw_mediadata_t();
raw->m_data = ptr;
raw->m_ptr = raw->m_data;
@@ -895,7 +883,8 @@
return true;
}
-bool NetStreamFfmpeg::decodeVideo(AvPkt& packet)
+
+bool NetStreamFfmpeg::decodeVideo(AVPacket* packet)
{
if (!m_VCodecCtx) return false;
@@ -925,7 +914,7 @@
}
else if (m_videoFrameFormat == render::YUV &&
m_VCodecCtx->pix_fmt != PIX_FMT_YUV420P)
{
- abort(); // TODO
+ assert( 0 ); // TODO
//img_convert((AVPicture*) pFrameYUV, PIX_FMT_YUV420P,
(AVPicture*) pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
// Don't use depreceted img_convert, use sws_scale
@@ -935,7 +924,7 @@
buffer.reset(media::VideoDecoderFfmpeg::convertRGB24(m_VCodecCtx, m_Frame));
}
- raw_mediadata_t* video = new raw_mediadata_t;
+ media::raw_mediadata_t* video = new media::raw_mediadata_t();
if (m_videoFrameFormat == render::YUV)
{
@@ -944,7 +933,7 @@
else if (m_videoFrameFormat == render::RGB)
{
image::rgb* tmp =
static_cast<image::rgb*>(m_imageframe);
- video->m_data = new
boost::uint8_t[m_imageframe->size()]; // tmp->m_pitch * tmp->m_height];
+ video->m_data = new boost::uint8_t[tmp->pitch() *
tmp->height()];
}
video->m_ptr = video->m_data;
@@ -1052,29 +1041,30 @@
return true;
}
- AvPkt packet;
+ AVPacket packet;
- int rc = av_read_frame(m_FormatCtx, packet.get());
+ int rc = av_read_frame(m_FormatCtx, &packet);
if (rc >= 0)
{
- if (packet->stream_index == m_audio_index &&
get_sound_handler())
+ if (packet.stream_index == m_audio_index && get_sound_handler())
{
- if (!decodeAudio(packet))
+ if (!decodeAudio(&packet))
{
log_error(_("Problems decoding audio frame"));
return false;
}
}
else
- if (packet->stream_index == m_video_index)
+ if (packet.stream_index == m_video_index)
{
- if (!decodeVideo(packet))
+ if (!decodeVideo(&packet))
{
log_error(_("Problems decoding video frame"));
return false;
}
}
+ av_free_packet(&packet);
}
else
{
@@ -1107,7 +1097,7 @@
{
AVStream* videostream = m_FormatCtx->streams[m_video_index];
- timebase = as_double(videostream->time_base);
+ timebase = static_cast<double>(videostream->time_base.num /
videostream->time_base.den);
newpos = static_cast<long>(pos / timebase);
if (av_seek_frame(m_FormatCtx, m_video_index, newpos, 0) < 0)
@@ -1144,19 +1134,23 @@
}
else
{
- AvPkt packet;
+ AVPacket Packet;
+ av_init_packet(&Packet);
double newtime = 0;
while (newtime == 0)
{
- if ( av_read_frame(m_FormatCtx, packet.get()) < 0)
+ if (av_read_frame(m_FormatCtx, &Packet) < 0)
{
av_seek_frame(m_FormatCtx, -1, 0,
AVSEEK_FLAG_BACKWARD);
+ av_free_packet( &Packet );
return;
}
newtime = timebase *
(double)m_FormatCtx->streams[m_video_index]->cur_dts;
}
+ av_free_packet( &Packet );
+
av_seek_frame(m_FormatCtx, m_video_index, newpos, 0);
boost::uint32_t newtime_ms =
static_cast<boost::int32_t>(newtime / 1000.0);
m_start_clock += m_last_audio_timestamp - newtime_ms;
@@ -1167,8 +1161,16 @@
}
// Flush the queues
- m_qvideo.clear();
- m_qaudio.clear();
+ while ( m_qvideo.size() > 0 )
+ {
+ delete m_qvideo.front();
+ m_qvideo.pop();
+ }
+ while ( m_qaudio.size() > 0 )
+ {
+ delete m_qaudio.front();
+ m_qaudio.pop();
+ }
}
@@ -1183,7 +1185,7 @@
{
// Get video frame from queue, will have the lowest timestamp
// will return NULL if empty(). See multithread_queue::front
- raw_mediadata_t* video = m_qvideo.front();
+ media::raw_mediadata_t* video = m_qvideo.front();
// If the queue is empty we have nothing to do
if (!video)
@@ -1279,7 +1281,7 @@
if (m_FormatCtx && m_FormatCtx->nb_streams > 0)
{
- double time = as_double(m_FormatCtx->streams[0]->time_base) *
(double)m_FormatCtx->streams[0]->cur_dts;
+ double time = (double)m_FormatCtx->streams[0]->time_base.num /
(double)m_FormatCtx->streams[0]->time_base.den *
(double)m_FormatCtx->streams[0]->cur_dts;
return static_cast<boost::int32_t>(time);
}
else if
@@ -1328,10 +1330,39 @@
// Re-connect to the soundhandler.
// It was disconnected to avoid to keep playing sound while paused
media::sound_handler* s = get_sound_handler();
- if (s && m_ACodecCtx) s->attach_aux_streamer(audio_streamer, (void*)
this);
+ if ( s ) s->attach_aux_streamer(audio_streamer, (void*) this);
+}
+
+
+long
+NetStreamFfmpeg::bytesLoaded ()
+{
+ long ret_val = 0;
+
+ if ( _netCon )
+ {
+ ret_val = _netCon->getBytesLoaded();
+ }
+
+ return ret_val;
+}
+
+
+long
+NetStreamFfmpeg::bytesTotal ()
+{
+ long ret_val = 0;
+
+ if ( _netCon )
+ {
+ ret_val = _netCon->getBytesTotal();
+ }
+
+ return ret_val;
}
} // gnash namespcae
#endif // USE_FFMPEG
+
Index: server/asobj/NetStreamFfmpeg.h
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/NetStreamFfmpeg.h,v
retrieving revision 1.56
retrieving revision 1.56.2.1
diff -u -b -r1.56 -r1.56.2.1
--- server/asobj/NetStreamFfmpeg.h 21 Jan 2008 20:55:57 -0000 1.56
+++ server/asobj/NetStreamFfmpeg.h 22 Feb 2008 14:16:40 -0000 1.56.2.1
@@ -15,7 +15,7 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-/* $Id: NetStreamFfmpeg.h,v 1.56 2008/01/21 20:55:57 rsavoye Exp $ */
+/* $Id: NetStreamFfmpeg.h,v 1.56.2.1 2008/02/22 14:16:40 strk Exp $ */
#ifndef __NETSTREAMFFMPEG_H__
#define __NETSTREAMFFMPEG_H__
@@ -37,7 +37,6 @@
#include <boost/thread/condition.hpp>
#include "impl.h"
-#include "video_stream_instance.h"
extern "C" {
#include <ffmpeg/avformat.h>
@@ -49,194 +48,11 @@
#include "FLVParser.h"
-namespace gnash {
-
-class raw_mediadata_t
-{
-public:
- raw_mediadata_t():
- m_stream_index(-1),
- m_size(0),
- m_data(NULL),
- m_ptr(NULL),
- m_pts(0)
- {
- }
-
- ~raw_mediadata_t()
- {
- if (m_data) delete [] m_data;
- }
-
- int m_stream_index;
- boost::uint32_t m_size;
- boost::uint8_t* m_data;
- boost::uint8_t* m_ptr;
- boost::uint32_t m_pts; // presentation timestamp in millisec
-};
-
-/// Threadsafe elements-owning queue
-//
-/// This class is a threadsafe queue, using std:queue and locking.
-/// It is used to store decoded audio and video data which are waiting to be
"played"
-/// Elements of the queue are owned by instances of this class.
-///
-template<class T>
-class multithread_queue
-{
- public:
-
- multithread_queue()
- {
- }
-
- // Destroy all elements of the queue. Locks.
- ~multithread_queue()
- {
- clear();
- }
-
- // Destroy all elements of the queue. Locks.
- void clear()
- {
- boost::mutex::scoped_lock lock(_mutex);
- while (!m_queue.empty())
- {
- T x = m_queue.front();
- m_queue.pop();
- delete x;
- }
- }
-
- /// Returns the size if the queue. Locks.
- //
- /// @return the size of the queue
- ///
- size_t size()
- {
- boost::mutex::scoped_lock lock(_mutex);
- size_t n = m_queue.size();
- return n;
- }
-
- /// Pushes an element to the queue. Locks.
- //
- /// @param member
- /// The element to be pushed unto the queue.
- ///
- /// @return true if queue isn't full and the element was pushed to the
queue,
- /// or false if the queue was full, and the element wasn't push unto it.
- ///
- bool push(T member)
- {
- bool rc = false;
- boost::mutex::scoped_lock lock(_mutex);
+#include "ffmpegNetStreamUtil.h"
- // We only keep max 20 items in the queue.
- // If it's "full" the item must wait, see calls
- // to this function in read_frame() to see how it is done.
- if (m_queue.size() < 20)
- {
- m_queue.push(member);
- rc = true;
- }
- return rc;
- }
-
- /// Returns a pointer to the first element on the queue. Locks.
- //
- /// If no elements are available this function returns NULL.
- ///
- /// @return a pointer to the first element on the queue, NULL if queue
is empty.
- ///
- T front()
- {
- boost::mutex::scoped_lock lock(_mutex);
- T member = NULL;
- if (!m_queue.empty())
- {
- member = m_queue.front();
- }
- return member;
- }
-
- /// Pops the first element from the queue. Locks.
- //
- /// If no elements are available this function is
- /// a noop.
- ///
- void pop()
- {
- boost::mutex::scoped_lock lock(_mutex);
- if (!m_queue.empty())
- {
- m_queue.pop();
- }
- }
-private:
-
- // Mutex used for locking
- boost::mutex _mutex;
-
- // The actual queue.
- std::queue < T > m_queue;
-};
-
-/// This class is used to provide an easy interface to libavcodecs audio
resampler.
-///
-class AudioResampler
-{
-public:
- AudioResampler() : _context(NULL) {}
- ~AudioResampler()
- {
- if(_context) {
- audio_resample_close (_context);
- }
- }
-
- /// Initializes the resampler
- //
- /// @param ctx
- /// The audio format container.
- ///
- /// @return true if resampling is needed, if not false
- ///
- bool init(AVCodecContext* ctx)
- {
- if (ctx->sample_rate != 44100 || ctx->channels != 2) {
- if (!_context) {
- _context = audio_resample_init(2,
ctx->channels,
- 44100, ctx->sample_rate);
- }
- return true;
- }
- return false;
- }
-
- /// Resamples audio
- //
- /// @param input
- /// A pointer to the audio data that needs resampling
- ///
- /// @param output
- /// A pointer to where the resampled output should be placed
- ///
- /// @param samples
- /// Number of samples in the audio
- ///
- /// @return the number of samples in the output data.
- ///
- int resample(boost::int16_t* input, boost::int16_t* output, int samples)
- {
- return audio_resample (_context, output, input, samples);
- }
+namespace gnash {
-private:
- // The container of the resample format information.
- ReSampleContext* _context;
-};
class NetStreamFfmpeg: public NetStream {
public:
@@ -247,7 +63,7 @@
void close();
// See dox in NetStream.h
- void pause(int mode);
+ void pause( PauseMode mode );
// See dox in NetStream.h
void play(const std::string& source);
@@ -284,48 +100,11 @@
///
static bool audio_streamer(void *udata, boost::uint8_t *stream, int
len);
-private:
-
- /// A C++ wrapper around ffmpeg's AVPacket structure
- //
- /// Used in decodeVideo() and decodeAudio().
- //
- /// Use Pkt.get() to access.
- ///
- class AvPkt
- {
- public:
-
- /// Constructs and initialize an AVPacket
- AvPkt ()
- {
- av_init_packet(&_pkt);
- }
-
- /// Properly deinitialize the owned AVPacket
- ~AvPkt ()
- {
- av_free_packet(&_pkt);
- }
-
- /// @ return AVPacket* owned by this instance
- AVPacket* get ()
- {
- return &_pkt;
-
- }
+ long bytesLoaded();
- /// @ return AVPacket* owned by this instance
- AVPacket* operator-> ()
- {
- return &_pkt;
- }
+ long bytesTotal();
- private:
- AVPacket _pkt;
- AvPkt(const AvPkt&);
- AvPkt& operator= (const AvPkt&);
- };
+private:
// Setups the playback
bool startPlayback();
@@ -378,10 +157,10 @@
bool decodeFLVFrame();
// Used to decode a video frame and push it on the videoqueue
- bool decodeVideo(AvPkt& packet);
+ bool decodeVideo( AVPacket* packet );
// Used to decode a audio frame and push it on the audioqueue
- bool decodeAudio(AvPkt& packet);
+ bool decodeAudio( AVPacket* packet );
// Used to calculate a decimal value from a ffmpeg fraction
inline double as_double(AVRational time)
@@ -406,7 +185,7 @@
AVFrame* m_Frame;
// Use for resampling audio
- AudioResampler _resampler;
+ media::AudioResampler _resampler;
// The decoding thread
boost::thread* _decodeThread;
@@ -422,15 +201,15 @@
boost::uint32_t m_current_timestamp;
// The queues of audio and video data.
- multithread_queue <raw_mediadata_t*> m_qaudio;
- multithread_queue <raw_mediadata_t*> m_qvideo;
+ media::multithread_queue <media::raw_mediadata_t*> m_qaudio;
+ media::multithread_queue <media::raw_mediadata_t*> m_qvideo;
// The time we started playing in seconds (since VM start ?)
volatile boost::uint64_t m_start_clock;
// When the queues are full, this is where we keep the audio/video frame
// there wasn't room for on its queue
- raw_mediadata_t* m_unqueued_data;
+ media::raw_mediadata_t* m_unqueued_data;
ByteIOContext ByteIOCxt;
@@ -438,8 +217,10 @@
volatile boost::uint64_t m_time_of_pause;
};
+
} // gnash namespace
+
#endif // USE_FFMPEG
#endif // __NETSTREAMFFMPEG_H__
Index: server/parser/Makefile.am
===================================================================
RCS file: /sources/gnash/gnash/server/parser/Makefile.am,v
retrieving revision 1.42
retrieving revision 1.42.2.1
diff -u -b -r1.42 -r1.42.2.1
--- server/parser/Makefile.am 21 Jan 2008 20:55:59 -0000 1.42
+++ server/parser/Makefile.am 22 Feb 2008 14:16:41 -0000 1.42.2.1
@@ -15,7 +15,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-# $Id: Makefile.am,v 1.42 2008/01/21 20:55:59 rsavoye Exp $
+# $Id: Makefile.am,v 1.42.2.1 2008/02/22 14:16:41 strk Exp $
AUTOMAKE_OPTIONS =
@@ -36,8 +36,6 @@
-I$(top_srcdir)/libbase \
-I$(top_srcdir)/libgeometry \
-I$(top_srcdir)/libmedia \
- -I$(top_srcdir)/libmedia/sdl \
- -I$(top_srcdir)/libmedia/gst \
-I$(top_srcdir)/libamf \
-I$(top_srcdir) \
$(LIBXML_CFLAGS) \
@@ -91,11 +89,13 @@
libgnashparser_la_LDFLAGS = $(BOOST_LIBS) -export-dynamic # -release
$(VERSION) -no-undefined
if USE_SOUND_GST
-AM_CPPFLAGS += $(GSTREAMER_CFLAGS)
+AM_CPPFLAGS += $(GSTREAMER_CFLAGS) \
+ -I$(top_srcdir)/libmedia/gst
endif
if USE_FFMPEG_ENGINE
-AM_CPPFLAGS += $(FFMPEG_CFLAGS)
+AM_CPPFLAGS += $(FFMPEG_CFLAGS) \
+ -I$(top_srcdir)/libmedia/ffmpeg
endif
# Rebuild with GCC 4.x Mudflap support
Index: server/parser/video_stream_def.cpp
===================================================================
RCS file: /sources/gnash/gnash/server/parser/video_stream_def.cpp,v
retrieving revision 1.39
retrieving revision 1.39.2.1
diff -u -b -r1.39 -r1.39.2.1
--- server/parser/video_stream_def.cpp 20 Feb 2008 15:54:59 -0000 1.39
+++ server/parser/video_stream_def.cpp 22 Feb 2008 14:16:41 -0000 1.39.2.1
@@ -16,14 +16,19 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
-// $Id: video_stream_def.cpp,v 1.39 2008/02/20 15:54:59 bjacques Exp $
+// $Id: video_stream_def.cpp,v 1.39.2.1 2008/02/22 14:16:41 strk Exp $
#include "video_stream_def.h"
#include "video_stream_instance.h"
#include "render.h"
#include "BitsReader.h"
-#include "VideoDecoderGst.h"
+#ifdef SOUND_GST
+# include "VideoDecoderGst.h"
+#elif defined(USE_FFMPEG)
+# include "VideoDecoderFfmpeg.h"
+#endif
+
#include <boost/bind.hpp>
@@ -39,22 +44,31 @@
{
}
+
+#ifdef SOUND_GST
void myunref(GstBuffer* buf)
{
gst_buffer_unref(buf);
}
+#endif
video_stream_definition::~video_stream_definition()
{
+#ifdef SOUND_GST
std::for_each(_video_frames.begin(), _video_frames.end(), myunref);
+#elif defined(USE_FFMPEG)
+ for ( int32_t size = _video_frames.size()-1; size >= 0; size-- ) {
+ delete _video_frames[size];
+ }
+ _video_frames.clear();
+#endif
}
void
video_stream_definition::readDefineVideoStream(stream* in, SWF::tag_type tag,
movie_definition* m)
{
-
// Character ID has been read already, and was loaded in the constructor
assert(tag == SWF::DEFINEVIDEOSTREAM);
@@ -62,7 +76,7 @@
m_start_frame = m->get_loading_frame();
- // numFrames:2 width:2 height:2 flags:1 codec:1
+ // numFrames:2 width:2 height:2 flags:1
in->ensureBytes(8);
m_num_frames = in->read_u16();
@@ -89,13 +103,18 @@
return;
}
+#ifdef SOUND_GST
_decoder.reset( new media::VideoDecoderGst(m_codec_id, _width, _height)
);
-
+#elif defined(USE_FFMPEG)
+ _decoder.reset( new media::VideoDecoderFfmpeg() );
+#endif
}
void
video_stream_definition::readDefineVideoFrame(stream* in, SWF::tag_type tag,
movie_definition* m)
{
+#ifdef SOUND_GST
+
// Character ID has been read already, and was loaded in the constructor
assert(tag == SWF::VIDEOFRAME);
@@ -129,6 +148,8 @@
in->read((char*)GST_BUFFER_DATA(buffer), dataSize);
_video_frames.push_back(buffer);
+
+#endif
}
@@ -139,15 +160,21 @@
return ch;
}
+#ifdef SOUND_GST
+
bool
has_frame_number(GstBuffer* buf, boost::uint32_t frameNumber)
{
return GST_BUFFER_OFFSET(buf) == frameNumber;
}
+#endif
+
std::auto_ptr<image::image_base>
video_stream_definition::get_frame_data(boost::uint32_t frameNum)
{
+#ifdef SOUND_GST
+
if (_video_frames.empty()) {
return std::auto_ptr<image::image_base>();
}
@@ -196,6 +223,12 @@
}
return std::auto_ptr<image::image_base>(buffer.release());
+
+#elif defined(USE_FFMPEG)
+
+ return std::auto_ptr<image::image_base>( NULL );
+
+#endif
}
Index: server/parser/video_stream_def.h
===================================================================
RCS file: /sources/gnash/gnash/server/parser/video_stream_def.h,v
retrieving revision 1.22
retrieving revision 1.22.2.1
diff -u -b -r1.22 -r1.22.2.1
--- server/parser/video_stream_def.h 27 Jan 2008 07:18:20 -0000 1.22
+++ server/parser/video_stream_def.h 22 Feb 2008 14:16:41 -0000 1.22.2.1
@@ -16,7 +16,7 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
-// $Id: video_stream_def.h,v 1.22 2008/01/27 07:18:20 bjacques Exp $
+// $Id: video_stream_def.h,v 1.22.2.1 2008/02/22 14:16:41 strk Exp $
#ifndef GNASH_VIDEO_STREAM_DEF_H
#define GNASH_VIDEO_STREAM_DEF_H
@@ -31,7 +31,13 @@
#include "swf.h"
#include "rect.h" // for composition
#include "ControlTag.h"
-#include "VideoDecoderGst.h"
+
+#ifdef SOUND_GST
+# include "VideoDecoderGst.h"
+#elif defined(USE_FFMPEG)
+# include "VideoDecoderFfmpeg.h"
+#endif
+
#include "image.h"
#include <map>
@@ -157,7 +163,12 @@
/// Elements of this vector are owned by this instance, and will be
deleted
/// at instance destruction time.
///
+#ifdef SOUND_GST
typedef std::vector<GstBuffer*> EmbedFrameVec;
+#elif defined(USE_FFMPEG)
+ typedef std::vector<uint8_t*> EmbedFrameVec;
+#endif
+
EmbedFrameVec _video_frames;
/// Last decoded frame number
@@ -170,7 +181,11 @@
boost::uint32_t _height;
/// The decoder used to decode the video frames
+#ifdef SOUND_GST
boost::scoped_ptr<media::VideoDecoderGst> _decoder;
+#elif defined(USE_FFMPEG)
+ boost::scoped_ptr<media::VideoDecoderFfmpeg> _decoder;
+#endif
};
} // end namespace gnash
Index: server/vm/Makefile.am
===================================================================
RCS file: /sources/gnash/gnash/server/vm/Makefile.am,v
retrieving revision 1.17
retrieving revision 1.17.2.1
diff -u -b -r1.17 -r1.17.2.1
--- server/vm/Makefile.am 21 Jan 2008 20:56:04 -0000 1.17
+++ server/vm/Makefile.am 22 Feb 2008 14:16:41 -0000 1.17.2.1
@@ -15,7 +15,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-# $Id: Makefile.am,v 1.17 2008/01/21 20:56:04 rsavoye Exp $
+# $Id: Makefile.am,v 1.17.2.1 2008/02/22 14:16:41 strk Exp $
AUTOMAKE_OPTIONS =
@@ -31,12 +31,8 @@
-I$(top_srcdir)/libgeometry \
-I$(top_srcdir)/libamf \
-I$(top_srcdir)/libmedia \
- -I$(top_srcdir)/libmedia/sdl \
- -I$(top_srcdir)/libmedia/gst \
-I$(top_srcdir) \
$(GLIB_CFLAGS) \
- $(FFMPEG_CFLAGS) \
- $(GSTREAMER_CFLAGS) \
$(PTHREAD_CFLAGS) \
$(DMALLOC_CFLAGS) \
$(BOOST_CFLAGS) \
Index: utilities/Makefile.am
===================================================================
RCS file: /sources/gnash/gnash/utilities/Makefile.am,v
retrieving revision 1.64
retrieving revision 1.64.2.1
diff -u -b -r1.64 -r1.64.2.1
--- utilities/Makefile.am 13 Feb 2008 02:36:34 -0000 1.64
+++ utilities/Makefile.am 22 Feb 2008 14:16:42 -0000 1.64.2.1
@@ -36,9 +36,7 @@
$(LIBLTDL) \
$(MYSQL_LIBS) \
$(LIBXML_LIBS) \
- $(FFMPEG_LIBS) \
$(CURL_LIBS) \
- $(INTLLIBS) \
$(BOOST_LIBS) \
$(PTHREAD_LIBS) \
$(NULL)
@@ -56,8 +54,6 @@
-I$(top_srcdir)/server/parser \
-I$(top_srcdir)/server/vm \
-I$(top_srcdir)/libmedia \
- -I$(top_srcdir)/libmedia/sdl \
- -I$(top_srcdir)/libmedia/gst \
-DLOCALEDIR=\"$(localedir)\" \
$(PTHREAD_CFLAGS) \
$(BOOST_CFLAGS) \
@@ -66,7 +62,8 @@
$(NULL)
if USE_FFMPEG_ENGINE
- AM_CPPFLAGS += $(FFMPEG_INCLS)
+ AM_CPPFLAGS += $(FFMPEG_CFLAGS) \
+ -I$(top_srcdir)/libmedia/ffmpeg
GNASH_LIBS += $(FFMPEG_LIBS)
endif
@@ -76,6 +73,8 @@
endif
#if USE_SOUND_GST
+ AM_CPPFLAGS += $(GSTREAMER_CFLAGS) \
+ -I$(top_srcdir)/libmedia/gst
GNASH_LIBS += $(GLIB_LIBS) $(GSTREAMER_LIBS)
#endif
@@ -86,14 +85,14 @@
gprocessor_CPPFLAGS = $(AM_CPPFLAGS)
# export our symbols so they can be used by Gnash plugins
gprocessor_LDFLAGS = -export-dynamic $(LIBLTDL)
-gprocessor_LDADD = $(GNASH_LIBS) $(AM_LDFLAGS)
+gprocessor_LDADD = $(GNASH_LIBS)
#gprocessor_DEPENDENCIES =
dumpshm_SOURCES = dumpshm.cpp
-dumpshm_LDADD = $(GNASH_LIBS) $(AM_LDFLAGS)
+dumpshm_LDADD = $(GNASH_LIBS)
soldumper_SOURCES = soldumper.cpp
-soldumper_LDADD = $(GNASH_LIBS) $(AM_LDFLAGS)
+soldumper_LDADD = $(GNASH_LIBS)
# Rebuild with GCC 4.x Mudflap support
mudflap:
Index: libmedia/ffmpeg/AudioDecoderFfmpeg.cpp
===================================================================
RCS file: libmedia/ffmpeg/AudioDecoderFfmpeg.cpp
diff -N libmedia/ffmpeg/AudioDecoderFfmpeg.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ libmedia/ffmpeg/AudioDecoderFfmpeg.cpp 22 Feb 2008 14:16:33 -0000
1.1.2.1
@@ -0,0 +1,284 @@
+// AudioDecoderFfmpeg.cpp: Audio decoding using the FFMPEG library.
+//
+// Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+// $Id: AudioDecoderFfmpeg.cpp,v 1.1.2.1 2008/02/22 14:16:33 strk Exp $
+
+#include "AudioDecoderFfmpeg.h"
+
+namespace gnash {
+namespace media {
+
+AudioDecoderFfmpeg::AudioDecoderFfmpeg ()
+ :
+ _audioCodec(NULL),
+ _audioCodecCtx(NULL),
+ _parser(NULL)
+{}
+
+AudioDecoderFfmpeg::~AudioDecoderFfmpeg()
+{
+ if (_audioCodecCtx)
+ {
+ avcodec_close(_audioCodecCtx);
+ av_free(_audioCodecCtx);
+ }
+ if (_parser) av_parser_close(_parser);
+}
+
+bool AudioDecoderFfmpeg::setup(SoundInfo* info)
+{
+ // Init the avdecoder-decoder
+ avcodec_init();
+ avcodec_register_all();// change this to only register need codec?
+
+ enum CodecID codec_id;
+
+ switch(info->getFormat()) {
+ case AUDIO_CODEC_RAW:
+ codec_id = CODEC_ID_PCM_U16LE;
+ break;
+ case AUDIO_CODEC_ADPCM:
+ codec_id = CODEC_ID_ADPCM_SWF;
+ break;
+ case AUDIO_CODEC_MP3:
+ codec_id = CODEC_ID_MP3;
+ // Init the parser
+ _parser = av_parser_init(codec_id);
+
+ if (!_parser) {
+ log_error(_("libavcodec can't parse the current
audio format"));
+ return false;
+ }
+ break;
+ default:
+ log_error(_("Unsupported audio codec %d"),
static_cast<int>(info->getFormat()));
+ return false;
+ }
+ _audioCodec = avcodec_find_decoder(codec_id);
+
+ if (!_audioCodec) {
+ log_error(_("libavcodec can't decode the current audio
format"));
+ return false;
+ }
+
+ _audioCodecCtx = avcodec_alloc_context();
+ if (!_audioCodecCtx) {
+ log_error(_("libavcodec couldn't allocate context"));
+ return false;
+ }
+
+ int ret = avcodec_open(_audioCodecCtx, _audioCodec);
+ if (ret < 0) {
+ avcodec_close(_audioCodecCtx);
+ log_error(_("libavcodec failed to initialize codec"));
+ return false;
+ }
+
+ if (_audioCodecCtx->codec->id != CODEC_ID_MP3) {
+ _audioCodecCtx->channels = (info->isStereo() ? 2 : 1);
+ _audioCodecCtx->sample_rate = info->getSampleRate();
+ _audioCodecCtx->sample_fmt = SAMPLE_FMT_S16;
+ }
+
+ return true;
+}
+
+bool AudioDecoderFfmpeg::setup(AudioInfo* info)
+{
+ // Init the avdecoder-decoder
+ avcodec_init();
+ avcodec_register_all();// change this to only register need codec?
+
+ if (info->type == FLASH) {
+ enum CodecID codec_id;
+
+ switch(info->codec)
+ {
+ case AUDIO_CODEC_RAW:
+ codec_id = CODEC_ID_PCM_U16LE;
+ break;
+ case AUDIO_CODEC_ADPCM:
+ codec_id = CODEC_ID_ADPCM_SWF;
+ break;
+ case AUDIO_CODEC_MP3:
+ codec_id = CODEC_ID_MP3;
+ break;
+ default:
+ log_error(_("Unsupported audio codec %d"),
static_cast<int>(info->codec));
+ return false;
+ }
+ _audioCodec = avcodec_find_decoder(codec_id);
+ // Init the parser
+ _parser = av_parser_init(codec_id);
+ }
+ else if (info->type == FFMPEG)
+ {
+ _audioCodec =
avcodec_find_decoder(static_cast<CodecID>(info->codec));
+ // Init the parser
+ _parser = av_parser_init(static_cast<CodecID>(info->codec));
+ }
+ else
+ {
+ return false;
+ }
+
+ if (!_audioCodec)
+ {
+ log_error(_("libavcodec can't decode the current audio
format"));
+ return false;
+ }
+
+ // Reuse the audioCodecCtx from the ffmpeg parser if exists/possible
+ if (info->audioCodecCtx)
+ {
+ log_debug("re-using the parser's audioCodecCtx");
+ _audioCodecCtx = info->audioCodecCtx;
+ }
+ else
+ {
+ _audioCodecCtx = avcodec_alloc_context();
+ }
+
+ if (!_audioCodecCtx) {
+ log_error(_("libavcodec couldn't allocate context"));
+ return false;
+ }
+
+ int ret = avcodec_open(_audioCodecCtx, _audioCodec);
+ if (ret < 0) {
+ avcodec_close(_audioCodecCtx);
+ log_error(_("libavcodec failed to initialize codec"));
+ return false;
+ }
+
+ if (_audioCodecCtx->codec->id != CODEC_ID_MP3) {
+ _audioCodecCtx->channels = (info->stereo ? 2 : 1);
+ _audioCodecCtx->sample_rate = info->sampleRate;
+ //_audioCodecCtx->sample_fmt = SAMPLE_FMT_S16;
+ }
+
+ return true;
+}
+
+boost::uint8_t* AudioDecoderFfmpeg::decode(boost::uint8_t* input,
boost::uint32_t inputSize, boost::uint32_t& outputSize, boost::uint32_t&
decodedBytes, bool parse)
+{
+
+ long bytes_decoded = 0;
+ int bufsize = (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2;
+ boost::uint8_t* output = new boost::uint8_t[bufsize];
+ boost::uint32_t orgbufsize = bufsize;
+ decodedBytes = 0;
+
+ if (parse) {
+
+ if (!_parser)
+ {
+ log_error(_("libavcodec can't parse the current audio
format"));
+ return NULL;
+ }
+
+
+ bufsize = 0;
+ while (bufsize == 0 && decodedBytes < inputSize) {
+ boost::uint8_t* frame;
+ int framesize;
+
+ bytes_decoded = av_parser_parse(_parser,
_audioCodecCtx, &frame, &framesize, input+decodedBytes, inputSize-decodedBytes,
0, 0); //the last 2 is pts & dts
+
+ int tmp = 0;
+#ifdef FFMPEG_AUDIO2
+ bufsize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
+ tmp = avcodec_decode_audio2(_audioCodecCtx,
reinterpret_cast<boost::int16_t*>(output), &bufsize, frame, framesize);
+#else
+ tmp = avcodec_decode_audio(_audioCodecCtx,
reinterpret_cast<boost::int16_t*>(output), &bufsize, frame, framesize);
+#endif
+
+ if (bytes_decoded < 0 || tmp < 0 || bufsize < 0) {
+ log_error(_("Error while decoding audio data.
Upgrading ffmpeg/libavcodec might fix this issue."));
+ // Setting data position to data size will get
the sound removed
+ // from the active sound list later on.
+ decodedBytes = inputSize;
+ break;
+ }
+
+ decodedBytes += bytes_decoded;
+ }
+
+ } else {
+
+ int tmp = 0;
+
+#ifdef FFMPEG_AUDIO2
+ tmp = avcodec_decode_audio2(_audioCodecCtx,
reinterpret_cast<boost::int16_t*>(output), &bufsize, input, inputSize);
+#else
+ tmp = avcodec_decode_audio(_audioCodecCtx,
reinterpret_cast<boost::int16_t*>(output), &bufsize, input, inputSize);
+#endif
+
+
+
+ if (bytes_decoded < 0 || tmp < 0 || bufsize < 0) {
+ log_error(_("Error while decoding audio data. Upgrading
ffmpeg/libavcodec might fix this issue."));
+ // Setting data position to data size will get the
sound removed
+ // from the active sound list later on.
+ decodedBytes = 0;
+ outputSize = 0;
+ delete [] output;
+ return NULL;
+ }
+
+ decodedBytes = inputSize;
+ }
+
+ // Error handling
+ if (bufsize < 1) {
+ log_error(_("Error while decoding audio data."));
+ delete [] output;
+ decodedBytes = 0;
+ outputSize = 0;
+ return NULL;
+ }
+
+ // Resampling is needed.
+ if (_resampler.init(_audioCodecCtx)) {
+ bool stereo = _audioCodecCtx->channels > 1 ? true : false;
+ int samples = stereo ? bufsize >> 2 : bufsize >> 1;
+
+ boost::uint8_t* tmp = new boost::uint8_t[orgbufsize];
+
+ samples =
_resampler.resample(reinterpret_cast<boost::int16_t*>(output),
+
reinterpret_cast<boost::int16_t*>(tmp),
+ samples);
+ outputSize = samples *2 *2; // the resampled audio has
samplesize 2, and is stereo
+ boost::uint8_t* ret = new boost::uint8_t[outputSize];
+ memcpy(ret, tmp, outputSize);
+ delete [] tmp;
+ delete [] output;
+ return ret;
+ } else {
+ outputSize = bufsize;
+ boost::uint8_t* ret = new boost::uint8_t[outputSize];
+ memcpy(ret, output, outputSize);
+ delete [] output;
+ return ret;
+ }
+}
+
+
+} // gnash.media namespace
+} // gnash namespace
Index: libmedia/ffmpeg/AudioDecoderFfmpeg.h
===================================================================
RCS file: libmedia/ffmpeg/AudioDecoderFfmpeg.h
diff -N libmedia/ffmpeg/AudioDecoderFfmpeg.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ libmedia/ffmpeg/AudioDecoderFfmpeg.h 22 Feb 2008 14:16:34 -0000
1.1.2.1
@@ -0,0 +1,63 @@
+// AudioDecoderFfmpeg.h: Audio decoding using the FFMPEG library.
+//
+// Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+// $Id: AudioDecoderFfmpeg.h,v 1.1.2.1 2008/02/22 14:16:34 strk Exp $
+
+#ifndef __AUDIODECODERFFMPEG_H__
+#define __AUDIODECODERFFMPEG_H__
+
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS
+#endif
+
+extern "C" {
+#include <ffmpeg/avcodec.h>
+}
+
+#include "log.h"
+#include "AudioDecoder.h"
+#include "ffmpegNetStreamUtil.h"
+
+namespace gnash {
+namespace media {
+
+class AudioDecoderFfmpeg : public AudioDecoder {
+
+public:
+ AudioDecoderFfmpeg();
+ ~AudioDecoderFfmpeg();
+
+ bool setup(AudioInfo* info);
+ bool setup(SoundInfo* info);
+
+ boost::uint8_t* decode(boost::uint8_t* input, boost::uint32_t
inputSize, boost::uint32_t& outputSize, boost::uint32_t& decodedBytes, bool
parse);
+
+private:
+
+ AVCodec* _audioCodec;
+ AVCodecContext* _audioCodecCtx;
+ AVCodecParserContext* _parser;
+
+ // Use for resampling audio
+ AudioResampler _resampler;
+};
+
+} // gnash.media namespace
+} // gnash namespace
+
+#endif // __AUDIODECODERFFMPEG_H__
Index: libmedia/ffmpeg/VideoDecoderFfmpeg.cpp
===================================================================
RCS file: libmedia/ffmpeg/VideoDecoderFfmpeg.cpp
diff -N libmedia/ffmpeg/VideoDecoderFfmpeg.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ libmedia/ffmpeg/VideoDecoderFfmpeg.cpp 22 Feb 2008 14:16:34 -0000
1.1.2.1
@@ -0,0 +1,374 @@
+// VideoDecoderFfmpeg.cpp: Video decoding using the FFMPEG library.
+//
+// Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+// $Id: VideoDecoderFfmpeg.cpp,v 1.1.2.1 2008/02/22 14:16:34 strk Exp $
+
+#include "VideoDecoderFfmpeg.h"
+
+#ifdef HAVE_SWSCALE_H
+extern "C" {
+#include <ffmpeg/swscale.h>
+}
+#endif
+#include <boost/scoped_array.hpp>
+
+namespace gnash {
+namespace media {
+
+VideoDecoderFfmpeg::VideoDecoderFfmpeg ()
+ :
+ _videoCodec(NULL),
+ _videoCodecCtx(NULL)
+{}
+
+VideoDecoderFfmpeg::~VideoDecoderFfmpeg()
+{
+ if (_videoCodecCtx)
+ {
+ avcodec_close(_videoCodecCtx);
+ av_free(_videoCodecCtx);
+ }
+}
+
+bool VideoDecoderFfmpeg::setup(
+ int width,
+ int height,
+ int /*deblocking*/,
+ bool /*smoothing*/,
+ videoCodecType format, // should this argument be of codecType
type ?
+ int /*outputFormat*/)
+{
+ // Init the avdecoder-decoder
+ avcodec_init();
+ avcodec_register_all();// change this to only register need codec?
+
+ enum CodecID codec_id;
+
+ // Find the decoder and init the parser
+ switch(format) {
+ case VIDEO_CODEC_H263:
+ codec_id = CODEC_ID_FLV1;
+ break;
+#ifdef FFMPEG_VP6
+ case VIDEO_CODEC_VP6:
+ codec_id = CODEC_ID_VP6F;
+ break;
+#endif
+ case VIDEO_CODEC_SCREENVIDEO:
+ codec_id = CODEC_ID_FLASHSV;
+ break;
+ default:
+ log_error(_("Unsupported video codec %d"),
+ static_cast<int>(format));
+ return false;
+ }
+
+ _videoCodec = avcodec_find_decoder(static_cast<CodecID>(codec_id));
+
+ if (!_videoCodec) {
+ log_error(_("libavcodec can't decode the current video
format"));
+ return false;
+ }
+
+ _videoCodecCtx = avcodec_alloc_context();
+ if (!_videoCodecCtx) {
+ log_error(_("libavcodec couldn't allocate context"));
+ return false;
+ }
+
+ int ret = avcodec_open(_videoCodecCtx, _videoCodec);
+ if (ret < 0) {
+ log_error(_("libavcodec failed to initialize codec"));
+ return false;
+ }
+ _videoCodecCtx->width = width;
+ _videoCodecCtx->height = height;
+
+ assert(_videoCodecCtx->width > 0);
+ assert(_videoCodecCtx->height > 0);
+ return true;
+}
+
+bool VideoDecoderFfmpeg::setup(VideoInfo* info)
+{
+ // Init the avdecoder-decoder
+ avcodec_init();
+ avcodec_register_all();// change this to only register need codec?
+
+ if (info->type == FLASH) {
+ enum CodecID codec_id;
+
+ // Find the decoder and init the parser
+ switch(info->codec) {
+ case VIDEO_CODEC_H263:
+ codec_id = CODEC_ID_FLV1;
+ break;
+#ifdef FFMPEG_VP6
+ case VIDEO_CODEC_VP6:
+ codec_id = CODEC_ID_VP6F;
+ break;
+#endif
+ case VIDEO_CODEC_SCREENVIDEO:
+ codec_id = CODEC_ID_FLASHSV;
+ break;
+ default:
+ log_error(_("Unsupported video codec %d"),
+ static_cast<int>(info->codec));
+ return false;
+ }
+ _videoCodec =
avcodec_find_decoder(static_cast<CodecID>(codec_id));
+ } else if (info->type == FFMPEG) {
+ _videoCodec =
avcodec_find_decoder(static_cast<CodecID>(info->codec));
+ } else {
+ //log_error("Video codecType unknown: %d, %d, %d",
+ // info->type, FLASH, FFMPEG);
+ return false;
+ }
+
+ if (!_videoCodec) {
+ log_error(_("libavcodec can't decode the current video
format"));
+ return false;
+ }
+
+ // Reuse the videoCodecCtx from the ffmpeg parser if exists/possible
+ if (info->videoCodecCtx) {
+ log_debug("re-using the parsers videoCodecCtx");
+ _videoCodecCtx = info->videoCodecCtx;
+ } else {
+ _videoCodecCtx = avcodec_alloc_context();
+ }
+
+ if (!_videoCodecCtx) {
+ log_error(_("libavcodec couldn't allocate context"));
+ return false;
+ }
+
+ int ret = avcodec_open(_videoCodecCtx, _videoCodec);
+ if (ret < 0) {
+ log_error(_("libavcodec failed to initialize codec"));
+ return false;
+ }
+
+ return true;
+}
+
+boost::uint8_t*
+VideoDecoderFfmpeg::convertRGB24(AVCodecContext* srcCtx, AVFrame* srcFrame)
+{
+ int width = srcCtx->width, height = srcCtx->height;
+
+ int bufsize = avpicture_get_size(PIX_FMT_RGB24, width, height);
+ if (bufsize == -1) {
+ return NULL;
+ }
+
+ boost::uint8_t* buffer = new boost::uint8_t[bufsize];
+ if (!buffer) {
+ return NULL;
+ }
+
+ AVPicture picture;
+
+ avpicture_fill(&picture, buffer, PIX_FMT_RGB24, width, height);
+
+#ifndef HAVE_SWSCALE_H
+ img_convert(&picture, PIX_FMT_RGB24, (AVPicture*) srcFrame,
+ srcCtx->pix_fmt, width, height);
+#else
+ static struct SwsContext* context = NULL;
+
+ if (!context)
+ {
+ context = sws_getContext(width, height, srcCtx->pix_fmt,
+ width, height, PIX_FMT_RGB24,
+ SWS_FAST_BILINEAR, NULL, NULL, NULL);
+
+ if (!context)
+ {
+ delete [] buffer;
+ return NULL;
+ }
+ }
+
+ int rv = sws_scale(
+ context, srcFrame->data, srcFrame->linesize, 0, height,
+ picture.data, picture.linesize
+ );
+
+ if (rv == -1)
+ {
+ delete [] buffer;
+ return NULL;
+ }
+
+#endif // HAVE_SWSCALE_H
+
+ srcFrame->linesize[0] = picture.linesize[0];
+ srcFrame->data[0] = picture.data[0];
+
+ return buffer;
+}
+
+boost::uint8_t* VideoDecoderFfmpeg::decode(boost::uint8_t* input,
+ boost::uint32_t inputSize,
+ boost::uint32_t& outputSize)
+{
+ // Allocate a frame to store the decoded frame in
+ AVFrame* frame = avcodec_alloc_frame();
+ if ( ! frame )
+ {
+ log_error(_("Out of memory while allocating avcodec frame"));
+ throw std::bad_alloc();
+ }
+
+ int got = 0;
+
+ avcodec_decode_video(_videoCodecCtx, frame, &got, input, inputSize);
+
+ if (got)
+ {
+ boost::scoped_array<boost::uint8_t> buffer;
+
+ // Set to the next multiple of four. Some videos have
+ // padding bytes, so that the source width is more than three
times
+ // the video width. A likely explanation (supported by
+ // tests) is that it is always padded out to a multiple of 4.
+ // Have found no documenation on this.
+ unsigned int srcwidth = (_videoCodecCtx->width * 3 + 3) &~ 3;
+
+ boost::uint8_t* decodedData = new boost::uint8_t[srcwidth *
_videoCodecCtx->height];
+
+ buffer.reset(convertRGB24(_videoCodecCtx, frame));
+
+ // Copy the data to the buffer in the correct RGB format
+ boost::uint8_t* srcptr = frame->data[0];
+ boost::uint8_t* srcend = frame->data[0]
+ + frame->linesize[0]
+ * _videoCodecCtx->height;
+ boost::uint8_t* dstptr = decodedData;
+
+ outputSize = 0;
+
+ while (srcptr < srcend)
+ {
+ memcpy(dstptr, srcptr, srcwidth);
+ srcptr += frame->linesize[0];
+ dstptr += srcwidth;
+ outputSize += srcwidth;
+ }
+
+ av_free(frame);
+ return decodedData;
+
+/* if (_videoFrameFormat == NONE) { // NullGui?
+ return;
+
+ } else if (_videoFrameFormat == YUV && _videoCodecCtx->pix_fmt
!= PIX_FMT_YUV420P) {
+ abort(); // TODO
+ //img_convert((AVPicture*) pFrameYUV, PIX_FMT_YUV420P,
(AVPicture*) pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
+ // Don't use depreceted img_convert, use sws_scale
+
+ } else if (_videoFrameFormat == RGB && _videoCodecCtx->pix_fmt
!= PIX_FMT_RGB24) {
+ buffer.reset(convertRGB24(_videoCodecCtx, frame));
+ }
+
+ raw_mediadata_t* video = new raw_mediadata_t;
+ if (_videoFrameFormat == YUV) {
+ abort(); // See image.cpp to see what yuv size is
+ //video->m_data = new
boost::uint8_t[static_cast<image::yuv*>(m_imageframe)->size()];
+ } else if (_videoFrameFormat == RGB) {
+ video->m_data = new
boost::uint8_t[_videoCodecCtx->width * _videoCodecCtx->height * 3];
+ //}
+
+ video->m_ptr = video->m_data;
+ video->m_stream_index = _videoIndex;
+ video->m_pts = 0;
+
+ video->m_pts =
static_cast<boost::uint32_t>((as_double(_videoStream->time_base) * packet->dts)
* 1000.0);
+
+
+ if (_videoFrameFormat == YUV) {
+ //image::yuv* yuvframe =
static_cast<image::yuv*>(_imageframe);
+ int copied = 0;
+ boost::uint8_t* ptr = video->m_data;
+ for (int i = 0; i < 3 ; i++)
+ {
+ int shift = (i == 0 ? 0 : 1);
+ boost::uint8_t* yuv_factor = _frame->data[i];
+ int h = _videoCodecCtx->height >> shift;
+ int w = _videoCodecCtx->width >> shift;
+ for (int j = 0; j < h; j++)
+ {
+ copied += w;
+ //assert(copied <= yuvframe->size());
+ memcpy(ptr, yuv_factor, w);
+ yuv_factor += _frame->linesize[i];
+ ptr += w;
+ }
+ }
+ video->m_size = copied;
+ } else if (_videoFrameFormat == RGB) {
+
+ boost::uint8_t* srcptr = _frame->data[0];
+ boost::uint8_t* srcend = _frame->data[0] +
_frame->linesize[0] * _videoCodecCtx->height;
+ boost::uint8_t* dstptr = video->m_data;
+ unsigned int srcwidth = _videoCodecCtx->width * 3;
+
+ video->m_size = 0;
+
+ while (srcptr < srcend) {
+ memcpy(dstptr, srcptr, srcwidth);
+ srcptr += _frame->linesize[0];
+ dstptr += srcwidth;
+ video->m_size += srcwidth;
+ }
+
+ }*/
+ }
+ else
+ {
+ log_error("Decoding of a video frame failed");
+ av_free(frame);
+ return NULL;
+ }
+}
+
+std::auto_ptr<image::image_base>
+VideoDecoderFfmpeg::decodeToImage(boost::uint8_t* input, boost::uint32_t
inputSize)
+{
+ boost::uint32_t outputSize = 0;
+ boost::uint8_t* decodedData = decode(input, inputSize, outputSize);
+
+ if (!decodedData || outputSize == 0)
+ {
+ return std::auto_ptr<image::image_base>(NULL);
+ }
+
+ std::auto_ptr<image::image_base> ret(new image::rgb(
+ _videoCodecCtx->width,
+ _videoCodecCtx->height
+ ));
+ ret->update(decodedData);
+ delete [] decodedData;
+ return ret;
+
+}
+
+} // gnash.media namespace
+} // gnash namespace
Index: libmedia/ffmpeg/VideoDecoderFfmpeg.h
===================================================================
RCS file: libmedia/ffmpeg/VideoDecoderFfmpeg.h
diff -N libmedia/ffmpeg/VideoDecoderFfmpeg.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ libmedia/ffmpeg/VideoDecoderFfmpeg.h 22 Feb 2008 14:16:35 -0000
1.1.2.1
@@ -0,0 +1,74 @@
+// VideoDecoderFfmpeg.h: Video decoding using the FFMPEG library.
+//
+// Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+// $Id: VideoDecoderFfmpeg.h,v 1.1.2.1 2008/02/22 14:16:35 strk Exp $
+
+#ifndef __VIDEODECODERFFMPEG_H__
+#define __VIDEODECODERFFMPEG_H__
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include "log.h"
+#include "VideoDecoder.h"
+
+extern "C" {
+#include <ffmpeg/avcodec.h>
+}
+
+
+namespace gnash {
+namespace media {
+
+
+class VideoDecoderFfmpeg : public VideoDecoder {
+
+public:
+ VideoDecoderFfmpeg();
+ ~VideoDecoderFfmpeg();
+
+ virtual unsigned getPaddingBytes() const { return
FF_INPUT_BUFFER_PADDING_SIZE; }
+
+ bool setup(VideoInfo* info);
+
+ bool setup(
+ int /*width*/,
+ int /*height*/,
+ int /*deblocking*/,
+ bool /*smoothing*/,
+ videoCodecType /*format*/,
+ int /*outputFormat*/);
+
+ boost::uint8_t* decode(boost::uint8_t* input, boost::uint32_t
inputSize, boost::uint32_t& outputSize);
+
+ std::auto_ptr<image::image_base> decodeToImage(boost::uint8_t*
/*input*/, boost::uint32_t /*inputSize*/);
+
+ static boost::uint8_t* convertRGB24(AVCodecContext* srcCtx, AVFrame*
srcFrame);
+
+private:
+
+ AVCodec* _videoCodec;
+ AVCodecContext* _videoCodecCtx;
+
+};
+
+} // gnash.media namespace
+} // gnash namespace
+
+#endif // __VIDEODECODERFFMPEG_H__
Index: libmedia/ffmpeg/ffmpegNetStreamUtil.cpp
===================================================================
RCS file: libmedia/ffmpeg/ffmpegNetStreamUtil.cpp
diff -N libmedia/ffmpeg/ffmpegNetStreamUtil.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ libmedia/ffmpeg/ffmpegNetStreamUtil.cpp 22 Feb 2008 14:16:35 -0000
1.1.2.1
@@ -0,0 +1,127 @@
+// ffmpegNetStreamUtil.cpp: Utility classes for use in
+// server/asobj/NetStreamFfmpeg.*
+//
+// Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+// #include "utility.h" // for convert_raw_data
+// #include "AudioDecoderSimple.h"
+// #include "AudioDecoderNellymoser.h"
+
+#ifdef USE_FFMPEG
+# include "ffmpegNetStreamUtil.h"
+#endif
+
+
+#include "log.h"
+#include <cmath>
+#include <vector>
+#include <boost/scoped_array.hpp>
+#include <SDL.h>
+
+namespace gnash {
+namespace media {
+
+raw_mediadata_t::raw_mediadata_t()
+ :
+ m_stream_index(-1),
+ m_size(0),
+ m_data(NULL),
+ m_ptr(NULL),
+ m_pts(0)
+{
+}
+
+raw_mediadata_t::~raw_mediadata_t()
+{
+ if ( m_data ) {
+ delete [] m_data;
+ }
+}
+
+
+AudioResampler::AudioResampler()
+ :_context(NULL)
+{
+}
+
+AudioResampler::~AudioResampler()
+{
+ if ( _context ) {
+ audio_resample_close( _context );
+ }
+}
+
+/// Initialize the resampler
+//
+/// @param ctx
+/// The audio format container.
+///
+/// @return true if resampling is needed, false if not
+///
+bool
+AudioResampler::init( AVCodecContext* ctx )
+{
+ if ( (ctx->sample_rate != 44100) || (ctx->channels != 2) ) {
+ if ( ! _context ) {
+ _context = audio_resample_init(
+ 2, ctx->channels, 44100, ctx->sample_rate
+ );
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/// Resample audio
+//
+/// @param input
+/// A pointer to the audio data that needs resampling
+///
+/// @param output
+/// A pointer to where the resampled output should be placed
+///
+/// @param samples
+/// Number of samples in the audio
+///
+/// @return the number of samples in the output data.
+///
+int
+AudioResampler::resample(
+ boost::int16_t* input,
+ boost::int16_t* output,
+ int samples
+ )
+{
+ return audio_resample( _context, output, input, samples );
+}
+
+
+} // gnash.media namespace
+} // namespace gnash
+
+// Local Variables:
+// mode: C++
+// End:
+
Index: libmedia/ffmpeg/ffmpegNetStreamUtil.h
===================================================================
RCS file: libmedia/ffmpeg/ffmpegNetStreamUtil.h
diff -N libmedia/ffmpeg/ffmpegNetStreamUtil.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ libmedia/ffmpeg/ffmpegNetStreamUtil.h 22 Feb 2008 14:16:36 -0000
1.1.2.1
@@ -0,0 +1,221 @@
+// ffmpegNetStreamUtil.h: Utility classes for use in
+// server/asobj/NetStreamFfmpeg.*
+//
+// Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+#ifndef FFMPEGNETSTREAMUTIL_H
+#define FFMPEGNETSTREAMUTIL_H
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include "log.h"
+
+#ifdef USE_FFMPEG
+extern "C" {
+#include <ffmpeg/avcodec.h>
+}
+#endif
+
+#include <queue>
+
+#include <SDL_audio.h>
+#include <boost/cstdint.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition.hpp>
+
+namespace gnash {
+namespace media {
+
+class raw_mediadata_t
+{
+public:
+ raw_mediadata_t();
+
+ ~raw_mediadata_t();
+
+ int m_stream_index;
+ boost::uint32_t m_size;
+ boost::uint8_t* m_data;
+ boost::uint8_t* m_ptr;
+ boost::uint32_t m_pts; // presentation timestamp in millisec
+};
+
+
+/// Threadsafe elements-owning queue
+//
+/// This class is a threadsafe queue, using std:queue and locking.
+/// It is used to store decoded audio and video data which are waiting to be
"played"
+/// Elements of the queue are owned by instances of this class.
+///
+template<class T>
+class multithread_queue
+{
+public:
+
+ multithread_queue()
+ {
+ }
+
+ // Destroy all elements of the queue. Locks.
+ ~multithread_queue()
+ {
+ clear();
+ }
+
+ // Destroy all elements of the queue. Locks.
+ void clear()
+ {
+ boost::mutex::scoped_lock lock( _mutex );
+
+ while ( ! m_queue.empty() ) {
+ T x = m_queue.front();
+ m_queue.pop();
+ delete x;
+ }
+ }
+
+ /// Returns the size if the queue. Locks.
+ //
+ /// @return the size of the queue
+ ///
+ size_t size()
+ {
+ boost::mutex::scoped_lock lock( _mutex );
+
+ size_t n = m_queue.size();
+
+ return n;
+ }
+
+ /// Pushes an element to the queue. Locks.
+ //
+ /// @param member
+ /// The element to be pushed unto the queue.
+ ///
+ /// @return true if queue isn't full and the element was pushed to the
queue,
+ /// or false if the queue was full, and the element wasn't push unto it.
+ ///
+ bool push(T member)
+ {
+ bool rc = false;
+ boost::mutex::scoped_lock lock( _mutex );
+
+ // We only keep max 20 items in the queue.
+ // If it's "full" the item must wait, see calls to
+ // this function in read_frame() to see how it is
+ // done.
+ if ( m_queue.size() < 20 ) {
+ m_queue.push( member );
+ rc = true;
+ }
+
+ return rc;
+ }
+
+ /// Returns a pointer to the first element on the queue. Locks.
+ //
+ /// If no elements are available this function returns NULL.
+ ///
+ /// @return a pointer to the first element on the queue, NULL if queue
is empty.
+ ///
+ T front()
+ {
+ boost::mutex::scoped_lock lock( _mutex );
+
+ T member = NULL;
+
+ if ( ! m_queue.empty() ) {
+ member = m_queue.front();
+ }
+
+ return member;
+ }
+
+ /// Pops the first element from the queue. Locks.
+ //
+ /// If no elements are available this function is
+ /// a noop.
+ ///
+ void pop()
+ {
+ boost::mutex::scoped_lock lock( _mutex );
+
+ if ( ! m_queue.empty() ) {
+ m_queue.pop();
+ }
+ }
+
+private:
+
+ // Mutex used for locking
+ boost::mutex _mutex;
+
+ // The actual queue.
+ std::queue<T> m_queue;
+};
+
+
+/// This class is used to provide an easy interface to libavcodecs audio
resampler.
+///
+class AudioResampler
+{
+public:
+ AudioResampler();
+
+ ~AudioResampler();
+
+ /// Initializes the resampler
+ //
+ /// @param ctx
+ /// The audio format container.
+ ///
+ /// @return true if resampling is needed, if not false
+ ///
+ bool init(AVCodecContext* ctx);
+
+ /// Resamples audio
+ //
+ /// @param input
+ /// A pointer to the audio data that needs resampling
+ ///
+ /// @param output
+ /// A pointer to where the resampled output should be placed
+ ///
+ /// @param samples
+ /// Number of samples in the audio
+ ///
+ /// @return the number of samples in the output data.
+ ///
+ int resample(
+ boost::int16_t* input, boost::int16_t* output, int samples
+ );
+
+private:
+ // The container of the resample format information.
+ ReSampleContext* _context;
+};
+
+} // gnash.media namespace
+} // namespace gnash
+
+
+#endif // FFMPEGNETSTREAMUTIL_H
Index: libmedia/ffmpeg/sound_handler_sdl.cpp
===================================================================
RCS file: libmedia/ffmpeg/sound_handler_sdl.cpp
diff -N libmedia/ffmpeg/sound_handler_sdl.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ libmedia/ffmpeg/sound_handler_sdl.cpp 22 Feb 2008 14:16:36 -0000
1.1.2.1
@@ -0,0 +1,826 @@
+// sound_handler_sdl.cpp: Sound handling using standard SDL
+//
+// Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+// Based on sound_handler_sdl.cpp by Thatcher Ulrich http://tulrich.com 2003
+// which has been donated to the Public Domain.
+
+// $Id: sound_handler_sdl.cpp,v 1.1.2.1 2008/02/22 14:16:36 strk Exp $
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include "sound_handler_sdl.h"
+#include "utility.h" // for convert_raw_data
+#include "AudioDecoderSimple.h"
+#include "AudioDecoderNellymoser.h"
+
+#ifdef USE_FFMPEG
+#include "AudioDecoderFfmpeg.h"
+#endif
+
+#ifdef USE_MAD
+#include "AudioDecoderMad.h"
+#endif
+
+#include "log.h"
+#include <cmath>
+#include <vector>
+#include <boost/scoped_array.hpp>
+#include <SDL.h>
+
+namespace gnash {
+namespace media {
+
+SDL_sound_handler::SDL_sound_handler()
+ : soundOpened(false),
+ soundsPlaying(0),
+ muted(false)
+{
+ // This is our sound settings
+ audioSpec.freq = 44100;
+ audioSpec.format = AUDIO_S16SYS; // AUDIO_S8 AUDIO_U8;
+ audioSpec.channels = 2;
+ audioSpec.callback = SDL_sound_handler::sdl_audio_callback;
+ audioSpec.userdata = this;
+ audioSpec.samples = 2048; //512 - not enough for
videostream
+}
+
+void
+SDL_sound_handler::reset()
+{
+ //delete_all_sounds();
+ stop_all_sounds();
+}
+
+void
+SDL_sound_handler::delete_all_sounds()
+{
+ stop_all_sounds();
+ for (size_t i=0, e=m_sound_data.size(); i < e; ++i)
+ {
+ stop_sound(i);
+ delete_sound(i);
+ }
+ m_sound_data.clear();
+}
+
+SDL_sound_handler::~SDL_sound_handler()
+{
+ delete_all_sounds();
+ if (soundOpened) SDL_CloseAudio();
+}
+
+
+int SDL_sound_handler::create_sound(
+ void* data,
+ unsigned int data_bytes,
+ std::auto_ptr<SoundInfo> sinfo)
+// Called to create a sample. We'll return a sample ID that
+// can be use for playing it.
+{
+
+ assert(sinfo.get());
+ std::auto_ptr<sound_data> sounddata ( new sound_data );
+
+ //sounddata->data_size = data_bytes;
+ sounddata->volume = 100;
+ sounddata->soundinfo = sinfo;
+
+ boost::mutex::scoped_lock lock(_mutex);
+
+ switch (sounddata->soundinfo->getFormat())
+ {
+ case AUDIO_CODEC_MP3:
+#ifndef USE_FFMPEG
+#ifndef USE_MAD
+ log_error(_("gnash has not been compiled to handle mp3 audio"));
+ return -1;
+#endif
+#endif
+ sounddata->append(reinterpret_cast<boost::uint8_t*>(data),
data_bytes);
+ break;
+
+ case AUDIO_CODEC_RAW:
+ case AUDIO_CODEC_ADPCM:
+ case AUDIO_CODEC_UNCOMPRESSED:
+ case AUDIO_CODEC_NELLYMOSER:
+ sounddata->append(reinterpret_cast<boost::uint8_t*>(data),
data_bytes);
+ break;
+
+ default:
+ // Unhandled format.
+ log_error(_("unknown sound format %d requested; gnash does not
handle it"), (int)sounddata->soundinfo->getFormat());
+ return -1; // Unhandled format, set to NULL.
+ }
+
+ m_sound_data.push_back(sounddata.release()); // the vector takes
ownership
+ int sound_id = m_sound_data.size()-1;
+
+ return sound_id;
+
+}
+
+// this gets called when a stream gets more data
+long SDL_sound_handler::fill_stream_data(unsigned char* data, unsigned int
data_bytes, unsigned int /*sample_count*/, int handle_id)
+
+{
+
+ boost::mutex::scoped_lock lock(_mutex);
+ // @@ does a negative handle_id have any meaning ?
+ // should we change it to unsigned instead ?
+ if (handle_id < 0 || (unsigned int) handle_id+1 > m_sound_data.size())
+ {
+ delete [] data;
+ return -1;
+ }
+ sound_data* sounddata = m_sound_data[handle_id];
+
+ // If doing ADPCM, knowing the framesize is needed to decode!
+ if (sounddata->soundinfo->getFormat() == AUDIO_CODEC_ADPCM) {
+ sounddata->m_frames_size[sounddata->size()] = data_bytes;
+ }
+
+ // Handling of the sound data
+ size_t start_size = sounddata->size();
+ sounddata->append(reinterpret_cast<boost::uint8_t*>(data), data_bytes);
+
+ return start_size;
+}
+
+
+void SDL_sound_handler::play_sound(int sound_handle, int loop_count, int
offset, long start_position, const std::vector<sound_envelope>* envelopes)
+// Play the index'd sample.
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ // Check if the sound exists, or if audio is muted
+ if (sound_handle < 0 || static_cast<unsigned int>(sound_handle) >=
m_sound_data.size() || muted)
+ {
+ // Invalid handle or muted
+ return;
+ }
+
+ sound_data* sounddata = m_sound_data[sound_handle];
+
+ // If this is called from a streamsoundblocktag, we only start if this
+ // sound isn't already playing.
+ if (start_position > 0 && sounddata->m_active_sounds.size() > 0) {
+ return;
+ }
+
+ // Make sure sound actually got some data
+ if (sounddata->size() < 1) {
+ IF_VERBOSE_MALFORMED_SWF(
+ log_swferror(_("Trying to play sound with size 0"));
+ );
+ return;
+ }
+
+ // Make a "active_sound" for this sound which is later placed on the
vector of instances of this sound being played
+ std::auto_ptr<active_sound> sound ( new active_sound() );
+
+ // Set source data to the active_sound
+ sound->set_data(sounddata);
+
+ // Set the given options of the sound
+ if (start_position < 0) sound->position = 0;
+ else sound->position = start_position;
+
+ if (offset < 0) sound->offset = 0;
+ else sound->offset = (sounddata->soundinfo->isStereo() ? offset :
offset*2); // offset is stored as stereo
+
+ sound->envelopes = envelopes;
+
+ // Set number of loop we should do. -1 is infinte loop, 0 plays it
once, 1 twice etc.
+ sound->loop_count = loop_count;
+
+ sound->decoder = NULL;
+
+ switch (sounddata->soundinfo->getFormat()) {
+ case AUDIO_CODEC_NELLYMOSER:
+ case AUDIO_CODEC_NELLYMOSER_8HZ_MONO:
+ sound->decoder = new AudioDecoderNellymoser();
+
+ if (!sound->decoder->setup(sounddata->soundinfo.get())) {
+ log_error("The audio decoder can't decode the audio");
+ delete sound->decoder;
+ sound->decoder = NULL;
+ }
+
+ break;
+ case AUDIO_CODEC_MP3:
+#ifdef USE_MAD
+ sound->decoder = new AudioDecoderMad();
+
+ if (!sound->decoder->setup(sounddata->soundinfo.get())) {
+ log_error("The audio decoder can't decode the audio");
+ delete sound->decoder;
+ sound->decoder = NULL;
+ }
+
+ break;
+#endif
+#ifdef USE_FFMPEG
+ sound->decoder = new AudioDecoderFfmpeg();
+
+ if (!sound->decoder->setup(sounddata->soundinfo.get())) {
+ log_error("The audio decoder can't decode the audio");
+ delete sound->decoder;
+ sound->decoder = NULL;
+ }
+
+ break;
+#endif
+ case AUDIO_CODEC_ADPCM:
+ default:
+
+ sound->decoder = new AudioDecoderSimple();
+
+ if (!sound->decoder->setup(sounddata->soundinfo.get())) {
+ log_error("The audio decoder can't decode the audio");
+ delete sound->decoder;
+ sound->decoder = NULL;
+ }
+
+ }
+
+ if (!soundOpened) {
+ if (SDL_OpenAudio(&audioSpec, NULL) < 0 ) {
+ log_error(_("Unable to start SDL sound: %s"),
SDL_GetError());
+ return;
+ }
+ soundOpened = true;
+
+ }
+
+ ++soundsPlaying;
+ ++_soundsStarted;
+ sounddata->m_active_sounds.push_back(sound.release());
+
+ if (soundsPlaying == 1) {
+ SDL_PauseAudio(0);
+ }
+
+}
+
+
+void SDL_sound_handler::stop_sound(int sound_handle)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ // Check if the sound exists.
+ if (sound_handle < 0 || (unsigned int) sound_handle >=
m_sound_data.size())
+ {
+ // Invalid handle.
+ } else {
+
+ sound_data* sounddata = m_sound_data[sound_handle];
+
+ for (boost::int32_t i = (boost::int32_t)
sounddata->m_active_sounds.size()-1; i >-1; i--) {
+
+ //active_sound* sound = sounddata->m_active_sounds[i];
+
+ // Stop sound, remove it from the active list
+ //sound->delete_raw_data();
+ //delete sound->decoder;
+
sounddata->m_active_sounds.erase(sounddata->m_active_sounds.begin() + i);
+
+ --soundsPlaying;
+ ++_soundsStopped;
+ }
+ }
+
+}
+
+
+// this gets called when it's done with a sample.
+void SDL_sound_handler::delete_sound(int sound_handle)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ if (sound_handle >= 0 && static_cast<unsigned int>(sound_handle) <
m_sound_data.size())
+ {
+ delete m_sound_data[sound_handle];
+ m_sound_data[sound_handle] = NULL;
+ }
+
+}
+
+// This will stop all sounds playing. Will cause problems if the soundhandler
is made static
+// and supplys sound_handling for many SWF's, since it will stop all sounds
with no regard
+// for what sounds is associated with what SWF.
+void SDL_sound_handler::stop_all_sounds()
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ boost::int32_t num_sounds = (boost::int32_t) m_sound_data.size()-1;
+ for (boost::int32_t j = num_sounds; j > -1; j--) {//Optimized
+ sound_data* sounddata = m_sound_data[j];
+ boost::int32_t num_active_sounds = (boost::int32_t)
sounddata->m_active_sounds.size()-1;
+ for (boost::int32_t i = num_active_sounds; i > -1; i--) {
+
+ //active_sound* sound = sounddata->m_active_sounds[i];
+
+ // Stop sound, remove it from the active list
+ //delete sound->decoder;
+
sounddata->m_active_sounds.erase(sounddata->m_active_sounds.begin() + i);
+
+ --soundsPlaying;
+ ++_soundsStopped;
+ }
+ }
+}
+
+
+// returns the sound volume level as an integer from 0 to 100,
+// where 0 is off and 100 is full volume. The default setting is 100.
+int SDL_sound_handler::get_volume(int sound_handle) {
+
+ boost::mutex::scoped_lock lock(_mutex);
+
+ int ret;
+ // Check if the sound exists.
+ if (sound_handle >= 0 && static_cast<unsigned int>(sound_handle) <
m_sound_data.size())
+ {
+ ret = m_sound_data[sound_handle]->volume;
+ } else {
+ ret = 0; // Invalid handle
+ }
+ return ret;
+}
+
+
+// A number from 0 to 100 representing a volume level.
+// 100 is full volume and 0 is no volume. The default setting is 100.
+void SDL_sound_handler::set_volume(int sound_handle, int volume) {
+
+ boost::mutex::scoped_lock lock(_mutex);
+
+ // Check if the sound exists.
+ if (sound_handle < 0 || static_cast<unsigned int>(sound_handle) >=
m_sound_data.size())
+ {
+ // Invalid handle.
+ } else {
+
+ // Set volume for this sound. Should this only apply to the
active sounds?
+ m_sound_data[sound_handle]->volume = volume;
+ }
+
+
+}
+
+SoundInfo* SDL_sound_handler::get_sound_info(int sound_handle) {
+
+ boost::mutex::scoped_lock lock(_mutex);
+
+ // Check if the sound exists.
+ if (sound_handle >= 0 && static_cast<unsigned int>(sound_handle) <
m_sound_data.size())
+ {
+ return m_sound_data[sound_handle]->soundinfo.get();
+ } else {
+ return NULL;
+ }
+
+}
+
+// gnash calls this to mute audio
+void SDL_sound_handler::mute() {
+ stop_all_sounds();
+ muted = true;
+}
+
+// gnash calls this to unmute audio
+void SDL_sound_handler::unmute() {
+ muted = false;
+}
+
+bool SDL_sound_handler::is_muted()
+{
+ return muted;
+}
+
+void SDL_sound_handler::attach_aux_streamer(aux_streamer_ptr ptr, void*
owner)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+ assert(owner);
+ assert(ptr);
+
+ if ( ! m_aux_streamer.insert(std::make_pair(owner, ptr)).second )
+ {
+ // Already in the hash.
+ return;
+ }
+
+ ++soundsPlaying;
+
+ if (!soundOpened) {
+ if (SDL_OpenAudio(&audioSpec, NULL) < 0 ) {
+ log_error(_("Unable to start aux SDL sound: %s"),
SDL_GetError());
+ return;
+ }
+ soundOpened = true;
+ }
+ SDL_PauseAudio(0);
+
+}
+
+void SDL_sound_handler::detach_aux_streamer(void* owner)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ CallbacksMap::iterator it2=m_aux_streamer.find(owner);
+ if ( it2 != m_aux_streamer.end() )
+ {
+ // WARNING: erasing would break any iteration in the map
+ --soundsPlaying;
+ m_aux_streamer.erase(it2);
+ }
+}
+
+unsigned int SDL_sound_handler::get_duration(int sound_handle)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ // Check if the sound exists.
+ if (sound_handle < 0 || (unsigned int) sound_handle >=
m_sound_data.size())
+ {
+ // Invalid handle.
+ return 0;
+ }
+
+ sound_data* sounddata = m_sound_data[sound_handle];
+
+ boost::uint32_t sampleCount = sounddata->soundinfo->getSampleCount();
+ boost::uint32_t sampleRate = sounddata->soundinfo->getSampleRate();
+
+ // Return the sound duration in milliseconds
+ if (sampleCount > 0 && sampleRate > 0) {
+ unsigned int ret = sampleCount / sampleRate * 1000;
+ ret += ((sampleCount % sampleRate) * 1000) / sampleRate;
+ if (sounddata->soundinfo->isStereo()) ret = ret / 2;
+ return ret;
+ } else {
+ return 0;
+ }
+}
+
+unsigned int SDL_sound_handler::get_position(int sound_handle)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ // Check if the sound exists.
+ if (sound_handle < 0 || (unsigned int) sound_handle >=
m_sound_data.size())
+ {
+ // Invalid handle.
+ return 0;
+ }
+
+ sound_data* sounddata = m_sound_data[sound_handle];
+
+ // If there is no active sounds, return 0
+ if (sounddata->m_active_sounds.size() == 0) return 0;
+
+ // We use the first active sound of this.
+ active_sound* asound = sounddata->m_active_sounds[0];
+
+ // Return the playhead position in milliseconds
+ unsigned int ret = asound->samples_played / audioSpec.freq * 1000;
+ ret += ((asound->samples_played % audioSpec.freq) * 1000) /
audioSpec.freq;
+ if (audioSpec.channels > 1) ret = ret / audioSpec.channels;
+ return ret;
+}
+
+sound_handler*
+create_sound_handler_sdl()
+// Factory.
+{
+ return new SDL_sound_handler;
+}
+
+// Pointer handling and checking functions
+boost::uint8_t*
+active_sound::get_raw_data_ptr(unsigned long int pos)
+{
+ if ( _decodedData.get() )
+ {
+ return _decodedData->data(pos);
+ }
+ else return 0;
+}
+
+boost::uint8_t*
+active_sound::get_data_ptr(unsigned long int pos)
+{
+ assert(_undecodedData);
+ return _undecodedData->data(pos);
+}
+
+void active_sound::set_data(sound_data* idata)
+{
+ _undecodedData = idata;
+}
+
+void active_sound::deleteDecodedData()
+{
+ _decodedData.reset();
+}
+
+// AS-volume adjustment
+void adjust_volume(boost::int16_t* data, int size, int volume)
+{
+ for (int i=0; i < size*0.5; i++) {
+ data[i] = data[i] * volume/100;
+ }
+}
+
+// envelope-volume adjustment
+static void
+use_envelopes(active_sound* sound, unsigned int length)
+{
+ // Check if this is the time to use envelopes yet
+ if (sound->current_env == 0 && (*sound->envelopes)[0].m_mark44 >
sound->samples_played+length/2)
+ {
+ return;
+
+ }
+ // switch to the next envelope if needed and possible
+ else if (sound->current_env < sound->envelopes->size()-1 &&
(*sound->envelopes)[sound->current_env+1].m_mark44 >= sound->samples_played)
+ {
+ sound->current_env++;
+ }
+
+ // Current envelope position
+ boost::int32_t cur_env_pos =
sound->envelopes->operator[](sound->current_env).m_mark44;
+
+ // Next envelope position
+ boost::uint32_t next_env_pos = 0;
+ if (sound->current_env == (sound->envelopes->size()-1)) {
+ // If there is no "next envelope" then set the next envelope
start point to be unreachable
+ next_env_pos = cur_env_pos + length;
+ } else {
+ next_env_pos =
(*sound->envelopes)[sound->current_env+1].m_mark44;
+ }
+
+ unsigned int startpos = 0;
+ // Make sure we start adjusting at the right sample
+ if (sound->current_env == 0 &&
(*sound->envelopes)[sound->current_env].m_mark44 > sound->samples_played) {
+ startpos = sound->raw_position +
((*sound->envelopes)[sound->current_env].m_mark44 - sound->samples_played)*2;
+ } else {
+ startpos = sound->raw_position;
+ }
+
+ boost::int16_t* data =
reinterpret_cast<boost::int16_t*>(sound->get_raw_data_ptr(startpos));
+
+ for (unsigned int i=0; i < length/2; i+=2) {
+ float left =
static_cast<float>((*sound->envelopes)[sound->current_env].m_level0 / 32768.0);
+ float right =
static_cast<float>((*sound->envelopes)[sound->current_env].m_level1 / 32768.0);
+
+ data[i] = static_cast<boost::int16_t>(data[i] * left); // Left
+ data[i+1] = static_cast<boost::int16_t>(data[i+1] * right); //
Right
+
+ if ((sound->samples_played+(length/2-i)) >= next_env_pos &&
sound->current_env != (sound->envelopes->size()-1)) {
+ sound->current_env++;
+ // Next envelope position
+ if (sound->current_env == (sound->envelopes->size()-1))
{
+ // If there is no "next envelope" then set the
next envelope start point to be unreachable
+ next_env_pos = cur_env_pos + length;
+ } else {
+ next_env_pos =
(*sound->envelopes)[sound->current_env+1].m_mark44;
+ }
+ }
+ }
+}
+
+
+// Prepare for mixing/adding (volume adjustments) and mix/add.
+static void
+do_mixing(Uint8* stream, active_sound* sound, Uint8* data, unsigned int
mix_length, unsigned int volume) {
+ // If the volume needs adjustments we call a function to do that
+ if (volume != 100) {
+ adjust_volume(reinterpret_cast<boost::int16_t*>(data),
mix_length, volume);
+ } else if (sound->envelopes != NULL) {
+ use_envelopes(sound, mix_length);
+ }
+
+ // Mix the raw data
+ SDL_MixAudio(static_cast<Uint8*>(stream),static_cast<const
Uint8*>(data), mix_length, SDL_MIX_MAXVOLUME);
+
+ // Update sound info
+ sound->raw_position += mix_length;
+
+ // Sample size is always 2
+ sound->samples_played += mix_length / 2;
+}
+
+
+// Callback invoked by the SDL audio thread.
+void SDL_sound_handler::sdl_audio_callback (void *udata, Uint8 *stream, int
buffer_length_in)
+{
+ if ( buffer_length_in < 0 )
+ {
+ log_error(_("Negative buffer length in sdl_audio_callback
(%d)"), buffer_length_in);
+ return;
+ }
+
+ if ( buffer_length_in == 0 )
+ {
+ log_error(_("Zero buffer length in sdl_audio_callback"));
+ return;
+ }
+
+ unsigned int buffer_length = static_cast<unsigned
int>(buffer_length_in);
+
+ // Get the soundhandler
+ SDL_sound_handler* handler = static_cast<SDL_sound_handler*>(udata);
+
+ // If nothing to play there is no reason to play
+ // Is this a potential deadlock problem?
+ if (handler->soundsPlaying == 0 && handler->m_aux_streamer.size() == 0)
{
+ SDL_PauseAudio(1);
+ return;
+ }
+
+ boost::mutex::scoped_lock lock(handler->_mutex);
+
+ // Mixed sounddata buffer
+ Uint8* buffer = stream;
+ memset(buffer, 0, buffer_length);
+
+ // call NetStream or Sound audio callbacks
+ if ( !handler->m_aux_streamer.empty() )
+ {
+ boost::scoped_array<boost::uint8_t> buf ( new
boost::uint8_t[buffer_length] );
+
+ // Loop through the attached sounds
+ CallbacksMap::iterator it = handler->m_aux_streamer.begin();
+ CallbacksMap::iterator end = handler->m_aux_streamer.end();
+ while (it != end) {
+ memset(buf.get(), 0, buffer_length);
+
+ SDL_sound_handler::aux_streamer_ptr aux_streamer =
it->second;
+ void* owner = it->first;
+
+ // If false is returned the sound doesn't want to be
attached anymore
+ bool ret = (aux_streamer)(owner, buf.get(),
buffer_length);
+ if (!ret) {
+ CallbacksMap::iterator it2=it;
+ ++it2; // before we erase it
+ handler->m_aux_streamer.erase(it); // FIXME:
isn't this terribly wrong ?
+ it = it2;
+ handler->soundsPlaying--;
+ } else {
+ ++it;
+ }
+ SDL_MixAudio(stream, buf.get(), buffer_length,
SDL_MIX_MAXVOLUME);
+
+ }
+ }
+
+ // Run through all the sounds. TODO: don't call .size() at every
iteration !
+ for(boost::uint32_t i=0; i < handler->m_sound_data.size(); i++) {
+ sound_data* sounddata = handler->m_sound_data[i];
+ for(boost::uint32_t j = 0; j <
sounddata->m_active_sounds.size(); j++) {
+
+ // Temp variables to make the code simpler and easier
to read
+ active_sound* sound = sounddata->m_active_sounds[j];
+
+ // If there exist no decoder, then we can't decode!
+ if (sound->decoder == NULL) continue;
+
+ // When the current sound dont have enough decoded data
to fill the buffer,
+ // we first mix what is already decoded, then decode
some more data, and
+ // mix some more until the buffer is full. If a sound
loops the magic
+ // happens here ;)
+ if (sound->rawDataSize() - sound->raw_position <
buffer_length
+ && (sound->position < sound->dataSize() ||
sound->loop_count != 0)) {
+ // First we mix what is decoded
+ unsigned int index = 0;
+ if (sound->rawDataSize() - sound->raw_position
> 0)
+ {
+ index = sound->rawDataSize() -
sound->raw_position;
+
+ do_mixing(stream, sound,
sound->get_raw_data_ptr(sound->raw_position),
+ index, sounddata->volume);
+
+ }
+
+ // Then we decode some data
+ // We loop until the size of the decoded sound
is greater than the buffer size,
+ // or there is no more to decode.
+ unsigned int decoded_size = 0;
+
+ // Delete any previous raw_data
+ sound->deleteDecodedData();
+
+ while(decoded_size < buffer_length)
+ {
+
+ // If we need to loop, we reset the
data pointer
+ if (sound->dataSize() ==
sound->position && sound->loop_count != 0) {
+ sound->loop_count--;
+ sound->position = 0;
+ sound->samples_played = 0;
+ }
+
+ // Test if we will get problems...
Should not happen...
+ assert(sound->dataSize() >
sound->position);
+
+ // temp raw buffer
+ Uint8* tmp_raw_buffer;
+ boost::uint32_t tmp_raw_buffer_size = 0;
+ boost::uint32_t decodedBytes = 0;
+
+ boost::uint32_t inputSize = 0;
+ bool parse = true;
+ if (sounddata->soundinfo->getFormat()
== AUDIO_CODEC_ADPCM) {
+ parse = false;
+ if
(sounddata->m_frames_size.size() > 0) inputSize =
sounddata->m_frames_size[sound->position];
+ else inputSize =
sound->dataSize() - sound->position;
+ } else {
+ inputSize = sound->dataSize() -
sound->position;
+ }
+
+ tmp_raw_buffer =
sound->decoder->decode(sound->get_data_ptr(sound->position),
+
inputSize, tmp_raw_buffer_size, decodedBytes, parse);
+
+ sound->position += decodedBytes;
+
+
sound->appendDecodedData(tmp_raw_buffer, tmp_raw_buffer_size);
+
+ decoded_size += tmp_raw_buffer_size;
+
+ // no more to decode from this sound,
so we break the loop
+ if (sound->dataSize() <=
sound->position && sound->loop_count == 0 || tmp_raw_buffer_size == 0 &&
decodedBytes == 0) {
+ sound->position =
sound->dataSize();
+ break;
+ }
+
+ } // end of "decode min. bufferlength data"
while loop
+
+ sound->raw_position = 0;
+
+ // Determine how much should be mixed
+ unsigned int mix_length = 0;
+ if (decoded_size >= buffer_length - index) {
+ mix_length = buffer_length - index;
+ } else {
+ mix_length = decoded_size;
+ }
+ if (sound->rawDataSize() < 2) continue; //
something went terrible wrong
+ do_mixing(stream+index, sound,
sound->get_raw_data_ptr(0), mix_length, sounddata->volume);
+
+ // When the current sound has enough decoded data to
fill
+ // the buffer, we do just that.
+ } else if (sound->rawDataSize() > sound->raw_position
&& sound->rawDataSize() - sound->raw_position > buffer_length ) {
+
+ do_mixing(stream, sound,
sound->get_raw_data_ptr(sound->raw_position),
+ buffer_length, sounddata->volume);
+
+ // When the current sound doesn't have anymore data to
decode,
+ // and doesn't loop (anymore), but still got unplayed
data,
+ // we put the last data on the stream
+ } else if (sound->rawDataSize() - sound->raw_position
<= buffer_length && sound->rawDataSize() > sound->raw_position+1) {
+
+
+ do_mixing(stream, sound,
sound->get_raw_data_ptr(sound->raw_position),
+ sound->rawDataSize() -
sound->raw_position, sounddata->volume);
+
+ sound->raw_position = sound->rawDataSize();
+ }
+
+ // Sound is done, remove it from the active list
+ if (sound->position == sound->dataSize() &&
sound->raw_position == sound->rawDataSize() && sound->loop_count == 0) {
+
sounddata->m_active_sounds.erase(sounddata->m_active_sounds.begin() + j);
+ handler->soundsPlaying--;
+ handler->_soundsStopped++;
+
+ }
+ } // active sounds loop
+ } // existing sounds loop
+
+}
+
+} // gnash.media namespace
+} // namespace gnash
+
+// Local Variables:
+// mode: C++
+// End:
+
Index: libmedia/ffmpeg/sound_handler_sdl.h
===================================================================
RCS file: libmedia/ffmpeg/sound_handler_sdl.h
diff -N libmedia/ffmpeg/sound_handler_sdl.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ libmedia/ffmpeg/sound_handler_sdl.h 22 Feb 2008 14:16:37 -0000 1.1.2.1
@@ -0,0 +1,401 @@
+// sound_handler_sdl.h: Sound handling using standard SDL
+//
+// Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+// $Id: sound_handler_sdl.h,v 1.1.2.1 2008/02/22 14:16:37 strk Exp $
+
+#ifndef SOUND_HANDLER_SDL_H
+#define SOUND_HANDLER_SDL_H
+
+
+#include "sound_handler.h" // for inheritance
+#include "AudioDecoder.h"
+
+#include "log.h"
+
+#ifdef USE_FFMPEG
+extern "C" {
+#include <ffmpeg/avcodec.h>
+}
+#elif defined(USE_MAD)
+#include <mad.h>
+#endif
+
+#include <vector>
+#include <map> // for composition
+
+#include <SDL_audio.h>
+#include <boost/thread/thread.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread/mutex.hpp>
+
+namespace gnash {
+namespace media {
+
+class active_sound;
+
+/// Used to hold the sounddata when doing on-demand-decoding
+class sound_data
+{
+ /// The undecoded data
+ Buffer _buf;
+
+public:
+
+ sound_data()
+ {}
+
+ ~sound_data();
+
+ /// Object holding information about the sound
+ std::auto_ptr<SoundInfo> soundinfo;
+
+ std::map<boost::uint32_t,boost::uint32_t> m_frames_size;
+
+ /// Append size bytes to this sound
+ //
+ /// @param data
+ /// Data bytes, allocated with new[]. Ownership transferred.
+ ///
+ /// @param size
+ /// Size of the 'data' buffer.
+ ///
+ void append(boost::uint8_t* data, unsigned int size)
+ {
+ _buf.append(data, size);
+ }
+
+ /// Return size of the data buffer
+ size_t size() const
+ {
+ return _buf.size();
+ }
+
+ /// Return a pointer to the underlying buffer
+ const boost::uint8_t* data() const {
+ return _buf.data();
+ }
+
+ /// Return a pointer to the underlying buffer
+ boost::uint8_t* data() {
+ return _buf.data();
+ }
+
+ /// Return a pointer to an offset in the underlying buffer
+ //
+ /// @param pos The offset value.
+ /// An assertion will fail if pos > size()
+ ///
+ const boost::uint8_t* data(size_t pos) const {
+ return _buf.data(pos);
+ }
+
+ /// Return a pointer to an offset in the underlying buffer
+ //
+ /// @param pos The offset value.
+ /// An assertion will fail if pos > size()
+ ///
+ boost::uint8_t* data(size_t pos) {
+ return _buf.data(pos);
+ }
+
+ /// Volume for AS-sounds, range: 0-100.
+ /// It's the SWF range that is represented here.
+ int volume;
+
+ /// Vector containing the active instances of this sounds being played
+ //
+ /// NOTE: This class *owns* all active sounds
+ ///
+ typedef std::vector<active_sound*> ActiveSounds;
+
+ ActiveSounds m_active_sounds;
+
+};
+
+/// Used to hold the info about active sounds
+//
+/// This class contains a pointer to the sound_data used for playing
+/// and an optional Buffer to use when decoding is needed.
+///
+/// When the Buffer is NULL we'll play the sound_data bytes directly
+/// (we assume they are decoded already)
+///
+class active_sound
+{
+public:
+ active_sound()
+ :
+ position(0),
+ raw_position(0),
+ loop_count(0),
+ offset(0),
+ current_env(0),
+ samples_played(0),
+ _undecodedData(0)
+ {}
+
+ ~active_sound()
+ {
+ deleteDecodedData();
+ if (decoder) delete decoder;
+ }
+
+ /// The decoder object used to convert the data into the playable format
+ AudioDecoder* decoder;
+
+ /// Current decoding position in the stream
+ unsigned long position;
+
+ /// Current playing position in the decoded stream
+ unsigned long raw_position;
+
+ /// Numbers of loops: -1 means loop forever, 0 means play once.
+ /// For every loop completed, it is decremented.
+ long loop_count;
+
+ /// Offset to make playback start in-sync, only used with mp3 streams.
+ unsigned int offset;
+
+ /// Sound envelopes for the current sound, which determine the volume
level
+ /// from a given position. Only used with sound events.
+ const std::vector<sound_handler::sound_envelope>* envelopes;
+
+ /// Index of current envelope.
+ boost::uint32_t current_env;
+
+ /// Number of samples played so far.
+ unsigned long samples_played;
+
+ /// Set the undecoded data pointer
+ //
+ /// @param newUndecodedData
+ /// Pointer to a sound_data being the undecoded data
+ /// Ownership will NOT be transferred.
+ ///
+ void set_data(sound_data* newUndecodedData);
+
+ /// Returns the data pointer in the undecoded datastream
+ /// for the given position. Boundaries are checked.
+ boost::uint8_t* get_data_ptr(unsigned long int pos);
+
+ /// Returns the data pointer in the decoded datastream
+ /// for the given position. Boundaries are checked.
+ boost::uint8_t* get_raw_data_ptr(unsigned long int pos);
+
+ /// Release resources associated with decoded data, if any.
+ //
+ /// After this call, the active_sound will have no decoded data
+ /// buffer, thus any pointer to the decoded data will be fetched
+ /// from the undecoded one.
+ ///
+ void deleteDecodedData();
+
+ /// Append size bytes to this raw data
+ //
+ /// @param data
+ /// Data bytes, allocated with new[]. Ownership transferred.
+ ///
+ /// @param size
+ /// Size of the 'data' buffer.
+ ///
+ void appendDecodedData(boost::uint8_t* data, unsigned int size)
+ {
+ if ( ! _decodedData.get() )
+ {
+ _decodedData.reset( new Buffer );
+ }
+
+ _decodedData->append(data, size);
+ }
+
+ /// Set decoded data
+ //
+ /// @param data
+ /// Data bytes, allocated with new[]. Ownership transferred.
+ ///
+ /// @param size
+ /// Size of the 'data' buffer.
+ ///
+ void setDecodedData(boost::uint8_t* data, unsigned int size)
+ {
+ if ( ! _decodedData.get() )
+ {
+ _decodedData.reset( new Buffer(data, size) );
+ }
+ else
+ {
+ _decodedData->assign(data, size);
+ }
+ }
+
+ size_t rawDataSize() const
+ {
+ if ( _decodedData.get() )
+ {
+ return _decodedData->size();
+ }
+ else return 0;
+ }
+
+ size_t dataSize() const
+ {
+ return _undecodedData ? _undecodedData->size() : 0;
+ }
+
+private:
+
+ /// The undecoded data
+ sound_data* _undecodedData;
+
+ /// The decoded buffer
+ //
+ /// If NULL, the _undecodedData will be considered
+ /// decoded instead
+ ///
+ std::auto_ptr<Buffer> _decodedData;
+
+};
+
+// This is here as it needs definition of active_sound
+sound_data::~sound_data()
+{
+ for (ActiveSounds::iterator i=m_active_sounds.begin(),
e=m_active_sounds.end(); i!=e; ++i)
+ {
+ delete *i;
+ }
+}
+
+// Use SDL and ffmpeg/mad/nothing to handle sounds.
+class SDL_sound_handler : public sound_handler
+{
+private:
+ /// AS classes (NetStream, Sound) audio callbacks
+ typedef std::map< void* /* owner */, aux_streamer_ptr /* callback */>
CallbacksMap;
+ CallbacksMap m_aux_streamer;
+
+ /// Vector containing all sounds.
+ //
+ /// Elemenst of the vector are owned by this class
+ ///
+ std::vector<sound_data*> m_sound_data;
+
+ /// Is sound device opened?
+ bool soundOpened;
+
+ /// The SDL_audio specs
+ SDL_AudioSpec audioSpec;
+
+ /// Keeps track of numbers of playing sounds
+ int soundsPlaying;
+
+ /// Is the audio muted?
+ bool muted;
+
+ /// Mutex for making sure threads doesn't mess things up
+ boost::mutex _mutex;
+
+ // stop and delete all sounds
+ void delete_all_sounds();
+
+public:
+ SDL_sound_handler();
+ virtual ~SDL_sound_handler();
+
+ /// Called to create a sound.
+ virtual int create_sound(void* data, unsigned int data_bytes,
std::auto_ptr<SoundInfo> sinfo);
+
+ /// this gets called when a stream gets more data
+ virtual long fill_stream_data(unsigned char* data, unsigned int
data_bytes,
+ unsigned int sample_count, int
handle_id);
+
+ /// Play the index'd sample.
+ virtual void play_sound(int sound_handle, int loop_count, int offset,
+ long start_position, const
std::vector<sound_envelope>* envelopes);
+
+ /// Stop the index'd sample.
+ virtual void stop_sound(int sound_handle);
+
+ /// This gets called when it's done with a sample.
+ virtual void delete_sound(int sound_handle);
+
+ // See dox in sound_handler.h
+ virtual void reset();
+
+ /// This will stop all sounds playing.
+ virtual void stop_all_sounds();
+
+ /// Returns the sound volume level as an integer from 0 to 100.
AS-script only.
+ virtual int get_volume(int sound_handle);
+
+ /// Sets the sound volume level as an integer from 0 to 100. AS-script
only.
+ virtual void set_volume(int sound_handle, int volume);
+
+ /// Gnash uses this to get info about a sound. Used when a stream needs
more data.
+ virtual SoundInfo* get_sound_info(int sound_handle);
+
+ /// Gnash calls this to mute audio.
+ virtual void mute();
+
+ /// Gnash calls this to unmute audio.
+ virtual void unmute();
+
+ /// Gnash calls this to get the mute state.
+ virtual bool is_muted();
+
+ /// Gets the duration in milliseconds of an event sound connected to an
AS Sound obejct.
+ virtual unsigned int get_duration(int sound_handle);
+
+ /// Gets the playhead position in milliseconds of an event sound
connected to an AS Soound obejct.
+ virtual unsigned int get_position(int sound_handle);
+
+ virtual void attach_aux_streamer(aux_streamer_ptr ptr, void* owner);
//vv
+ virtual void detach_aux_streamer(void* owner); //vv
+
+ /// Callback invoked by the SDL audio thread.
+ //
+ /// Refills the output stream/buffer with data.
+ ///
+ /// We run trough all the attached auxiliary streamers fetching decoded
+ /// audio blocks and mixing them into the given output stream.
+ ///
+ /// If sound is compresssed (mp3) a mp3-frame is decoded into a buffer,
+ /// and resampled if needed. When the buffer has been sampled, another
+ /// frame is decoded until all frames has been decoded.
+ /// If a sound is looping it will be decoded from the beginning again.
+ ///
+ /// TODO: make a static method of the SDL_sound_handler class
+ ///
+ /// @param udata
+ /// User data pointer (SDL_sound_handler instance in our case).
+ /// We'll lock the SDL_sound_handler::_mutex during operations.
+ ///
+ /// @param stream
+ /// The output stream/buffer to fill
+ ///
+ /// @param buffer_length_in
+ /// Length of the buffer.
+ /// If zero or negative we log an error and return
+ /// (negative is probably an SDL bug, zero dunno yet).
+ ///
+ static void sdl_audio_callback (void *udata, Uint8 *stream, int
buffer_length_in);
+};
+
+} // gnash.media namespace
+} // namespace gnash
+
+#endif // SOUND_HANDLER_SDL_H
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Gnash-commit] gnash ChangeLog configure.ac gui/Makefile.am li... [release_0_8_2_rc1],
Sandro Santilli <=