summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2011-07-19 03:42:28 +0000
committerNanang Izzuddin <nanang@teluu.com>2011-07-19 03:42:28 +0000
commitcd283c8825c9a94400f27735acb1c9385e90ffc8 (patch)
tree56d5722310fa8957ce5d1ba7cbd137cf8802dcc7 /pjmedia
parented8f8d08abba9040f769e922aa0c1adbde86fbbc (diff)
Re #1326: Initial code integration from branch 2.0-dev to trunk as "2.0-pre-alpha-svn".
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3664 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r--pjmedia/build/Makefile48
-rw-r--r--pjmedia/build/os-auto.mak.in53
-rw-r--r--pjmedia/build/pjaut.dsp102
-rw-r--r--pjmedia/build/pjmedia.dsp488
-rw-r--r--pjmedia/build/pjmedia.dsw140
-rw-r--r--pjmedia/build/pjmedia.vcproj647
-rw-r--r--pjmedia/build/pjmedia_audiodev.dsp154
-rw-r--r--pjmedia/build/pjmedia_codec.dsp223
-rw-r--r--pjmedia/build/pjmedia_codec.vcproj24
-rw-r--r--pjmedia/build/pjmedia_test.dsp144
-rw-r--r--pjmedia/build/pjmedia_test.vcproj722
-rw-r--r--pjmedia/build/pjmedia_videodev.vcproj2856
-rw-r--r--pjmedia/include/pjmedia-audiodev/audiodev.h2
-rw-r--r--pjmedia/include/pjmedia-audiodev/config.h4
-rw-r--r--pjmedia/include/pjmedia-codec.h2
-rw-r--r--pjmedia/include/pjmedia-codec/audio_codecs.h98
-rw-r--r--pjmedia/include/pjmedia-codec/config.h14
-rw-r--r--pjmedia/include/pjmedia-codec/ffmpeg_codecs.h62
-rw-r--r--pjmedia/include/pjmedia-codec/h263_packetizer.h146
-rw-r--r--pjmedia/include/pjmedia-codec/h264_packetizer.h157
-rw-r--r--pjmedia/include/pjmedia-codec/types.h25
-rw-r--r--pjmedia/include/pjmedia-videodev/config.h192
-rw-r--r--pjmedia/include/pjmedia-videodev/errno.h159
-rw-r--r--pjmedia/include/pjmedia-videodev/videodev.h618
-rw-r--r--pjmedia/include/pjmedia-videodev/videodev_imp.h202
-rw-r--r--pjmedia/include/pjmedia.h16
-rw-r--r--pjmedia/include/pjmedia/avi.h202
-rw-r--r--pjmedia/include/pjmedia/avi_stream.h170
-rw-r--r--pjmedia/include/pjmedia/circbuf.h1
-rw-r--r--pjmedia/include/pjmedia/clock.h129
-rw-r--r--pjmedia/include/pjmedia/codec.h202
-rw-r--r--pjmedia/include/pjmedia/conference.h6
-rw-r--r--pjmedia/include/pjmedia/config.h162
-rw-r--r--pjmedia/include/pjmedia/converter.h322
-rw-r--r--pjmedia/include/pjmedia/endpoint.h56
-rw-r--r--pjmedia/include/pjmedia/errno.h11
-rw-r--r--pjmedia/include/pjmedia/event.h402
-rw-r--r--pjmedia/include/pjmedia/format.h748
-rw-r--r--pjmedia/include/pjmedia/frame.h332
-rw-r--r--pjmedia/include/pjmedia/jbuf.h113
-rw-r--r--pjmedia/include/pjmedia/port.h205
-rw-r--r--pjmedia/include/pjmedia/sdp.h27
-rw-r--r--pjmedia/include/pjmedia/sdp_neg.h89
-rw-r--r--pjmedia/include/pjmedia/signatures.h217
-rw-r--r--pjmedia/include/pjmedia/sound_port.h14
-rw-r--r--pjmedia/include/pjmedia/stream.h41
-rw-r--r--pjmedia/include/pjmedia/stream_common.h57
-rw-r--r--pjmedia/include/pjmedia/transport.h33
-rw-r--r--pjmedia/include/pjmedia/transport_ice.h24
-rw-r--r--pjmedia/include/pjmedia/types.h448
-rw-r--r--pjmedia/include/pjmedia/vid_codec.h978
-rw-r--r--pjmedia/include/pjmedia/vid_codec_util.h158
-rw-r--r--pjmedia/include/pjmedia/vid_port.h241
-rw-r--r--pjmedia/include/pjmedia/vid_stream.h319
-rw-r--r--pjmedia/include/pjmedia/vid_tee.h142
-rw-r--r--pjmedia/include/pjmedia_videodev.h30
-rw-r--r--pjmedia/src/pjmedia-audiodev/audiodev.c4
-rw-r--r--pjmedia/src/pjmedia-audiodev/wmme_dev.c12
-rw-r--r--pjmedia/src/pjmedia-codec/audio_codecs.c112
-rw-r--r--pjmedia/src/pjmedia-codec/ffmpeg_codecs.c1492
-rw-r--r--pjmedia/src/pjmedia-codec/g722.c3
-rw-r--r--pjmedia/src/pjmedia-codec/g7221.c3
-rw-r--r--pjmedia/src/pjmedia-codec/gsm.c3
-rw-r--r--pjmedia/src/pjmedia-codec/h263_packetizer.c287
-rw-r--r--pjmedia/src/pjmedia-codec/h264_packetizer.c530
-rw-r--r--pjmedia/src/pjmedia-codec/ilbc.c3
-rw-r--r--pjmedia/src/pjmedia-codec/ipp_codecs.c3
-rw-r--r--pjmedia/src/pjmedia-codec/l16.c3
-rw-r--r--pjmedia/src/pjmedia-codec/passthrough.c3
-rw-r--r--pjmedia/src/pjmedia-codec/speex_codec.c3
-rw-r--r--pjmedia/src/pjmedia-videodev/colorbar_dev.c622
-rw-r--r--pjmedia/src/pjmedia-videodev/dshow_dev.c1016
-rw-r--r--pjmedia/src/pjmedia-videodev/dshowclasses.cpp245
-rw-r--r--pjmedia/src/pjmedia-videodev/errno.c112
-rw-r--r--pjmedia/src/pjmedia-videodev/ffmpeg_dev.c514
-rw-r--r--pjmedia/src/pjmedia-videodev/ios_dev.m685
-rw-r--r--pjmedia/src/pjmedia-videodev/qt_dev.m636
-rw-r--r--pjmedia/src/pjmedia-videodev/sdl_dev.c1291
-rw-r--r--pjmedia/src/pjmedia-videodev/sdl_dev_m.m20
-rw-r--r--pjmedia/src/pjmedia-videodev/v4l2_dev.c820
-rw-r--r--pjmedia/src/pjmedia-videodev/videodev.c806
-rw-r--r--pjmedia/src/pjmedia/avi_player.c711
-rw-r--r--pjmedia/src/pjmedia/bidirectional.c14
-rw-r--r--pjmedia/src/pjmedia/clock_thread.c124
-rw-r--r--pjmedia/src/pjmedia/codec.c16
-rw-r--r--pjmedia/src/pjmedia/conf_switch.c105
-rw-r--r--pjmedia/src/pjmedia/conference.c21
-rw-r--r--pjmedia/src/pjmedia/converter.c178
-rw-r--r--pjmedia/src/pjmedia/converter_libswscale.c200
-rw-r--r--pjmedia/src/pjmedia/delaybuf.c1
-rw-r--r--pjmedia/src/pjmedia/dummy.c24
-rw-r--r--pjmedia/src/pjmedia/echo_common.c1
-rw-r--r--pjmedia/src/pjmedia/echo_port.c32
-rw-r--r--pjmedia/src/pjmedia/echo_speex.c1
-rw-r--r--pjmedia/src/pjmedia/echo_suppress.c1
-rw-r--r--pjmedia/src/pjmedia/endpoint.c371
-rw-r--r--pjmedia/src/pjmedia/errno.c2
-rw-r--r--pjmedia/src/pjmedia/event.c148
-rw-r--r--pjmedia/src/pjmedia/ffmpeg_util.c153
-rw-r--r--pjmedia/src/pjmedia/ffmpeg_util.h55
-rw-r--r--pjmedia/src/pjmedia/format.c366
-rw-r--r--pjmedia/src/pjmedia/g711.c3
-rw-r--r--pjmedia/src/pjmedia/jbuf.c149
-rw-r--r--pjmedia/src/pjmedia/master_port.c37
-rw-r--r--pjmedia/src/pjmedia/mem_capture.c6
-rw-r--r--pjmedia/src/pjmedia/mem_player.c12
-rw-r--r--pjmedia/src/pjmedia/null_port.c12
-rw-r--r--pjmedia/src/pjmedia/port.c65
-rw-r--r--pjmedia/src/pjmedia/resample_port.c50
-rw-r--r--pjmedia/src/pjmedia/sdp_cmp.c7
-rw-r--r--pjmedia/src/pjmedia/sdp_neg.c233
-rw-r--r--pjmedia/src/pjmedia/session.c4
-rw-r--r--pjmedia/src/pjmedia/silencedet.c1
-rw-r--r--pjmedia/src/pjmedia/sound_port.c41
-rw-r--r--pjmedia/src/pjmedia/splitcomb.c105
-rw-r--r--pjmedia/src/pjmedia/stereo_port.c67
-rw-r--r--pjmedia/src/pjmedia/stream.c701
-rw-r--r--pjmedia/src/pjmedia/stream_common.c111
-rw-r--r--pjmedia/src/pjmedia/tonegen.c14
-rw-r--r--pjmedia/src/pjmedia/transport_ice.c17
-rw-r--r--pjmedia/src/pjmedia/types.c47
-rw-r--r--pjmedia/src/pjmedia/vid_codec.c731
-rw-r--r--pjmedia/src/pjmedia/vid_codec_util.c619
-rw-r--r--pjmedia/src/pjmedia/vid_port.c948
-rw-r--r--pjmedia/src/pjmedia/vid_stream.c1940
-rw-r--r--pjmedia/src/pjmedia/vid_tee.c384
-rw-r--r--pjmedia/src/pjmedia/wav_player.c33
-rw-r--r--pjmedia/src/pjmedia/wav_playlist.c31
-rw-r--r--pjmedia/src/pjmedia/wav_writer.c10
-rw-r--r--pjmedia/src/test/codec_vectors.c26
-rw-r--r--pjmedia/src/test/main.c10
-rw-r--r--pjmedia/src/test/mips_test.c32
-rw-r--r--pjmedia/src/test/test.c23
-rw-r--r--pjmedia/src/test/test.h6
-rw-r--r--pjmedia/src/test/vid_codec_test.c467
-rw-r--r--pjmedia/src/test/vid_dev_test.c292
-rw-r--r--pjmedia/src/test/vid_port_test.c241
137 files changed, 29460 insertions, 2944 deletions
diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile
index b0e2014d..902a9630 100644
--- a/pjmedia/build/Makefile
+++ b/pjmedia/build/Makefile
@@ -18,6 +18,7 @@ export PJMEDIA_LIB:=../lib/libpjmedia-$(TARGET_NAME)$(LIBEXT)
export PJMEDIA_CODEC_LIB:=../lib/libpjmedia-codec-$(TARGET_NAME)$(LIBEXT)
export PJSDP_LIB:=../lib/libpjsdp-$(TARGET_NAME)$(LIBEXT)
export PJMEDIA_AUDIODEV_LIB:=../lib/libpjmedia-audiodev-$(TARGET_NAME)$(LIBEXT)
+export PJMEDIA_VIDEODEV_LIB:=../lib/libpjmedia-videodev-$(TARGET_NAME)$(LIBEXT)
###############################################################################
@@ -27,14 +28,16 @@ export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \
$(CFLAGS) $(CC_INC)../include \
$(CC_INC)../../pjlib/include \
$(CC_INC)../../pjlib-util/include \
+ $(CC_INC)../../pjmedia/include \
$(CC_INC)../../pjnath/include \
$(CC_INC)../.. \
$(SRTP_INC)
export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \
$(HOST_CXXFLAGS) $(CXXFLAGS)
-export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJMEDIA_LIB)) \
- $(subst /,$(HOST_PSEP),$(PJMEDIA_AUDIODEV_LIB)) \
+export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJMEDIA_VIDEODEV_LIB)) \
$(subst /,$(HOST_PSEP),$(PJMEDIA_CODEC_LIB)) \
+ $(subst /,$(HOST_PSEP),$(PJMEDIA_LIB)) \
+ $(subst /,$(HOST_PSEP),$(PJMEDIA_AUDIODEV_LIB)) \
$(subst /,$(HOST_PSEP),$(PJLIB_LIB)) \
$(subst /,$(HOST_PSEP),$(PJLIB_UTIL_LIB)) \
$(subst /,$(HOST_PSEP),$(PJNATH_LIB)) \
@@ -49,18 +52,22 @@ export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJMEDIA_LIB)) \
#
export PJMEDIA_SRCDIR = ../src/pjmedia
export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
- alaw_ulaw.o alaw_ulaw_table.o bidirectional.o clock_thread.o codec.o \
- conference.o conf_switch.o delaybuf.o echo_common.o \
+ alaw_ulaw.o alaw_ulaw_table.o avi_player.o \
+ bidirectional.o clock_thread.o codec.o conference.o \
+ conf_switch.o converter.o converter_libswscale.o \
+ delaybuf.o echo_common.o \
echo_port.o echo_suppress.o endpoint.o errno.o \
+ event.o format.o ffmpeg_util.o \
g711.o jbuf.o master_port.o mem_capture.o mem_player.o \
null_port.o plc_common.o port.o splitcomb.o \
resample_resample.o resample_libsamplerate.o \
resample_port.o rtcp.o rtcp_xr.o rtp.o \
sdp.o sdp_cmp.o sdp_neg.o session.o silencedet.o \
sound_legacy.o sound_port.o stereo_port.o \
- stream.o tonegen.o transport_adapter_sample.o \
- transport_ice.o transport_loop.o \
- transport_srtp.o transport_udp.o \
+ stream_common.o stream.o tonegen.o transport_adapter_sample.o \
+ transport_ice.o transport_loop.o transport_srtp.o transport_udp.o \
+ types.o vid_codec.o vid_codec_util.o \
+ vid_port.o vid_stream.o vid_tee.o \
wav_player.o wav_playlist.o wav_writer.o wave.o \
wsola.o
@@ -78,6 +85,15 @@ export PJMEDIA_AUDIODEV_CFLAGS += $(_CFLAGS)
###############################################################################
+# Defines for building PJMEDIA-VIDEODEV library
+#
+export PJMEDIA_VIDEODEV_SRCDIR = ../src/pjmedia-videodev
+export PJMEDIA_VIDEODEV_OBJS += errno.o videodev.o ffmpeg_dev.o \
+ colorbar_dev.o v4l2_dev.o
+export PJMEDIA_VIDEODEV_CFLAGS += $(_CFLAGS)
+
+
+###############################################################################
# Defines for building PJSDP library
# Note that SDP functionality is already INCLUDED in PJMEDIA.
# The PJSDP library should only be used for applications that want SDP
@@ -93,7 +109,8 @@ export PJSDP_CFLAGS += $(_CFLAGS)
# Defines for building PJMEDIA-Codec library
#
export PJMEDIA_CODEC_SRCDIR = ../src/pjmedia-codec
-export PJMEDIA_CODEC_OBJS += \
+export PJMEDIA_CODEC_OBJS += audio_codecs.o ffmpeg_codecs.o \
+ h263_packetizer.o h264_packetizer.o \
$(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
ipp_codecs.o $(CODEC_OBJS)
export PJMEDIA_CODEC_CFLAGS += $(_CFLAGS) $(GSM_CFLAGS) $(SPEEX_CFLAGS) \
@@ -104,7 +121,9 @@ export PJMEDIA_CODEC_CFLAGS += $(_CFLAGS) $(GSM_CFLAGS) $(SPEEX_CFLAGS) \
# Defines for building test application
#
export PJMEDIA_TEST_SRCDIR = ../src/test
-export PJMEDIA_TEST_OBJS += codec_vectors.o jbuf_test.o main.o mips_test.o rtp_test.o test.o
+export PJMEDIA_TEST_OBJS += codec_vectors.o jbuf_test.o main.o mips_test.o \
+ vid_codec_test.o vid_dev_test.o vid_port_test.o \
+ rtp_test.o test.o
export PJMEDIA_TEST_OBJS += sdp_neg_test.o
export PJMEDIA_TEST_CFLAGS += $(_CFLAGS)
export PJMEDIA_TEST_LDFLAGS += $(_LDFLAGS)
@@ -117,7 +136,7 @@ export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT
#
# $(TARGET) is defined in os-$(OS_NAME).mak file in current directory.
#
-TARGETS := pjmedia pjmedia-audiodev pjmedia-codec pjsdp pjmedia-test
+TARGETS := pjmedia pjmedia-videodev pjmedia-audiodev pjmedia-codec pjsdp pjmedia-test
all: $(TARGETS)
@@ -132,7 +151,7 @@ doc:
dep: depend
distclean: realclean
-.PHONY: dep depend pjmedia pjmedia-codec pjmedia-audiodev pjmedia-test clean realclean distclean
+.PHONY: dep depend pjmedia pjmedia-codec pjmedia-videodev pjmedia-audiodev pjmedia-test clean realclean distclean
pjmedia:
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $(PJMEDIA_LIB)
@@ -140,6 +159,9 @@ pjmedia:
pjmedia-codec:
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $(PJMEDIA_CODEC_LIB)
+pjmedia-videodev:
+ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $(PJMEDIA_VIDEODEV_LIB)
+
pjmedia-audiodev:
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $(PJMEDIA_AUDIODEV_LIB)
@@ -166,18 +188,21 @@ pjmedia-test: $(PJMEDIA_LIB)
clean:
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $@
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $@
+ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $@
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $@
$(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $@
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $@
realclean:
$(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-$(TARGET_NAME).depend),$(HOST_RMR))
+ $(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-videodev-$(TARGET_NAME).depend),$(HOST_RMR))
$(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-audiodev-$(TARGET_NAME).depend),$(HOST_RMR))
$(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-codec-$(TARGET_NAME).depend),$(HOST_RMR))
$(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-test-$(TARGET_NAME).depend),$(HOST_RMR))
$(subst @@,$(subst /,$(HOST_PSEP),.pjsdp-$(TARGET_NAME).depend),$(HOST_RMR))
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $@
+ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $@
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $@
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $@
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $@
@@ -185,6 +210,7 @@ realclean:
depend:
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $@
+ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $@
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $@
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $@
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $@
diff --git a/pjmedia/build/os-auto.mak.in b/pjmedia/build/os-auto.mak.in
index a329f76c..044339e0 100644
--- a/pjmedia/build/os-auto.mak.in
+++ b/pjmedia/build/os-auto.mak.in
@@ -1,7 +1,35 @@
# @configure_input@
+# Define the desired video device backend
+# Valid values are:
+# - mac_os
+# - iphone_os
+AC_PJMEDIA_VIDEO = @ac_pjmedia_video@
+
+# SDL flags
+SDL_CFLAGS = @ac_sdl_cflags@
+SDL_LDFLAGS = @ac_sdl_ldflags@
+
+# FFMPEG dlags
+FFMPEG_CFLAGS = @ac_ffmpeg_cflags@
+FFMPEG_LDFLAGS = @ac_ffmpeg_ldflags@
+
+# Video4Linux2
+V4L2_CFLAGS = @ac_v4l2_cflags@
+V4L2_LDFLAGS = @ac_v4l2_ldflags@
+
+# QT
+AC_PJMEDIA_VIDEO_HAS_QT = @ac_pjmedia_video_has_qt@
+QT_CFLAGS = @ac_qt_cflags@
+
+# iOS
+IOS_CFLAGS = @ac_ios_cflags@
+
# PJMEDIA features exclusion
-export CFLAGS += @ac_no_small_filter@ @ac_no_large_filter@ @ac_no_speex_aec@
+export CFLAGS += @ac_no_small_filter@ @ac_no_large_filter@ @ac_no_speex_aec@ \
+ $(SDL_CFLAGS) $(FFMPEG_CFLAGS) $(V4L2_CFLAGS) $(QT_CFLAGS) \
+ $(IOS_CFLAGS)
+export LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS)
# Define the desired sound device backend
# Valid values are:
@@ -118,4 +146,27 @@ ifeq ($(AC_PJMEDIA_SND),external)
export CFLAGS += -DPJMEDIA_AUDIO_DEV_HAS_PORTAUDIO=0 -DPJMEDIA_AUDIO_DEV_HAS_WMME=0
endif
+#
+# QT video device
+#
+ifeq ($(AC_PJMEDIA_VIDEO_HAS_QT),yes)
+export PJMEDIA_VIDEODEV_OBJS += qt_dev.o
+endif
+
+#
+# iOS video device
+#
+ifeq ($(AC_PJMEDIA_VIDEO),iphone_os)
+export PJMEDIA_VIDEODEV_OBJS += ios_dev.o
+endif
+#
+# Determine whether we should compile the obj-c version of a particular source code
+#
+ifeq ($(AC_PJMEDIA_VIDEO),$(filter $(AC_PJMEDIA_VIDEO),mac_os iphone_os))
+# Mac and iPhone OS specific, use obj-c
+export PJMEDIA_VIDEODEV_OBJS += sdl_dev_m.o
+else
+# Other platforms, compile .c
+export PJMEDIA_VIDEODEV_OBJS += sdl_dev.o
+endif
diff --git a/pjmedia/build/pjaut.dsp b/pjmedia/build/pjaut.dsp
deleted file mode 100644
index 99145d35..00000000
--- a/pjmedia/build/pjaut.dsp
+++ /dev/null
@@ -1,102 +0,0 @@
-# Microsoft Developer Studio Project File - Name="pjaut" - Package Owner=<4>
-# Microsoft Developer Studio Generated Build File, Format Version 6.00
-# ** DO NOT EDIT **
-
-# TARGTYPE "Win32 (x86) Console Application" 0x0103
-
-CFG=pjaut - Win32 Debug
-!MESSAGE This is not a valid makefile. To build this project using NMAKE,
-!MESSAGE use the Export Makefile command and run
-!MESSAGE
-!MESSAGE NMAKE /f "pjaut.mak".
-!MESSAGE
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "pjaut.mak" CFG="pjaut - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "pjaut - Win32 Release" (based on "Win32 (x86) Console Application")
-!MESSAGE "pjaut - Win32 Debug" (based on "Win32 (x86) Console Application")
-!MESSAGE
-
-# Begin Project
-# PROP AllowPerConfigDependencies 0
-# PROP Scc_ProjName ""
-# PROP Scc_LocalPath ""
-CPP=cl.exe
-RSC=rc.exe
-
-!IF "$(CFG)" == "pjaut - Win32 Release"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir ".\output\pjaut_i386_win32_vc6_release"
-# PROP BASE Intermediate_Dir ".\output\pjaut_i386_win32_vc6_release"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 0
-# PROP Output_Dir ".\output\pjaut_i386_win32_vc6_release"
-# PROP Intermediate_Dir ".\output\pjaut_i386_win32_vc6_release"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../pjlib/src" /I "../src" /I "../../pjsdp/src" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /d "NDEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
-# ADD LINK32 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/pjaut_vc6.exe"
-
-!ELSEIF "$(CFG)" == "pjaut - Win32 Debug"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir ".\output\pjaut_i386_win32_vc6_debug"
-# PROP BASE Intermediate_Dir ".\output\pjaut_i386_win32_vc6_debug"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 1
-# PROP Output_Dir ".\output\pjaut_i386_win32_vc6_debug"
-# PROP Intermediate_Dir ".\output\pjaut_i386_win32_vc6_debug"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../pjlib/src" /I "../src" /I "../../pjsdp/src" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
-# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /d "_DEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
-# ADD LINK32 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjaut_vc6d.exe" /pdbtype:sept
-
-!ENDIF
-
-# Begin Target
-
-# Name "pjaut - Win32 Release"
-# Name "pjaut - Win32 Debug"
-# Begin Group "Source Files"
-
-# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
-# Begin Source File
-
-SOURCE=..\src\test\audio_tool.c
-# End Source File
-# End Group
-# Begin Group "Header Files"
-
-# PROP Default_Filter "h;hpp;hxx;hm;inl"
-# End Group
-# Begin Group "Resource Files"
-
-# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
-# End Group
-# End Target
-# End Project
diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp
deleted file mode 100644
index aa127e77..00000000
--- a/pjmedia/build/pjmedia.dsp
+++ /dev/null
@@ -1,488 +0,0 @@
-# Microsoft Developer Studio Project File - Name="pjmedia" - Package Owner=<4>
-# Microsoft Developer Studio Generated Build File, Format Version 6.00
-# ** DO NOT EDIT **
-
-# TARGTYPE "Win32 (x86) Static Library" 0x0104
-
-CFG=pjmedia - Win32 Debug
-!MESSAGE This is not a valid makefile. To build this project using NMAKE,
-!MESSAGE use the Export Makefile command and run
-!MESSAGE
-!MESSAGE NMAKE /f "pjmedia.mak".
-!MESSAGE
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "pjmedia.mak" CFG="pjmedia - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "pjmedia - Win32 Release" (based on "Win32 (x86) Static Library")
-!MESSAGE "pjmedia - Win32 Debug" (based on "Win32 (x86) Static Library")
-!MESSAGE
-
-# Begin Project
-# PROP AllowPerConfigDependencies 0
-# PROP Scc_ProjName ""
-# PROP Scc_LocalPath ""
-CPP=cl.exe
-RSC=rc.exe
-
-!IF "$(CFG)" == "pjmedia - Win32 Release"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir ".\output\pjmedia-i386-win32-vc6-release"
-# PROP BASE Intermediate_Dir ".\output\pjmedia-i386-win32-vc6-release"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 0
-# PROP Output_Dir ".\output\pjmedia-i386-win32-vc6-release"
-# PROP Intermediate_Dir ".\output\pjmedia-i386-win32-vc6-release"
-# PROP Target_Dir ""
-F90=df.exe
-# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
-# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjnath/include" /I "../../third_party/portaudio/include" /I "../../third_party/speex/include" /I "../../third_party/build/srtp" /I "../../third_party/srtp/crypto/include" /I "../../third_party/srtp/include" /I "../.." /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /c
-# SUBTRACT CPP /YX
-# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /d "NDEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LIB32=link.exe -lib
-# ADD BASE LIB32 /nologo
-# ADD LIB32 /nologo /out:"../lib/pjmedia-i386-win32-vc6-release.lib"
-
-!ELSEIF "$(CFG)" == "pjmedia - Win32 Debug"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir ".\output\pjmedia-i386-win32-vc6-debug"
-# PROP BASE Intermediate_Dir ".\output\pjmedia-i386-win32-vc6-debug"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 1
-# PROP Output_Dir ".\output\pjmedia-i386-win32-vc6-debug"
-# PROP Intermediate_Dir ".\output\pjmedia-i386-win32-vc6-debug"
-# PROP Target_Dir ""
-F90=df.exe
-# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjnath/include" /I "../../third_party/portaudio/include" /I "../../third_party/speex/include" /I "../../third_party/build/srtp" /I "../../third_party/srtp/crypto/include" /I "../../third_party/srtp/include" /I "../.." /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
-# SUBTRACT CPP /YX
-# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /d "_DEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LIB32=link.exe -lib
-# ADD BASE LIB32 /nologo
-# ADD LIB32 /nologo /out:"../lib/pjmedia-i386-win32-vc6-debug.lib"
-
-!ENDIF
-
-# Begin Target
-
-# Name "pjmedia - Win32 Release"
-# Name "pjmedia - Win32 Debug"
-# Begin Group "Source Files"
-
-# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
-# Begin Source File
-
-SOURCE=..\src\pjmedia\alaw_ulaw.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\alaw_ulaw_table.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\bidirectional.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\clock_thread.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\codec.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\conf_switch.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\conference.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\delaybuf.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\echo_common.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\echo_internal.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\echo_port.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\echo_speex.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\echo_suppress.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\endpoint.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\errno.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\g711.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\jbuf.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\master_port.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\mem_capture.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\mem_player.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\null_port.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\plc_common.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\port.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\resample_libsamplerate.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\resample_port.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\resample_resample.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\resample_speex.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\rtcp.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\rtcp_xr.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\rtp.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\sdp.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\sdp_cmp.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\sdp_neg.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\session.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\silencedet.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\sound_legacy.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\sound_port.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\splitcomb.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\stereo_port.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\stream.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\tonegen.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\transport_adapter_sample.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\transport_ice.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\transport_loop.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\transport_srtp.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\transport_udp.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\wav_player.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\wav_playlist.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\wav_writer.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\wave.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\wsola.c
-# End Source File
-# End Group
-# Begin Group "Header Files"
-
-# PROP Default_Filter "h;hpp;hxx;hm;inl"
-# Begin Source File
-
-SOURCE=..\include\pjmedia\alaw_ulaw.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\audio_dev.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\bidirectional.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\circbuf.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\clock.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\codec.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\conference.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\config.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\delaybuf.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\doxygen.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\echo.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\echo_port.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\endpoint.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\errno.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\g711.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\jbuf.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\master_port.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\mem_port.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\null_port.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\plc.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\port.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\resample.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\rtcp.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\rtcp_xr.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\rtp.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\sdp.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\sdp_neg.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\session.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\silencedet.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\sound.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\sound_port.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\splitcomb.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\stereo.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\stream.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\tonegen.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\transport.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\transport_adapter_sample.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\transport_ice.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\transport_loop.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\transport_srtp.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\transport_udp.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\types.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\wav_playlist.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\wav_port.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\wave.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\include\pjmedia\wsola.h
-# End Source File
-# End Group
-# End Target
-# End Project
diff --git a/pjmedia/build/pjmedia.dsw b/pjmedia/build/pjmedia.dsw
deleted file mode 100644
index b7147b6d..00000000
--- a/pjmedia/build/pjmedia.dsw
+++ /dev/null
@@ -1,140 +0,0 @@
-Microsoft Developer Studio Workspace File, Format Version 6.00
-# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
-
-###############################################################################
-
-Project: "libportaudio"="..\..\third_party\build\portaudio\libportaudio.dsp" - Package Owner=<4>
-
-Package=<5>
-{{{
-}}}
-
-Package=<4>
-{{{
-}}}
-
-###############################################################################
-
-Project: "libsrtp"="..\..\third_party\build\srtp\libsrtp.dsp" - Package Owner=<4>
-
-Package=<5>
-{{{
-}}}
-
-Package=<4>
-{{{
-}}}
-
-###############################################################################
-
-Project: "pjaut"=".\pjaut.dsp" - Package Owner=<4>
-
-Package=<5>
-{{{
-}}}
-
-Package=<4>
-{{{
- Begin Project Dependency
- Project_Dep_Name pjlib
- End Project Dependency
- Begin Project Dependency
- Project_Dep_Name pjmedia
- End Project Dependency
-}}}
-
-###############################################################################
-
-Project: "pjlib"="..\..\pjlib\build\pjlib.dsp" - Package Owner=<4>
-
-Package=<5>
-{{{
-}}}
-
-Package=<4>
-{{{
-}}}
-
-###############################################################################
-
-Project: "pjlib_util"="..\..\pjlib-util\build\pjlib_util.dsp" - Package Owner=<4>
-
-Package=<5>
-{{{
-}}}
-
-Package=<4>
-{{{
-}}}
-
-###############################################################################
-
-Project: "pjmedia"=".\pjmedia.dsp" - Package Owner=<4>
-
-Package=<5>
-{{{
-}}}
-
-Package=<4>
-{{{
- Begin Project Dependency
- Project_Dep_Name pjlib
- End Project Dependency
-}}}
-
-###############################################################################
-
-Project: "pjmedia_codec"=".\pjmedia_codec.dsp" - Package Owner=<4>
-
-Package=<5>
-{{{
-}}}
-
-Package=<4>
-{{{
-}}}
-
-###############################################################################
-
-Project: "pjmedia_test"=".\pjmedia_test.dsp" - Package Owner=<4>
-
-Package=<5>
-{{{
-}}}
-
-Package=<4>
-{{{
- Begin Project Dependency
- Project_Dep_Name pjmedia
- End Project Dependency
- Begin Project Dependency
- Project_Dep_Name pjlib
- End Project Dependency
- Begin Project Dependency
- Project_Dep_Name pjsdp
- End Project Dependency
- Begin Project Dependency
- Project_Dep_Name pjlib_util
- End Project Dependency
- Begin Project Dependency
- Project_Dep_Name libportaudio
- End Project Dependency
- Begin Project Dependency
- Project_Dep_Name libsrtp
- End Project Dependency
-}}}
-
-###############################################################################
-
-Global:
-
-Package=<5>
-{{{
-}}}
-
-Package=<3>
-{{{
-}}}
-
-###############################################################################
-
diff --git a/pjmedia/build/pjmedia.vcproj b/pjmedia/build/pjmedia.vcproj
index 3b7c518e..eca4ed6e 100644
--- a/pjmedia/build/pjmedia.vcproj
+++ b/pjmedia/build/pjmedia.vcproj
@@ -11,16 +11,16 @@
Name="Win32"
/>
<Platform
- Name="Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Pocket PC 2003 (ARMV4)"
/>
<Platform
- Name="Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Smartphone 2003 (ARMV4)"
/>
<Platform
- Name="Pocket PC 2003 (ARMV4)"
+ Name="Windows Mobile 6 Standard SDK (ARMV4I)"
/>
<Platform
- Name="Smartphone 2003 (ARMV4)"
+ Name="Windows Mobile 6 Professional SDK (ARMV4I)"
/>
<Platform
Name="Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
@@ -34,11 +34,11 @@
<Configurations>
<Configuration
Name="Release|Win32"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="2"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
@@ -57,8 +57,8 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
AdditionalIncludeDirectories="../include;../../pjlib/include;&quot;../../pjlib-util/include&quot;;../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../..;&quot;$(DXSDK_DIR)include&quot;"
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -90,11 +90,11 @@
/>
</Configuration>
<Configuration
- Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release|Pocket PC 2003 (ARMV4)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -113,9 +113,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -129,7 +129,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -156,11 +156,11 @@
/>
</Configuration>
<Configuration
- Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release|Smartphone 2003 (ARMV4)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -179,9 +179,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -195,7 +195,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -223,11 +223,11 @@
</Configuration>
<Configuration
Name="Debug|Win32"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="2"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
@@ -246,8 +246,8 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
AdditionalIncludeDirectories="../include;../../pjlib/include;&quot;../../pjlib-util/include&quot;;../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../..;&quot;$(DXSDK_DIR)include&quot;"
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -261,6 +261,7 @@
/>
<Tool
Name="VCLibrarianTool"
+ IgnoreDefaultLibraryNames=""
/>
<Tool
Name="VCALinkTool"
@@ -279,11 +280,11 @@
/>
</Configuration>
<Configuration
- Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug|Pocket PC 2003 (ARMV4)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -302,9 +303,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -318,7 +319,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -345,11 +346,11 @@
/>
</Configuration>
<Configuration
- Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug|Smartphone 2003 (ARMV4)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -368,9 +369,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -384,7 +385,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -412,11 +413,11 @@
</Configuration>
<Configuration
Name="Debug-Static|Win32"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="2"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
@@ -435,8 +436,8 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
AdditionalIncludeDirectories="../include;../../pjlib/include;&quot;../../pjlib-util/include&quot;;../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../..;&quot;$(DXSDK_DIR)include&quot;"
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -468,11 +469,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug-Static|Pocket PC 2003 (ARMV4)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -491,9 +492,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -507,7 +508,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -534,11 +535,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug-Static|Smartphone 2003 (ARMV4)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -557,9 +558,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -573,7 +574,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -601,11 +602,11 @@
</Configuration>
<Configuration
Name="Release-Dynamic|Win32"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="2"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
@@ -624,8 +625,8 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
AdditionalIncludeDirectories="../include;../../pjlib/include;&quot;../../pjlib-util/include&quot;;../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../..;&quot;$(DXSDK_DIR)include&quot;"
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -657,11 +658,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -680,9 +681,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -696,7 +697,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -723,11 +724,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -746,9 +747,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -762,7 +763,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -790,11 +791,11 @@
</Configuration>
<Configuration
Name="Debug-Dynamic|Win32"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="2"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
@@ -813,8 +814,8 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
AdditionalIncludeDirectories="../include;../../pjlib/include;&quot;../../pjlib-util/include&quot;;../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../..;&quot;$(DXSDK_DIR)include&quot;"
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -846,11 +847,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -869,9 +870,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -885,7 +886,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -912,11 +913,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -935,9 +936,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -951,7 +952,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -979,11 +980,11 @@
</Configuration>
<Configuration
Name="Release-Static|Win32"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="2"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1002,8 +1003,8 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
AdditionalIncludeDirectories="../include;../../pjlib/include;&quot;../../pjlib-util/include&quot;;../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../..;&quot;$(DXSDK_DIR)include&quot;"
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1035,11 +1036,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release-Static|Pocket PC 2003 (ARMV4)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1058,9 +1059,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1074,7 +1075,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1101,11 +1102,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release-Static|Smartphone 2003 (ARMV4)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1124,9 +1125,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1140,7 +1141,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1167,11 +1168,11 @@
/>
</Configuration>
<Configuration
- Name="Release|Pocket PC 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1190,9 +1191,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1206,7 +1207,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1233,11 +1234,11 @@
/>
</Configuration>
<Configuration
- Name="Debug|Pocket PC 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1256,9 +1257,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1272,7 +1273,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1299,11 +1300,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Static|Pocket PC 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1322,9 +1323,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1338,7 +1339,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1365,11 +1366,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1388,9 +1389,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1404,7 +1405,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1431,11 +1432,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1454,9 +1455,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1470,7 +1471,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1497,11 +1498,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Static|Pocket PC 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1520,9 +1521,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1536,7 +1537,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1563,11 +1564,11 @@
/>
</Configuration>
<Configuration
- Name="Release|Smartphone 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1586,9 +1587,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1602,7 +1603,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1629,11 +1630,11 @@
/>
</Configuration>
<Configuration
- Name="Debug|Smartphone 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1652,9 +1653,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1668,7 +1669,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1695,11 +1696,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Static|Smartphone 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1718,9 +1719,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1734,7 +1735,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1761,11 +1762,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1784,9 +1785,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1800,7 +1801,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1827,11 +1828,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1850,9 +1851,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1866,7 +1867,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1893,11 +1894,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Static|Smartphone 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1916,9 +1917,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1932,7 +1933,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -1959,11 +1960,11 @@
/>
</Configuration>
<Configuration
- Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1982,9 +1983,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1998,7 +1999,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -2025,11 +2026,11 @@
/>
</Configuration>
<Configuration
- Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2048,9 +2049,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -2064,7 +2065,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -2091,11 +2092,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2114,9 +2115,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -2130,7 +2131,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -2157,11 +2158,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2180,9 +2181,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -2196,7 +2197,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -2223,11 +2224,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2246,9 +2247,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -2262,7 +2263,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -2289,11 +2290,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2312,9 +2313,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -2328,7 +2329,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -2355,11 +2356,11 @@
/>
</Configuration>
<Configuration
- Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2378,9 +2379,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -2394,7 +2395,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -2421,11 +2422,11 @@
/>
</Configuration>
<Configuration
- Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2444,9 +2445,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -2460,7 +2461,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -2487,11 +2488,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2510,9 +2511,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -2526,7 +2527,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -2553,11 +2554,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2576,9 +2577,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -2592,7 +2593,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -2619,11 +2620,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
+ Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2642,9 +2643,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -2658,7 +2659,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -2686,10 +2687,10 @@
</Configuration>
<Configuration
Name="Release-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
- ATLMinimizesCRunTimeLibraryUsage="false"
- CharacterSet="1"
ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2708,9 +2709,9 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_LIB;"
ExecutionBucket="7"
AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."
+ PreprocessorDefinitions="_LIB;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -2724,7 +2725,7 @@
/>
<Tool
Name="VCLibrarianTool"
- OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+ OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
/>
<Tool
Name="VCALinkTool"
@@ -2875,6 +2876,10 @@
</FileConfiguration>
</File>
<File
+ RelativePath="..\src\pjmedia\avi_player.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia\bidirectional.c"
>
<FileConfiguration
@@ -3111,6 +3116,14 @@
</FileConfiguration>
</File>
<File
+ RelativePath="..\src\pjmedia\converter.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia\converter_libswscale.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia\delaybuf.c"
>
</File>
@@ -3463,6 +3476,18 @@
</FileConfiguration>
</File>
<File
+ RelativePath="..\src\pjmedia\event.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia\ffmpeg_util.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia\format.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia\g711.c"
>
<FileConfiguration
@@ -4291,64 +4316,6 @@
</FileConfiguration>
</File>
<File
- RelativePath="..\src\pjmedia\session.c"
- >
- <FileConfiguration
- Name="Release|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=""
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=""
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Debug-Static|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=""
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release-Dynamic|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=""
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Debug-Dynamic|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=""
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release-Static|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=""
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- </File>
- <File
RelativePath="..\src\pjmedia\silencedet.c"
>
<FileConfiguration
@@ -4589,6 +4556,10 @@
</FileConfiguration>
</File>
<File
+ RelativePath="..\src\pjmedia\stream_common.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia\tonegen.c"
>
<FileConfiguration
@@ -4721,6 +4692,30 @@
</FileConfiguration>
</File>
<File
+ RelativePath="..\src\pjmedia\types.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia\vid_codec.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia\vid_codec_util.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia\vid_port.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia\vid_stream.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia\vid_tee.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia\wav_player.c"
>
<FileConfiguration
@@ -4966,6 +4961,14 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia\avi.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\pjmedia\avi_stream.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia\bidirectional.h"
>
</File>
@@ -4990,6 +4993,10 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia\converter.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia\delaybuf.h"
>
</File>
@@ -5014,6 +5021,18 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia\event.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\pjmedia\format.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\pjmedia\frame.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia\g711.h"
>
</File>
@@ -5070,7 +5089,7 @@
>
</File>
<File
- RelativePath="..\include\pjmedia\session.h"
+ RelativePath="..\include\pjmedia\signatures.h"
>
</File>
<File
@@ -5098,6 +5117,10 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia\stream_common.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia\tonegen.h"
>
</File>
@@ -5130,6 +5153,26 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia\vid_codec.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\pjmedia\vid_codec_util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\pjmedia\vid_port.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\pjmedia\vid_stream.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\pjmedia\vid_tee.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia\wav_playlist.h"
>
</File>
diff --git a/pjmedia/build/pjmedia_audiodev.dsp b/pjmedia/build/pjmedia_audiodev.dsp
deleted file mode 100644
index 0c2266fe..00000000
--- a/pjmedia/build/pjmedia_audiodev.dsp
+++ /dev/null
@@ -1,154 +0,0 @@
-# Microsoft Developer Studio Project File - Name="pjmedia_audiodev" - Package Owner=<4>
-# Microsoft Developer Studio Generated Build File, Format Version 6.00
-# ** DO NOT EDIT **
-
-# TARGTYPE "Win32 (x86) Static Library" 0x0104
-
-CFG=pjmedia_audiodev - Win32 Debug
-!MESSAGE This is not a valid makefile. To build this project using NMAKE,
-!MESSAGE use the Export Makefile command and run
-!MESSAGE
-!MESSAGE NMAKE /f "pjmedia_audiodev.mak".
-!MESSAGE
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "pjmedia_audiodev.mak" CFG="pjmedia_audiodev - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "pjmedia_audiodev - Win32 Release" (based on "Win32 (x86) Static Library")
-!MESSAGE "pjmedia_audiodev - Win32 Debug" (based on "Win32 (x86) Static Library")
-!MESSAGE
-
-# Begin Project
-# PROP AllowPerConfigDependencies 0
-# PROP Scc_ProjName ""
-# PROP Scc_LocalPath ""
-CPP=cl.exe
-RSC=rc.exe
-
-!IF "$(CFG)" == "pjmedia_audiodev - Win32 Release"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir ".\output\pjmedia-audiodev-i386-win32-vc6-release"
-# PROP BASE Intermediate_Dir ".\output\pjmedia-audiodev-i386-win32-vc6-release"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 0
-# PROP Output_Dir ".\output\pjmedia-audiodev-i386-win32-vc6-release"
-# PROP Intermediate_Dir ".\output\pjmedia-audiodev-i386-win32-vc6-release"
-# PROP Target_Dir ""
-F90=df.exe
-# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
-# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjnath/include" /I "../../third_party/portaudio/include" /I "../../third_party/speex/include" /I "../../third_party/build/srtp" /I "../../third_party/srtp/crypto/include" /I "../../third_party/srtp/include" /I "../.." /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /c
-# SUBTRACT CPP /YX
-# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /d "NDEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LIB32=link.exe -lib
-# ADD BASE LIB32 /nologo
-# ADD LIB32 /nologo /out:"../lib/pjmedia-audiodev-i386-win32-vc6-release.lib"
-
-!ELSEIF "$(CFG)" == "pjmedia_audiodev - Win32 Debug"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir ".\output\pjmedia_audiodev-i386-win32-vc6-debug"
-# PROP BASE Intermediate_Dir ".\output\pjmedia_audiodev-i386-win32-vc6-debug"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 1
-# PROP Output_Dir ".\output\pjmedia-audiodev-i386-win32-vc6-debug"
-# PROP Intermediate_Dir ".\output\pjmedia-audiodev-i386-win32-vc6-debug"
-# PROP Target_Dir ""
-F90=df.exe
-# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjnath/include" /I "../../third_party/portaudio/include" /I "../../third_party/speex/include" /I "../../third_party/build/srtp" /I "../../third_party/srtp/crypto/include" /I "../../third_party/srtp/include" /I "../.." /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
-# SUBTRACT CPP /YX
-# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /d "_DEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LIB32=link.exe -lib
-# ADD BASE LIB32 /nologo
-# ADD LIB32 /nologo /out:"../lib/pjmedia-audiodev-i386-win32-vc6-debug.lib"
-
-!ENDIF
-
-# Begin Target
-
-# Name "pjmedia_audiodev - Win32 Release"
-# Name "pjmedia_audiodev - Win32 Debug"
-# Begin Group "Source Files"
-
-# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
-# Begin Source File
-
-SOURCE="..\src\pjmedia-audiodev\audiodev.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-audiodev\audiotest.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-audiodev\errno.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-audiodev\legacy_dev.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-audiodev\null_dev.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-audiodev\pa_dev.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-audiodev\symb_aps_dev.cpp"
-# PROP Exclude_From_Build 1
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-audiodev\symb_mda_dev.cpp"
-# PROP Exclude_From_Build 1
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-audiodev\wmme_dev.c"
-# End Source File
-# End Group
-# Begin Group "Header Files"
-
-# PROP Default_Filter "h;hpp;hxx;hm;inl"
-# Begin Source File
-
-SOURCE="..\include\pjmedia-audiodev\audiodev.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-audiodev\audiodev_imp.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-audiodev\audiotest.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-audiodev\config.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-audiodev\errno.h"
-# End Source File
-# End Group
-# End Target
-# End Project
diff --git a/pjmedia/build/pjmedia_codec.dsp b/pjmedia/build/pjmedia_codec.dsp
deleted file mode 100644
index 3f9eb75f..00000000
--- a/pjmedia/build/pjmedia_codec.dsp
+++ /dev/null
@@ -1,223 +0,0 @@
-# Microsoft Developer Studio Project File - Name="pjmedia_codec" - Package Owner=<4>
-# Microsoft Developer Studio Generated Build File, Format Version 6.00
-# ** DO NOT EDIT **
-
-# TARGTYPE "Win32 (x86) Static Library" 0x0104
-
-CFG=pjmedia_codec - Win32 Debug
-!MESSAGE This is not a valid makefile. To build this project using NMAKE,
-!MESSAGE use the Export Makefile command and run
-!MESSAGE
-!MESSAGE NMAKE /f "pjmedia_codec.mak".
-!MESSAGE
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "pjmedia_codec.mak" CFG="pjmedia_codec - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "pjmedia_codec - Win32 Release" (based on "Win32 (x86) Static Library")
-!MESSAGE "pjmedia_codec - Win32 Debug" (based on "Win32 (x86) Static Library")
-!MESSAGE
-
-# Begin Project
-# PROP AllowPerConfigDependencies 0
-# PROP Scc_ProjName ""
-# PROP Scc_LocalPath ""
-CPP=cl.exe
-RSC=rc.exe
-
-!IF "$(CFG)" == "pjmedia_codec - Win32 Release"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir ".\output\pjmedia-codec-i386-win32-vc6-debug"
-# PROP BASE Intermediate_Dir ".\output\pjmedia-codec-i386-win32-vc6-release"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 0
-# PROP Output_Dir ".\output\pjmedia-codec-i386-win32-vc6-release"
-# PROP Intermediate_Dir ".\output\pjmedia-codec-i386-win32-vc6-release"
-# PROP Target_Dir ""
-F90=df.exe
-# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
-# ADD CPP /nologo /MD /W4 /Zi /O2 /I "../include" /I "../../pjlib/include" /I "../src/pjmedia-codec" /I "../../third_party" /I "../../third_party/speex/include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "HAVE_CONFIG_H" /FR /FD /c
-# SUBTRACT CPP /YX
-# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /d "NDEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LIB32=link.exe -lib
-# ADD BASE LIB32 /nologo
-# ADD LIB32 /nologo /out:"..\lib\pjmedia-codec-i386-win32-vc6-release.lib"
-
-!ELSEIF "$(CFG)" == "pjmedia_codec - Win32 Debug"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir ".\output\pjmedia-codec-i386-win32-vc6-debug"
-# PROP BASE Intermediate_Dir ".\output\pjmedia-codec-i386-win32-vc6-debug"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 1
-# PROP Output_Dir ".\output\pjmedia-codec-i386-win32-vc6-debug"
-# PROP Intermediate_Dir ".\output\pjmedia-codec-i386-win32-vc6-debug"
-# PROP Target_Dir ""
-F90=df.exe
-# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /ZI /Od /I "../include" /I "../../pjlib/include" /I "../src/pjmedia-codec" /I "../../third_party" /I "../../third_party/speex/include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "HAVE_CONFIG_H" /FR /FD /GZ /c
-# SUBTRACT CPP /YX
-# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /d "_DEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LIB32=link.exe -lib
-# ADD BASE LIB32 /nologo
-# ADD LIB32 /nologo /out:"..\lib\pjmedia-codec-i386-win32-vc6-debug.lib"
-
-!ENDIF
-
-# Begin Target
-
-# Name "pjmedia_codec - Win32 Release"
-# Name "pjmedia_codec - Win32 Debug"
-# Begin Group "Source Files"
-
-# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
-# Begin Group "g722 Files"
-
-# PROP Default_Filter ""
-# Begin Source File
-
-SOURCE="..\src\pjmedia-codec\g722\g722_dec.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-codec\g722\g722_dec.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-codec\g722\g722_enc.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-codec\g722\g722_enc.h"
-# End Source File
-# End Group
-# Begin Source File
-
-SOURCE="..\src\pjmedia-codec\g722.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-codec\g7221.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-codec\gsm.c"
-
-!IF "$(CFG)" == "pjmedia_codec - Win32 Release"
-
-!ELSEIF "$(CFG)" == "pjmedia_codec - Win32 Debug"
-
-# ADD CPP /W4
-
-!ENDIF
-
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-codec\ilbc.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-codec\ipp_codecs.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-codec\l16.c"
-
-!IF "$(CFG)" == "pjmedia_codec - Win32 Release"
-
-!ELSEIF "$(CFG)" == "pjmedia_codec - Win32 Debug"
-
-# ADD CPP /W4
-
-!ENDIF
-
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-codec\passthrough.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjmedia-codec\speex_codec.c"
-
-!IF "$(CFG)" == "pjmedia_codec - Win32 Release"
-
-!ELSEIF "$(CFG)" == "pjmedia_codec - Win32 Debug"
-
-# ADD CPP /W4
-
-!ENDIF
-
-# End Source File
-# End Group
-# Begin Group "Header Files"
-
-# PROP Default_Filter "h;hpp;hxx;hm;inl"
-# Begin Source File
-
-SOURCE="..\include\pjmedia-codec\amr_helper.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-codec\config.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-codec\g722.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-codec\g7221.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-codec\gsm.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-codec\ilbc.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-codec\ipp_codecs.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-codec\l16.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-codec\passthrough.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-codec.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-codec\speex.h"
-# End Source File
-# Begin Source File
-
-SOURCE="..\include\pjmedia-codec\types.h"
-# End Source File
-# End Group
-# End Target
-# End Project
diff --git a/pjmedia/build/pjmedia_codec.vcproj b/pjmedia/build/pjmedia_codec.vcproj
index 25cf77bf..d16e73f7 100644
--- a/pjmedia/build/pjmedia_codec.vcproj
+++ b/pjmedia/build/pjmedia_codec.vcproj
@@ -2765,6 +2765,10 @@
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
>
<File
+ RelativePath="..\src\pjmedia-codec\ffmpeg_codecs.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia-codec\g722.c"
>
</File>
@@ -2831,6 +2835,14 @@
</FileConfiguration>
</File>
<File
+ RelativePath="..\src\pjmedia-codec\h263_packetizer.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia-codec\h264_packetizer.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia-codec\ilbc.c"
>
<FileConfiguration
@@ -3046,6 +3058,10 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia-codec\ffmpeg_codecs.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia-codec\g722.h"
>
</File>
@@ -3058,6 +3074,14 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia-codec\h263_packetizer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\pjmedia-codec\h264_packetizer.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia-codec\ilbc.h"
>
</File>
diff --git a/pjmedia/build/pjmedia_test.dsp b/pjmedia/build/pjmedia_test.dsp
deleted file mode 100644
index b5514976..00000000
--- a/pjmedia/build/pjmedia_test.dsp
+++ /dev/null
@@ -1,144 +0,0 @@
-# Microsoft Developer Studio Project File - Name="pjmedia_test" - Package Owner=<4>
-# Microsoft Developer Studio Generated Build File, Format Version 6.00
-# ** DO NOT EDIT **
-
-# TARGTYPE "Win32 (x86) Console Application" 0x0103
-
-CFG=pjmedia_test - Win32 Debug
-!MESSAGE This is not a valid makefile. To build this project using NMAKE,
-!MESSAGE use the Export Makefile command and run
-!MESSAGE
-!MESSAGE NMAKE /f "pjmedia_test.mak".
-!MESSAGE
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "pjmedia_test.mak" CFG="pjmedia_test - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "pjmedia_test - Win32 Release" (based on "Win32 (x86) Console Application")
-!MESSAGE "pjmedia_test - Win32 Debug" (based on "Win32 (x86) Console Application")
-!MESSAGE
-
-# Begin Project
-# PROP AllowPerConfigDependencies 0
-# PROP Scc_ProjName ""
-# PROP Scc_LocalPath ""
-CPP=cl.exe
-RSC=rc.exe
-
-!IF "$(CFG)" == "pjmedia_test - Win32 Release"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir ".\output\pjmedia_test_vc6_Release"
-# PROP BASE Intermediate_Dir ".\output\pjmedia_test_vc6_Release"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 0
-# PROP Output_Dir ".\output\pjmedia_test_vc6_Release"
-# PROP Intermediate_Dir ".\output\pjmedia_test_vc6_Release"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../pjlib/include" /I "../include" /I "../../pjnath/include" /I "../../pjlib-util/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /c
-# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /d "NDEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
-# ADD LINK32 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/pjmedia_test_vc6.exe"
-
-!ELSEIF "$(CFG)" == "pjmedia_test - Win32 Debug"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir ".\output\pjmedia_test_vc6_Debug"
-# PROP BASE Intermediate_Dir ".\output\pjmedia_test_vc6_Debug"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 1
-# PROP Output_Dir ".\output\pjmedia_test_vc6_Debug"
-# PROP Intermediate_Dir ".\output\pjmedia_test_vc6_Debug"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../pjlib/include" /I "../include" /I "../../pjnath/include" /I "../../pjlib-util/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c
-# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /d "_DEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
-# ADD LINK32 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjmedia_test_vc6d.exe" /pdbtype:sept
-
-!ENDIF
-
-# Begin Target
-
-# Name "pjmedia_test - Win32 Release"
-# Name "pjmedia_test - Win32 Debug"
-# Begin Group "Source Files"
-
-# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
-# Begin Source File
-
-SOURCE=..\src\test\codec_vectors.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\test\jbuf_test.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\test\main.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\test\mips_test.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\test\rtp_test.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\test\sdp_neg_test.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\test\sdptest.c
-# PROP Exclude_From_Build 1
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\test\session_test.c
-# PROP Exclude_From_Build 1
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\test\test.c
-# End Source File
-# End Group
-# Begin Group "Header Files"
-
-# PROP Default_Filter "h;hpp;hxx;hm;inl"
-# Begin Source File
-
-SOURCE=..\src\test\test.h
-# End Source File
-# End Group
-# Begin Group "Resource Files"
-
-# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
-# End Group
-# Begin Source File
-
-SOURCE=.\JBTEST.DAT
-# End Source File
-# End Target
-# End Project
diff --git a/pjmedia/build/pjmedia_test.vcproj b/pjmedia/build/pjmedia_test.vcproj
index bac1a3d4..5da8f9cc 100644
--- a/pjmedia/build/pjmedia_test.vcproj
+++ b/pjmedia/build/pjmedia_test.vcproj
@@ -11,16 +11,16 @@
Name="Win32"
/>
<Platform
- Name="Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Pocket PC 2003 (ARMV4)"
/>
<Platform
- Name="Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Smartphone 2003 (ARMV4)"
/>
<Platform
- Name="Pocket PC 2003 (ARMV4)"
+ Name="Windows Mobile 6 Standard SDK (ARMV4I)"
/>
<Platform
- Name="Smartphone 2003 (ARMV4)"
+ Name="Windows Mobile 6 Professional SDK (ARMV4I)"
/>
<Platform
Name="Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
@@ -34,14 +34,11 @@
<Configurations>
<Configuration
Name="Debug|Win32"
+ ConfigurationType="1"
InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
-
+ UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="2"
-
- ConfigurationType="1"
- UseOfMFC="0"
>
<Tool
Name="VCPreBuildEventTool"
@@ -60,9 +57,8 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_CONSOLE;"
-
AdditionalIncludeDirectories="../../pjlib/include,../include,../../pjnath/include,../../pjlib-util/include"
+ PreprocessorDefinitions="_CONSOLE;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -78,7 +74,7 @@
Name="VCLinkerTool"
AdditionalDependencies="dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib"
OutputFile="..\bin\pjmedia-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-
+ IgnoreDefaultLibraryNames="MSVCRT.LIB"
/>
<Tool
Name="VCALinkTool"
@@ -106,14 +102,11 @@
/>
</Configuration>
<Configuration
- Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
-
+ Name="Debug|Pocket PC 2003 (ARMV4)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -148,8 +141,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -176,14 +168,11 @@
/>
</Configuration>
<Configuration
- Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
-
+ Name="Debug|Smartphone 2003 (ARMV4)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -218,8 +207,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -247,14 +235,11 @@
</Configuration>
<Configuration
Name="Release|Win32"
+ ConfigurationType="1"
InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
-
+ UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="2"
-
- ConfigurationType="1"
- UseOfMFC="0"
>
<Tool
Name="VCPreBuildEventTool"
@@ -273,9 +258,8 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_CONSOLE;"
-
AdditionalIncludeDirectories="../../pjlib/include,../include,../../pjnath/include,../../pjlib-util/include"
+ PreprocessorDefinitions="_CONSOLE;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -291,7 +275,7 @@
Name="VCLinkerTool"
AdditionalDependencies="dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib"
OutputFile="..\bin\pjmedia-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-
+ IgnoreDefaultLibraryNames=""
/>
<Tool
Name="VCALinkTool"
@@ -319,14 +303,11 @@
/>
</Configuration>
<Configuration
- Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
-
+ Name="Release|Pocket PC 2003 (ARMV4)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -361,8 +342,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -389,14 +369,11 @@
/>
</Configuration>
<Configuration
- Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
-
+ Name="Release|Smartphone 2003 (ARMV4)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -431,8 +408,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -460,14 +436,11 @@
</Configuration>
<Configuration
Name="Debug-Static|Win32"
+ ConfigurationType="1"
InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
-
+ UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="2"
-
- ConfigurationType="1"
- UseOfMFC="0"
>
<Tool
Name="VCPreBuildEventTool"
@@ -486,9 +459,8 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_CONSOLE;"
-
AdditionalIncludeDirectories="../../pjlib/include,../include,../../pjnath/include,../../pjlib-util/include"
+ PreprocessorDefinitions="_CONSOLE;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -504,7 +476,7 @@
Name="VCLinkerTool"
AdditionalDependencies="dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib"
OutputFile="..\bin\pjmedia-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-
+ IgnoreDefaultLibraryNames="MSVCRT.LIB"
/>
<Tool
Name="VCALinkTool"
@@ -532,14 +504,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
-
+ Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -574,8 +543,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -602,14 +570,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
-
+ Name="Debug-Static|Smartphone 2003 (ARMV4)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -644,8 +609,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -673,14 +637,11 @@
</Configuration>
<Configuration
Name="Release-Dynamic|Win32"
+ ConfigurationType="1"
InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
-
+ UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="2"
-
- ConfigurationType="1"
- UseOfMFC="0"
>
<Tool
Name="VCPreBuildEventTool"
@@ -699,9 +660,8 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_CONSOLE;"
-
AdditionalIncludeDirectories="../../pjlib/include,../include,../../pjnath/include,../../pjlib-util/include"
+ PreprocessorDefinitions="_CONSOLE;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -717,7 +677,7 @@
Name="VCLinkerTool"
AdditionalDependencies="dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib"
OutputFile="..\bin\pjmedia-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-
+ IgnoreDefaultLibraryNames=""
/>
<Tool
Name="VCALinkTool"
@@ -745,14 +705,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
-
+ Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -787,8 +744,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -815,14 +771,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
-
+ Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -857,8 +810,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -886,14 +838,11 @@
</Configuration>
<Configuration
Name="Debug-Dynamic|Win32"
+ ConfigurationType="1"
InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
-
+ UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="2"
-
- ConfigurationType="1"
- UseOfMFC="0"
>
<Tool
Name="VCPreBuildEventTool"
@@ -912,9 +861,8 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_CONSOLE;"
-
AdditionalIncludeDirectories="../../pjlib/include,../include,../../pjnath/include,../../pjlib-util/include"
+ PreprocessorDefinitions="_CONSOLE;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -930,7 +878,7 @@
Name="VCLinkerTool"
AdditionalDependencies="dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib"
OutputFile="..\bin\pjmedia-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-
+ IgnoreDefaultLibraryNames="MSVCRT.LIB"
/>
<Tool
Name="VCALinkTool"
@@ -958,14 +906,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
-
+ Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1000,8 +945,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1028,14 +972,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
-
+ Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1070,8 +1011,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1099,14 +1039,11 @@
</Configuration>
<Configuration
Name="Release-Static|Win32"
+ ConfigurationType="1"
InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
-
+ UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="2"
-
- ConfigurationType="1"
- UseOfMFC="0"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1125,9 +1062,8 @@
/>
<Tool
Name="VCCLCompilerTool"
- PreprocessorDefinitions="_CONSOLE;"
-
AdditionalIncludeDirectories="../../pjlib/include,../include,../../pjnath/include,../../pjlib-util/include"
+ PreprocessorDefinitions="_CONSOLE;"
PrecompiledHeaderFile=""
/>
<Tool
@@ -1143,7 +1079,7 @@
Name="VCLinkerTool"
AdditionalDependencies="dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib"
OutputFile="..\bin\pjmedia-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-
+ IgnoreDefaultLibraryNames="MSVCRT.LIB"
/>
<Tool
Name="VCALinkTool"
@@ -1171,14 +1107,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
-
+ Name="Release-Static|Pocket PC 2003 (ARMV4)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1213,8 +1146,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1241,14 +1173,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
-
+ Name="Release-Static|Smartphone 2003 (ARMV4)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1283,8 +1212,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1311,14 +1239,11 @@
/>
</Configuration>
<Configuration
- Name="Debug|Pocket PC 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
-
+ Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1353,8 +1278,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1381,14 +1305,11 @@
/>
</Configuration>
<Configuration
- Name="Release|Pocket PC 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
-
+ Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1423,8 +1344,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1451,14 +1371,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Static|Pocket PC 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
-
+ Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1493,8 +1410,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1521,14 +1437,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
-
+ Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1563,8 +1476,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1591,14 +1503,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
-
+ Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1633,8 +1542,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1661,14 +1569,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Static|Pocket PC 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
-
+ Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1703,8 +1608,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1731,14 +1635,11 @@
/>
</Configuration>
<Configuration
- Name="Debug|Smartphone 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
-
+ Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1773,8 +1674,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1801,14 +1701,11 @@
/>
</Configuration>
<Configuration
- Name="Release|Smartphone 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
-
+ Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1843,8 +1740,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1871,14 +1767,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Static|Smartphone 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
-
+ Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1913,8 +1806,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -1941,14 +1833,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
-
+ Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -1983,8 +1872,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -2011,14 +1899,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
-
+ Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2053,8 +1938,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -2081,14 +1965,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Static|Smartphone 2003 (ARMV4)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
-
+ Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2123,8 +2004,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -2151,14 +2031,11 @@
/>
</Configuration>
<Configuration
- Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
-
+ Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2193,8 +2070,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -2221,14 +2097,11 @@
/>
</Configuration>
<Configuration
- Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
-
+ Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2263,8 +2136,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -2291,14 +2163,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
-
+ Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2334,7 +2203,6 @@
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
-
/>
<Tool
Name="VCALinkTool"
@@ -2361,14 +2229,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ ConfigurationType="1"
InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
-
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2403,8 +2268,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -2431,14 +2295,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
-
+ Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2473,8 +2334,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -2501,14 +2361,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
-
+ Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2543,8 +2400,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -2571,14 +2427,11 @@
/>
</Configuration>
<Configuration
- Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
-
+ Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2613,8 +2466,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -2641,14 +2493,11 @@
/>
</Configuration>
<Configuration
- Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
-
+ Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2684,7 +2533,6 @@
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
-
/>
<Tool
Name="VCALinkTool"
@@ -2711,14 +2559,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
-
+ Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2753,8 +2598,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -2781,14 +2625,11 @@
/>
</Configuration>
<Configuration
- Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
-
+ Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2823,8 +2664,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -2851,14 +2691,11 @@
/>
</Configuration>
<Configuration
- Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
- InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
-
+ Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2893,8 +2730,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
- OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
-
+ OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
/>
<Tool
Name="VCALinkTool"
@@ -2922,13 +2758,10 @@
</Configuration>
<Configuration
Name="Release-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ ConfigurationType="1"
InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
-
ATLMinimizesCRunTimeLibraryUsage="false"
-
CharacterSet="1"
-
- ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
@@ -2964,7 +2797,6 @@
Name="VCLinkerTool"
AdditionalDependencies="ws2.lib"
OutputFile="..\bin\pjmedia-test-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
-
/>
<Tool
Name="VCALinkTool"
@@ -3073,7 +2905,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Debug|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3081,7 +2913,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Debug|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3098,7 +2930,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Release|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3106,7 +2938,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Release|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3123,7 +2955,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Debug-Static|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3131,7 +2963,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Debug-Static|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3148,7 +2980,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3156,7 +2988,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3173,7 +3005,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3181,7 +3013,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3198,7 +3030,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Release-Static|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3206,7 +3038,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Release-Static|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3214,7 +3046,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Pocket PC 2003 (ARMV4)"
+ Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3222,7 +3054,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Pocket PC 2003 (ARMV4)"
+ Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3230,7 +3062,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+ Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3238,7 +3070,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
+ Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3246,7 +3078,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+ Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3254,7 +3086,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Pocket PC 2003 (ARMV4)"
+ Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3262,7 +3094,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Smartphone 2003 (ARMV4)"
+ Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3270,7 +3102,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Smartphone 2003 (ARMV4)"
+ Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3278,7 +3110,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Smartphone 2003 (ARMV4)"
+ Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3286,7 +3118,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+ Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3294,7 +3126,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
+ Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3302,7 +3134,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Smartphone 2003 (ARMV4)"
+ Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3310,7 +3142,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3318,7 +3150,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3326,7 +3158,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3334,7 +3166,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3342,7 +3174,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3350,7 +3182,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3358,7 +3190,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3366,7 +3198,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3374,7 +3206,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3382,7 +3214,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3390,7 +3222,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3594,7 +3426,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Debug|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3602,7 +3434,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Debug|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3620,7 +3452,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Release|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3628,7 +3460,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Release|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3646,7 +3478,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Debug-Static|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3654,7 +3486,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Debug-Static|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3672,7 +3504,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3680,7 +3512,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3698,7 +3530,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3706,7 +3538,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3724,7 +3556,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Release-Static|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3732,7 +3564,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Release-Static|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3740,7 +3572,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Pocket PC 2003 (ARMV4)"
+ Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3748,7 +3580,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Pocket PC 2003 (ARMV4)"
+ Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3756,7 +3588,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+ Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3764,7 +3596,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
+ Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3772,7 +3604,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+ Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3780,7 +3612,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Pocket PC 2003 (ARMV4)"
+ Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3788,7 +3620,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Smartphone 2003 (ARMV4)"
+ Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3796,7 +3628,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Smartphone 2003 (ARMV4)"
+ Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3804,7 +3636,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Smartphone 2003 (ARMV4)"
+ Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3812,7 +3644,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+ Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3820,7 +3652,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
+ Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3828,7 +3660,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Smartphone 2003 (ARMV4)"
+ Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3836,7 +3668,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3844,7 +3676,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3852,7 +3684,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3860,7 +3692,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3868,7 +3700,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3876,7 +3708,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3884,7 +3716,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3892,7 +3724,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3900,7 +3732,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3908,7 +3740,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3916,7 +3748,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -3946,7 +3778,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Debug|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3954,7 +3786,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Debug|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3972,7 +3804,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Release|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3980,7 +3812,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Release|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -3998,7 +3830,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Debug-Static|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -4006,7 +3838,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Debug-Static|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -4024,7 +3856,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -4032,7 +3864,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -4050,7 +3882,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -4058,7 +3890,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -4076,7 +3908,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+ Name="Release-Static|Pocket PC 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -4084,7 +3916,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+ Name="Release-Static|Smartphone 2003 (ARMV4)"
ExcludedFromBuild="true"
>
<Tool
@@ -4092,7 +3924,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Pocket PC 2003 (ARMV4)"
+ Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4100,7 +3932,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Pocket PC 2003 (ARMV4)"
+ Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4108,7 +3940,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+ Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4116,7 +3948,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
+ Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4124,7 +3956,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+ Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4132,7 +3964,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Pocket PC 2003 (ARMV4)"
+ Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4140,7 +3972,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Smartphone 2003 (ARMV4)"
+ Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4148,7 +3980,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Smartphone 2003 (ARMV4)"
+ Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4156,7 +3988,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Smartphone 2003 (ARMV4)"
+ Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4164,7 +3996,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+ Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4172,7 +4004,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
+ Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4180,7 +4012,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Smartphone 2003 (ARMV4)"
+ Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4188,7 +4020,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4196,7 +4028,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4204,7 +4036,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4212,7 +4044,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4220,7 +4052,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4228,7 +4060,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4236,7 +4068,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4244,7 +4076,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4252,7 +4084,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4260,7 +4092,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4268,7 +4100,7 @@
/>
</FileConfiguration>
<FileConfiguration
- Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
ExcludedFromBuild="true"
>
<Tool
@@ -4343,6 +4175,18 @@
</FileConfiguration>
</File>
<File
+ RelativePath="..\src\test\vid_codec_test.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\test\vid_dev_test.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\test\vid_port_test.c"
+ >
+ </File>
+ <File
RelativePath="..\src\test\wince_main.c"
>
<FileConfiguration
diff --git a/pjmedia/build/pjmedia_videodev.vcproj b/pjmedia/build/pjmedia_videodev.vcproj
new file mode 100644
index 00000000..54d14f20
--- /dev/null
+++ b/pjmedia/build/pjmedia_videodev.vcproj
@@ -0,0 +1,2856 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="pjmedia_videodev"
+ ProjectGUID="{A1989FF3-9894-40F4-B5A6-6EA364476E45}"
+ RootNamespace="pjmedia_audiodev"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="Pocket PC 2003 (ARMV4)"
+ />
+ <Platform
+ Name="Smartphone 2003 (ARMV4)"
+ />
+ <Platform
+ Name="Windows Mobile 6 Standard SDK (ARMV4I)"
+ />
+ <Platform
+ Name="Windows Mobile 6 Professional SDK (ARMV4I)"
+ />
+ <Platform
+ Name="Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ />
+ <Platform
+ Name="Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-videodev-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Pocket PC 2003 (ARMV4)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Smartphone 2003 (ARMV4)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-videodev-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Pocket PC 2003 (ARMV4)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Smartphone 2003 (ARMV4)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Static|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-videodev-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Static|Smartphone 2003 (ARMV4)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Dynamic|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-videodev-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Dynamic|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-videodev-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Static|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-videodev-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Static|Pocket PC 2003 (ARMV4)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Static|Smartphone 2003 (ARMV4)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+ ConfigurationType="4"
+ InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/crypto/include,../../third_party/srtp/include,../.."
+ PreprocessorDefinitions="_LIB;"
+ PrecompiledHeaderFile=""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="..\lib\pjmedia-audiodev-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath="..\src\pjmedia-videodev\colorbar_dev.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia-videodev\dshow_dev.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia-videodev\dshowclasses.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia-videodev\errno.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia-videodev\ffmpeg_dev.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia-videodev\sdl_dev.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\pjmedia-videodev\videodev.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="..\include\pjmedia-videodev\config.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\pjmedia-videodev\errno.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\pjmedia-videodev\videodev.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\pjmedia-videodev\videodev_imp.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/pjmedia/include/pjmedia-audiodev/audiodev.h b/pjmedia/include/pjmedia-audiodev/audiodev.h
index 245489a9..32fd08ad 100644
--- a/pjmedia/include/pjmedia-audiodev/audiodev.h
+++ b/pjmedia/include/pjmedia-audiodev/audiodev.h
@@ -26,6 +26,8 @@
*/
#include <pjmedia-audiodev/config.h>
#include <pjmedia-audiodev/errno.h>
+#include <pjmedia/format.h>
+#include <pjmedia/frame.h>
#include <pjmedia/types.h>
#include <pj/pool.h>
diff --git a/pjmedia/include/pjmedia-audiodev/config.h b/pjmedia/include/pjmedia-audiodev/config.h
index f450a9ac..2ef9e9f6 100644
--- a/pjmedia/include/pjmedia-audiodev/config.h
+++ b/pjmedia/include/pjmedia-audiodev/config.h
@@ -21,8 +21,8 @@
#define __PJMEDIA_AUDIODEV_CONFIG_H__
/**
- * @file audiodev.h
- * @brief Audio device API.
+ * @file config.h
+ * @brief Audio config.
*/
#include <pjmedia/types.h>
#include <pj/pool.h>
diff --git a/pjmedia/include/pjmedia-codec.h b/pjmedia/include/pjmedia-codec.h
index 7ff72257..7e77b1d7 100644
--- a/pjmedia/include/pjmedia-codec.h
+++ b/pjmedia/include/pjmedia-codec.h
@@ -25,7 +25,9 @@
* @brief Include all codecs API in PJMEDIA-CODEC
*/
+#include <pjmedia-codec/audio_codecs.h>
#include <pjmedia-codec/l16.h>
+#include <pjmedia-codec/ffmpeg_codecs.h>
#include <pjmedia-codec/gsm.h>
#include <pjmedia-codec/speex.h>
#include <pjmedia-codec/ilbc.h>
diff --git a/pjmedia/include/pjmedia-codec/audio_codecs.h b/pjmedia/include/pjmedia-codec/audio_codecs.h
new file mode 100644
index 00000000..2a651e95
--- /dev/null
+++ b/pjmedia/include/pjmedia-codec/audio_codecs.h
@@ -0,0 +1,98 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_CODEC_ALL_CODECS_H__
+#define __PJMEDIA_CODEC_ALL_CODECS_H__
+
+/**
+ * @file pjmedia-codec/all_codecs.h
+ * @brief Helper function to register all codecs
+ */
+#include <pjmedia/endpoint.h>
+#include <pjmedia-codec/passthrough.h>
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJMEDIA_CODEC_REGISTER_ALL Codec registration helper
+ * @ingroup PJMEDIA_CODEC_CODECS
+ * @brief Helper function to register all codecs
+ * @{
+ *
+ * Helper function to register all codecs that are implemented in
+ * PJMEDIA-CODEC library.
+ */
+
+/**
+ * Codec configuration. Call #pjmedia_audio_codec_config_default() to initialize
+ * this structure with the default values.
+ */
+typedef struct pjmedia_audio_codec_config
+{
+ /** Speex codec settings. See #pjmedia_codec_speex_init() for more info */
+ struct {
+ unsigned option; /**< Bitmask of options. */
+ unsigned quality; /**< Codec quality. */
+ unsigned complexity; /**< Codec complexity. */
+ } speex;
+
+ /** iLBC settings */
+ struct {
+ unsigned mode; /**< iLBC mode. */
+ } ilbc;
+
+ /** Passthrough */
+ struct {
+ pjmedia_codec_passthrough_setting setting; /**< Passthrough */
+ } passthrough;
+
+} pjmedia_audio_codec_config;
+
+
+/**
+ * Initialize pjmedia_audio_codec_config structure with default values.
+ *
+ * @param cfg The codec config to be initialized.
+ */
+PJ_DECL(void)
+pjmedia_audio_codec_config_default(pjmedia_audio_codec_config *cfg);
+
+/**
+ * Register all known audio codecs implemented in PJMEDA-CODEC library to the
+ * specified media endpoint.
+ *
+ * @param endpt The media endpoint.
+ * @param c Optional codec configuration, or NULL to use default
+ * values.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt,
+ const pjmedia_audio_codec_config *c);
+
+
+/**
+ * @} PJMEDIA_CODEC_REGISTER_ALL
+ */
+
+
+PJ_END_DECL
+
+#endif /* __PJMEDIA_CODEC_ALL_CODECS_H__ */
diff --git a/pjmedia/include/pjmedia-codec/config.h b/pjmedia/include/pjmedia-codec/config.h
index 693fb106..fb02d47a 100644
--- a/pjmedia/include/pjmedia-codec/config.h
+++ b/pjmedia/include/pjmedia-codec/config.h
@@ -34,7 +34,6 @@
#include <pjmedia/types.h>
-
/*
* Include config_auto.h if autoconf is used (PJ_AUTOCONF is set)
*/
@@ -42,6 +41,7 @@
# include <pjmedia-codec/config_auto.h>
#endif
+
/**
* Unless specified otherwise, L16 codec is included by default.
*/
@@ -343,8 +343,20 @@
to control which implementation to be used.
#endif
+
+/**
+ * Specify if FFMPEG codecs are available.
+ *
+ * Default: PJMEDIA_HAS_LIBAVCODEC
+ */
+#ifndef PJMEDIA_HAS_FFMPEG_CODEC
+# define PJMEDIA_HAS_FFMPEG_CODEC PJMEDIA_HAS_LIBAVCODEC
+#endif
+
/**
* @}
*/
+
+
#endif /* __PJMEDIA_CODEC_CONFIG_H__ */
diff --git a/pjmedia/include/pjmedia-codec/ffmpeg_codecs.h b/pjmedia/include/pjmedia-codec/ffmpeg_codecs.h
new file mode 100644
index 00000000..d9280443
--- /dev/null
+++ b/pjmedia/include/pjmedia-codec/ffmpeg_codecs.h
@@ -0,0 +1,62 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_CODECS_FFMPEG_H__
+#define __PJMEDIA_CODECS_FFMPEG_H__
+
+
+#include <pjmedia-codec/types.h>
+#include <pjmedia/vid_codec.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Initialize and register FFMPEG codecs factory to pjmedia endpoint.
+ *
+ * @param mgr The video codec manager instance where this codec will
+ * be registered to. Specify NULL to use default instance
+ * (in that case, an instance of video codec manager must
+ * have been created beforehand).
+ * @param pf Pool factory.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_ffmpeg_init(pjmedia_vid_codec_mgr *mgr,
+ pj_pool_factory *pf);
+
+
+/**
+ * Unregister FFMPEG codecs factory from the video codec manager and
+ * deinitialize the codecs library.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_ffmpeg_deinit(void);
+
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+#endif /* __PJMEDIA_CODECS_FFMPEG_H__ */
+
diff --git a/pjmedia/include/pjmedia-codec/h263_packetizer.h b/pjmedia/include/pjmedia-codec/h263_packetizer.h
new file mode 100644
index 00000000..b80faf84
--- /dev/null
+++ b/pjmedia/include/pjmedia-codec/h263_packetizer.h
@@ -0,0 +1,146 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_H263_PACKETIZER_H__
+#define __PJMEDIA_H263_PACKETIZER_H__
+
+
+/**
+ * @file h263_packetizer.h
+ * @brief Packetizes/unpacketizes H.263 bitstream into RTP payload.
+ */
+
+#include <pj/pool.h>
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Opaque declaration for H.263 packetizer.
+ */
+typedef struct pjmedia_h263_packetizer pjmedia_h263_packetizer;
+
+
+/**
+ * Enumeration of H.263 packetization modes.
+ */
+typedef enum
+{
+ /**
+ * H.263 RTP packetization using RFC 4629.
+ */
+ PJMEDIA_H263_PACKETIZER_MODE_RFC4629,
+
+ /**
+ * H.263 RTP packetization using legacy RFC 2190.
+ * This is currently not supported.
+ */
+ PJMEDIA_H263_PACKETIZER_MODE_RFC2190,
+
+} pjmedia_h263_packetizer_mode;
+
+
+/**
+ * H.263 packetizer configuration.
+ */
+typedef struct pjmedia_h263_packetizer_cfg
+{
+ /**
+ * Maximum payload length.
+ * Default: PJMEDIA_MAX_MTU
+ */
+ int mtu;
+
+ /**
+ * Packetization mode.
+ * Default: PJMEDIA_H263_PACKETIZER_MODE_RFC4629
+ */
+ pjmedia_h263_packetizer_mode mode;
+
+} pjmedia_h263_packetizer_cfg;
+
+
+/**
+ * Create H.263 packetizer.
+ *
+ * @param pool The memory pool.
+ * @param cfg Packetizer settings, if NULL, default setting
+ * will be used.
+ * @param p_pktz Pointer to receive the packetizer.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_h263_packetizer_create(
+ pj_pool_t *pool,
+ const pjmedia_h263_packetizer_cfg *cfg,
+ pjmedia_h263_packetizer **p_pktz);
+
+
+/**
+ * Generate an RTP payload from a H.263 picture bitstream. Note that this
+ * function will apply in-place processing, so the bitstream may be modified
+ * during the packetization.
+ *
+ * @param pktz The packetizer.
+ * @param bits The picture bitstream to be packetized.
+ * @param bits_len The length of the bitstream.
+ * @param bits_pos The bitstream offset to be packetized.
+ * @param payload The output payload.
+ * @param payload_len The output payload length.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_h263_packetize(pjmedia_h263_packetizer *pktz,
+ pj_uint8_t *bits,
+ pj_size_t bits_len,
+ unsigned *bits_pos,
+ const pj_uint8_t **payload,
+ pj_size_t *payload_len);
+
+
+/**
+ * Append an RTP payload to an H.263 picture bitstream. Note that in case of
+ * noticing packet lost, application should keep calling this function with
+ * payload pointer set to NULL, as the packetizer need to update its internal
+ * state.
+ *
+ * @param pktz The packetizer.
+ * @param payload The payload to be unpacketized.
+ * @param payload_len The payload length.
+ * @param bits The bitstream buffer.
+ * @param bits_size The bitstream buffer size.
+ * @param bits_pos The bitstream offset to put the unpacketized payload
+ * in the bitstream, upon return, this will be updated
+ * to the latest offset as a result of the unpacketized
+ * payload.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_h263_unpacketize(pjmedia_h263_packetizer *pktz,
+ const pj_uint8_t *payload,
+ pj_size_t payload_len,
+ pj_uint8_t *bits,
+ pj_size_t bits_size,
+ unsigned *bits_pos);
+
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_H263_PACKETIZER_H__ */
diff --git a/pjmedia/include/pjmedia-codec/h264_packetizer.h b/pjmedia/include/pjmedia-codec/h264_packetizer.h
new file mode 100644
index 00000000..a676a04b
--- /dev/null
+++ b/pjmedia/include/pjmedia-codec/h264_packetizer.h
@@ -0,0 +1,157 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_H264_PACKETIZER_H__
+#define __PJMEDIA_H264_PACKETIZER_H__
+
+/**
+ * @file h264_packetizer.h
+ * @brief Packetizes H.264 bitstream into RTP payload and vice versa.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * Opaque declaration for H.264 packetizer.
+ */
+typedef struct pjmedia_h264_packetizer pjmedia_h264_packetizer;
+
+
+/**
+ * Enumeration of H.264 packetization modes.
+ */
+typedef enum
+{
+ /**
+ * Single NAL unit packetization mode will only generate payloads
+ * containing a complete single NAL unit packet. As H.264 NAL unit
+ * size can be very large, this mode is usually not applicable for
+ * network environments with MTU size limitation.
+ */
+ PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL,
+
+ /**
+ * Non-interleaved packetization mode will generate payloads with the
+ * following possible formats:
+ * - single NAL unit packets,
+ * - NAL units aggregation STAP-A packets,
+ * - fragmented NAL unit FU-A packets.
+ */
+ PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED,
+
+ /**
+ * Interleaved packetization mode will generate payloads with the
+ * following possible formats:
+ * - single NAL unit packets,
+ * - NAL units aggregation STAP-A & STAP-B packets,
+ * - fragmented NAL unit FU-A & FU-B packets.
+ * This packetization mode is currently unsupported.
+ */
+ PJMEDIA_H264_PACKETIZER_MODE_INTERLEAVED,
+} pjmedia_h264_packetizer_mode;
+
+
+/**
+ * H.264 packetizer setting.
+ */
+typedef struct pjmedia_h264_packetizer_cfg
+{
+ /**
+ * Maximum payload length.
+ * Default: PJMEDIA_MAX_MTU
+ */
+ int mtu;
+
+ /**
+ * Packetization mode.
+ * Default: PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED
+ */
+ pjmedia_h264_packetizer_mode mode;
+}
+pjmedia_h264_packetizer_cfg;
+
+
+/**
+ * Create H.264 packetizer.
+ *
+ * @param pool The memory pool.
+ * @param cfg Packetizer settings, if NULL, default setting
+ * will be used.
+ * @param p_pktz Pointer to receive the packetizer.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_h264_packetizer_create(
+ pj_pool_t *pool,
+ const pjmedia_h264_packetizer_cfg *cfg,
+ pjmedia_h264_packetizer **p_pktz);
+
+
+/**
+ * Generate an RTP payload from a H.264 picture bitstream. Note that this
+ * function will apply in-place processing, so the bitstream may be modified
+ * during the packetization.
+ *
+ * @param pktz The packetizer.
+ * @param bits The picture bitstream to be packetized.
+ * @param bits_len The length of the bitstream.
+ * @param bits_pos The bitstream offset to be packetized.
+ * @param payload The output payload.
+ * @param payload_len The output payload length.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_h264_packetize(pjmedia_h264_packetizer *pktz,
+ pj_uint8_t *bits,
+ pj_size_t bits_len,
+ unsigned *bits_pos,
+ const pj_uint8_t **payload,
+ pj_size_t *payload_len);
+
+
+/**
+ * Append an RTP payload to an H.264 picture bitstream. Note that in case of
+ * noticing packet lost, application should keep calling this function with
+ * payload pointer set to NULL, as the packetizer need to update its internal
+ * state.
+ *
+ * @param pktz The packetizer.
+ * @param payload The payload to be unpacketized.
+ * @param payload_len The payload length.
+ * @param bits The bitstream buffer.
+ * @param bits_size The bitstream buffer size.
+ * @param bits_pos The bitstream offset to put the unpacketized payload
+ * in the bitstream, upon return, this will be updated
+ * to the latest offset as a result of the unpacketized
+ * payload.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_h264_unpacketize(pjmedia_h264_packetizer *pktz,
+ const pj_uint8_t *payload,
+ pj_size_t payload_len,
+ pj_uint8_t *bits,
+ pj_size_t bits_len,
+ unsigned *bits_pos);
+
+
+PJ_END_DECL
+
+#endif /* __PJMEDIA_H264_PACKETIZER_H__ */
diff --git a/pjmedia/include/pjmedia-codec/types.h b/pjmedia/include/pjmedia-codec/types.h
index e7599875..752a1fcb 100644
--- a/pjmedia/include/pjmedia-codec/types.h
+++ b/pjmedia/include/pjmedia-codec/types.h
@@ -38,11 +38,11 @@
/**
- * These are the dynamic payload types that are used by codecs in
+ * These are the dynamic payload types that are used by audio codecs in
* this library. Also see the header file <pjmedia/codec.h> for list
* of static payload types.
*/
-enum
+enum pjmedia_audio_pt
{
/* According to IANA specifications, dynamic payload types are to be in
* the range 96-127 (inclusive). This enum is structured to place the
@@ -98,6 +98,27 @@ enum
};
/**
+ * These are the dynamic payload types that are used by video codecs in
+ * this library.
+ */
+enum pjmedia_video_pt
+{
+ /* Video payload types */
+ PJMEDIA_RTP_PT_VID_START = (PJMEDIA_RTP_PT_DYNAMIC-1),
+ PJMEDIA_RTP_PT_H263P,
+ PJMEDIA_RTP_PT_H264,
+ PJMEDIA_RTP_PT_H264_RSV1,
+ PJMEDIA_RTP_PT_H264_RSV2,
+ PJMEDIA_RTP_PT_H264_RSV3,
+ PJMEDIA_RTP_PT_H264_RSV4,
+
+ /* Caution!
+ * Ensure the value of the last pt above is <= 127.
+ */
+};
+
+
+/**
* @}
*/
diff --git a/pjmedia/include/pjmedia-videodev/config.h b/pjmedia/include/pjmedia-videodev/config.h
new file mode 100644
index 00000000..03137918
--- /dev/null
+++ b/pjmedia/include/pjmedia-videodev/config.h
@@ -0,0 +1,192 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_VIDEODEV_CONFIG_H__
+#define __PJMEDIA_VIDEODEV_CONFIG_H__
+
+/**
+ * @file config.h
+ * @brief Video config.
+ */
+#include <pjmedia/types.h>
+#include <pj/pool.h>
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup video_device_api Video Device API
+ * @brief PJMEDIA video device abstraction API.
+ */
+
+/**
+ * @defgroup s1_video_device_config Compile time configurations
+ * @ingroup video_device_api
+ * @brief Compile time configurations
+ * @{
+ */
+
+/**
+ * This setting controls the maximum number of formats that can be
+ * supported by a video device.
+ *
+ * Default: 16
+ */
+#ifndef PJMEDIA_VID_DEV_INFO_FMT_CNT
+# define PJMEDIA_VID_DEV_INFO_FMT_CNT 16
+#endif
+
+
+/**
+ * This setting controls whether SDL support should be included.
+ *
+ * Default: 0 (or detected by configure)
+ */
+#ifndef PJMEDIA_VIDEO_DEV_HAS_SDL
+# define PJMEDIA_VIDEO_DEV_HAS_SDL 0
+# define PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL 0
+#endif
+
+
+/**
+ * This setting controls whether QT support should be included.
+ *
+ * Default: 0 (or detected by configure)
+ */
+#ifndef PJMEDIA_VIDEO_DEV_HAS_QT
+# define PJMEDIA_VIDEO_DEV_HAS_QT 0
+#endif
+
+
+/**
+ * This setting controls whether IOS support should be included.
+ *
+ * Default: 0 (or detected by configure)
+ */
+#ifndef PJMEDIA_VIDEO_DEV_HAS_IOS
+# define PJMEDIA_VIDEO_DEV_HAS_IOS 0
+#endif
+
+
+/**
+ * This setting controls whether Direct Show support should be included.
+ *
+ * Default: 0 (unfinished)
+ */
+#ifndef PJMEDIA_VIDEO_DEV_HAS_DSHOW
+# define PJMEDIA_VIDEO_DEV_HAS_DSHOW 0 //PJ_WIN32
+#endif
+
+
+/**
+ * This setting controls whether colorbar source support should be included.
+ *
+ * Default: 1
+ */
+#ifndef PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC
+# define PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC 1
+#endif
+
+
+/**
+ * This setting controls whether ffmpeg support should be included.
+ *
+ * Default: 0 (unfinished)
+ */
+#ifndef PJMEDIA_VIDEO_DEV_HAS_FFMPEG
+# define PJMEDIA_VIDEO_DEV_HAS_FFMPEG 0
+#endif
+
+
+/**
+ * Video4Linux2
+ *
+ * Default: 0 (or detected by configure)
+ */
+#ifndef PJMEDIA_VIDEO_DEV_HAS_V4L2
+# define PJMEDIA_VIDEO_DEV_HAS_V4L2 0
+#endif
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_VIDEODEV_CONFIG_H__ */
+
+/*
+ --------------------- DOCUMENTATION FOLLOWS ---------------------------
+ */
+
+/**
+ * @addtogroup video_device_api Video Device API
+ * @{
+
+PJMEDIA Video Device API is a cross-platform video API appropriate for use with
+VoIP applications and many other types of video streaming applications.
+
+The API abstracts many different video API's on various platforms, such as:
+ - native Direct Show video for Win32 and Windows Mobile devices
+ - null-video implementation
+ - and more to be implemented in the future
+
+The Video Device API/library is an evolution from PJMEDIA @ref PJMED_SND and
+contains many enhancements:
+
+ - Forward compatibility:
+\n
+ The new API has been designed to be extensible, it will support new API's as
+ well as new features that may be introduced in the future without breaking
+ compatibility with applications that use this API as well as compatibility
+ with existing device implementations.
+
+ - Device capabilities:
+\n
+ At the heart of the API is device capabilities management, where all possible
+ video capabilities of video devices should be able to be handled in a generic
+ manner. With this framework, new capabilities that may be discovered in the
+ future can be handled in manner without breaking existing applications.
+
+ - Built-in features:
+\n
+ The device capabilities framework enables applications to use and control
+ video features built-in in the device, such as:
+ - built-in formats,
+ - etc.
+
+ - Codec support:
+\n
+ Some video devices support built-in hardware video codecs, and application
+ can use the video device in encoded mode to make use of these hardware
+ codecs.
+
+ - Multiple backends:
+\n
+ The new API supports multiple video backends (called factories or drivers in
+ the code) to be active simultaneously, and video backends may be added or
+ removed during run-time.
+
+*/
+
+
+/**
+ * @}
+ */
+
diff --git a/pjmedia/include/pjmedia-videodev/errno.h b/pjmedia/include/pjmedia-videodev/errno.h
new file mode 100644
index 00000000..5978bf93
--- /dev/null
+++ b/pjmedia/include/pjmedia-videodev/errno.h
@@ -0,0 +1,159 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_VIDEODEV_VIDEODEV_ERRNO_H__
+#define __PJMEDIA_VIDEODEV_VIDEODEV_ERRNO_H__
+
+/**
+ * @file errno.h Error Codes
+ * @brief Videodev specific error codes.
+ */
+
+#include <pjmedia-videodev/config.h>
+#include <pj/errno.h>
+
+/**
+ * @defgroup error_codes Error Codes
+ * @ingroup video_device_api
+ * @brief Video device library specific error codes.
+ * @{
+ */
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Start of error code relative to PJ_ERRNO_START_USER.
+ * This value is 520000.
+ */
+#define PJMEDIA_VIDEODEV_ERRNO_START \
+ (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*7)
+#define PJMEDIA_VIDEODEV_ERRNO_END \
+ (PJMEDIA_VIDEODEV_ERRNO_START + PJ_ERRNO_SPACE_SIZE - 1)
+
+
+/************************************************************
+ * Video Device API error codes
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * General/unknown error.
+ */
+#define PJMEDIA_EVID_ERR (PJMEDIA_VIDEODEV_ERRNO_START+1) /* 520001 */
+
+/**
+ * @hideinitializer
+ * Unknown error from video driver
+ */
+#define PJMEDIA_EVID_SYSERR (PJMEDIA_VIDEODEV_ERRNO_START+2) /* 520002 */
+
+/**
+ * @hideinitializer
+ * Video subsystem not initialized
+ */
+#define PJMEDIA_EVID_INIT (PJMEDIA_VIDEODEV_ERRNO_START+3) /* 520003 */
+
+/**
+ * @hideinitializer
+ * Invalid video device
+ */
+#define PJMEDIA_EVID_INVDEV (PJMEDIA_VIDEODEV_ERRNO_START+4) /* 520004 */
+
+/**
+ * @hideinitializer
+ * Found no devices
+ */
+#define PJMEDIA_EVID_NODEV (PJMEDIA_VIDEODEV_ERRNO_START+5) /* 520005 */
+
+/**
+ * @hideinitializer
+ * Unable to find default device
+ */
+#define PJMEDIA_EVID_NODEFDEV (PJMEDIA_VIDEODEV_ERRNO_START+6) /* 520006 */
+
+/**
+ * @hideinitializer
+ * Device not ready
+ */
+#define PJMEDIA_EVID_NOTREADY (PJMEDIA_VIDEODEV_ERRNO_START+7) /* 520007 */
+
+/**
+ * @hideinitializer
+ * The video capability is invalid or not supported
+ */
+#define PJMEDIA_EVID_INVCAP (PJMEDIA_VIDEODEV_ERRNO_START+8) /* 520008 */
+
+/**
+ * @hideinitializer
+ * The operation is invalid or not supported
+ */
+#define PJMEDIA_EVID_INVOP (PJMEDIA_VIDEODEV_ERRNO_START+9) /* 520009 */
+
+/**
+ * @hideinitializer
+ * Bad or invalid video device format
+ */
+#define PJMEDIA_EVID_BADFORMAT (PJMEDIA_VIDEODEV_ERRNO_START+10) /* 520010 */
+
+/**
+ * @hideinitializer
+ * Invalid video device sample format
+ */
+#define PJMEDIA_EVID_SAMPFORMAT (PJMEDIA_VIDEODEV_ERRNO_START+11) /* 520011 */
+
+/**
+ * @hideinitializer
+ * Bad latency setting
+ */
+#define PJMEDIA_EVID_BADLATENCY (PJMEDIA_VIDEODEV_ERRNO_START+12) /* 520012 */
+
+/**
+ * @hideinitializer
+ * Bad/unsupported video size
+ */
+#define PJMEDIA_EVID_BADSIZE (PJMEDIA_VIDEODEV_ERRNO_START+13) /* 520013 */
+
+
+/**
+ * Get error message for the specified error code. Note that this
+ * function is only able to decode PJMEDIA Videodev specific error code.
+ * Application should use pj_strerror(), which should be able to
+ * decode all error codes belonging to all subsystems (e.g. pjlib,
+ * pjmedia, pjsip, etc).
+ *
+ * @param status The error code.
+ * @param buffer The buffer where to put the error message.
+ * @param bufsize Size of the buffer.
+ *
+ * @return The error message as NULL terminated string,
+ * wrapped with pj_str_t.
+ */
+PJ_DECL(pj_str_t) pjmedia_videodev_strerror(pj_status_t status, char *buffer,
+ pj_size_t bufsize);
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+
+#endif /* __PJMEDIA_VIDEODEV_VIDEODEV_ERRNO_H__ */
+
diff --git a/pjmedia/include/pjmedia-videodev/videodev.h b/pjmedia/include/pjmedia-videodev/videodev.h
new file mode 100644
index 00000000..a1118d51
--- /dev/null
+++ b/pjmedia/include/pjmedia-videodev/videodev.h
@@ -0,0 +1,618 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_VIDEODEV_VIDEODEV_H__
+#define __PJMEDIA_VIDEODEV_VIDEODEV_H__
+
+/**
+ * @file videodev.h
+ * @brief Video device API.
+ */
+#include <pjmedia-videodev/config.h>
+#include <pjmedia-videodev/errno.h>
+#include <pjmedia/event.h>
+#include <pjmedia/frame.h>
+#include <pjmedia/format.h>
+#include <pj/pool.h>
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup video_device_reference Video Device API Reference
+ * @ingroup video_device_api
+ * @brief API Reference
+ * @{
+ */
+
+/**
+ * Type for device index.
+ */
+typedef pj_int32_t pjmedia_vid_dev_index;
+
+/**
+ * Device index constants.
+ */
+enum
+{
+ /**
+ * Constant to denote default capture device
+ */
+ PJMEDIA_VID_DEFAULT_CAPTURE_DEV = -1,
+
+ /**
+ * Constant to denote default render device
+ */
+ PJMEDIA_VID_DEFAULT_RENDER_DEV = -2,
+
+ /**
+ * Constant to denote invalid device index.
+ */
+ PJMEDIA_VID_INVALID_DEV = -3
+};
+
+
+/**
+ * This enumeration identifies various video device capabilities. These video
+ * capabilities indicates what features are supported by the underlying
+ * video device implementation.
+ *
+ * Applications get these capabilities in the #pjmedia_vid_dev_info structure.
+ *
+ * Application can also set the specific features/capabilities when opening
+ * the video stream by setting the \a flags member of #pjmedia_vid_dev_param
+ * structure.
+ *
+ * Once video stream is running, application can also retrieve or set some
+ * specific video capability, by using #pjmedia_vid_dev_stream_get_cap() and
+ * #pjmedia_vid_dev_stream_set_cap() and specifying the desired capability. The
+ * value of the capability is specified as pointer, and application needs to
+ * supply the pointer with the correct value, according to the documentation
+ * of each of the capability.
+ */
+typedef enum pjmedia_vid_dev_cap
+{
+ /**
+ * Support for video formats. The value of this capability
+ * is represented by #pjmedia_format structure.
+ */
+ PJMEDIA_VID_DEV_CAP_FORMAT = 1,
+
+ /**
+ * Support for video input scaling
+ */
+ PJMEDIA_VID_DEV_CAP_INPUT_SCALE = 2,
+
+ /**
+ * The application can provide a window for the renderer to
+ * display the video.
+ */
+ PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW = 4,
+
+ /**
+ * Support for resizing video output. This capability SHOULD be
+ * implemented by renderer, to alter the video output dimension on the fly.
+ * Value is pjmedia_rect_size.
+ */
+ PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE = 8,
+
+ /**
+ * Support for setting the video window's position.
+ * Value is pjmedia_coord specifying the window's new coordinate.
+ */
+ PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION = 16,
+
+ /**
+ * Support for setting the video output's visibility.
+ * The value of this capability is a pj_bool_t containing boolean
+ * PJ_TRUE or PJ_FALSE.
+ */
+ PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE = 32,
+
+ /**
+ * End of capability
+ */
+ PJMEDIA_VID_DEV_CAP_MAX = 16384
+
+} pjmedia_vid_dev_cap;
+
+/**
+ * Device information structure returned by #pjmedia_vid_dev_get_info().
+ */
+typedef struct pjmedia_vid_dev_info
+{
+ /** The device ID */
+ pjmedia_vid_dev_index id;
+
+ /** The device name */
+ char name[64];
+
+ /** The underlying driver name */
+ char driver[32];
+
+ /**
+ * The supported direction of the video device, i.e. whether it supports
+ * capture only, render only, or both.
+ */
+ pjmedia_dir dir;
+
+ /** Specify whether the device supports callback */
+ pj_bool_t has_callback;
+
+ /** Device capabilities, as bitmask combination of #pjmedia_vid_dev_cap */
+ unsigned caps;
+
+ /** Number of video formats supported by this device */
+ unsigned fmt_cnt;
+
+ /**
+ * Array of supported video formats. Some fields in each supported video
+ * format may be set to zero or of "unknown" value, to indicate that the
+ * value is unknown or should be ignored. When these value are not set
+ * to zero, it indicates that the exact format combination is being used.
+ */
+ pjmedia_format fmt[PJMEDIA_VID_DEV_INFO_FMT_CNT];
+
+} pjmedia_vid_dev_info;
+
+
+/** Forward declaration for pjmedia_vid_dev_stream */
+typedef struct pjmedia_vid_dev_stream pjmedia_vid_dev_stream;
+
+typedef struct pjmedia_vid_dev_cb
+{
+ /**
+ * This callback is called by capturer stream when it has captured the
+ * whole packet worth of video samples.
+ *
+ * @param stream The video stream.
+ * @param user_data User data associated with the stream.
+ * @param frame Captured frame.
+ *
+ * @return Returning non-PJ_SUCCESS will cause the video
+ * stream to stop
+ */
+ pj_status_t (*capture_cb)(pjmedia_vid_dev_stream *stream,
+ void *user_data,
+ pjmedia_frame *frame);
+
+ /**
+ * This callback is called by renderer stream when it needs additional
+ * data to be rendered by the device. Application must fill in the whole
+ * of output buffer with video samples.
+ *
+ * The frame argument contains the following values:
+ * - timestamp Rendering timestamp, in samples.
+ * - buf Buffer to be filled out by application.
+ * - size The size requested in bytes, which will be equal
+ * to the size of one whole packet.
+ *
+ * @param stream The video stream.
+ * @param user_data User data associated with the stream.
+ * @param frame Video frame, which buffer is to be filled in by
+ * the application.
+ *
+ * @return Returning non-PJ_SUCCESS will cause the video
+ * stream to stop
+ */
+ pj_status_t (*render_cb)(pjmedia_vid_dev_stream *stream,
+ void *user_data,
+ pjmedia_frame *frame);
+
+} pjmedia_vid_dev_cb;
+
+
+/**
+ * This structure specifies the parameters to open the video stream.
+ */
+typedef struct pjmedia_vid_dev_param
+{
+ /**
+ * The video direction. This setting is mandatory.
+ */
+ pjmedia_dir dir;
+
+ /**
+ * The video capture device ID. This setting is mandatory if the video
+ * direction includes input/capture direction.
+ */
+ pjmedia_vid_dev_index cap_id;
+
+ /**
+ * The video render device ID. This setting is mandatory if the video
+ * direction includes output/render direction.
+ */
+ pjmedia_vid_dev_index rend_id;
+
+ /**
+ * Video clock rate. This setting is mandatory if the video
+ * direction includes input/capture direction
+ */
+ unsigned clock_rate;
+
+ /**
+ * Video frame rate. This setting is mandatory if the video
+ * direction includes input/capture direction
+ */
+// pjmedia_ratio frame_rate;
+
+ /**
+ * This flags specifies which of the optional settings are valid in this
+ * structure. The flags is bitmask combination of pjmedia_vid_dev_cap.
+ */
+ unsigned flags;
+
+ /**
+ * Set the video format. This setting is mandatory.
+ */
+ pjmedia_format fmt;
+
+ /**
+ * Window for the renderer to display the video. This setting is optional,
+ * and will only be used if PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW is set in
+ * the flags.
+ */
+ void *window;
+
+ /**
+ * Video display size. This setting is optional, and will only be used
+ * if PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE is set in the flags.
+ */
+ pjmedia_rect_size disp_size;
+
+ /**
+ * Video window position. This setting is optional, and will only be used
+ * if PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION is set in the flags.
+ */
+ pjmedia_coord window_pos;
+
+ /**
+ * Video window's visibility. This setting is optional, and will only be
+ * used if PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE is set in the flags.
+ */
+ pj_bool_t window_hide;
+
+} pjmedia_vid_dev_param;
+
+
+/** Forward declaration for video device factory */
+typedef struct pjmedia_vid_dev_factory pjmedia_vid_dev_factory;
+
+/* typedef for factory creation function */
+typedef pjmedia_vid_dev_factory*
+(*pjmedia_vid_dev_factory_create_func_ptr)(pj_pool_factory*);
+
+
+/**
+ * Get string info for the specified capability.
+ *
+ * @param cap The capability ID.
+ * @param p_desc Optional pointer which will be filled with longer
+ * description about the capability.
+ *
+ * @return Capability name.
+ */
+PJ_DECL(const char*) pjmedia_vid_dev_cap_name(pjmedia_vid_dev_cap cap,
+ const char **p_desc);
+
+
+/**
+ * Set a capability field value in #pjmedia_vid_dev_param structure. This will
+ * also set the flags field for the specified capability in the structure.
+ *
+ * @param param The structure.
+ * @param cap The video capability which value is to be set.
+ * @param pval Pointer to value. Please see the type of value to
+ * be supplied in the pjmedia_vid_dev_cap documentation.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_dev_param_set_cap(pjmedia_vid_dev_param *param,
+ pjmedia_vid_dev_cap cap,
+ const void *pval);
+
+
+/**
+ * Get a capability field value from #pjmedia_vid_dev_param structure. This
+ * function will return PJMEDIA_EVID_INVCAP error if the flag for that
+ * capability is not set in the flags field in the structure.
+ *
+ * @param param The structure.
+ * @param cap The video capability which value is to be retrieved.
+ * @param pval Pointer to value. Please see the type of value to
+ * be supplied in the pjmedia_vid_dev_cap documentation.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_dev_param_get_cap(const pjmedia_vid_dev_param *param,
+ pjmedia_vid_dev_cap cap,
+ void *pval);
+
+/**
+ * Initialize the video device subsystem. This will register all supported
+ * video device factories to the video device subsystem. This function may be
+ * called more than once, but each call to this function must have the
+ * corresponding #pjmedia_vid_dev_subsys_shutdown() call.
+ *
+ * @param pf The pool factory.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_subsys_init(pj_pool_factory *pf);
+
+
+/**
+ * Get the pool factory registered to the video device subsystem.
+ *
+ * @return The pool factory.
+ */
+PJ_DECL(pj_pool_factory*) pjmedia_vid_dev_subsys_get_pool_factory(void);
+
+
+/**
+ * Shutdown the video device subsystem. This will destroy all video device
+ * factories registered in the video device subsystem. Note that currently
+ * opened video streams may or may not be closed, depending on the
+ * implementation of the video device factories.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_subsys_shutdown(void);
+
+
+/**
+ * Register a supported video device factory to the video device subsystem.
+ * This function can only be called after calling
+ * #pjmedia_vid_dev_subsys_init().
+ *
+ * @param vdf The video device factory.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_register_factory(pjmedia_vid_dev_factory_create_func_ptr vdf);
+
+
+/**
+ * Unregister a video device factory from the video device subsystem. This
+ * function can only be called after calling #pjmedia_vid_dev_subsys_init().
+ * Devices from this factory will be unlisted. If a device from this factory
+ * is currently in use, then the behavior is undefined.
+ *
+ * @param vdf The video device factory.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_unregister_factory(pjmedia_vid_dev_factory_create_func_ptr vdf);
+
+
+/**
+ * Refresh the list of video devices installed in the system. This function
+ * will only refresh the list of videoo device so all active video streams will
+ * be unaffected. After refreshing the device list, application MUST make sure
+ * to update all index references to video devices (i.e. all variables of type
+ * pjmedia_vid_dev_index) before calling any function that accepts video device
+ * index as its parameter.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_refresh(void);
+
+
+/**
+ * Get the number of video devices installed in the system.
+ *
+ * @return The number of video devices installed in the system.
+ */
+PJ_DECL(unsigned) pjmedia_vid_dev_count(void);
+
+
+/**
+ * Get device information.
+ *
+ * @param id The video device ID.
+ * @param info The device information which will be filled in by this
+ * function once it returns successfully.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_get_info(pjmedia_vid_dev_index id,
+ pjmedia_vid_dev_info *info);
+
+
+/**
+ * Lookup device index based on the driver and device name.
+ *
+ * @param drv_name The driver name.
+ * @param dev_name The device name.
+ * @param id Pointer to store the returned device ID.
+ *
+ * @return PJ_SUCCESS if the device can be found.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_lookup(const char *drv_name,
+ const char *dev_name,
+ pjmedia_vid_dev_index *id);
+
+
+/**
+ * Initialize the video device parameters with default values for the
+ * specified device.
+ *
+ * @param id The video device ID.
+ * @param param The video device parameters which will be initialized
+ * by this function once it returns successfully.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_dev_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_index id,
+ pjmedia_vid_dev_param *param);
+
+
+/**
+ * Open video stream object using the specified parameters. If stream is
+ * created successfully, this function will return PJ_SUCCESS and the
+ * stream pointer will be returned in the p_strm argument.
+ *
+ * The opened stream may have been opened with different size and fps
+ * than the requested values in the \a param argument. Application should
+ * check the actual size and fps that the stream was opened with by inspecting
+ * the values in the \a param argument and see if they have changed. Also
+ * if the device ID in the \a param specifies default device, it may be
+ * replaced with the actual device ID upon return.
+ *
+ * @param param Sound device parameters to be used for the stream.
+ * @param cb Pointer to structure containing video stream
+ * callbacks.
+ * @param user_data Arbitrary user data, which will be given back in the
+ * callbacks.
+ * @param p_strm Pointer to receive the video stream.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_create(
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_strm);
+
+/**
+ * Get the running parameters for the specified video stream.
+ *
+ * @param strm The video stream.
+ * @param param Video stream parameters to be filled in by this
+ * function once it returns successfully.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_param(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_param *param);
+
+/**
+ * Get the value of a specific capability of the video stream.
+ *
+ * @param strm The video stream.
+ * @param cap The video capability which value is to be retrieved.
+ * @param value Pointer to value to be filled in by this function
+ * once it returns successfully. Please see the type
+ * of value to be supplied in the pjmedia_vid_dev_cap
+ * documentation.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_cap(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
+
+/**
+ * Set the value of a specific capability of the video stream.
+ *
+ * @param strm The video stream.
+ * @param cap The video capability which value is to be set.
+ * @param value Pointer to value. Please see the type of value to
+ * be supplied in the pjmedia_vid_dev_cap documentation.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_set_cap(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
+
+/**
+ * Start the stream.
+ *
+ * @param strm The video stream.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_start(
+ pjmedia_vid_dev_stream *strm);
+
+/**
+ * Get the event publisher object for the video stream. Caller typically use
+ * the returned object to subscribe or unsubscribe events from the video
+ * stream.
+ *
+ * @param strm The video stream.
+ *
+ * @return The event publisher object.
+ */
+PJ_DECL(pjmedia_event_publisher*)
+pjmedia_vid_dev_stream_get_event_publisher(pjmedia_vid_dev_stream *strm);
+
+/* Get/put frame API for passive stream */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_frame(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_frame *frame);
+
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_put_frame(
+ pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame);
+
+/**
+ * Stop the stream.
+ *
+ * @param strm The video stream.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_stop(
+ pjmedia_vid_dev_stream *strm);
+
+/**
+ * Destroy the stream.
+ *
+ * @param strm The video stream.
+ *
+ * @return PJ_SUCCESS on successful operation or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_destroy(
+ pjmedia_vid_dev_stream *strm);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_VIDEODEV_VIDEODEV_H__ */
diff --git a/pjmedia/include/pjmedia-videodev/videodev_imp.h b/pjmedia/include/pjmedia-videodev/videodev_imp.h
new file mode 100644
index 00000000..db19cde0
--- /dev/null
+++ b/pjmedia/include/pjmedia-videodev/videodev_imp.h
@@ -0,0 +1,202 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __VIDEODEV_IMP_H__
+#define __VIDEODEV_IMP_H__
+
+#include <pjmedia-videodev/videodev.h>
+
+/**
+ * @defgroup s8_video_device_implementors_api Video Device Implementors API
+ * @ingroup video_device_api
+ * @brief API for video device implementors
+ * @{
+ */
+
+/**
+ * Video device factory operations.
+ */
+typedef struct pjmedia_vid_dev_factory_op
+{
+ /**
+ * Initialize the video device factory.
+ *
+ * @param f The video device factory.
+ */
+ pj_status_t (*init)(pjmedia_vid_dev_factory *f);
+
+ /**
+ * Close this video device factory and release all resources back to the
+ * operating system.
+ *
+ * @param f The video device factory.
+ */
+ pj_status_t (*destroy)(pjmedia_vid_dev_factory *f);
+
+ /**
+ * Get the number of video devices installed in the system.
+ *
+ * @param f The video device factory.
+ */
+ unsigned (*get_dev_count)(pjmedia_vid_dev_factory *f);
+
+ /**
+ * Get the video device information and capabilities.
+ *
+ * @param f The video device factory.
+ * @param index Device index.
+ * @param info The video device information structure which will be
+ * initialized by this function once it returns
+ * successfully.
+ */
+ pj_status_t (*get_dev_info)(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info);
+
+ /**
+ * Initialize the specified video device parameter with the default
+ * values for the specified device.
+ *
+ * @param f The video device factory.
+ * @param index Device index.
+ * @param param The video device parameter.
+ */
+ pj_status_t (*default_param)(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param);
+
+ /**
+ * Open the video device and create video stream. See
+ * #pjmedia_vid_dev_stream_create()
+ */
+ pj_status_t (*create_stream)(pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm);
+
+ /**
+ * Refresh the list of video devices installed in the system.
+ *
+ * @param f The video device factory.
+ */
+ pj_status_t (*refresh)(pjmedia_vid_dev_factory *f);
+
+} pjmedia_vid_dev_factory_op;
+
+
+/**
+ * This structure describes a video device factory.
+ */
+struct pjmedia_vid_dev_factory
+{
+ /** Internal data to be initialized by video subsystem. */
+ struct {
+ /** Driver index */
+ unsigned drv_idx;
+ } sys;
+
+ /** Operations */
+ pjmedia_vid_dev_factory_op *op;
+};
+
+
+/**
+ * Video stream operations.
+ */
+typedef struct pjmedia_vid_dev_stream_op
+{
+ /**
+ * See #pjmedia_vid_dev_stream_get_param()
+ */
+ pj_status_t (*get_param)(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_param *param);
+
+ /**
+ * See #pjmedia_vid_dev_stream_get_cap()
+ */
+ pj_status_t (*get_cap)(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
+
+ /**
+ * See #pjmedia_vid_dev_stream_set_cap()
+ */
+ pj_status_t (*set_cap)(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
+
+ /**
+ * See #pjmedia_vid_dev_stream_start()
+ */
+ pj_status_t (*start)(pjmedia_vid_dev_stream *strm);
+
+ /**
+ * See #pjmedia_vid_dev_stream_get_frame()
+ */
+ pj_status_t (*get_frame)(pjmedia_vid_dev_stream *strm,
+ pjmedia_frame *frame);
+
+ /**
+ * See #pjmedia_vid_dev_stream_put_frame()
+ */
+ pj_status_t (*put_frame)(pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame);
+
+ /**
+ * See #pjmedia_vid_dev_stream_stop().
+ */
+ pj_status_t (*stop)(pjmedia_vid_dev_stream *strm);
+
+ /**
+ * See #pjmedia_vid_dev_stream_destroy().
+ */
+ pj_status_t (*destroy)(pjmedia_vid_dev_stream *strm);
+
+} pjmedia_vid_dev_stream_op;
+
+
+/**
+ * This structure describes the video device stream.
+ */
+struct pjmedia_vid_dev_stream
+{
+ /** Internal data to be initialized by video subsystem */
+ struct {
+ /** Driver index */
+ unsigned drv_idx;
+ } sys;
+
+ /** Operations */
+ pjmedia_vid_dev_stream_op *op;
+
+ /** Event producer */
+ pjmedia_event_publisher epub;
+};
+
+
+
+
+/**
+ * @}
+ */
+
+
+
+#endif /* __VIDEODEV_IMP_H__ */
diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h
index 7f8e996b..4792d612 100644
--- a/pjmedia/include/pjmedia.h
+++ b/pjmedia/include/pjmedia.h
@@ -24,19 +24,22 @@
* @file pjmedia.h
* @brief PJMEDIA main header file.
*/
-
-#include <pjmedia/types.h>
#include <pjmedia/alaw_ulaw.h>
+#include <pjmedia/avi_stream.h>
#include <pjmedia/bidirectional.h>
#include <pjmedia/circbuf.h>
#include <pjmedia/clock.h>
#include <pjmedia/codec.h>
#include <pjmedia/conference.h>
+#include <pjmedia/converter.h>
#include <pjmedia/delaybuf.h>
#include <pjmedia/echo.h>
#include <pjmedia/echo_port.h>
-#include <pjmedia/errno.h>
#include <pjmedia/endpoint.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/event.h>
+#include <pjmedia/frame.h>
+#include <pjmedia/format.h>
#include <pjmedia/g711.h>
#include <pjmedia/jbuf.h>
#include <pjmedia/master_port.h>
@@ -50,13 +53,14 @@
#include <pjmedia/rtp.h>
#include <pjmedia/sdp.h>
#include <pjmedia/sdp_neg.h>
-#include <pjmedia/session.h>
+//#include <pjmedia/session.h>
#include <pjmedia/silencedet.h>
#include <pjmedia/sound.h>
#include <pjmedia/sound_port.h>
#include <pjmedia/splitcomb.h>
#include <pjmedia/stereo.h>
#include <pjmedia/stream.h>
+#include <pjmedia/stream_common.h>
#include <pjmedia/tonegen.h>
#include <pjmedia/transport.h>
#include <pjmedia/transport_adapter_sample.h>
@@ -64,6 +68,10 @@
#include <pjmedia/transport_loop.h>
#include <pjmedia/transport_srtp.h>
#include <pjmedia/transport_udp.h>
+#include <pjmedia/vid_port.h>
+#include <pjmedia/vid_codec.h>
+#include <pjmedia/vid_stream.h>
+#include <pjmedia/vid_tee.h>
#include <pjmedia/wav_playlist.h>
#include <pjmedia/wav_port.h>
#include <pjmedia/wave.h>
diff --git a/pjmedia/include/pjmedia/avi.h b/pjmedia/include/pjmedia/avi.h
new file mode 100644
index 00000000..685f00eb
--- /dev/null
+++ b/pjmedia/include/pjmedia/avi.h
@@ -0,0 +1,202 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_AVI_H__
+#define __PJMEDIA_AVI_H__
+
+
+/**
+ * @file avi.h
+ * @brief AVI file manipulation.
+ */
+
+/**
+ * @defgroup PJMEDIA_FILE_FORMAT File Formats
+ * @brief Supported file formats
+ */
+
+
+/**
+ * @defgroup PJMEDIA_AVI AVI Header
+ * @ingroup PJMEDIA_FILE_FORMAT
+ * @brief Representation of RIFF/AVI file format
+ * @{
+ *
+ * This the the low level representation of RIFF/AVI file format. For
+ * higher abstraction, please see \ref PJMEDIA_FILE_PLAY and
+ * \ref PJMEDIA_FILE_REC.
+ */
+
+
+PJ_BEGIN_DECL
+
+#define PJMEDIA_AVI_MAX_NUM_STREAMS 4
+
+static const char avi_tags[][4] = {
+ { 'R', 'I', 'F', 'F' }, { 'A', 'V', 'I', ' ' },
+ { 'h', 'd', 'r', 'l' }, { 'a', 'v', 'i', 'h' },
+ { 's', 't', 'r', 'l' }, { 's', 't', 'r', 'h' },
+ { 'a', 'u', 'd', 's' }, { 'v', 'i', 'd', 's' },
+ { 's', 't', 'r', 'f' }, { 'm', 'o', 'v', 'i' },
+ { 'L', 'I', 'S', 'T' }, { 'J', 'U', 'N', 'K' },
+};
+
+typedef enum {
+ PJMEDIA_AVI_RIFF_TAG = 0,
+ PJMEDIA_AVI_AVI_TAG,
+ PJMEDIA_AVI_HDRL_TAG,
+ PJMEDIA_AVI_AVIH_TAG,
+ PJMEDIA_AVI_STRL_TAG,
+ PJMEDIA_AVI_STRH_TAG,
+ PJMEDIA_AVI_AUDS_TAG,
+ PJMEDIA_AVI_VIDS_TAG,
+ PJMEDIA_AVI_STRF_TAG,
+ PJMEDIA_AVI_MOVI_TAG,
+ PJMEDIA_AVI_LIST_TAG,
+ PJMEDIA_AVI_JUNK_TAG,
+} pjmedia_avi_tag;
+
+
+/**
+ * These types describe the simpler/canonical version of an AVI file.
+ * They do not support the full AVI RIFF format specification.
+ */
+#pragma pack(2)
+
+/** This structure describes RIFF AVI file header */
+typedef struct riff_hdr_t {
+ pj_uint32_t riff; /**< "RIFF" ASCII tag. */
+ pj_uint32_t file_len; /**< File length minus 8 bytes */
+ pj_uint32_t avi; /**< "AVI" ASCII tag. */
+} riff_hdr_t;
+
+/** This structure describes avih header */
+typedef struct avih_hdr_t {
+ pj_uint32_t list_tag;
+ pj_uint32_t list_sz;
+ pj_uint32_t hdrl_tag;
+ pj_uint32_t avih;
+ pj_uint32_t size;
+ pj_uint32_t msec_per_frame; /**< microsecs between frames */
+ pj_uint32_t max_Bps;
+ pj_uint32_t pad;
+ pj_uint32_t flags;
+ pj_uint32_t tot_frames;
+ pj_uint32_t init_frames;
+ pj_uint32_t num_streams;
+ pj_uint32_t buf_size;
+ pj_uint32_t width;
+ pj_uint32_t height;
+ pj_uint32_t reserved[4];
+} avih_hdr_t;
+
+/** This structure describes strl header */
+typedef struct strl_hdr_t {
+ pj_uint32_t list_tag;
+ pj_uint32_t list_sz;
+ pj_uint32_t strl_tag;
+
+ pj_uint32_t strh;
+ pj_uint32_t strh_size;
+ pj_uint32_t data_type;
+ pj_uint32_t codec;
+ pj_uint32_t flags;
+ pj_uint32_t bogus_priority_language; /**< Do not access this data */
+ pj_uint32_t init_frames;
+ pj_uint32_t scale;
+ pj_uint32_t rate;
+ pj_uint32_t start;
+ pj_uint32_t length;
+ pj_uint32_t buf_size;
+ pj_uint32_t quality;
+ pj_uint32_t sample_size;
+ pj_uint32_t bogus_frame[2]; /**< Do not access this data */
+} strl_hdr_t;
+
+typedef struct {
+ pj_uint32_t strf;
+ pj_uint32_t strf_size;
+ pj_uint16_t fmt_tag; /**< 1 for PCM */
+ pj_uint16_t nchannels; /**< Number of channels. */
+ pj_uint32_t sample_rate; /**< Sampling rate. */
+ pj_uint32_t bytes_per_sec; /**< Average bytes per second. */
+ pj_uint16_t block_align; /**< nchannels * bits / 8 */
+ pj_uint16_t bits_per_sample; /**< Bits per sample. */
+ pj_uint16_t extra_size;
+} strf_audio_hdr_t;
+
+/**
+ * Sizes of strf_audio_hdr_t struct, started by the size (in bytes) of
+ * 32-bits struct members, alternated with the size of 16-bits members.
+ */
+static const pj_uint8_t strf_audio_hdr_sizes [] = {8, 4, 8, 6};
+
+typedef struct {
+ pj_uint32_t strf;
+ pj_uint32_t strf_size;
+ pj_uint32_t biSize;
+ pj_int32_t biWidth;
+ pj_int32_t biHeight;
+ pj_uint16_t biPlanes;
+ pj_uint16_t biBitCount;
+ pj_uint32_t biCompression;
+ pj_uint32_t biSizeImage;
+ pj_int32_t biXPelsPerMeter;
+ pj_int32_t biYPelsPerMeter;
+ pj_uint32_t biClrUsed;
+ pj_uint32_t biClrImportant;
+} strf_video_hdr_t;
+
+static const pj_uint8_t strf_video_hdr_sizes [] = {20, 4, 24};
+
+struct pjmedia_avi_hdr
+{
+ riff_hdr_t riff_hdr;
+ avih_hdr_t avih_hdr;
+ strl_hdr_t strl_hdr[PJMEDIA_AVI_MAX_NUM_STREAMS];
+ union {
+ strf_audio_hdr_t strf_audio_hdr;
+ strf_video_hdr_t strf_video_hdr;
+ } strf_hdr[PJMEDIA_AVI_MAX_NUM_STREAMS];
+};
+
+#pragma pack()
+
+/**
+ * @see pjmedia_avi_hdr
+ */
+typedef struct pjmedia_avi_hdr pjmedia_avi_hdr;
+
+/**
+ * This structure describes generic RIFF subchunk header.
+ */
+typedef struct pjmedia_avi_subchunk
+{
+ pj_uint32_t id; /**< Subchunk ASCII tag. */
+ pj_uint32_t len; /**< Length following this field */
+} pjmedia_avi_subchunk;
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+
+#endif /* __PJMEDIA_AVI_H__ */
diff --git a/pjmedia/include/pjmedia/avi_stream.h b/pjmedia/include/pjmedia/avi_stream.h
new file mode 100644
index 00000000..9ac68f85
--- /dev/null
+++ b/pjmedia/include/pjmedia/avi_stream.h
@@ -0,0 +1,170 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_AVI_STREAM_H__
+#define __PJMEDIA_AVI_STREAM_H__
+
+/**
+ * @file avi_stream.h
+ * @brief AVI file player.
+ */
+#include <pjmedia/port.h>
+
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMEDIA_FILE_PLAY AVI File Player
+ * @ingroup PJMEDIA_PORT
+ * @brief Video and audio playback from AVI file
+ * @{
+ */
+
+/**
+ * AVI file player options.
+ */
+enum pjmedia_avi_file_player_option
+{
+ /**
+ * Tell the file player to return NULL frame when the whole
+ * file has been played.
+ */
+ PJMEDIA_AVI_FILE_NO_LOOP = 1
+};
+
+/**
+ * AVI stream data type.
+ */
+typedef pjmedia_port pjmedia_avi_stream;
+
+/**
+ * Opaque data type for AVI streams. AVI streams is a collection of
+ * zero or more AVI stream.
+ */
+typedef struct pjmedia_avi_streams pjmedia_avi_streams;
+
+/**
+ * Create avi streams to play an AVI file. AVI player supports
+ * reading AVI file with uncompressed video format and
+ * 16 bit PCM or compressed G.711 A-law/U-law audio format.
+ *
+ * @param pool Pool to create the streams.
+ * @param filename File name to open.
+ * @param flags Avi streams creation flags.
+ * @param p_streams Pointer to receive the avi streams instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_avi_player_create_streams(pj_pool_t *pool,
+ const char *filename,
+ unsigned flags,
+ pjmedia_avi_streams **p_streams);
+
+/**
+ * Get the number of AVI stream.
+ *
+ * @param streams The AVI streams.
+ *
+ * @return The number of AVI stream.
+ */
+PJ_DECL(unsigned)
+pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams);
+
+/**
+ * Return the idx-th stream of the AVI streams.
+ *
+ * @param streams The AVI streams.
+ * @param idx The stream index.
+ *
+ * @return The AVI stream or NULL if it does not exist.
+ */
+PJ_DECL(pjmedia_avi_stream *)
+pjmedia_avi_streams_get_stream(pjmedia_avi_streams *streams,
+ unsigned idx);
+
+/**
+ * Return an AVI stream with a certain media type from the AVI streams.
+ *
+ * @param streams The AVI streams.
+ * @param start_idx The starting index.
+ * @param media_type The media type of the stream.
+ *
+ * @return The AVI stream or NULL if it does not exist.
+ */
+PJ_DECL(pjmedia_avi_stream *)
+pjmedia_avi_streams_get_stream_by_media(pjmedia_avi_streams *streams,
+ unsigned start_idx,
+ pjmedia_type media_type);
+
+/**
+ * Return the media port of an AVI stream.
+ *
+ * @param stream The AVI stream.
+ *
+ * @return The media port.
+ */
+PJ_INLINE(pjmedia_port *)
+pjmedia_avi_stream_get_port(pjmedia_avi_stream *stream)
+{
+ return (pjmedia_port *)stream;
+}
+
+/**
+ * Get the data length, in bytes.
+ *
+ * @param stream The AVI stream.
+ *
+ * @return The length of the data, in bytes. Upon error it will
+ * return negative value.
+ */
+PJ_DECL(pj_ssize_t) pjmedia_avi_stream_get_len(pjmedia_avi_stream *stream);
+
+
+/**
+ * Register a callback to be called when the file reading has reached the
+ * end of file. If the file is set to play repeatedly, then the callback
+ * will be called multiple times. Note that only one callback can be
+ * registered for each AVI stream.
+ *
+ * @param stream The AVI stream.
+ * @param user_data User data to be specified in the callback
+ * @param cb Callback to be called. If the callback returns non-
+ * PJ_SUCCESS, the playback will stop. Note that if
+ * application destroys the file port in the callback,
+ * it must return non-PJ_SUCCESS here.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_avi_stream_set_eof_cb(pjmedia_avi_stream *stream,
+ void *user_data,
+ pj_status_t (*cb)(pjmedia_avi_stream *stream,
+ void *usr_data));
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_AVI_STREAM_H__ */
diff --git a/pjmedia/include/pjmedia/circbuf.h b/pjmedia/include/pjmedia/circbuf.h
index e891d984..eb3a0740 100644
--- a/pjmedia/include/pjmedia/circbuf.h
+++ b/pjmedia/include/pjmedia/circbuf.h
@@ -29,6 +29,7 @@
#include <pj/assert.h>
#include <pj/errno.h>
#include <pj/pool.h>
+#include <pjmedia/frame.h>
/**
* @defgroup PJMED_CIRCBUF Circular Buffer
diff --git a/pjmedia/include/pjmedia/clock.h b/pjmedia/include/pjmedia/clock.h
index d7dc26e3..dd33a108 100644
--- a/pjmedia/include/pjmedia/clock.h
+++ b/pjmedia/include/pjmedia/clock.h
@@ -79,6 +79,82 @@
PJ_BEGIN_DECL
+/**
+ * Media clock source.
+ */
+typedef struct pjmedia_clock_src
+{
+ pjmedia_type media_type; /**< Media type. */
+ unsigned clock_rate; /**< Clock rate. */
+ unsigned ptime_usec; /**< Frame interval (in usec). */
+ /**
+ * The timestamp field holds an increasing value in samples and its
+ * value is expected to be increased by clock_rate samples per second.
+ */
+ pj_timestamp timestamp;
+ /**
+ * Timestamp's last update. The last_update field contains a value in
+ * ticks, and it is expected to be increased by pj_get_timestamp_freq()
+ * ticks per second.
+ */
+ pj_timestamp last_update;
+} pjmedia_clock_src;
+
+/**
+ * This is an auxiliary function to initialize the media clock source.
+ *
+ * @param clocksrc The clock source to be initialized.
+ * @param media_type The media type.
+ * @param clock_rate The clock rate.
+ * @param ptime_usec Media frame interval (in usec).
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_clock_src_init( pjmedia_clock_src *clocksrc,
+ pjmedia_type media_type,
+ unsigned clock_rate,
+ unsigned ptime_usec );
+
+/**
+ * This function updates the clock source's timestamp. Application should
+ * use this function instead of updating the timestamp directly since this
+ * function will also update the last_update field of the clock source.
+ *
+ * @param clocksrc The clock source to be updated.
+ * @param timestamp The new timestamp, can be NULL if the current
+ * timestamp does not change (in this case it
+ * will only update the last_update field).
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_clock_src_update( pjmedia_clock_src *clocksrc,
+ const pj_timestamp *timestamp );
+
+/**
+ * This function gets the clock source's current timestamp. Application
+ * should use this function instead of accessing the timestamp directly
+ * since this function will calculate the predicted timestamp for current
+ * time, based on the values of timestamp, last_update, and clock_rate.
+ *
+ * @param clocksrc The clock source.
+ * @param timestamp Argument to receive the current timestamp
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_clock_src_get_current_timestamp( const pjmedia_clock_src *clocksrc,
+ pj_timestamp *timestamp);
+
+/**
+ * This function gets the clock source's time in msec.
+ *
+ * @param clocksrc The clock source.
+ *
+ * @return The clock source's time (in msec).
+ */
+PJ_DECL(pj_uint32_t)
+pjmedia_clock_src_get_time_msec( const pjmedia_clock_src *clocksrc );
+
/**
* Opaque declaration for media clock.
@@ -105,6 +181,19 @@ enum pjmedia_clock_options
};
+typedef struct pjmedia_clock_param
+{
+ /**
+ * The frame interval, in microseconds.
+ */
+ unsigned usec_interval;
+ /**
+ * The media clock rate, to determine timestamp
+ * increment for each call.
+ */
+ unsigned clock_rate;
+} pjmedia_clock_param;
+
/**
* Type of media clock callback.
*
@@ -118,7 +207,12 @@ typedef void pjmedia_clock_callback(const pj_timestamp *ts,
/**
- * Create media clock.
+ * Create media clock. This creates a media clock object that will run
+ * periodically at an interval that is calculated from the audio parameters.
+ * Once created, application must call #pjmedia_clock_start() to actually
+ * start the clock.
+ *
+ * @see pjmedia_clock_create2()
*
* @param pool Pool to allocate memory.
* @param clock_rate Number of samples per second.
@@ -143,6 +237,29 @@ PJ_DECL(pj_status_t) pjmedia_clock_create( pj_pool_t *pool,
void *user_data,
pjmedia_clock **p_clock);
+
+/**
+ * Create media clock. This creates a media clock object that will run
+ * periodically at the specified interval. Once created, application must
+ * call #pjmedia_clock_start() to actually start the clock.
+ *
+ * @param pool Pool to allocate memory.
+ * @param param The clock parameter.
+ * @param options Bitmask of pjmedia_clock_options.
+ * @param cb Callback to be called for each clock tick.
+ * @param user_data User data, which will be passed to the callback.
+ * @param p_clock Pointer to receive the clock instance.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error
+ * code.
+ */
+PJ_DECL(pj_status_t) pjmedia_clock_create2(pj_pool_t *pool,
+ const pjmedia_clock_param *param,
+ unsigned options,
+ pjmedia_clock_callback *cb,
+ void *user_data,
+ pjmedia_clock **p_clock);
+
/**
* Start the clock. For clock created with asynchronous flag set to TRUE,
* this may start a worker thread for the clock (depending on the
@@ -165,6 +282,16 @@ PJ_DECL(pj_status_t) pjmedia_clock_start(pjmedia_clock *clock);
PJ_DECL(pj_status_t) pjmedia_clock_stop(pjmedia_clock *clock);
+/**
+ * Modify the clock's parameter.
+ *
+ * @param clock The media clock.
+ * @param param The clock's new parameter.
+ * @return PJ_SUCCES on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_clock_modify(pjmedia_clock *clock,
+ const pjmedia_clock_param *param);
+
/**
* Poll the media clock, and execute the callback when the clock tick has
diff --git a/pjmedia/include/pjmedia/codec.h b/pjmedia/include/pjmedia/codec.h
index 50f70297..8e2f23bc 100644
--- a/pjmedia/include/pjmedia/codec.h
+++ b/pjmedia/include/pjmedia/codec.h
@@ -27,6 +27,7 @@
*/
#include <pjmedia/port.h>
+#include <pj/errno.h>
#include <pj/list.h>
#include <pj/pool.h>
@@ -240,8 +241,6 @@ typedef struct pjmedia_codec_info
unsigned channel_cnt; /**< Channel count. */
} pjmedia_codec_info;
-#define PJMEDIA_CODEC_MAX_FMTP_CNT 8
-
/**
* Structure of codec specific parameters which contains name=value pairs.
* The codec specific parameters are to be used with SDP according to
@@ -325,6 +324,9 @@ typedef struct pjmedia_codec_op
/**
* Initialize codec using the specified attribute.
*
+ * Application should call #pjmedia_codec_init() instead of
+ * calling this function directly.
+ *
* @param codec The codec instance.
* @param pool Pool to use when the codec needs to allocate
* some memory.
@@ -340,6 +342,9 @@ typedef struct pjmedia_codec_op
* and fills in the unspecified values (such as enc_ptime, when
* encoder ptime is different than decoder ptime).
*
+ * Application should call #pjmedia_codec_open() instead of
+ * calling this function directly.
+ *
* @param codec The codec instance.
* @param param Codec initialization parameter.
*
@@ -352,6 +357,9 @@ typedef struct pjmedia_codec_op
* Close and shutdown codec, releasing all resources allocated by
* this codec, if any.
*
+ * Application should call #pjmedia_codec_close() instead of
+ * calling this function directly.
+ *
* @param codec The codec instance.
*
* @return PJ_SUCCESS on success.
@@ -367,6 +375,9 @@ typedef struct pjmedia_codec_op
* Application can expect changing trivial codec settings such as
* changing VAD setting to succeed.
*
+ * Application should call #pjmedia_codec_modify() instead of
+ * calling this function directly.
+ *
* @param codec The codec instance.
* @param param The new codec parameter.
*
@@ -381,6 +392,9 @@ typedef struct pjmedia_codec_op
* have ptime that is equal to basic frame ptime (i.e. the value of
* info.frm_ptime in #pjmedia_codec_param).
*
+ * Application should call #pjmedia_codec_parse() instead of
+ * calling this function directly.
+ *
* @param codec The codec instance
* @param pkt The input packet.
* @param pkt_size Size of the packet.
@@ -405,6 +419,9 @@ typedef struct pjmedia_codec_op
* PCM samples MUST have ptime that is multiplication of base frame
* ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param).
*
+ * Application should call #pjmedia_codec_encode() instead of
+ * calling this function directly.
+ *
* @param codec The codec instance.
* @param input The input frame.
* @param out_size The length of buffer in the output frame.
@@ -424,6 +441,9 @@ typedef struct pjmedia_codec_op
* Application can achieve this by parsing the packet into base
* frames before decoding each frame.
*
+ * Application should call #pjmedia_codec_decode() instead of
+ * calling this function directly.
+ *
* @param codec The codec instance.
* @param input The input frame.
* @param out_size The length of buffer in the output frame.
@@ -439,6 +459,9 @@ typedef struct pjmedia_codec_op
/**
* Instruct the codec to recover a missing frame.
*
+ * Application should call #pjmedia_codec_recover() instead of
+ * calling this function directly.
+ *
* @param codec The codec instance.
* @param out_size The length of buffer in the output frame.
* @param output The output frame where generated signal
@@ -554,6 +577,11 @@ typedef struct pjmedia_codec_factory_op
pj_status_t (*dealloc_codec)(pjmedia_codec_factory *factory,
pjmedia_codec *codec );
+ /**
+ * This callback will be called to deinitialize and destroy this factory.
+ */
+ pj_status_t (*destroy)(void);
+
} pjmedia_codec_factory_op;
@@ -725,10 +753,11 @@ pjmedia_codec_mgr_register_factory( pjmedia_codec_mgr *mgr,
/**
* Unregister codec factory from the codec manager. This will also
* remove all the codecs registered by the codec factory from the
- * codec manager's list of supported codecs.
+ * codec manager's list of supported codecs. This function should
+ * only be called by the codec implementers and not by application.
*
- * @param mgr The codec manager instance. Application can get the
- * instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr The codec manager instance, use
+ * #pjmedia_endpt_get_codec_mgr().
* @param factory The codec factory to be unregistered.
*
* @return PJ_SUCCESS on success.
@@ -908,6 +937,169 @@ PJ_DECL(pj_status_t) pjmedia_codec_mgr_dealloc_codec(pjmedia_codec_mgr *mgr,
+/**
+ * Initialize codec using the specified attribute.
+ *
+ * @param codec The codec instance.
+ * @param pool Pool to use when the codec needs to allocate some memory.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_INLINE(pj_status_t) pjmedia_codec_init( pjmedia_codec *codec,
+ pj_pool_t *pool )
+{
+ return (*codec->op->init)(codec, pool);
+}
+
+
+/**
+ * Open the codec and initialize with the specified parameter.
+ * Upon successful initialization, the codec may modify the parameter
+ * and fills in the unspecified values (such as enc_ptime, when
+ * encoder ptime is different than decoder ptime).
+ *
+ * @param codec The codec instance.
+ * @param param Codec initialization parameter.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_INLINE(pj_status_t) pjmedia_codec_open( pjmedia_codec *codec,
+ pjmedia_codec_param *param )
+{
+ return (*codec->op->open)(codec, param);
+}
+
+
+/**
+ * Close and shutdown codec, releasing all resources allocated by
+ * this codec, if any.
+ *
+ * @param codec The codec instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_INLINE(pj_status_t) pjmedia_codec_close( pjmedia_codec *codec )
+{
+ return (*codec->op->close)(codec);
+}
+
+
+/**
+ * Modify the codec parameter after the codec is open.
+ * Note that not all codec parameters can be modified during run-time.
+ * When the parameter cannot be changed, this function will return
+ * non-PJ_SUCCESS, and the original parameters will not be changed.
+ *
+ * Application can expect changing trivial codec settings such as
+ * changing VAD setting to succeed.
+ *
+ * @param codec The codec instance.
+ * @param param The new codec parameter.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_INLINE(pj_status_t) pjmedia_codec_modify(pjmedia_codec *codec,
+ const pjmedia_codec_param *param)
+{
+ return (*codec->op->modify)(codec, param);
+}
+
+
+/**
+ * Instruct the codec to inspect the specified payload/packet and
+ * split the packet into individual base frames. Each output frames will
+ * have ptime that is equal to basic frame ptime (i.e. the value of
+ * info.frm_ptime in #pjmedia_codec_param).
+ *
+ * @param codec The codec instance
+ * @param pkt The input packet.
+ * @param pkt_size Size of the packet.
+ * @param timestamp The timestamp of the first sample in the packet.
+ * @param frame_cnt On input, specifies the maximum number of frames
+ * in the array. On output, the codec must fill
+ * with number of frames detected in the packet.
+ * @param frames On output, specifies the frames that have been
+ * detected in the packet.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_INLINE(pj_status_t) pjmedia_codec_parse( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *timestamp,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[] )
+{
+ return (*codec->op->parse)(codec, pkt, pkt_size, timestamp,
+ frame_cnt, frames);
+}
+
+
+/**
+ * Instruct the codec to encode the specified input frame. The input
+ * PCM samples MUST have ptime that is multiplication of base frame
+ * ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param).
+ *
+ * @param codec The codec instance.
+ * @param input The input frame.
+ * @param out_size The length of buffer in the output frame.
+ * @param output The output frame.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+PJ_INLINE(pj_status_t) pjmedia_codec_encode(
+ pjmedia_codec *codec,
+ const struct pjmedia_frame *input,
+ unsigned out_size,
+ struct pjmedia_frame *output )
+{
+ return (*codec->op->encode)(codec, input, out_size, output);
+}
+
+
+/**
+ * Instruct the codec to decode the specified input frame. The input
+ * frame MUST have ptime that is exactly equal to base frame
+ * ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param).
+ * Application can achieve this by parsing the packet into base
+ * frames before decoding each frame.
+ *
+ * @param codec The codec instance.
+ * @param input The input frame.
+ * @param out_size The length of buffer in the output frame.
+ * @param output The output frame.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+PJ_INLINE(pj_status_t) pjmedia_codec_decode(
+ pjmedia_codec *codec,
+ const struct pjmedia_frame *input,
+ unsigned out_size,
+ struct pjmedia_frame *output )
+{
+ return (*codec->op->decode)(codec, input, out_size, output);
+}
+
+
+/**
+ * Instruct the codec to recover a missing frame.
+ *
+ * @param codec The codec instance.
+ * @param out_size The length of buffer in the output frame.
+ * @param output The output frame where generated signal
+ * will be placed.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+PJ_INLINE(pj_status_t) pjmedia_codec_recover( pjmedia_codec *codec,
+ unsigned out_size,
+ struct pjmedia_frame *output )
+{
+ if (codec->op && codec->op->recover)
+ return (*codec->op->recover)(codec, out_size, output);
+ else
+ return PJ_ENOTSUP;
+}
/**
diff --git a/pjmedia/include/pjmedia/conference.h b/pjmedia/include/pjmedia/conference.h
index e6ac6617..649a4d49 100644
--- a/pjmedia/include/pjmedia/conference.h
+++ b/pjmedia/include/pjmedia/conference.h
@@ -46,14 +46,12 @@ PJ_BEGIN_DECL
/**
* The conference bridge signature in pjmedia_port_info.
*/
-#define PJMEDIA_CONF_BRIDGE_SIGNATURE \
- PJMEDIA_PORT_SIGNATURE('C', 'O', 'N', 'F')
+#define PJMEDIA_CONF_BRIDGE_SIGNATURE PJMEDIA_SIG_PORT_CONF
/**
* The audio switchboard signature in pjmedia_port_info.
*/
-#define PJMEDIA_CONF_SWITCH_SIGNATURE \
- PJMEDIA_PORT_SIGNATURE('A', 'S', 'W', 'I')
+#define PJMEDIA_CONF_SWITCH_SIGNATURE PJMEDIA_SIG_PORT_CONF_SWITCH
/**
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index 6b7fc9b4..bbcf2224 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -515,6 +515,15 @@
#endif
/**
+ * Reserve some space for application extra data, e.g: SRTP auth tag,
+ * in RTP payload, so the total payload length will not exceed the MTU.
+ */
+#ifndef PJMEDIA_STREAM_RESV_PAYLOAD_LEN
+# define PJMEDIA_STREAM_RESV_PAYLOAD_LEN 20
+#endif
+
+
+/**
* Specify the maximum duration of silence period in the codec, in msec.
* This is useful for example to keep NAT binding open in the firewall
* and to prevent server from disconnecting the call because no
@@ -568,6 +577,16 @@
/**
+ * Maximum number of parameters in SDP fmtp attribute.
+ *
+ * Default: 16
+ */
+#ifndef PJMEDIA_CODEC_MAX_FMTP_CNT
+# define PJMEDIA_CODEC_MAX_FMTP_CNT 16
+#endif
+
+
+/**
* This specifies the behavior of the SDP negotiator when responding to an
* offer, whether it should rather use the codec preference as set by
* remote, or should it rather use the codec preference as specified by
@@ -592,6 +611,15 @@
/**
+ * This specifies the maximum number of the customized SDP format
+ * negotiation callbacks.
+ */
+#ifndef PJMEDIA_SDP_NEG_MAX_CUSTOM_FMT_NEG_CB
+# define PJMEDIA_SDP_NEG_MAX_CUSTOM_FMT_NEG_CB 8
+#endif
+
+
+/**
* Support for sending and decoding RTCP port in SDP (RFC 3605).
* Default is equal to PJMEDIA_ADVERTISE_RTCP setting.
*/
@@ -880,6 +908,140 @@
#endif
+/*
+ * .... new stuffs ...
+ */
+
+/*
+ * Video
+ */
+
+/**
+ * Top level option to disable video.
+ */
+#ifndef PJMEDIA_HAS_VIDEO
+# define PJMEDIA_HAS_VIDEO 1
+#endif
+
+
+/**
+ * Specify if FFMPEG is available. The value here will be used as the default
+ * value for other FFMPEG settings below.
+ *
+ * Default: 0
+ */
+#ifndef PJMEDIA_HAS_FFMPEG
+# define PJMEDIA_HAS_FFMPEG 0
+#endif
+
+/**
+ * Specify if FFMPEG libavformat is available.
+ *
+ * Default: PJMEDIA_HAS_FFMPEG (or detected by configure)
+ */
+#ifndef PJMEDIA_HAS_LIBAVFORMAT
+# define PJMEDIA_HAS_LIBAVFORMAT PJMEDIA_HAS_FFMPEG
+#endif
+
+/**
+ * Specify if FFMPEG libavformat is available.
+ *
+ * Default: PJMEDIA_HAS_FFMPEG (or detected by configure)
+ */
+#ifndef PJMEDIA_HAS_LIBAVCODEC
+# define PJMEDIA_HAS_LIBAVCODEC PJMEDIA_HAS_FFMPEG
+#endif
+
+/**
+ * Specify if FFMPEG libavutil is available.
+ *
+ * Default: PJMEDIA_HAS_FFMPEG (or detected by configure)
+ */
+#ifndef PJMEDIA_HAS_LIBAVUTIL
+# define PJMEDIA_HAS_LIBAVUTIL PJMEDIA_HAS_FFMPEG
+#endif
+
+/**
+ * Specify if FFMPEG libswscale is available.
+ *
+ * Default: PJMEDIA_HAS_FFMPEG (or detected by configure)
+ */
+#ifndef PJMEDIA_HAS_LIBSWSCALE
+# define PJMEDIA_HAS_LIBSWSCALE PJMEDIA_HAS_FFMPEG
+#endif
+
+/**
+ * Specify if FFMPEG libavdevice is available.
+ *
+ * Default: PJMEDIA_HAS_FFMPEG (or detected by configure)
+ */
+#ifndef PJMEDIA_HAS_LIBAVDEVICE
+# define PJMEDIA_HAS_LIBAVDEVICE PJMEDIA_HAS_FFMPEG
+#endif
+
+/**
+ * Specify if FFMPEG libavcore is available.
+ *
+ * Default: PJMEDIA_HAS_FFMPEG (or detected by configure)
+ */
+#ifndef PJMEDIA_HAS_LIBAVCORE
+# define PJMEDIA_HAS_LIBAVCORE PJMEDIA_HAS_FFMPEG
+#endif
+
+/**
+ * Maximum video planes.
+ *
+ * Default: 4
+ */
+#ifndef PJMEDIA_MAX_VIDEO_PLANES
+# define PJMEDIA_MAX_VIDEO_PLANES 4
+#endif
+
+/**
+ * Maximum number of video formats.
+ *
+ * Default: 32
+ */
+#ifndef PJMEDIA_MAX_VIDEO_FORMATS
+# define PJMEDIA_MAX_VIDEO_FORMATS 32
+#endif
+
+/**
+ * Specify the maximum time difference (in ms) for synchronization between
+ * two medias. If the synchronization media source is ahead of time
+ * greater than this duration, it is considered to make a very large jump
+ * and the synchronization will be reset.
+ *
+ * Default: 20000
+ */
+#ifndef PJMEDIA_CLOCK_SYNC_MAX_SYNC_MSEC
+# define PJMEDIA_CLOCK_SYNC_MAX_SYNC_MSEC 20000
+#endif
+
+/**
+ * Maximum video frame size.
+ * Default: 128kB
+ */
+#ifndef PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE
+# define PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE (1<<17)
+#endif
+
+
+/**
+ * Specify the maximum duration (in ms) for resynchronization. When a media
+ * is late to another media it is supposed to be synchronized to, it is
+ * guaranteed to be synchronized again after this duration. While if the
+ * media is ahead/early by t ms, it is guaranteed to be synchronized after
+ * t + this duration. This timing only applies if there is no additional
+ * resynchronization required during the specified duration.
+ *
+ * Default: 2000
+ */
+#ifndef PJMEDIA_CLOCK_SYNC_MAX_RESYNC_DURATION
+# define PJMEDIA_CLOCK_SYNC_MAX_RESYNC_DURATION 2000
+#endif
+
+
/**
* @}
*/
diff --git a/pjmedia/include/pjmedia/converter.h b/pjmedia/include/pjmedia/converter.h
new file mode 100644
index 00000000..8a9dd11a
--- /dev/null
+++ b/pjmedia/include/pjmedia/converter.h
@@ -0,0 +1,322 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_CONVERTER_H__
+#define __PJMEDIA_CONVERTER_H__
+
+
+/**
+ * @file pjmedia/converter.h Format conversion utilities
+ * @brief Format conversion utilities
+ */
+
+#include <pjmedia/frame.h>
+#include <pjmedia/format.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+
+
+/**
+ * @defgroup PJMEDIA_CONVERTER Format converter
+ * @ingroup PJMEDIA_FRAME_OP
+ * @brief Audio and video converter utilities
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+/**
+ * This describes conversion parameter. It specifies the source and
+ * destination formats of the conversion.
+ */
+typedef struct pjmedia_conversion_param
+{
+ pjmedia_format src; /**< Source format. */
+ pjmedia_format dst; /**< Destination format. */
+} pjmedia_conversion_param;
+
+
+/** Forward declaration of factory operation structure */
+typedef struct pjmedia_converter_factory_op pjmedia_converter_factory_op;
+
+/**
+ * Converter priority guides. Converter priority determines which converter
+ * instance to be used if more than one converters are able to perform the
+ * requested conversion. Converter implementor can use this value to order
+ * the preference based on attributes such as quality or performance. Higher
+ * number indicates higher priority.
+ */
+typedef enum pjmedia_converter_priority_guide
+{
+ /** Lowest priority. */
+ PJMEDIA_CONVERTER_PRIORITY_LOWEST = 0,
+
+ /** Normal priority. */
+ PJMEDIA_CONVERTER_PRIORITY_NORMAL = 15000,
+
+ /** Highest priority. */
+ PJMEDIA_CONVERTER_PRIORITY_HIGHEST = 32000
+} pjmedia_converter_priority_guide;
+
+/**
+ * Converter factory. The converter factory registers a callback function
+ * to create converters.
+ */
+typedef struct pjmedia_converter_factory
+{
+ /**
+ * Standard list members.
+ */
+ PJ_DECL_LIST_MEMBER(struct pjmedia_converter_factory);
+
+ /**
+ * Factory name.
+ */
+ const char *name;
+
+ /**
+ * Converter priority determines which converter instance to be used if
+ * more than one converters are able to perform the requested conversion.
+ * Converter implementor can use this value to order the preference based
+ * on attributes such as quality or performance. Higher number indicates
+ * higher priority. The pjmedia_converter_priority_guide enumeration shall
+ * be used as the base value to set the priority.
+ */
+ int priority;
+
+ /**
+ * Pointer to factory operation.
+ */
+ pjmedia_converter_factory_op *op;
+
+} pjmedia_converter_factory;
+
+/** Forward declaration for converter operation. */
+typedef struct pjmedia_converter_op pjmedia_converter_op;
+
+/**
+ * This structure describes a converter instance.
+ */
+typedef struct pjmedia_converter
+{
+ /**
+ * Pointer to converter operation.
+ */
+ pjmedia_converter_op *op;
+
+} pjmedia_converter;
+
+
+/**
+ * Converter factory operation.
+ */
+struct pjmedia_converter_factory_op
+{
+ /**
+ * This function creates a converter with the specified conversion format,
+ * if such format is supported.
+ *
+ * @param cf The converter factory.
+ * @param pool Pool to allocate memory from.
+ * @param prm Conversion parameter.
+ * @param p_cv Pointer to hold the created converter instance.
+ *
+ * @return PJ_SUCCESS if converter has been created successfully.
+ */
+ pj_status_t (*create_converter)(pjmedia_converter_factory *cf,
+ pj_pool_t *pool,
+ const pjmedia_conversion_param *prm,
+ pjmedia_converter **p_cv);
+
+ /**
+ * Destroy the factory.
+ *
+ * @param cf The converter factory.
+ */
+ void (*destroy_factory)(pjmedia_converter_factory *cf);
+};
+
+/**
+ * Converter operation.
+ */
+struct pjmedia_converter_op
+{
+ /**
+ * Convert the buffer in the source frame and save the result in the
+ * buffer of the destination frame, according to conversion format that
+ * was specified when the converter was created.
+ *
+ * Note that application should use #pjmedia_converter_convert() instead
+ * of calling this function directly.
+ *
+ * @param cv The converter instance.
+ * @param src_frame The source frame.
+ * @param dst_frame The destination frame.
+ *
+ * @return PJ_SUCCESS if conversion has been performed
+ * successfully.
+ */
+ pj_status_t (*convert)(pjmedia_converter *cv,
+ pjmedia_frame *src_frame,
+ pjmedia_frame *dst_frame);
+
+ /**
+ * Destroy the converter instance.
+ *
+ * Note that application should use #pjmedia_converter_destroy() instead
+ * of calling this function directly.
+ *
+ * @param cv The converter.
+ */
+ void (*destroy)(pjmedia_converter *cv);
+
+};
+
+
+/**
+ * Opaque data type for conversion manager. Typically, the conversion manager
+ * is a singleton instance, although application may instantiate more than one
+ * instances of this if required.
+ */
+typedef struct pjmedia_converter_mgr pjmedia_converter_mgr;
+
+
+/**
+ * Create a new conversion manager instance. This will also set the pointer
+ * to the singleton instance if the value is still NULL.
+ *
+ * @param pool Pool to allocate memory from.
+ * @param mgr Pointer to hold the created instance of the
+ * conversion manager.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_converter_mgr_create(pj_pool_t *pool,
+ pjmedia_converter_mgr **mgr);
+
+/**
+ * Get the singleton instance of the conversion manager.
+ *
+ * @return The instance.
+ */
+PJ_DECL(pjmedia_converter_mgr*) pjmedia_converter_mgr_instance(void);
+
+/**
+ * Manually assign a specific video manager instance as the singleton
+ * instance. Normally this is not needed if only one instance is ever
+ * going to be created, as the library automatically assign the singleton
+ * instance.
+ *
+ * @param mgr The instance to be used as the singleton instance.
+ * Application may specify NULL to clear the singleton
+ * singleton instance.
+ */
+PJ_DECL(void) pjmedia_converter_mgr_set_instance(pjmedia_converter_mgr *mgr);
+
+/**
+ * Destroy a converter manager. If the manager happens to be the singleton
+ * instance, the singleton instance will be set to NULL.
+ *
+ * @param mgr The converter manager. Specify NULL to use
+ * the singleton instance.
+ */
+PJ_DECL(void) pjmedia_converter_mgr_destroy(pjmedia_converter_mgr *mgr);
+
+/**
+ * Register a converter factory to the converter manager.
+ *
+ * @param mgr The converter manager. Specify NULL to use
+ * the singleton instance.
+ * @param f The converter factory to be registered.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_converter_mgr_register_factory(pjmedia_converter_mgr *mgr,
+ pjmedia_converter_factory *f);
+
+/**
+ * Unregister a previously registered converter factory from the converter
+ * manager.
+ *
+ * @param mgr The converter manager. Specify NULL to use
+ * the singleton instance.
+ * @param f The converter factory to be unregistered.
+ * @param call_destroy If this is set to non-zero, the \a destroy_factory()
+ * callback of the factory will be called while
+ * unregistering the factory from the manager.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_converter_mgr_unregister_factory(pjmedia_converter_mgr *mgr,
+ pjmedia_converter_factory *f,
+ pj_bool_t call_destroy);
+
+/**
+ * Create a converter instance to perform the specified format conversion
+ * as specified in \a param.
+ *
+ * @param mgr The converter manager. Specify NULL to use
+ * the singleton instance.
+ * @param pool Pool to allocate the memory from.
+ * @param param Conversion parameter.
+ * @param p_cv Pointer to hold the created converter.
+ *
+ * @return PJ_SUCCESS if a converter has been created successfully
+ * or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_converter_create(pjmedia_converter_mgr *mgr,
+ pj_pool_t *pool,
+ pjmedia_conversion_param *param,
+ pjmedia_converter **p_cv);
+
+/**
+ * Convert the buffer in the source frame and save the result in the
+ * buffer of the destination frame, according to conversion format that
+ * was specified when the converter was created.
+ *
+ * @param cv The converter instance.
+ * @param src_frame The source frame.
+ * @param dst_frame The destination frame.
+ *
+ * @return PJ_SUCCESS if conversion has been performed
+ * successfully.
+ */
+PJ_DECL(pj_status_t) pjmedia_converter_convert(pjmedia_converter *cv,
+ pjmedia_frame *src_frame,
+ pjmedia_frame *dst_frame);
+
+/**
+ * Destroy the converter.
+ *
+ * @param cv The converter instance.
+ */
+PJ_DECL(void) pjmedia_converter_destroy(pjmedia_converter *cv);
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+
+#endif /* __PJMEDIA_CONVERTER_H__ */
+
+
diff --git a/pjmedia/include/pjmedia/endpoint.h b/pjmedia/include/pjmedia/endpoint.h
index 37778b7a..9956b95f 100644
--- a/pjmedia/include/pjmedia/endpoint.h
+++ b/pjmedia/include/pjmedia/endpoint.h
@@ -39,6 +39,7 @@
#include <pjmedia/codec.h>
#include <pjmedia/sdp.h>
+#include <pjmedia/transport.h>
PJ_BEGIN_DECL
@@ -179,6 +180,9 @@ PJ_DECL(pjmedia_codec_mgr*) pjmedia_endpt_get_codec_mgr(pjmedia_endpt *endpt);
* @param stream_cnt Number of elements in the sock_info array. This
* also denotes the maximum number of streams (i.e.
* the "m=" lines) that will be created in the SDP.
+ * By convention, if this value is greater than one,
+ * the first media will be audio and the remaining
+ * media is video.
* @param sock_info Array of socket transport information. One
* transport is needed for each media stream, and
* each transport consists of an RTP and RTCP socket
@@ -193,6 +197,58 @@ PJ_DECL(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
const pjmedia_sock_info sock_info[],
pjmedia_sdp_session **p_sdp );
+/**
+ * Create a "blank" SDP session description. The SDP will contain basic SDP
+ * fields such as origin, time, and name, but without any media lines.
+ *
+ * @param endpt The media endpoint.
+ * @param pool Pool to allocate memory from.
+ * @param sess_name Optional SDP session name, or NULL to use default
+ * value.
+ * @param origin Address to put in the origin field.
+ * @param p_sdp Pointer to receive the created SDP session.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_endpt_create_base_sdp(pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ const pj_str_t *sess_name,
+ const pj_sockaddr *origin,
+ pjmedia_sdp_session **p_sdp);
+
+/**
+ * Create SDP media line for audio media.
+ *
+ * @param endpt The media endpoint.
+ * @param pool Pool to allocate memory from.
+ * @param si Socket information.
+ * @param options Option flags, must be zero for now.
+ * @param p_m Pointer to receive the created SDP media.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ const pjmedia_sock_info*si,
+ unsigned options,
+ pjmedia_sdp_media **p_m);
+
+/**
+ * Create SDP media line for video media.
+ *
+ * @param endpt The media endpoint.
+ * @param pool Pool to allocate memory from.
+ * @param si Socket information.
+ * @param options Option flags, must be zero for now.
+ * @param p_m Pointer to receive the created SDP media.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ const pjmedia_sock_info*si,
+ unsigned options,
+ pjmedia_sdp_media **p_m);
/**
* Dump media endpoint capabilities.
diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h
index f47ea112..4f3a6c3e 100644
--- a/pjmedia/include/pjmedia/errno.h
+++ b/pjmedia/include/pjmedia/errno.h
@@ -391,7 +391,11 @@ PJ_BEGIN_DECL
* Remote does not support RFC 2833
*/
#define PJMEDIA_RTP_EREMNORFC2833 (PJMEDIA_ERRNO_START+107) /* 220107 */
-
+/**
+ * @hideinitializer
+ * Invalid or bad format
+ */
+#define PJMEDIA_EBADFMT (PJMEDIA_ERRNO_START+108) /* 220108 */
/************************************************************
@@ -517,6 +521,11 @@ PJ_BEGIN_DECL
* Sound frame is too large for file buffer.
*/
#define PJMEDIA_EFRMFILETOOBIG (PJMEDIA_ERRNO_START+183) /* 220183 */
+/**
+ * @hideinitializer
+ * Unsupported AVI file.
+ */
+#define PJMEDIA_EAVIUNSUPP (PJMEDIA_ERRNO_START+191) /* 220191 */
/************************************************************
diff --git a/pjmedia/include/pjmedia/event.h b/pjmedia/include/pjmedia/event.h
new file mode 100644
index 00000000..ddcffc02
--- /dev/null
+++ b/pjmedia/include/pjmedia/event.h
@@ -0,0 +1,402 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_EVENT_H__
+#define __PJMEDIA_EVENT_H__
+
+/**
+ * @file pjmedia/event.h
+ * @brief Event framework
+ */
+#include <pjmedia/format.h>
+#include <pjmedia/signatures.h>
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJMEDIA_EVENT Event Framework
+ * @brief PJMEDIA event framework
+ * @{
+ */
+
+/**
+ * This enumeration describes list of media events.
+ */
+typedef enum pjmedia_event_type
+{
+ /**
+ * No event.
+ */
+ PJMEDIA_EVENT_NONE,
+
+ /**
+ * Media format has changed event.
+ */
+ PJMEDIA_EVENT_FMT_CHANGED = PJMEDIA_FOURCC('F', 'M', 'C', 'H'),
+
+ /**
+ * Video window is being closed.
+ */
+ PJMEDIA_EVENT_WND_CLOSING = PJMEDIA_FOURCC('W', 'N', 'C', 'L'),
+
+ /**
+ * Video window has been closed event.
+ */
+ PJMEDIA_EVENT_WND_CLOSED = PJMEDIA_FOURCC('W', 'N', 'C', 'O'),
+
+ /**
+ * Video window has been resized event.
+ */
+ PJMEDIA_EVENT_WND_RESIZED = PJMEDIA_FOURCC('W', 'N', 'R', 'Z'),
+
+ /**
+ * Mouse button has been pressed event.
+ */
+ PJMEDIA_EVENT_MOUSE_BTN_DOWN = PJMEDIA_FOURCC('M', 'S', 'D', 'N'),
+
+ /**
+ * Video key frame has just been decoded event.
+ */
+ PJMEDIA_EVENT_KEY_FRAME_FOUND = PJMEDIA_FOURCC('I', 'F', 'R', 'F'),
+
+ /**
+ * Video decoding error due to missing key frame event.
+ */
+ PJMEDIA_EVENT_KEY_FRAME_MISSING = PJMEDIA_FOURCC('I', 'F', 'R', 'M')
+
+} pjmedia_event_type;
+
+/**
+ * Forward declaration for event subscription.
+ */
+typedef struct pjmedia_event_subscription pjmedia_event_subscription;
+
+/**
+ * Forward declaration for event publisher.
+ */
+typedef struct pjmedia_event_publisher pjmedia_event_publisher;
+
+/**
+ * Additional data/parameters for media format changed event
+ * (PJMEDIA_EVENT_FMT_CHANGED).
+ */
+typedef struct pjmedia_event_fmt_changed_data
+{
+ /** The media flow direction */
+ pjmedia_dir dir;
+
+ /** The new media format. */
+ pjmedia_format new_fmt;
+} pjmedia_event_fmt_changed_data;
+
+/**
+ * Additional data/parameters are not needed.
+ */
+typedef struct pjmedia_event_dummy_data
+{
+ /** Dummy data */
+ int dummy;
+} pjmedia_event_dummy_data;
+
+/**
+ * Additional data/parameters for window resized event
+ * (PJMEDIA_EVENT_WND_RESIZED).
+ */
+typedef struct pjmedia_event_wnd_resized_data
+{
+ /**
+ * The new window size.
+ */
+ pjmedia_rect_size new_size;
+} pjmedia_event_wnd_resized_data;
+
+/**
+ * Additional data/parameters for window closing event.
+ */
+typedef struct pjmedia_event_wnd_closing_data
+{
+ /** Consumer may set this field to PJ_TRUE to cancel the closing */
+ pj_bool_t cancel;
+} pjmedia_event_wnd_closing_data;
+
+/** Additional parameters for window changed event. */
+typedef pjmedia_event_dummy_data pjmedia_event_wnd_closed_data;
+
+/** Additional parameters for mouse button down event */
+typedef pjmedia_event_dummy_data pjmedia_event_mouse_btn_down_data;
+
+/** Additional parameters for key frame found event */
+typedef pjmedia_event_dummy_data pjmedia_event_key_frame_found_data;
+
+/** Additional parameters for key frame missing event */
+typedef pjmedia_event_dummy_data pjmedia_event_key_frame_missing_data;
+
+/**
+ * Maximum size of additional parameters section in pjmedia_event structure
+ */
+#define PJMEDIA_EVENT_DATA_MAX_SIZE sizeof(pjmedia_event_fmt_changed_data)
+
+/** Type of storage to hold user data in pjmedia_event structure */
+typedef char pjmedia_event_user_data[PJMEDIA_EVENT_DATA_MAX_SIZE];
+
+/**
+ * This structure describes a media event. It consists mainly of the event
+ * type and additional data/parameters for the event. Event publishers need
+ * to use #pjmedia_event_init() to initialize this event structure with
+ * basic information about the event.
+ */
+typedef struct pjmedia_event
+{
+ /**
+ * The event type.
+ */
+ pjmedia_event_type type;
+
+ /**
+ * The media timestamp when the event occurs.
+ */
+ pj_timestamp timestamp;
+
+ /**
+ * This keeps count on the number of subscribers that have
+ * processed this event.
+ */
+ unsigned proc_cnt;
+
+ /**
+ * The object signature of the event publisher. Application may use
+ * this to check which publisher published the event.
+ */
+ pjmedia_obj_sig epub_sig;
+
+ /**
+ * Pointer information about the source of this event. This field
+ * is provided mainly so that the event subscribers can compare it
+ * against the publisher that it subscribed the events from initially,
+ * a publisher can republish events from other publisher. Event
+ * subscription must be careful when using this pointer other than for
+ * comparison purpose, since access to the publisher may require special
+ * care (e.g. mutex locking).
+ */
+ const pjmedia_event_publisher *epub;
+
+ /**
+ * Additional data/parameters about the event. The type of data
+ * will be specific to the event type being reported.
+ */
+ union {
+ /** Media format changed event data. */
+ pjmedia_event_fmt_changed_data fmt_changed;
+
+ /** Window resized event data */
+ pjmedia_event_wnd_resized_data wnd_resized;
+
+ /** Window closing event data. */
+ pjmedia_event_wnd_closing_data wnd_closing;
+
+ /** Window closed event data */
+ pjmedia_event_wnd_closed_data wnd_closed;
+
+ /** Mouse button down event data */
+ pjmedia_event_mouse_btn_down_data mouse_btn_down;
+
+ /** Key frame found event data */
+ pjmedia_event_key_frame_found_data key_frm_found;
+
+ /** Key frame missing event data */
+ pjmedia_event_key_frame_missing_data key_frm_missing;
+
+ /** Storage for user event data */
+ pjmedia_event_user_data user;
+
+ /** Pointer to storage to user event data, if it's outside
+ * this struct
+ */
+ void *ptr;
+ } data;
+} pjmedia_event;
+
+/**
+ * The callback to receive media events. The callback should increase
+ * \a proc_cnt field of the event if it processes the event.
+ *
+ * @param esub The subscription that was made initially to receive
+ * this event.
+ * @param event The media event itself.
+ *
+ * @return If the callback returns non-PJ_SUCCESS, this return
+ * code may be propagated back to the producer.
+ */
+typedef pj_status_t pjmedia_event_cb(pjmedia_event_subscription *esub,
+ pjmedia_event *event);
+
+/**
+ * This structure keeps the data needed to maintain an event subscription.
+ * This data is normally kept by event publishers.
+ */
+struct pjmedia_event_subscription
+{
+ /** Standard list members */
+ PJ_DECL_LIST_MEMBER(pjmedia_event_subscription);
+
+ /** Callback that will be called by publisher to report events. */
+ pjmedia_event_cb *cb;
+
+ /** User data for this subscription */
+ void *user_data;
+
+ /** Current publisher it is subscribed to */
+ pjmedia_event_publisher *subscribe_to;
+};
+
+/**
+ * This describes an event publisher. An event publisher is an object that
+ * maintains event subscriptions. When an event is published on behalf of
+ * a publisher with #pjmedia_event_publish(), that event will be propagated
+ * to all of the subscribers registered to the publisher.
+ */
+struct pjmedia_event_publisher
+{
+ /** The object signature of the publisher */
+ pjmedia_obj_sig sig;
+
+ /** List of subscriptions for this event publisher */
+ pjmedia_event_subscription subscription_list;
+};
+
+/**
+ * Initialize event structure with basic data about the event.
+ *
+ * @param event The event to be initialized.
+ * @param type The event type to be set for this event.
+ * @param ts Event timestamp. May be set to NULL to set the event
+ * timestamp to zero.
+ * @param epub Event publisher.
+ */
+PJ_DECL(void) pjmedia_event_init(pjmedia_event *event,
+ pjmedia_event_type type,
+ const pj_timestamp *ts,
+ const pjmedia_event_publisher *epub);
+
+/**
+ * Initialize an event publisher structure.
+ *
+ * @param epub The event publisher.
+ * @param sig The object signature of the publisher.
+ */
+PJ_DECL(void) pjmedia_event_publisher_init(pjmedia_event_publisher *epub,
+ pjmedia_obj_sig sig);
+
+/**
+ * Initialize subscription data.
+ *
+ * @param esub The event subscription.
+ * @param cb The callback to receive events.
+ * @param user_data Arbitrary user data to be associated with the
+ * subscription.
+ */
+PJ_DECL(void) pjmedia_event_subscription_init(pjmedia_event_subscription *esub,
+ pjmedia_event_cb *cb,
+ void *user_data);
+
+/**
+ * Subscribe to events published by the specified publisher using the
+ * specified subscription object. The callback and user data fields of
+ * the subscription object must have been initialized prior to calling
+ * this function, and the subscription object must be kept alive throughout
+ * the duration of the subscription (e.g. it must not be allocated from
+ * the stack).
+ *
+ * Note that the subscriber may receive not only events emitted by
+ * the specific publisher specified in the argument, but also from other
+ * publishers contained by the publisher, if the publisher is republishing
+ * events from other publishers.
+ *
+ * @param epub The event publisher.
+ * @param esub The event subscription object.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_event_subscribe(pjmedia_event_publisher *epub,
+ pjmedia_event_subscription *esub);
+
+/**
+ * Unsubscribe the specified subscription object from publisher it is
+ * currently subscribed to. If the subscription object is not currently
+ * subscribed to anything, the function will do nothing.
+ *
+ * @param esub The event subscription object, which must be the same
+ * object that was given to #pjmedia_event_subscribe().
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_event_unsubscribe(pjmedia_event_subscription *esub);
+
+/**
+ * Check if the specified publisher has subscribers.
+ *
+ * @param epub The event publisher.
+ *
+ * @return PJ_TRUE if the publisher has at least one subscriber.
+ */
+PJ_DECL(pj_bool_t)
+pjmedia_event_publisher_has_sub(pjmedia_event_publisher *epub);
+
+/**
+ * Publish the specified event to all subscribers of the specified event
+ * publisher.
+ *
+ * @param epub The event publisher.
+ * @param event The event to be published.
+ *
+ * @return PJ_SUCCESS only if all subscription callbacks returned
+ * PJ_SUCCESS.
+ */
+PJ_DECL(pj_status_t) pjmedia_event_publish(pjmedia_event_publisher *epub,
+ pjmedia_event *event);
+
+/**
+ * Subscribe to events produced by the source publisher in \a esrc and
+ * republish the events to all subscribers in \a epub publisher.
+ *
+ * @param esrc The event source from which events will be
+ * republished.
+ * @param epub Events from the event source above will be
+ * republished to subscribers of this publisher.
+ * @param esub The subscription object to be used to subscribe
+ * to \a esrc. This doesn't need to be initialized,
+ * but it must be kept alive throughout the lifetime
+ * of the subsciption.
+ *
+ * @return PJ_SUCCESS only if all subscription callbacks returned
+ * PJ_SUCCESS.
+ */
+PJ_DECL(pj_status_t) pjmedia_event_republish(pjmedia_event_publisher *esrc,
+ pjmedia_event_publisher *epub,
+ pjmedia_event_subscription *esub);
+
+/**
+ * @} PJMEDIA_EVENT
+ */
+
+
+PJ_END_DECL
+
+#endif /* __PJMEDIA_EVENT_H__ */
diff --git a/pjmedia/include/pjmedia/format.h b/pjmedia/include/pjmedia/format.h
new file mode 100644
index 00000000..ac9fdb93
--- /dev/null
+++ b/pjmedia/include/pjmedia/format.h
@@ -0,0 +1,748 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_FORMAT_H__
+#define __PJMEDIA_FORMAT_H__
+
+/**
+ * @file pjmedia/format.h Media format
+ * @brief Media format
+ */
+#include <pjmedia/types.h>
+
+/**
+ * @defgroup PJMEDIA_FORMAT Media format
+ * @ingroup PJMEDIA_TYPES
+ * @brief Media format
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+/**
+ * Macro for packing format from a four character code, similar to FOURCC.
+ * This macro is used for building the constants in pjmedia_format_id
+ * enumeration.
+ */
+#define PJMEDIA_FORMAT_PACK(C1, C2, C3, C4) PJMEDIA_FOURCC(C1, C2, C3, C4)
+
+/**
+ * This enumeration uniquely identify audio sample and/or video pixel formats.
+ * Some well known formats are listed here. The format ids are built by
+ * combining four character codes, similar to FOURCC. The format id is
+ * extensible, as application may define and use format ids not declared
+ * on this enumeration.
+ *
+ * This format id along with other information will fully describe the media
+ * in #pjmedia_format structure.
+ */
+typedef enum pjmedia_format_id
+{
+ /*
+ * Audio formats
+ */
+
+ /** 16bit signed integer linear PCM audio */
+ PJMEDIA_FORMAT_L16 = 0,
+
+ /** Alias for PJMEDIA_FORMAT_L16 */
+ PJMEDIA_FORMAT_PCM = PJMEDIA_FORMAT_L16,
+
+ /** G.711 ALAW */
+ PJMEDIA_FORMAT_PCMA = PJMEDIA_FORMAT_PACK('A', 'L', 'A', 'W'),
+
+ /** Alias for PJMEDIA_FORMAT_PCMA */
+ PJMEDIA_FORMAT_ALAW = PJMEDIA_FORMAT_PCMA,
+
+ /** G.711 ULAW */
+ PJMEDIA_FORMAT_PCMU = PJMEDIA_FORMAT_PACK('u', 'L', 'A', 'W'),
+
+ /** Aliaw for PJMEDIA_FORMAT_PCMU */
+ PJMEDIA_FORMAT_ULAW = PJMEDIA_FORMAT_PCMU,
+
+ /** AMR narrowband */
+ PJMEDIA_FORMAT_AMR = PJMEDIA_FORMAT_PACK(' ', 'A', 'M', 'R'),
+
+ /** ITU G.729 */
+ PJMEDIA_FORMAT_G729 = PJMEDIA_FORMAT_PACK('G', '7', '2', '9'),
+
+ /** Internet Low Bit-Rate Codec (ILBC) */
+ PJMEDIA_FORMAT_ILBC = PJMEDIA_FORMAT_PACK('I', 'L', 'B', 'C'),
+
+
+ /*
+ * Video formats.
+ */
+ /**
+ * 24bit RGB
+ */
+ PJMEDIA_FORMAT_RGB24 = PJMEDIA_FORMAT_PACK('R', 'G', 'B', '3'),
+
+ /**
+ * 32bit RGB with alpha channel
+ */
+ PJMEDIA_FORMAT_RGBA = PJMEDIA_FORMAT_PACK('R', 'G', 'B', 'A'),
+ PJMEDIA_FORMAT_BGRA = PJMEDIA_FORMAT_PACK('B', 'G', 'R', 'A'),
+
+ /**
+ * Alias for PJMEDIA_FORMAT_RGBA
+ */
+ PJMEDIA_FORMAT_RGB32 = PJMEDIA_FORMAT_RGBA,
+
+ /**
+ * Device Independent Bitmap, alias for 24 bit RGB
+ */
+ PJMEDIA_FORMAT_DIB = PJMEDIA_FORMAT_PACK('D', 'I', 'B', ' '),
+
+ /**
+ * This is a packed 4:4:4/32bpp format, where each pixel is encoded as
+ * four consecutive bytes, arranged in the following sequence: V0, U0,
+ * Y0, A0. Source:
+ * http://msdn.microsoft.com/en-us/library/dd206750%28v=VS.85%29.aspx#ayuv
+ */
+ PJMEDIA_FORMAT_AYUV = PJMEDIA_FORMAT_PACK('A', 'Y', 'U', 'V'),
+
+ /**
+ * This is packed 4:2:2/16bpp YUV format, the data can be treated as
+ * an array of unsigned char values, where the first byte contains
+ * the first Y sample, the second byte contains the first U (Cb) sample,
+ * the third byte contains the second Y sample, and the fourth byte
+ * contains the first V (Cr) sample, and so forth. Source:
+ * http://msdn.microsoft.com/en-us/library/dd206750%28v=VS.85%29.aspx#yuy2
+ */
+ PJMEDIA_FORMAT_YUY2 = PJMEDIA_FORMAT_PACK('Y', 'U', 'Y', '2'),
+
+ /**
+ * This format is the same as the YUY2 format except the byte order is
+ * reversed -- that is, the chroma and luma bytes are flipped. If the
+ * image is addressed as an array of two little-endian WORD values, the
+ * first WORD contains U in the LSBs and Y0 in the MSBs, and the second
+ * WORD contains V in the LSBs and Y1 in the MSBs. Source:
+ * http://msdn.microsoft.com/en-us/library/dd206750%28v=VS.85%29.aspx#uyvy
+ */
+ PJMEDIA_FORMAT_UYVY = PJMEDIA_FORMAT_PACK('U', 'Y', 'V', 'Y'),
+
+ /**
+ * This format is the same as the YUY2 and UYVY format except the byte
+ * order is reversed -- that is, the chroma and luma bytes are flipped.
+ * If the image is addressed as an array of two little-endian WORD values,
+ * the first WORD contains Y0 in the LSBs and V in the MSBs, and the second
+ * WORD contains Y1 in the LSBs and U in the MSBs.
+ */
+ PJMEDIA_FORMAT_YVYU = PJMEDIA_FORMAT_PACK('Y', 'V', 'Y', 'U'),
+
+ /**
+ * This is planar 4:2:0/12bpp YUV format, the data can be treated as
+ * three planes of color components, where the first plane contains
+ * only the Y samples, the second plane contains only the U (Cb) samples,
+ * and the third plane contains only the V (Cr) sample.
+ */
+ PJMEDIA_FORMAT_I420 = PJMEDIA_FORMAT_PACK('I', '4', '2', '0'),
+
+ /**
+ * IYUV is alias for I420.
+ */
+ PJMEDIA_FORMAT_IYUV = PJMEDIA_FORMAT_I420,
+
+ /**
+ * This is planar 4:2:2/16bpp YUV format.
+ */
+ PJMEDIA_FORMAT_YV12 = PJMEDIA_FORMAT_PACK('Y', 'V', '1', '2'),
+
+ /**
+ * The JPEG version of planar 4:2:0/12bpp YUV format.
+ */
+ PJMEDIA_FORMAT_I420JPEG = PJMEDIA_FORMAT_PACK('J', '4', '2', '0'),
+
+ /**
+ * The JPEG version of planar 4:2:2/16bpp YUV format.
+ */
+ PJMEDIA_FORMAT_I422JPEG = PJMEDIA_FORMAT_PACK('J', '4', '2', '2'),
+
+ /**
+ * Encoded video formats
+ */
+
+ PJMEDIA_FORMAT_H261 = PJMEDIA_FORMAT_PACK('H', '2', '6', '1'),
+ PJMEDIA_FORMAT_H263 = PJMEDIA_FORMAT_PACK('H', '2', '6', '3'),
+ PJMEDIA_FORMAT_H263P = PJMEDIA_FORMAT_PACK('P', '2', '6', '3'),
+ PJMEDIA_FORMAT_H264 = PJMEDIA_FORMAT_PACK('H', '2', '6', '4'),
+
+ PJMEDIA_FORMAT_MJPEG = PJMEDIA_FORMAT_PACK('M', 'J', 'P', 'G'),
+ PJMEDIA_FORMAT_MPEG1VIDEO = PJMEDIA_FORMAT_PACK('M', 'P', '1', 'V'),
+ PJMEDIA_FORMAT_MPEG2VIDEO = PJMEDIA_FORMAT_PACK('M', 'P', '2', 'V'),
+ PJMEDIA_FORMAT_MPEG4 = PJMEDIA_FORMAT_PACK('M', 'P', 'G', '4'),
+ PJMEDIA_FORMAT_XVID = PJMEDIA_FORMAT_PACK('x', 'v', 'i', 'd'),
+
+} pjmedia_format_id;
+
+/**
+ * This enumeration specifies what type of detail is included in a
+ * #pjmedia_format structure.
+ */
+typedef enum pjmedia_format_detail_type
+{
+ /** Format detail is not specified. */
+ PJMEDIA_FORMAT_DETAIL_NONE,
+
+ /** Audio format detail. */
+ PJMEDIA_FORMAT_DETAIL_AUDIO,
+
+ /** Video format detail. */
+ PJMEDIA_FORMAT_DETAIL_VIDEO,
+
+ /** Number of format detail type that has been defined. */
+ PJMEDIA_FORMAT_DETAIL_MAX
+
+} pjmedia_format_detail_type;
+
+/**
+ * This structure is put in \a detail field of #pjmedia_format to describe
+ * detail information about an audio media.
+ */
+typedef struct pjmedia_audio_format_detail
+{
+ unsigned clock_rate; /**< Audio clock rate in samples or Hz. */
+ unsigned channel_count; /**< Number of channels. */
+ unsigned frame_time_usec;/**< Frame interval, in microseconds. */
+ unsigned bits_per_sample;/**< Number of bits per sample. */
+ pj_uint32_t avg_bps; /**< Average bitrate */
+ pj_uint32_t max_bps; /**< Maximum bitrate */
+} pjmedia_audio_format_detail;
+
+/**
+ * This structure is put in \a detail field of #pjmedia_format to describe
+ * detail information about a video media.
+ *
+ * Additional information about a video format can also be retrieved by
+ * calling #pjmedia_get_video_format_info().
+ */
+typedef struct pjmedia_video_format_detail
+{
+ pjmedia_rect_size size; /**< Video size (width, height) */
+ pjmedia_ratio fps; /**< Number of frames per second. */
+ pj_uint32_t avg_bps;/**< Average bitrate. */
+ pj_uint32_t max_bps;/**< Maximum bitrate. */
+} pjmedia_video_format_detail;
+
+/**
+ * This macro declares the size of the detail section in #pjmedia_format
+ * to be reserved for user defined detail.
+ */
+#ifndef PJMEDIA_FORMAT_DETAIL_USER_SIZE
+# define PJMEDIA_FORMAT_DETAIL_USER_SIZE 1
+#endif
+
+/**
+ * This structure contains all the information needed to completely describe
+ * a media.
+ */
+typedef struct pjmedia_format
+{
+ /**
+ * The format id that specifies the audio sample or video pixel format.
+ * Some well known formats ids are declared in pjmedia_format_id
+ * enumeration.
+ *
+ * @see pjmedia_format_id
+ */
+ pj_uint32_t id;
+
+ /**
+ * The top-most type of the media, as an information.
+ */
+ pjmedia_type type;
+
+ /**
+ * The type of detail structure in the \a detail pointer.
+ */
+ pjmedia_format_detail_type detail_type;
+
+ /**
+ * Detail section to describe the media.
+ */
+ union
+ {
+ /**
+ * Detail section for audio format.
+ */
+ pjmedia_audio_format_detail aud;
+
+ /**
+ * Detail section for video format.
+ */
+ pjmedia_video_format_detail vid;
+
+ /**
+ * Reserved area for user-defined format detail.
+ */
+ char user[PJMEDIA_FORMAT_DETAIL_USER_SIZE];
+ } det;
+
+} pjmedia_format;
+
+/**
+ * This enumeration describes video color model. It mostly serves as
+ * information only.
+ */
+typedef enum pjmedia_color_model
+{
+ /** The color model is unknown or unspecified. */
+ PJMEDIA_COLOR_MODEL_NONE,
+
+ /** RGB color model. */
+ PJMEDIA_COLOR_MODEL_RGB,
+
+ /** YUV color model. */
+ PJMEDIA_COLOR_MODEL_YUV
+} pjmedia_color_model;
+
+/**
+ * This structure holds information to apply a specific video format
+ * against size and buffer information, and get additional information
+ * from it. To do that, application fills up the input fields of this
+ * structure, and give this structure to \a apply_fmt() function
+ * of #pjmedia_video_format_info structure.
+ */
+typedef struct pjmedia_video_apply_fmt_param
+{
+ /* input fields: */
+
+ /**
+ * [IN] The image size. This field is mandatory, and has to be set
+ * correctly prior to calling \a apply_fmt() function.
+ */
+ pjmedia_rect_size size;
+
+ /**
+ * [IN] Pointer to the buffer that holds the frame. The \a apply_fmt()
+ * function uses this pointer to calculate the pointer for each video
+ * planes of the media. This field is optional -- however, the
+ * \a apply_fmt() would still fill up the \a planes[] array with the
+ * correct pointer even though the buffer is set to NULL. This could be
+ * useful to calculate the size (in bytes) of each plane.
+ */
+ pj_uint8_t *buffer;
+
+ /* output fields: */
+
+ /**
+ * [OUT] The size (in bytes) required of the buffer to hold the video
+ * frame of the particular frame size (width, height).
+ */
+ pj_size_t framebytes;
+
+ /**
+ * [OUT] Array of strides value (in bytes) for each video plane.
+ */
+ int strides[PJMEDIA_MAX_VIDEO_PLANES];
+
+ /**
+ * [OUT] Array of pointers to each of the video planes. The values are
+ * calculated from the \a buffer field.
+ */
+ pj_uint8_t *planes[PJMEDIA_MAX_VIDEO_PLANES];
+
+ /**
+ * [OUT] Array of video plane sizes.
+ */
+ pj_size_t plane_bytes[PJMEDIA_MAX_VIDEO_PLANES];
+
+} pjmedia_video_apply_fmt_param;
+
+/**
+ * This structure holds information to describe a video format. Application
+ * can retrieve this structure by calling #pjmedia_get_video_format_info()
+ * funcion.
+ */
+typedef struct pjmedia_video_format_info
+{
+ /**
+ * The unique format ID of the media. Well known format ids are declared
+ * in pjmedia_format_id enumeration.
+ */
+ pj_uint32_t id;
+
+ /**
+ * Null terminated string containing short identification about the
+ * format.
+ */
+ char name[8];
+
+ /**
+ * Information about the color model of this video format.
+ */
+ pjmedia_color_model color_model;
+
+ /**
+ * Number of bits needed to store one pixel of this video format.
+ */
+ pj_uint8_t bpp;
+
+ /**
+ * Number of video planes that this format uses. Value 1 indicates
+ * packed format, while value greater than 1 indicates planar format.
+ */
+ pj_uint8_t plane_cnt;
+
+ /**
+ * Pointer to function to apply this format against size and buffer
+ * information in pjmedia_video_apply_fmt_param argument. Application
+ * uses this function to obtain various information such as the
+ * memory size of a frame buffer, strides value of the image, the
+ * location of the planes, and so on. See pjmedia_video_apply_fmt_param
+ * for additional information.
+ *
+ * @param vfi The video format info.
+ * @param vafp The parameters to investigate.
+ *
+ * @return PJ_SUCCESS if the function has calculated the
+ * information in \a vafp successfully.
+ */
+ pj_status_t (*apply_fmt)(const struct pjmedia_video_format_info *vfi,
+ pjmedia_video_apply_fmt_param *vafp);
+
+} pjmedia_video_format_info;
+
+
+/*****************************************************************************
+ * UTILITIES:
+ */
+
+/**
+ * General utility routine to calculate samples per frame value from clock
+ * rate, ptime (in usec), and channel count. Application should use this
+ * macro whenever possible due to possible overflow in the math calculation.
+ *
+ * @param clock_rate Clock rate.
+ * @param usec_ptime Frame interval, in microsecond.
+ * @param channel_count Number of channels.
+ *
+ * @return The samples per frame value.
+ */
+PJ_INLINE(unsigned) PJMEDIA_SPF(unsigned clock_rate, unsigned usec_ptime,
+ unsigned channel_count)
+{
+#if PJ_HAS_INT64
+ return ((unsigned)((pj_uint64_t)usec_ptime * \
+ clock_rate / channel_count / 1000000));
+#elif PJ_HAS_FLOATING_POINT
+ return ((unsigned)(1.0*usec_ptime * clock_rate / channel_count / 1000000));
+#else
+ return ((unsigned)(usec_ptime / 1000L * clock_rate / \
+ channel_count / 1000));
+#endif
+}
+
+/**
+ * Variant of #PJMEDIA_SPF() which takes frame rate instead of ptime.
+ */
+PJ_INLINE(unsigned) PJMEDIA_SPF2(unsigned clock_rate, const pjmedia_ratio *fr,
+ unsigned channel_count)
+{
+#if PJ_HAS_INT64
+ return ((unsigned)((pj_uint64_t)clock_rate * fr->denum \
+ / fr->num / channel_count));
+#elif PJ_HAS_FLOATING_POINT
+ return ((unsigned)(1.0* clock_rate * fr->denum / fr->num /channel_count));
+#else
+ return ((unsigned)(1L * clock_rate * fr->denum / fr->num / channel_count));
+#endif
+}
+
+
+/**
+ * Utility routine to calculate frame size (in bytes) from bitrate and frame
+ * interval values. Application should use this macro whenever possible due
+ * to possible overflow in the math calculation.
+ *
+ * @param bps The bitrate of the stream.
+ * @param usec_ptime Frame interval, in microsecond.
+ *
+ * @return Frame size in bytes.
+ */
+PJ_INLINE(unsigned) PJMEDIA_FSZ(unsigned bps, unsigned usec_ptime)
+{
+#if PJ_HAS_INT64
+ return ((unsigned)((pj_uint64_t)bps * usec_ptime / PJ_UINT64(8000000)));
+#elif PJ_HAS_FLOATING_POINT
+ return ((unsigned)(1.0 * bps * usec_ptime / 8000000.0));
+#else
+ return ((unsigned)(bps / 8L * usec_ptime / 1000000));
+#endif
+}
+
+/**
+ * General utility routine to calculate ptime value from frame rate.
+ * Application should use this macro whenever possible due to possible
+ * overflow in the math calculation.
+ *
+ * @param frame_rate Frame rate
+ *
+ * @return The ptime value (in usec).
+ */
+PJ_INLINE(unsigned) PJMEDIA_PTIME(const pjmedia_ratio *frame_rate)
+{
+#if PJ_HAS_INT64
+ return ((unsigned)((pj_uint64_t)1000000 * \
+ frame_rate->denum / frame_rate->num));
+#elif PJ_HAS_FLOATING_POINT
+ return ((unsigned)(1000000.0 * frame_rate->denum / \
+ frame_rate->num));
+#else
+ return ((unsigned)((1000L * frame_rate->denum / \
+ frame_rate->num) * 1000);
+#endif
+}
+
+/**
+ * Utility to retrieve samples_per_frame value from
+ * pjmedia_audio_format_detail.
+ *
+ * @param pafd Pointer to pjmedia_audio_format_detail
+ * @return Samples per frame
+ */
+PJ_INLINE(unsigned) PJMEDIA_AFD_SPF(const pjmedia_audio_format_detail *pafd)
+{
+ return PJMEDIA_SPF(pafd->clock_rate, pafd->frame_time_usec,
+ pafd->channel_count);
+}
+
+/**
+ * Utility to retrieve average frame size from pjmedia_audio_format_detail.
+ * The average frame size is derived from the average bitrate of the audio
+ * stream.
+ *
+ * @param afd Pointer to pjmedia_audio_format_detail
+ * @return Average frame size.
+ */
+PJ_INLINE(unsigned) PJMEDIA_AFD_AVG_FSZ(const pjmedia_audio_format_detail *afd)
+{
+ return PJMEDIA_FSZ(afd->avg_bps, afd->frame_time_usec);
+}
+
+/**
+ * Utility to retrieve maximum frame size from pjmedia_audio_format_detail.
+ * The maximum frame size is derived from the maximum bitrate of the audio
+ * stream.
+ *
+ * @param afd Pointer to pjmedia_audio_format_detail
+ * @return Average frame size.
+ */
+PJ_INLINE(unsigned) PJMEDIA_AFD_MAX_FSZ(const pjmedia_audio_format_detail *afd)
+{
+ return PJMEDIA_FSZ(afd->max_bps, afd->frame_time_usec);
+}
+
+
+/**
+ * Initialize the format as audio format with the specified parameters.
+ *
+ * @param fmt The format to be initialized.
+ * @param fmt_id Format ID. See #pjmedia_format_id
+ * @param clock_rate Audio clock rate.
+ * @param channel_count Number of channels.
+ * @param bits_per_sample Number of bits per sample.
+ * @param frame_time_usec Frame interval, in microsecond.
+ * @param avg_bps Average bitrate.
+ * @param max_bps Maximum bitrate.
+ */
+PJ_DECL(void) pjmedia_format_init_audio(pjmedia_format *fmt,
+ pj_uint32_t fmt_id,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned bits_per_sample,
+ unsigned frame_time_usec,
+ pj_uint32_t avg_bps,
+ pj_uint32_t max_bps);
+
+/**
+ * Initialize the format as video format with the specified parameters.
+ * A format manager should have been created, as this function will need
+ * to consult to a format manager in order to fill in detailed
+ * information about the format.
+ *
+ * @param fmt The format to be initialised.
+ * @param fmt_id Format ID. See #pjmedia_format_id
+ * @param width Image width.
+ * @param height Image heigth.
+ * @param fps_num FPS numerator.
+ * @param fps_denum FPS denumerator.
+ * @param avg_bps Average bitrate.
+ * @param max_bps Maximum bitrate.
+ */
+PJ_DECL(void) pjmedia_format_init_video(pjmedia_format *fmt,
+ pj_uint32_t fmt_id,
+ unsigned width,
+ unsigned height,
+ unsigned fps_num,
+ unsigned fps_denum);
+
+/**
+ * Copy format to another.
+ *
+ * @param dst The destination format.
+ * @param src The source format.
+ *
+ * @return Pointer to destination format.
+ */
+PJ_DECL(pjmedia_format*) pjmedia_format_copy(pjmedia_format *dst,
+ const pjmedia_format *src);
+
+/**
+ * Check if the format contains audio format, and retrieve the audio format
+ * detail in the format.
+ *
+ * @param fmt The format structure.
+ * @param assert_valid If this is set to non-zero, an assertion will be
+ * raised if the detail type is not audio or if the
+ * the detail is NULL.
+ *
+ * @return The instance of audio format detail in the format
+ * structure, or NULL if the format doesn't contain
+ * audio detail.
+ */
+PJ_DECL(pjmedia_audio_format_detail*)
+pjmedia_format_get_audio_format_detail(const pjmedia_format *fmt,
+ pj_bool_t assert_valid);
+
+/**
+ * Check if the format contains video format, and retrieve the video format
+ * detail in the format.
+ *
+ * @param fmt The format structure.
+ * @param assert_valid If this is set to non-zero, an assertion will be
+ * raised if the detail type is not video or if the
+ * the detail is NULL.
+ *
+ * @return The instance of video format detail in the format
+ * structure, or NULL if the format doesn't contain
+ * video detail.
+ */
+PJ_DECL(pjmedia_video_format_detail*)
+pjmedia_format_get_video_format_detail(const pjmedia_format *fmt,
+ pj_bool_t assert_valid);
+
+/*****************************************************************************
+ * FORMAT MANAGEMENT:
+ */
+
+/**
+ * Opaque data type for video format manager. The video format manager manages
+ * the repository of video formats that the framework recognises. Typically it
+ * is a singleton instance, although application may instantiate more than one
+ * instances of this if required.
+ */
+typedef struct pjmedia_video_format_mgr pjmedia_video_format_mgr;
+
+
+/**
+ * Create a new video format manager instance. This will also set the pointer
+ * to the singleton instance if the value is still NULL.
+ *
+ * @param pool The pool to allocate memory.
+ * @param max_fmt Maximum number of formats to accommodate.
+ * @param options Option flags. Must be zero for now.
+ * @param p_mgr Pointer to hold the created instance.
+ *
+ * @return PJ_SUCCESS on success, or the appripriate error value.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_video_format_mgr_create(pj_pool_t *pool,
+ unsigned max_fmt,
+ unsigned options,
+ pjmedia_video_format_mgr **p_mgr);
+
+/**
+ * Get the singleton instance of the video format manager.
+ *
+ * @return The instance.
+ */
+PJ_DECL(pjmedia_video_format_mgr*) pjmedia_video_format_mgr_instance(void);
+
+/**
+ * Manually assign a specific video manager instance as the singleton
+ * instance. Normally this is not needed if only one instance is ever
+ * going to be created, as the library automatically assign the singleton
+ * instance.
+ *
+ * @param mgr The instance to be used as the singleton instance.
+ * Application may specify NULL to clear the singleton
+ * singleton instance.
+ */
+PJ_DECL(void)
+pjmedia_video_format_mgr_set_instance(pjmedia_video_format_mgr *mgr);
+
+/**
+ * Retrieve a video format info for the specified format id.
+ *
+ * @param mgr The video format manager. Specify NULL to use
+ * the singleton instance (however, a video format
+ * manager still must have been created prior to
+ * calling this function).
+ * @param id The format id which format info is to be
+ * retrieved.
+ *
+ * @return The video format info.
+ */
+PJ_DECL(const pjmedia_video_format_info*)
+pjmedia_get_video_format_info(pjmedia_video_format_mgr *mgr,
+ pj_uint32_t id);
+
+/**
+ * Register a new video format to the framework. By default, built-in
+ * formats will be registered automatically to the format manager when
+ * it is created (note: built-in formats are ones which format id is
+ * listed in pjmedia_format_id enumeration). This function allows
+ * application to use user defined format id by registering that format
+ * into the framework.
+ *
+ * @param mgr The video format manager. Specify NULL to use
+ * the singleton instance (however, a video format
+ * manager still must have been created prior to
+ * calling this function).
+ * @param vfi The video format info to be registered. This
+ * structure must remain valid until the format
+ * manager is destroyed.
+ *
+ * @return PJ_SUCCESS on success, or the appripriate error value.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_register_video_format_info(pjmedia_video_format_mgr *mgr,
+ pjmedia_video_format_info *vfi);
+
+/**
+ * Destroy a video format manager. If the manager happens to be the singleton
+ * instance, the singleton instance will be set to NULL.
+ *
+ * @param mgr The video format manager. Specify NULL to use
+ * the singleton instance (however, a video format
+ * manager still must have been created prior to
+ * calling this function).
+ */
+PJ_DECL(void) pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr *mgr);
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif /* __PJMEDIA_FORMAT_H__ */
+
diff --git a/pjmedia/include/pjmedia/frame.h b/pjmedia/include/pjmedia/frame.h
new file mode 100644
index 00000000..faa33af0
--- /dev/null
+++ b/pjmedia/include/pjmedia/frame.h
@@ -0,0 +1,332 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_FRAME_H__
+#define __PJMEDIA_FRAME_H__
+
+/**
+ * @file pjmedia/frame.h Media frame
+ * @brief Frame
+ */
+#include <pjmedia/types.h>
+#include <pj/string.h>
+
+/**
+ * @defgroup PJMEDIA_FRAME Media frame
+ * @ingroup PJMEDIA_TYPES
+ * @brief Frame
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Types of media frame.
+ */
+typedef enum pjmedia_frame_type
+{
+ PJMEDIA_FRAME_TYPE_NONE, /**< No frame. */
+ PJMEDIA_FRAME_TYPE_AUDIO, /**< Normal audio frame. */
+ PJMEDIA_FRAME_TYPE_EXTENDED, /**< Extended audio frame. */
+ PJMEDIA_FRAME_TYPE_VIDEO /**< Video frame. */
+
+} pjmedia_frame_type;
+
+
+/**
+ * This structure describes a media frame.
+ */
+typedef struct pjmedia_frame
+{
+ pjmedia_frame_type type; /**< Frame type. */
+ void *buf; /**< Pointer to buffer. */
+ pj_size_t size; /**< Frame size in bytes. */
+ pj_timestamp timestamp; /**< Frame timestamp. */
+ pj_uint32_t bit_info; /**< Bit info of the frame, sample case:
+ a frame may not exactly start and end
+ at the octet boundary, so this field
+ may be used for specifying start &
+ end bit offset. */
+} pjmedia_frame;
+
+
+/**
+ * The pjmedia_frame_ext is used to carry a more complex audio frames than
+ * the typical PCM audio frames, and it is signaled by setting the "type"
+ * field of a pjmedia_frame to PJMEDIA_FRAME_TYPE_EXTENDED. With this set,
+ * application may typecast pjmedia_frame to pjmedia_frame_ext.
+ *
+ * This structure may contain more than one audio frames, which subsequently
+ * will be called subframes in this structure. The subframes section
+ * immediately follows the end of this structure, and each subframe is
+ * represented by pjmedia_frame_ext_subframe structure. Every next
+ * subframe immediately follows the previous subframe, and all subframes
+ * are byte-aligned although its payload may not be byte-aligned.
+ */
+
+#pragma pack(1)
+typedef struct pjmedia_frame_ext {
+ pjmedia_frame base; /**< Base frame info */
+ pj_uint16_t samples_cnt; /**< Number of samples in this frame */
+ pj_uint16_t subframe_cnt; /**< Number of (sub)frames in this frame */
+
+ /* Zero or more (sub)frames follows immediately after this,
+ * each will be represented by pjmedia_frame_ext_subframe
+ */
+} pjmedia_frame_ext;
+#pragma pack()
+
+/**
+ * This structure represents the individual subframes in the
+ * pjmedia_frame_ext structure.
+ */
+#pragma pack(1)
+typedef struct pjmedia_frame_ext_subframe {
+ pj_uint16_t bitlen; /**< Number of bits in the data */
+ pj_uint8_t data[1]; /**< Start of encoded data */
+} pjmedia_frame_ext_subframe;
+
+#pragma pack()
+
+/**
+ * Copy one frame to another. If the destination frame's size is smaller than
+ * the source frame's, the destination buffer will be truncated.
+ *
+ * @param src Source frame.
+ * @param dst Destination frame.
+ */
+PJ_INLINE(void) pjmedia_frame_copy(pjmedia_frame *dst,
+ const pjmedia_frame *src)
+{
+ dst->type = src->type;
+ dst->timestamp = src->timestamp;
+ dst->bit_info = src->bit_info;
+ dst->size = (dst->size < src->size? dst->size: src->size);
+ pj_memcpy(dst->buf, src->buf, dst->size);
+}
+
+/**
+ * Append one subframe to #pjmedia_frame_ext.
+ *
+ * @param frm The #pjmedia_frame_ext.
+ * @param src Subframe data.
+ * @param bitlen Length of subframe, in bits.
+ * @param samples_cnt Number of audio samples in subframe.
+ */
+PJ_INLINE(void) pjmedia_frame_ext_append_subframe(pjmedia_frame_ext *frm,
+ const void *src,
+ unsigned bitlen,
+ unsigned samples_cnt)
+{
+ pjmedia_frame_ext_subframe *fsub;
+ pj_uint8_t *p;
+ unsigned i;
+
+ p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
+ for (i = 0; i < frm->subframe_cnt; ++i) {
+ fsub = (pjmedia_frame_ext_subframe*) p;
+ p += sizeof(fsub->bitlen) + ((fsub->bitlen+7) >> 3);
+ }
+
+ fsub = (pjmedia_frame_ext_subframe*) p;
+ fsub->bitlen = (pj_uint16_t)bitlen;
+ if (bitlen)
+ pj_memcpy(fsub->data, src, (bitlen+7) >> 3);
+
+ frm->subframe_cnt++;
+ frm->samples_cnt = (pj_uint16_t)(frm->samples_cnt + samples_cnt);
+}
+
+/**
+ * Get a subframe from #pjmedia_frame_ext.
+ *
+ * @param frm The #pjmedia_frame_ext.
+ * @param n Subframe index, zero based.
+ *
+ * @return The n-th subframe, or NULL if n is out-of-range.
+ */
+PJ_INLINE(pjmedia_frame_ext_subframe*)
+pjmedia_frame_ext_get_subframe(const pjmedia_frame_ext *frm, unsigned n)
+{
+ pjmedia_frame_ext_subframe *sf = NULL;
+
+ if (n < frm->subframe_cnt) {
+ pj_uint8_t *p;
+ unsigned i;
+
+ p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
+ for (i = 0; i < n; ++i) {
+ sf = (pjmedia_frame_ext_subframe*) p;
+ p += sizeof(sf->bitlen) + ((sf->bitlen+7) >> 3);
+ }
+
+ sf = (pjmedia_frame_ext_subframe*) p;
+ }
+
+ return sf;
+}
+
+/**
+ * Extract all frame payload to the specified buffer.
+ *
+ * @param frm The frame.
+ * @param dst Destination buffer.
+ * @param maxlen Maximum size to copy (i.e. the size of the
+ * destination buffer).
+ *
+ * @return Total size of payload copied.
+ */
+PJ_INLINE(unsigned)
+pjmedia_frame_ext_copy_payload(const pjmedia_frame_ext *frm,
+ void *dst,
+ unsigned maxlen)
+{
+ unsigned i, copied=0;
+ for (i=0; i<frm->subframe_cnt; ++i) {
+ pjmedia_frame_ext_subframe *sf;
+ unsigned sz;
+
+ sf = pjmedia_frame_ext_get_subframe(frm, i);
+ if (!sf)
+ continue;
+
+ sz = ((sf->bitlen + 7) >> 3);
+ if (sz + copied > maxlen)
+ break;
+
+ pj_memcpy(((pj_uint8_t*)dst) + copied, sf->data, sz);
+ copied += sz;
+ }
+ return copied;
+}
+
+
+/**
+ * Pop out first n subframes from #pjmedia_frame_ext.
+ *
+ * @param frm The #pjmedia_frame_ext.
+ * @param n Number of first subframes to be popped out.
+ *
+ * @return PJ_SUCCESS when successful.
+ */
+PJ_INLINE(pj_status_t)
+pjmedia_frame_ext_pop_subframes(pjmedia_frame_ext *frm, unsigned n)
+{
+ pjmedia_frame_ext_subframe *sf;
+ pj_uint8_t *move_src;
+ unsigned move_len;
+
+ if (frm->subframe_cnt <= n) {
+ frm->subframe_cnt = 0;
+ frm->samples_cnt = 0;
+ return PJ_SUCCESS;
+ }
+
+ move_src = (pj_uint8_t*)pjmedia_frame_ext_get_subframe(frm, n);
+ sf = pjmedia_frame_ext_get_subframe(frm, frm->subframe_cnt-1);
+ move_len = (pj_uint8_t*)sf - move_src + sizeof(sf->bitlen) +
+ ((sf->bitlen+7) >> 3);
+ pj_memmove((pj_uint8_t*)frm+sizeof(pjmedia_frame_ext),
+ move_src, move_len);
+
+ frm->samples_cnt = (pj_uint16_t)
+ (frm->samples_cnt - n*frm->samples_cnt/frm->subframe_cnt);
+ frm->subframe_cnt = (pj_uint16_t) (frm->subframe_cnt - n);
+
+ return PJ_SUCCESS;
+}
+
+
+/**
+ * This is a general purpose function set PCM samples to zero.
+ * Since this function is needed by many parts of the library,
+ * by putting this functionality in one place, it enables some.
+ * clever people to optimize this function.
+ *
+ * @param samples The 16bit PCM samples.
+ * @param count Number of samples.
+ */
+PJ_INLINE(void) pjmedia_zero_samples(pj_int16_t *samples, unsigned count)
+{
+#if 1
+ pj_bzero(samples, (count<<1));
+#elif 0
+ unsigned i;
+ for (i=0; i<count; ++i) samples[i] = 0;
+#else
+ unsigned i;
+ count >>= 1;
+ for (i=0; i<count; ++i) ((pj_int32_t*)samples)[i] = (pj_int32_t)0;
+#endif
+}
+
+
+/**
+ * This is a general purpose function to copy samples from/to buffers with
+ * equal size. Since this function is needed by many parts of the library,
+ * by putting this functionality in one place, it enables some.
+ * clever people to optimize this function.
+ */
+PJ_INLINE(void) pjmedia_copy_samples(pj_int16_t *dst, const pj_int16_t *src,
+ unsigned count)
+{
+#if 1
+ pj_memcpy(dst, src, (count<<1));
+#elif 0
+ unsigned i;
+ for (i=0; i<count; ++i) dst[i] = src[i];
+#else
+ unsigned i;
+ count >>= 1;
+ for (i=0; i<count; ++i)
+ ((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
+#endif
+}
+
+
+/**
+ * This is a general purpose function to copy samples from/to buffers with
+ * equal size. Since this function is needed by many parts of the library,
+ * by putting this functionality in one place, it enables some.
+ * clever people to optimize this function.
+ */
+PJ_INLINE(void) pjmedia_move_samples(pj_int16_t *dst, const pj_int16_t *src,
+ unsigned count)
+{
+#if 1
+ pj_memmove(dst, src, (count<<1));
+#elif 0
+ unsigned i;
+ for (i=0; i<count; ++i) dst[i] = src[i];
+#else
+ unsigned i;
+ count >>= 1;
+ for (i=0; i<count; ++i)
+ ((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
+#endif
+}
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif /* __PJMEDIA_FRAME_H__ */
diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h
index 18576ef9..83ee852b 100644
--- a/pjmedia/include/pjmedia/jbuf.h
+++ b/pjmedia/include/pjmedia/jbuf.h
@@ -68,7 +68,7 @@ typedef enum pjmedia_jb_frame_type pjmedia_jb_frame_type;
/**
* This structure describes jitter buffer state.
*/
-struct pjmedia_jb_state
+typedef struct pjmedia_jb_state
{
/* Setting */
unsigned frame_size; /**< Individual frame size, in bytes. */
@@ -89,13 +89,7 @@ struct pjmedia_jb_state
unsigned lost; /**< Number of lost frames. */
unsigned discard; /**< Number of discarded frames. */
unsigned empty; /**< Number of empty on GET events. */
-};
-
-
-/**
- * @see pjmedia_jb_state
- */
-typedef struct pjmedia_jb_state pjmedia_jb_state;
+} pjmedia_jb_state;
/**
@@ -180,6 +174,19 @@ PJ_DECL(pj_status_t) pjmedia_jbuf_set_adaptive( pjmedia_jbuf *jb,
/**
+ * Enable/disable the jitter buffer drift detection and handling mechanism.
+ * The default behavior is enabled.
+ *
+ * @param jb The jitter buffer
+ * @param enable Set to PJ_TRUE to enable or PJ_FALSE to disable.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_jbuf_enable_discard(pjmedia_jbuf *jb,
+ pj_bool_t enable);
+
+
+/**
* Destroy jitter buffer instance.
*
* @param jb The jitter buffer.
@@ -244,6 +251,32 @@ PJ_DECL(void) pjmedia_jbuf_put_frame2( pjmedia_jbuf *jb,
pj_bool_t *discarded);
/**
+ * Put a frame to the jitter buffer. If the frame can be accepted (based
+ * on the sequence number), the jitter buffer will copy the frame and put
+ * it in the appropriate position in the buffer.
+ *
+ * Application MUST manage it's own synchronization when multiple threads
+ * are accessing the jitter buffer at the same time.
+ *
+ * @param jb The jitter buffer.
+ * @param frame Pointer to frame buffer to be stored in the jitter
+ * buffer.
+ * @param size The frame size.
+ * @param bit_info Bit precise info of the frame, e.g: a frame may not
+ * exactly start and end at the octet boundary, so this
+ * field may be used for specifying start & end bit offset.
+ * @param frame_seq The frame sequence number.
+ * @param frame_ts The frame timestamp.
+ * @param discarded Flag whether the frame is discarded by jitter buffer.
+ */
+PJ_DECL(void) pjmedia_jbuf_put_frame3( pjmedia_jbuf *jb,
+ const void *frame,
+ pj_size_t size,
+ pj_uint32_t bit_info,
+ int frame_seq,
+ pj_uint32_t frame_ts,
+ pj_bool_t *discarded);
+/**
* Get a frame from the jitter buffer. The jitter buffer will return the
* oldest frame from it's buffer, when it is available.
*
@@ -294,6 +327,70 @@ PJ_DECL(void) pjmedia_jbuf_get_frame2(pjmedia_jbuf *jb,
/**
+ * Get a frame from the jitter buffer. The jitter buffer will return the
+ * oldest frame from it's buffer, when it is available.
+ *
+ * @param jb The jitter buffer.
+ * @param frame Buffer to receive the payload from the jitter buffer.
+ * @see pjmedia_jbuf_get_frame().
+ * @param size Pointer to receive frame size.
+ * @param p_frm_type Pointer to receive frame type.
+ * @see pjmedia_jbuf_get_frame().
+ * @param bit_info Bit precise info of the frame, e.g: a frame may not
+ * exactly start and end at the octet boundary, so this
+ * field may be used for specifying start & end bit offset.
+ * @param ts Frame timestamp.
+ * @param seq Frame sequence number.
+ */
+PJ_DECL(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb,
+ void *frame,
+ pj_size_t *size,
+ char *p_frm_type,
+ pj_uint32_t *bit_info,
+ pj_uint32_t *ts,
+ int *seq);
+
+
+/**
+ * Peek a frame from the jitter buffer. The jitter buffer state will not be
+ * modified.
+ *
+ * @param jb The jitter buffer.
+ * @param offset Offset from the oldest frame to be peeked.
+ * @param frame Buffer to receive the payload from the jitter buffer.
+ * @see pjmedia_jbuf_get_frame().
+ * @param size Pointer to receive frame size.
+ * @param p_frm_type Pointer to receive frame type.
+ * @see pjmedia_jbuf_get_frame().
+ * @param bit_info Bit precise info of the frame, e.g: a frame may not
+ * exactly start and end at the octet boundary, so this
+ * field may be used for specifying start & end bit offset.
+ * @param ts Frame timestamp.
+ * @param seq Frame sequence number.
+ */
+PJ_DECL(void) pjmedia_jbuf_peek_frame(pjmedia_jbuf *jb,
+ unsigned offset,
+ const void **frame,
+ pj_size_t *size,
+ char *p_frm_type,
+ pj_uint32_t *bit_info,
+ pj_uint32_t *ts,
+ int *seq);
+
+
+/**
+ * Remove frames from the jitter buffer.
+ *
+ * @param jb The jitter buffer.
+ * @param frame_cnt Number of frames to be removed.
+ *
+ * @return The number of frame successfully removed.
+ */
+PJ_DECL(unsigned) pjmedia_jbuf_remove_frame(pjmedia_jbuf *jb,
+ unsigned frame_cnt);
+
+
+/**
* Get jitter buffer current state/settings.
*
* @param jb The jitter buffer.
diff --git a/pjmedia/include/pjmedia/port.h b/pjmedia/include/pjmedia/port.h
index 5c45913c..b6493413 100644
--- a/pjmedia/include/pjmedia/port.h
+++ b/pjmedia/include/pjmedia/port.h
@@ -24,7 +24,11 @@
* @file port.h
* @brief Port interface declaration
*/
-#include <pjmedia/types.h>
+#include <pjmedia/clock.h>
+#include <pjmedia/event.h>
+#include <pjmedia/format.h>
+#include <pjmedia/frame.h>
+#include <pjmedia/signatures.h>
#include <pj/assert.h>
#include <pj/os.h>
@@ -184,6 +188,12 @@ PJ_BEGIN_DECL
/**
+ * Create 32bit port signature from ASCII characters.
+ */
+#define PJMEDIA_PORT_SIG(a,b,c,d) PJMEDIA_OBJ_SIG(a,b,c,d)
+
+
+/**
* Port operation setting.
*/
typedef enum pjmedia_port_op
@@ -220,19 +230,133 @@ typedef struct pjmedia_port_info
{
pj_str_t name; /**< Port name. */
pj_uint32_t signature; /**< Port signature. */
- pjmedia_type type; /**< Media type. */
- pj_bool_t has_info; /**< Has info? */
- pj_bool_t need_info; /**< Need info on connect? */
- unsigned pt; /**< Payload type (can be dynamic). */
- pjmedia_format format; /**< Format. */
- pj_str_t encoding_name; /**< Encoding name. */
- unsigned clock_rate; /**< Sampling rate. */
- unsigned channel_count; /**< Number of channels. */
- unsigned bits_per_sample; /**< Bits/sample */
- unsigned samples_per_frame; /**< No of samples per frame. */
- unsigned bytes_per_frame; /**< No of bytes per frame. */
+ pjmedia_dir dir; /**< Port direction. */
+ pjmedia_format fmt; /**< Format. */
} pjmedia_port_info;
+/**
+ * Utility to retrieve audio clock rate/sampling rate value from
+ * pjmedia_port_info.
+ *
+ * @param pia Pointer to port info containing audio format.
+ * @return Audio clock rate.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_SRATE(const pjmedia_port_info *pia)
+{
+ pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+ pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+ return pia->fmt.det.aud.clock_rate;
+}
+
+/**
+ * Utility to retrieve audio channel count value from pjmedia_port_info.
+ *
+ * @param pia Pointer to port info containing audio format.
+ * @return Audio channel count.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_CCNT(const pjmedia_port_info *pia)
+{
+ pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+ pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+ return pia->fmt.det.aud.channel_count;
+}
+
+/**
+ * Utility to retrieve audio bits per sample value from pjmedia_port_info.
+ *
+ * @param pia Pointer to port info containing audio format.
+ * @return Number of bits per sample.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_BITS(const pjmedia_port_info *pia)
+{
+ pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+ pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+ return pia->fmt.det.aud.bits_per_sample;
+}
+
+/**
+ * Utility to retrieve audio frame interval (ptime) value from
+ * pjmedia_port_info.
+ *
+ * @param pia Pointer to port info containing audio format.
+ * @return Frame interval in msec.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_PTIME(const pjmedia_port_info *pia)
+{
+ pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+ pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+ return pia->fmt.det.aud.frame_time_usec / 1000;
+}
+
+/**
+ * This is a utility routine to retrieve the audio samples_per_frame value
+ * from port info.
+ *
+ * @param pia Pointer to port info containing audio format.
+ * @return Samples per frame value.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_SPF(const pjmedia_port_info *pia)
+{
+ pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+ pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+ return PJMEDIA_AFD_SPF(&pia->fmt.det.aud);
+}
+
+/**
+ * This is a utility routine to retrieve the average bitrate value
+ * from port info.
+ *
+ * @param pia Pointer to port info containing audio format.
+ * @return Bitrate, in bits per second.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_AVG_BPS(const pjmedia_port_info *pia)
+{
+ pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+ pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+ return pia->fmt.det.aud.avg_bps;
+}
+
+/**
+ * This is a utility routine to retrieve the maximum bitrate value
+ * from port info.
+ *
+ * @param pia Pointer to port info containing audio format.
+ * @return Bitrate, in bits per second.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_MAX_BPS(const pjmedia_port_info *pia)
+{
+ pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+ pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+ return pia->fmt.det.aud.max_bps;
+}
+
+/**
+ * This is a utility routine to retrieve the average audio frame size value
+ * from pjmedia_port_info.
+ *
+ * @param pia Pointer to port info containing audio format.
+ * @return Frame size in bytes.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_AVG_FSZ(const pjmedia_port_info *pia)
+{
+ pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+ pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+ return PJMEDIA_AFD_AVG_FSZ(&pia->fmt.det.aud);
+}
+
+/**
+ * Utility to retrieve audio frame size from maximum bitrate from
+ * pjmedia_port_info.
+ *
+ * @param pia Pointer to port info containing audio format.
+ * @return Frame size in bytes.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_MAX_FSZ(const pjmedia_port_info *pia)
+{
+ pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+ pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+ return PJMEDIA_AFD_MAX_FSZ(&pia->fmt.det.aud);
+}
/**
* Port interface.
@@ -250,11 +374,18 @@ typedef struct pjmedia_port
} port_data;
/**
+ * Get clock source.
+ * This should only be called by #pjmedia_port_get_clock_src().
+ */
+ pjmedia_clock_src* (*get_clock_src)(struct pjmedia_port *this_port,
+ pjmedia_dir dir);
+
+ /**
* Sink interface.
* This should only be called by #pjmedia_port_put_frame().
*/
pj_status_t (*put_frame)(struct pjmedia_port *this_port,
- const pjmedia_frame *frame);
+ pjmedia_frame *frame);
/**
* Source interface.
@@ -268,6 +399,11 @@ typedef struct pjmedia_port
*/
pj_status_t (*on_destroy)(struct pjmedia_port *this_port);
+ /**
+ * Get event publisher for this media port, if any.
+ */
+ pjmedia_event_publisher *(*get_event_pub)(struct pjmedia_port *this_port);
+
} pjmedia_port;
@@ -293,6 +429,37 @@ PJ_DECL(pj_status_t) pjmedia_port_info_init( pjmedia_port_info *info,
unsigned bits_per_sample,
unsigned samples_per_frame);
+/**
+ * This is an auxiliary function to initialize port info for
+ * ports which deal with PCM audio.
+ *
+ * @param info The port info to be initialized.
+ * @param name Port name.
+ * @param signature Port signature.
+ * @param dir Port's direction.
+ * @param fmt Port's media format.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_port_info_init2(pjmedia_port_info *info,
+ const pj_str_t *name,
+ unsigned signature,
+ pjmedia_dir dir,
+ const pjmedia_format *fmt);
+
+
+/**
+ * Get a clock source from the port.
+ *
+ * @param port The media port.
+ * @param dir Media port's direction.
+ *
+ * @return The clock source or NULL if clock source is not present
+ * in the port.
+ */
+PJ_DECL(pjmedia_clock_src *) pjmedia_port_get_clock_src( pjmedia_port *port,
+ pjmedia_dir dir );
+
/**
* Get a frame from the port (and subsequent downstream ports).
@@ -314,8 +481,18 @@ PJ_DECL(pj_status_t) pjmedia_port_get_frame( pjmedia_port *port,
* @return PJ_SUCCESS on success, or the appropriate error code.
*/
PJ_DECL(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port,
- const pjmedia_frame *frame );
+ pjmedia_frame *frame );
+/**
+ * Get the event publisher for the media port, if any.
+ *
+ * @param port The media port.
+ *
+ * @return The event publisher, or NULL if the port does not publish
+ * events.
+ */
+PJ_DECL(pjmedia_event_publisher*)
+pjmedia_port_get_event_publisher(pjmedia_port *port);
/**
* Destroy port (and subsequent downstream ports)
diff --git a/pjmedia/include/pjmedia/sdp.h b/pjmedia/include/pjmedia/sdp.h
index 9e67ce46..cd89b847 100644
--- a/pjmedia/include/pjmedia/sdp.h
+++ b/pjmedia/include/pjmedia/sdp.h
@@ -25,7 +25,7 @@
* @brief SDP header file.
*/
#include <pjmedia/types.h>
-
+#include <pj/sock.h>
/**
* @defgroup PJMEDIA_SDP SDP Parsing and Data Structure
@@ -276,18 +276,11 @@ pjmedia_sdp_rtpmap_to_attr( pj_pool_t *pool,
/**
* This structure describes SDP \a fmtp attribute.
*/
-struct pjmedia_sdp_fmtp
+typedef struct pjmedia_sdp_fmtp
{
pj_str_t fmt; /**< Format type. */
pj_str_t fmt_param; /**< Format specific parameter. */
-};
-
-
-/**
- * @see pjmedia_sdp_fmtp
- */
-typedef struct pjmedia_sdp_fmtp pjmedia_sdp_fmtp;
-
+} pjmedia_sdp_fmtp;
/**
@@ -374,6 +367,20 @@ PJ_DECL(pjmedia_sdp_conn*) pjmedia_sdp_conn_clone(pj_pool_t *pool,
const pjmedia_sdp_conn *rhs);
+/**
+ * Compare connection info.
+ *
+ * @param conn1 The first connection info to compare.
+ * @param conn1 The second connection info to compare.
+ * @param option Comparison option, which should be zero for now.
+ *
+ * @return PJ_SUCCESS when both connection info are equal, otherwise
+ * returns PJMEDIA_SDP_ECONNNOTEQUAL.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_conn_cmp(const pjmedia_sdp_conn *conn1,
+ const pjmedia_sdp_conn *conn2,
+ unsigned option);
+
/* **************************************************************************
* SDP BANDWIDTH INFO
diff --git a/pjmedia/include/pjmedia/sdp_neg.h b/pjmedia/include/pjmedia/sdp_neg.h
index bc36b19e..bc8c52cb 100644
--- a/pjmedia/include/pjmedia/sdp_neg.h
+++ b/pjmedia/include/pjmedia/sdp_neg.h
@@ -663,6 +663,95 @@ PJ_DECL(pj_status_t) pjmedia_sdp_neg_negotiate( pj_pool_t *pool,
pj_bool_t allow_asym);
+/**
+ * Enumeration of customized SDP format matching option flags. See
+ * #pjmedia_sdp_neg_register_fmt_match_cb() for more info.
+ */
+typedef enum pjmedia_sdp_neg_fmt_match_flag
+{
+ /**
+ * In generating answer, the SDP fmtp in the answer candidate may need
+ * to be modified by the customized SDP format matching callback to
+ * achieve flexible SDP negotiation, e.g: AMR fmtp 'octet-align' field
+ * can be adjusted with the offer when the codec implementation support
+ * both packetization modes octet-aligned and bandwidth-efficient.
+ */
+ PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER = 1,
+
+} pjmedia_sdp_neg_fmt_match_flag;
+
+
+/**
+ * The declaration of customized SDP format matching callback. See
+ * #pjmedia_sdp_neg_register_fmt_match_cb() for more info.
+ *
+ * @param pool The memory pool.
+ * @param offer The SDP media offer.
+ * @param o_fmt_idx Index of the format in the SDP media offer.
+ * @param answer The SDP media answer.
+ * @param a_fmt_idx Index of the format in the SDP media answer.
+ * @param option The format matching option, see
+ * #pjmedia_sdp_neg_fmt_match_flag.
+ *
+ * @return PJ_SUCCESS when the formats in offer and answer match.
+ */
+typedef pj_status_t (*pjmedia_sdp_neg_fmt_match_cb)(pj_pool_t *pool,
+ pjmedia_sdp_media *offer,
+ unsigned o_fmt_idx,
+ pjmedia_sdp_media *answer,
+ unsigned a_fmt_idx,
+ unsigned option);
+
+
+/**
+ * Register customized SDP format matching callback function for the specified
+ * format. The customized SDP format matching is needed when the format
+ * identification in a media stream session cannot be simply determined by
+ * encoding name and clock rate, but also involves one or more format specific
+ * parameters, which are specified in SDP fmtp attribute. For example,
+ * an H.264 video stream is also identified by profile, level, and
+ * packetization-mode parameters. As those parameters are format specifics,
+ * the negotiation must be done by the format or codec implementation.
+ *
+ * To unregister the callback of specific format, just call this function with
+ * parameter #cb set to NULL.
+ *
+ * @param fmt_name The format name, e.g: "H.264", "AMR", "G7221". Note
+ * that the string buffer must remain valid until the
+ * callback is unregistered.
+ * @param cb The customized SDP format negotiation callback or
+ * NULL to unregister the specified format callback.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_neg_register_fmt_match_cb(
+ const pj_str_t *fmt_name,
+ pjmedia_sdp_neg_fmt_match_cb cb);
+
+
+/**
+ * Match format in the SDP media offer and answer. The matching mechanism
+ * will be done by comparing the encoding name and clock rate, and if the
+ * custom format matching callback for the specified format is registered,
+ * see #pjmedia_sdp_neg_register_fmt_match_cb(), it will be called for more
+ * detail verification, e.g: format parameters specified in SDP fmtp.
+ *
+ * @param pool The memory pool.
+ * @param offer The SDP media offer.
+ * @param o_fmt_idx Index of the format in the SDP media offer.
+ * @param answer The SDP media answer.
+ * @param a_fmt_idx Index of the format in the SDP media answer.
+ * @param option The format matching option, see
+ * #pjmedia_sdp_neg_fmt_match_flag.
+ *
+ * @return PJ_SUCCESS when the formats in offer and answer match.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_neg_fmt_match( pj_pool_t *pool,
+ pjmedia_sdp_media *offer,
+ unsigned o_fmt_idx,
+ pjmedia_sdp_media *answer,
+ unsigned a_fmt_idx,
+ unsigned option);
PJ_END_DECL
diff --git a/pjmedia/include/pjmedia/signatures.h b/pjmedia/include/pjmedia/signatures.h
new file mode 100644
index 00000000..3d4402e9
--- /dev/null
+++ b/pjmedia/include/pjmedia/signatures.h
@@ -0,0 +1,217 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_SIGNATURES_H__
+#define __PJMEDIA_SIGNATURES_H__
+
+/**
+ * @file pjmedia/signatures.h
+ * @brief Standard PJMEDIA object signatures
+ */
+#include <pjmedia/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJMEDIA_SIG Object Signatures
+ * @ingroup PJMEDIA_BASE
+ * @brief Standard PJMEDIA object signatures
+ * @{
+ *
+ * Object signature is a 32-bit integral value similar to FOURCC to help
+ * identify PJMEDIA objects such as media ports, transports, codecs, etc.
+ * There are several uses of this signature, for example a media port can
+ * use the port object signature to verify that the given port instance
+ * is the one that it created, and a receiver of \ref PJMEDIA_EVENT can
+ * use the signature of the publisher to know which object emitted the
+ * event.
+ *
+ * The 32-bit value of an object signature is generated by the following
+ * macro:
+ *
+ * \verbatim
+ #define PJMEDIA_SIGNATURE(a,b,c,d) (a<<24 | b<<16 | c<<8 | d)
+ * \endverbatim
+ *
+ * The following convention is used to maintain order to the signature
+ * values so that application can make use of it more effectively, and to
+ * avoid conflict between the values themselves. For each object type or
+ * class, a specific prefix will be assigned as signature, and a macro
+ * is created to build a signature for such object:
+ *
+ * \verbatim
+ Class Signature Signature creation and test macros
+ ---------------------------------------------------------------
+ Codec Cxxx PJMEDIA_SIG_CLASS_CODEC(b,c,d)
+ PJMEDIA_SIG_IS_CLASS_CODEC(sig)
+
+ Audio codec CAxx PJMEDIA_SIG_CLASS_AUD_CODEC(c,d)
+ PJMEDIA_SIG_IS_CLASS_AUD_CODEC(sig)
+
+ Video codec CVxx PJMEDIA_SIG_CLASS_VID_CODEC(c,d)
+ PJMEDIA_SIG_IS_CLASS_VID_CODEC(sig)
+
+ Media port Pxxx PJMEDIA_SIG_CLASS_PORT(b,c,d)
+ PJMEDIA_SIG_IS_CLASS_PORT(sig)
+
+ Audio media port PAxx PJMEDIA_SIG_CLASS_PORT_AUD(c,d)
+ PJMEDIA_SIG_IS_CLASS_PORT_AUD(sig)
+
+ Video media port PVxx PJMEDIA_SIG_CLASS_PORT_VID(c,d)
+ PJMEDIA_SIG_IS_CLASS_PORT_VID(sig)
+
+ Video device VDxx PJMEDIA_SIG_CLASS_VID_DEV(c,d)
+ PJMEDIA_SIG_IS_CLASS_VID_DEV(sig)
+
+ Video other VOxx PJMEDIA_SIG_CLASS_VID_OTHER(c,d)
+ PJMEDIA_SIG_IS_CLASS_VID_OTHER(sig)
+
+ Application object Axxx PJMEDIA_SIG_CLASS_APP(b,c,d)
+ PJMEDIA_SIG_IS_CLASS_APP(sig)
+
+ * \endverbatim
+ *
+ * In addition, signatures created in application code should have lowercase
+ * letters to avoid conflict with built-in objects.
+ */
+
+/**
+ * Type to store object signature.
+ */
+typedef pj_uint32_t pjmedia_obj_sig;
+
+/**
+ * A utility function to convert signature to four letters string.
+ *
+ * @param sig The signature value.
+ * @param buf Buffer to store the string, which MUST be at least
+ * five bytes long.
+ *
+ * @return The string.
+ */
+PJ_INLINE(const char*) pjmedia_sig_name(pjmedia_obj_sig sig, char buf[])
+{
+ return pjmedia_fourcc_name(sig, buf);
+}
+
+/**
+ * Macro to generate signature from four ASCII letters.
+ */
+#define PJMEDIA_SIGNATURE(a,b,c,d) PJMEDIA_FOURCC(a,b,c,d)
+
+/*************************************************************************
+ * Codec signature ('Cxxx'). Please keep the constant names sorted.
+ */
+#define PJMEDIA_SIG_CLASS_CODEC(b,c,d) PJMEDIA_SIGNATURE('C',b,c,d)
+#define PJMEDIA_SIG_IS_CLASS_CODEC(sig) ((sig) >> 24 == 'C')
+
+/*************************************************************************
+ * Audio codec signatures ('CAxx'). Please keep the constant names sorted.
+ */
+#define PJMEDIA_SIG_CLASS_AUD_CODEC(c,d) PJMEDIA_SIG_CLASS_CODEC('A',c,d)
+#define PJMEDIA_SIG_IS_CLASS_AUD_CODEC(s) ((s)>>24=='C' && (s)>>16=='A')
+
+/*************************************************************************
+ * Video codec signatures ('CVxx'). Please keep the constant names sorted.
+ */
+#define PJMEDIA_SIG_CLASS_VID_CODEC(c,d) PJMEDIA_SIG_CLASS_CODEC('V',c,d)
+#define PJMEDIA_SIG_IS_CLASS_VID_CODEC(sig) ((s)>>24=='C' && (s)>>16=='V')
+
+#define PJMEDIA_SIG_VID_CODEC_FFMPEG PJMEDIA_SIG_CLASS_VID_CODEC('F','F')
+
+/*************************************************************************
+ * Port signatures ('Pxxx'). Please keep the constant names sorted.
+ */
+#define PJMEDIA_SIG_CLASS_PORT(b,c,d) PJMEDIA_SIGNATURE('P',b,c,d)
+#define PJMEDIA_SIG_IS_CLASS_PORT(sig) ((sig) >> 24 == 'P')
+
+/*************************************************************************
+ * Audio ports signatures ('PAxx'). Please keep the constant names sorted.
+ */
+#define PJMEDIA_SIG_CLASS_PORT_AUD(c,d) PJMEDIA_SIG_CLASS_PORT('A',c,d)
+#define PJMEDIA_SIG_IS_CLASS_PORT_AUD(s) ((s)>>24=='P' && (s)>>16=='A')
+
+#define PJMEDIA_SIG_PORT_BIDIR PJMEDIA_SIG_CLASS_PORT_AUD('B','D')
+#define PJMEDIA_SIG_PORT_CONF PJMEDIA_SIG_CLASS_PORT_AUD('C','F')
+#define PJMEDIA_SIG_PORT_CONF_PASV PJMEDIA_SIG_CLASS_PORT_AUD('C','P')
+#define PJMEDIA_SIG_PORT_CONF_SWITCH PJMEDIA_SIG_CLASS_PORT_AUD('C','S')
+#define PJMEDIA_SIG_PORT_ECHO PJMEDIA_SIG_CLASS_PORT_AUD('E','C')
+#define PJMEDIA_SIG_PORT_MEM_CAPTURE PJMEDIA_SIG_CLASS_PORT_AUD('M','C')
+#define PJMEDIA_SIG_PORT_MEM_PLAYER PJMEDIA_SIG_CLASS_PORT_AUD('M','P')
+#define PJMEDIA_SIG_PORT_NULL PJMEDIA_SIG_CLASS_PORT_AUD('N','U')
+#define PJMEDIA_SIG_PORT_RESAMPLE PJMEDIA_SIG_CLASS_PORT_AUD('R','E')
+#define PJMEDIA_SIG_PORT_SPLIT_COMB PJMEDIA_SIG_CLASS_PORT_AUD('S','C')
+#define PJMEDIA_SIG_PORT_SPLIT_COMB_P PJMEDIA_SIG_CLASS_PORT_AUD('S','P')
+#define PJMEDIA_SIG_PORT_STEREO PJMEDIA_SIG_CLASS_PORT_AUD('S','R')
+#define PJMEDIA_SIG_PORT_STREAM PJMEDIA_SIG_CLASS_PORT_AUD('S','T')
+#define PJMEDIA_SIG_PORT_TONEGEN PJMEDIA_SIG_CLASS_PORT_AUD('T','O')
+#define PJMEDIA_SIG_PORT_WAV_PLAYER PJMEDIA_SIG_CLASS_PORT_AUD('W','P')
+#define PJMEDIA_SIG_PORT_WAV_PLAYLIST PJMEDIA_SIG_CLASS_PORT_AUD('W','Y')
+#define PJMEDIA_SIG_PORT_WAV_WRITER PJMEDIA_SIG_CLASS_PORT_AUD('W','W')
+
+
+/*************************************************************************
+ * Video ports signatures ('PVxx'). Please keep the constant names sorted.
+ */
+#define PJMEDIA_SIG_CLASS_PORT_VID(c,d) PJMEDIA_SIG_CLASS_PORT('V',c,d)
+#define PJMEDIA_SIG_IS_CLASS_PORT_VID(s) ((s)>>24=='P' && (s)>>16=='V')
+
+/** AVI player signature. */
+#define PJMEDIA_SIG_PORT_VID_AVI_PLAYER PJMEDIA_SIG_CLASS_PORT_VID('A','V')
+#define PJMEDIA_SIG_PORT_VID_STREAM PJMEDIA_SIG_CLASS_PORT_VID('S','T')
+#define PJMEDIA_SIG_PORT_VID_TEE PJMEDIA_SIG_CLASS_PORT_VID('T','E')
+
+
+/**************************************************************************
+ * Video device signatures ('VDxx'). Please keep the constant names sorted.
+ */
+#define PJMEDIA_SIG_CLASS_VID_DEV(c,d) PJMEDIA_SIGNATURE('V','D',c,d)
+#define PJMEDIA_SIG_IS_CLASS_VID_DEV(s) ((s)>>24=='V' && (s)>>16=='D')
+
+#define PJMEDIA_SIG_VID_DEV_COLORBAR PJMEDIA_SIG_CLASS_VID_DEV('C','B')
+#define PJMEDIA_SIG_VID_DEV_SDL PJMEDIA_SIG_CLASS_VID_DEV('S','D')
+#define PJMEDIA_SIG_VID_DEV_V4L2 PJMEDIA_SIG_CLASS_VID_DEV('V','2')
+#define PJMEDIA_SIG_VID_DEV_DSHOW PJMEDIA_SIG_CLASS_VID_DEV('D','S')
+#define PJMEDIA_SIG_VID_DEV_QT PJMEDIA_SIG_CLASS_VID_DEV('Q','T')
+#define PJMEDIA_SIG_VID_DEV_IOS PJMEDIA_SIG_CLASS_VID_DEV('I','P')
+
+
+/*********************************************************************
+ * Other video objects ('VOxx'). Please keep the constant names sorted.
+ */
+#define PJMEDIA_SIG_CLASS_VID_OTHER(c,d) PJMEDIA_SIGNATURE('V','O',c,d)
+#define PJMEDIA_SIG_IS_CLASS_VID_OTHER(s) ((s)>>24=='V' && (s)>>16=='O')
+
+#define PJMEDIA_SIG_VID_PORT PJMEDIA_SIG_CLASS_VID_OTHER('P','O')
+
+
+/*********************************************************************
+ * Application class ('Axxx').
+ */
+#define PJMEDIA_SIG_CLASS_APP(b,c,d) PJMEDIA_SIGNATURE('A',b,c,d)
+#define PJMEDIA_SIG_IS_CLASS_APP(s) ((s)>>24=='A')
+
+
+/**
+ * @} PJSIP_MSG
+ */
+
+
+PJ_END_DECL
+
+#endif /* __PJMEDIA_SIGNATURES_H__ */
diff --git a/pjmedia/include/pjmedia/sound_port.h b/pjmedia/include/pjmedia/sound_port.h
index 4c47da43..2bc98d4d 100644
--- a/pjmedia/include/pjmedia/sound_port.h
+++ b/pjmedia/include/pjmedia/sound_port.h
@@ -25,6 +25,7 @@
* @brief Media port connection abstraction to sound device.
*/
#include <pjmedia-audiodev/audiodev.h>
+#include <pjmedia/clock.h>
#include <pjmedia/port.h>
PJ_BEGIN_DECL
@@ -269,6 +270,19 @@ PJ_DECL(pj_status_t) pjmedia_snd_port_get_ec_tail(pjmedia_snd_port *snd_port,
/**
+ * Get a clock source from the sound port.
+ *
+ * @param snd_port The sound port.
+ * @param dir Sound port's direction.
+ *
+ * @return The clock source.
+ */
+PJ_DECL(pjmedia_clock_src *)
+pjmedia_snd_port_get_clock_src( pjmedia_snd_port *snd_port,
+ pjmedia_dir dir );
+
+
+/**
* Connect a port to the sound device port. If the sound device port has a
* sound recorder device, then this will start periodic function call to
* the port's put_frame() function. If the sound device has a sound player
diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h
index 9e1976e9..aa8fa0bd 100644
--- a/pjmedia/include/pjmedia/stream.h
+++ b/pjmedia/include/pjmedia/stream.h
@@ -32,6 +32,7 @@
#include <pjmedia/port.h>
#include <pjmedia/rtcp.h>
#include <pjmedia/transport.h>
+#include <pjmedia/vid_codec.h>
#include <pj/sock.h>
PJ_BEGIN_DECL
@@ -87,7 +88,7 @@ typedef struct pjmedia_channel pjmedia_channel;
* corresponds to one "m=" line in SDP session descriptor, and it has
* its own RTP/RTCP socket pair.
*/
-struct pjmedia_stream_info
+typedef struct pjmedia_stream_info
{
pjmedia_type type; /**< Media type (audio, video) */
pjmedia_tp_proto proto; /**< Transport protocol (RTP/AVP, etc.) */
@@ -133,14 +134,32 @@ struct pjmedia_stream_info
(see #PJMEDIA_STREAM_ENABLE_KA)
is enabled? */
#endif
-};
+} pjmedia_stream_info;
/**
- * @see pjmedia_stream_info.
+ * This function will initialize the stream info based on information
+ * in both SDP session descriptors for the specified stream index.
+ * The remaining information will be taken from default codec parameters.
+ * If socket info array is specified, the socket will be copied to the
+ * session info as well.
+ *
+ * @param si Stream info structure to be initialized.
+ * @param pool Pool to allocate memory.
+ * @param endpt PJMEDIA endpoint instance.
+ * @param local Local SDP session descriptor.
+ * @param remote Remote SDP session descriptor.
+ * @param stream_idx Media stream index in the session descriptor.
+ *
+ * @return PJ_SUCCESS if stream info is successfully initialized.
*/
-typedef struct pjmedia_stream_info pjmedia_stream_info;
-
+PJ_DECL(pj_status_t)
+pjmedia_stream_info_from_sdp( pjmedia_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_endpt *endpt,
+ const pjmedia_sdp_session *local,
+ const pjmedia_sdp_session *remote,
+ unsigned stream_idx);
/**
@@ -231,6 +250,17 @@ PJ_DECL(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream);
/**
+ * Get the stream info.
+ *
+ * @param stream The media stream.
+ * @param info Stream info.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_stream_get_info( const pjmedia_stream *stream,
+ pjmedia_stream_info *info);
+
+/**
* Get the stream statistics. See also
* #pjmedia_stream_get_stat_jbuf()
*
@@ -371,6 +401,7 @@ pjmedia_stream_set_dtmf_callback(pjmedia_stream *stream,
int digit),
void *user_data);
+
/**
* @}
*/
diff --git a/pjmedia/include/pjmedia/stream_common.h b/pjmedia/include/pjmedia/stream_common.h
new file mode 100644
index 00000000..1c8b168d
--- /dev/null
+++ b/pjmedia/include/pjmedia/stream_common.h
@@ -0,0 +1,57 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_STREAM_COMMON_H__
+#define __PJMEDIA_STREAM_COMMON_H__
+
+
+/**
+ * @file stream_common.h
+ * @brief Stream common functions.
+ */
+
+#include <pjmedia/codec.h>
+#include <pjmedia/sdp.h>
+
+
+PJ_BEGIN_DECL
+
+/**
+ * This is internal function for parsing SDP format parameter of specific
+ * format or payload type, used by stream in generating stream info from SDP.
+ *
+ * @param pool Pool to allocate memory, if pool is NULL, the fmtp
+ * string pointers will point to the original string in
+ * the SDP media descriptor.
+ * @param m The SDP media containing the format parameter to
+ * be parsed.
+ * @param pt The format or payload type.
+ * @param fmtp The format parameter to store the parsing result.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_stream_info_parse_fmtp(pj_pool_t *pool,
+ const pjmedia_sdp_media *m,
+ unsigned pt,
+ pjmedia_codec_fmtp *fmtp);
+
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_STREAM_COMMON_H__ */
diff --git a/pjmedia/include/pjmedia/transport.h b/pjmedia/include/pjmedia/transport.h
index 9762987b..e3e664f7 100644
--- a/pjmedia/include/pjmedia/transport.h
+++ b/pjmedia/include/pjmedia/transport.h
@@ -28,6 +28,7 @@
#include <pjmedia/types.h>
#include <pjmedia/errno.h>
+#include <pj/string.h>
/**
* @defgroup PJMEDIA_TRANSPORT Media Transport
@@ -257,6 +258,35 @@ typedef enum pjmedia_tranport_media_option
/**
+ * Media socket info is used to describe the underlying sockets
+ * to be used as media transport.
+ */
+typedef struct pjmedia_sock_info
+{
+ /** The RTP socket handle */
+ pj_sock_t rtp_sock;
+
+ /** Address to be advertised as the local address for the RTP
+ * socket, which does not need to be equal as the bound
+ * address (for example, this address can be the address resolved
+ * with STUN).
+ */
+ pj_sockaddr rtp_addr_name;
+
+ /** The RTCP socket handle. */
+ pj_sock_t rtcp_sock;
+
+ /** Address to be advertised as the local address for the RTCP
+ * socket, which does not need to be equal as the bound
+ * address (for example, this address can be the address resolved
+ * with STUN).
+ */
+ pj_sockaddr rtcp_addr_name;
+
+} pjmedia_sock_info;
+
+
+/**
* This structure describes the operations for the stream transport.
*/
struct pjmedia_transport_op
@@ -453,6 +483,9 @@ struct pjmedia_transport
/** Transport's "virtual" function table. */
pjmedia_transport_op *op;
+
+ /** Application/user data */
+ void *user_data;
};
/**
diff --git a/pjmedia/include/pjmedia/transport_ice.h b/pjmedia/include/pjmedia/transport_ice.h
index 74b19976..05515dc1 100644
--- a/pjmedia/include/pjmedia/transport_ice.h
+++ b/pjmedia/include/pjmedia/transport_ice.h
@@ -181,6 +181,30 @@ PJ_DECL(pj_status_t) pjmedia_ice_create2(pjmedia_endpt *endpt,
unsigned options,
pjmedia_transport **p_tp);
+/**
+ * The same as #pjmedia_ice_create2() with additional \a user_data param.
+ *
+ * @param endpt The media endpoint.
+ * @param name Optional name to identify this ICE media transport
+ * for logging purposes.
+ * @param comp_cnt Number of components to be created.
+ * @param cfg Pointer to configuration settings.
+ * @param cb Optional structure containing ICE specific callbacks.
+ * @param options Options, see #pjmedia_transport_ice_options.
+ * @param user_data User data to be attached to the transport.
+ * @param p_tp Pointer to receive the media transport instance.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_ice_create3(pjmedia_endpt *endpt,
+ const char *name,
+ unsigned comp_cnt,
+ const pj_ice_strans_cfg *cfg,
+ const pjmedia_ice_cb *cb,
+ unsigned options,
+ void *user_data,
+ pjmedia_transport **p_tp);
+
PJ_END_DECL
diff --git a/pjmedia/include/pjmedia/types.h b/pjmedia/include/pjmedia/types.h
index f8ea125c..6a660f0c 100644
--- a/pjmedia/include/pjmedia/types.h
+++ b/pjmedia/include/pjmedia/types.h
@@ -26,8 +26,9 @@
*/
#include <pjmedia/config.h>
-#include <pj/sock.h> /* pjmedia_sock_info */
-#include <pj/string.h> /* pj_memcpy(), pj_memset() */
+#include <pj/sock.h>
+#include <pj/types.h>
+
/**
* @defgroup PJMEDIA_PORT Media Ports Framework
@@ -48,26 +49,24 @@
*/
/**
- * Top most media type.
+ * Top most media type. See also #pjmedia_type_name().
*/
typedef enum pjmedia_type
{
- /** No type. */
- PJMEDIA_TYPE_NONE = 0,
+ /** Type is not specified. */
+ PJMEDIA_TYPE_NONE,
/** The media is audio */
- PJMEDIA_TYPE_AUDIO = 1,
+ PJMEDIA_TYPE_AUDIO,
/** The media is video. */
- PJMEDIA_TYPE_VIDEO = 2,
-
- /** Unknown media type, in this case the name will be specified in
- * encoding_name.
- */
- PJMEDIA_TYPE_UNKNOWN = 3,
+ PJMEDIA_TYPE_VIDEO,
/** The media is application. */
- PJMEDIA_TYPE_APPLICATION = 4
+ PJMEDIA_TYPE_APPLICATION,
+
+ /** The media type is unknown or unsupported. */
+ PJMEDIA_TYPE_UNKNOWN
} pjmedia_type;
@@ -100,42 +99,31 @@ typedef enum pjmedia_dir
/** None */
PJMEDIA_DIR_NONE = 0,
- /** Encoding (outgoing to network) stream */
+ /** Encoding (outgoing to network) stream, also known as capture */
PJMEDIA_DIR_ENCODING = 1,
- /** Decoding (incoming from network) stream. */
- PJMEDIA_DIR_DECODING = 2,
-
- /** Incoming and outgoing stream. */
- PJMEDIA_DIR_ENCODING_DECODING = 3
+ /** Same as encoding direction. */
+ PJMEDIA_DIR_CAPTURE = PJMEDIA_DIR_ENCODING,
-} pjmedia_dir;
+ /** Decoding (incoming from network) stream, also known as playback. */
+ PJMEDIA_DIR_DECODING = 2,
+ /** Same as decoding. */
+ PJMEDIA_DIR_PLAYBACK = PJMEDIA_DIR_DECODING,
+ /** Same as decoding. */
+ PJMEDIA_DIR_RENDER = PJMEDIA_DIR_DECODING,
-/* Alternate names for media direction: */
+ /** Incoming and outgoing stream, same as PJMEDIA_DIR_CAPTURE_PLAYBACK */
+ PJMEDIA_DIR_ENCODING_DECODING = 3,
-/**
- * Direction is capturing audio frames.
- */
-#define PJMEDIA_DIR_CAPTURE PJMEDIA_DIR_ENCODING
+ /** Same as ENCODING_DECODING */
+ PJMEDIA_DIR_CAPTURE_PLAYBACK = PJMEDIA_DIR_ENCODING_DECODING,
-/**
- * Direction is playback of audio frames.
- */
-#define PJMEDIA_DIR_PLAYBACK PJMEDIA_DIR_DECODING
+ /** Same as ENCODING_DECODING */
+ PJMEDIA_DIR_CAPTURE_RENDER = PJMEDIA_DIR_ENCODING_DECODING
-/**
- * Direction is both capture and playback.
- */
-#define PJMEDIA_DIR_CAPTURE_PLAYBACK PJMEDIA_DIR_ENCODING_DECODING
-
-
-/**
- * Create 32bit port signature from ASCII characters.
- */
-#define PJMEDIA_PORT_SIGNATURE(a,b,c,d) \
- (a<<24 | b<<16 | c<<8 | d)
+} pjmedia_dir;
/**
@@ -143,384 +131,98 @@ typedef enum pjmedia_dir
*/
typedef struct pjmedia_endpt pjmedia_endpt;
-
/*
* Forward declaration for stream (needed by transport).
*/
typedef struct pjmedia_stream pjmedia_stream;
-
-/**
- * Media socket info is used to describe the underlying sockets
- * to be used as media transport.
- */
-typedef struct pjmedia_sock_info
-{
- /** The RTP socket handle */
- pj_sock_t rtp_sock;
-
- /** Address to be advertised as the local address for the RTP
- * socket, which does not need to be equal as the bound
- * address (for example, this address can be the address resolved
- * with STUN).
- */
- pj_sockaddr rtp_addr_name;
-
- /** The RTCP socket handle. */
- pj_sock_t rtcp_sock;
-
- /** Address to be advertised as the local address for the RTCP
- * socket, which does not need to be equal as the bound
- * address (for example, this address can be the address resolved
- * with STUN).
- */
- pj_sockaddr rtcp_addr_name;
-
-} pjmedia_sock_info;
-
-
/**
- * Macro for packing format.
+ * Enumeration for picture coordinate base.
*/
-#define PJMEDIA_FORMAT_PACK(C1, C2, C3, C4) ( C4<<24 | C3<<16 | C2<<8 | C1 )
-
-/**
- * This enumeration describes format ID.
- */
-typedef enum pjmedia_format_id
+typedef enum pjmedia_coord_base
{
/**
- * 16bit linear
+ * This specifies that the pixel [0, 0] location is at the left-top
+ * position.
*/
- PJMEDIA_FORMAT_L16 = 0,
-
- /**
- * Alias for PJMEDIA_FORMAT_L16
- */
- PJMEDIA_FORMAT_PCM = PJMEDIA_FORMAT_L16,
+ PJMEDIA_COORD_BASE_LEFT_TOP,
/**
- * G.711 ALAW
+ * This specifies that the pixel [0, 0] location is at the left-bottom
+ * position.
*/
- PJMEDIA_FORMAT_PCMA = PJMEDIA_FORMAT_PACK('A', 'L', 'A', 'W'),
-
- /**
- * Alias for PJMEDIA_FORMAT_PCMA
- */
- PJMEDIA_FORMAT_ALAW = PJMEDIA_FORMAT_PCMA,
-
- /**
- * G.711 ULAW
- */
- PJMEDIA_FORMAT_PCMU = PJMEDIA_FORMAT_PACK('u', 'L', 'A', 'W'),
-
- /**
- * Aliaw for PJMEDIA_FORMAT_PCMU
- */
- PJMEDIA_FORMAT_ULAW = PJMEDIA_FORMAT_PCMU,
-
- /**
- * AMR narrowband
- */
- PJMEDIA_FORMAT_AMR = PJMEDIA_FORMAT_PACK(' ', 'A', 'M', 'R'),
-
- /**
- * ITU G.729
- */
- PJMEDIA_FORMAT_G729 = PJMEDIA_FORMAT_PACK('G', '7', '2', '9'),
-
- /**
- * Internet Low Bit-Rate Codec (ILBC)
- */
- PJMEDIA_FORMAT_ILBC = PJMEDIA_FORMAT_PACK('I', 'L', 'B', 'C')
-
-} pjmedia_format_id;
+ PJMEDIA_COORD_BASE_LEFT_BOTTOM
+} pjmedia_coord_base;
/**
- * Media format information.
+ * This structure is used to represent rational numbers.
*/
-typedef struct pjmedia_format
+typedef struct pjmedia_ratio
{
- /** Format ID */
- pjmedia_format_id id;
-
- /** Bitrate. */
- pj_uint32_t bitrate;
-
- /** Flag to indicate whether VAD is enabled */
- pj_bool_t vad;
-
-} pjmedia_format;
-
-
+ int num; /** < Numerator. */
+ int denum; /** < Denumerator. */
+} pjmedia_ratio;
/**
- * This is a general purpose function set PCM samples to zero.
- * Since this function is needed by many parts of the library,
- * by putting this functionality in one place, it enables some.
- * clever people to optimize this function.
- *
- * @param samples The 16bit PCM samples.
- * @param count Number of samples.
+ * This structure represent a coordinate.
*/
-PJ_INLINE(void) pjmedia_zero_samples(pj_int16_t *samples, unsigned count)
+typedef struct pjmedia_coord
{
-#if 1
- pj_bzero(samples, (count<<1));
-#elif 0
- unsigned i;
- for (i=0; i<count; ++i) samples[i] = 0;
-#else
- unsigned i;
- count >>= 1;
- for (i=0; i<count; ++i) ((pj_int32_t*)samples)[i] = (pj_int32_t)0;
-#endif
-}
-
+ int x; /**< X position of the coordinate */
+ int y; /**< Y position of the coordinate */
+} pjmedia_coord;
/**
- * This is a general purpose function to copy samples from/to buffers with
- * equal size. Since this function is needed by many parts of the library,
- * by putting this functionality in one place, it enables some.
- * clever people to optimize this function.
+ * This structure represents rectangle size.
*/
-PJ_INLINE(void) pjmedia_copy_samples(pj_int16_t *dst, const pj_int16_t *src,
- unsigned count)
+typedef struct pjmedia_rect_size
{
-#if 1
- pj_memcpy(dst, src, (count<<1));
-#elif 0
- unsigned i;
- for (i=0; i<count; ++i) dst[i] = src[i];
-#else
- unsigned i;
- count >>= 1;
- for (i=0; i<count; ++i)
- ((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
-#endif
-}
-
+ unsigned w; /**< The width. */
+ unsigned h; /**< The height. */
+} pjmedia_rect_size;
/**
- * This is a general purpose function to copy samples from/to buffers with
- * equal size. Since this function is needed by many parts of the library,
- * by putting this functionality in one place, it enables some.
- * clever people to optimize this function.
- */
-PJ_INLINE(void) pjmedia_move_samples(pj_int16_t *dst, const pj_int16_t *src,
- unsigned count)
-{
-#if 1
- pj_memmove(dst, src, (count<<1));
-#elif 0
- unsigned i;
- for (i=0; i<count; ++i) dst[i] = src[i];
-#else
- unsigned i;
- count >>= 1;
- for (i=0; i<count; ++i)
- ((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
-#endif
-}
-
-/**
- * Types of media frame.
- */
-typedef enum pjmedia_frame_type
-{
- PJMEDIA_FRAME_TYPE_NONE, /**< No frame. */
- PJMEDIA_FRAME_TYPE_AUDIO, /**< Normal audio frame. */
- PJMEDIA_FRAME_TYPE_EXTENDED /**< Extended audio frame. */
-
-} pjmedia_frame_type;
-
-
-/**
- * This structure describes a media frame.
+ * This structure describes a rectangle.
*/
-typedef struct pjmedia_frame
+typedef struct pjmedia_rect
{
- pjmedia_frame_type type; /**< Frame type. */
- void *buf; /**< Pointer to buffer. */
- pj_size_t size; /**< Frame size in bytes. */
- pj_timestamp timestamp; /**< Frame timestamp. */
- pj_uint32_t bit_info; /**< Bit info of the frame, sample case:
- a frame may not exactly start and end
- at the octet boundary, so this field
- may be used for specifying start &
- end bit offset. */
-} pjmedia_frame;
-
-
-/**
- * The pjmedia_frame_ext is used to carry a more complex audio frames than
- * the typical PCM audio frames, and it is signaled by setting the "type"
- * field of a pjmedia_frame to PJMEDIA_FRAME_TYPE_EXTENDED. With this set,
- * application may typecast pjmedia_frame to pjmedia_frame_ext.
- *
- * This structure may contain more than one audio frames, which subsequently
- * will be called subframes in this structure. The subframes section
- * immediately follows the end of this structure, and each subframe is
- * represented by pjmedia_frame_ext_subframe structure. Every next
- * subframe immediately follows the previous subframe, and all subframes
- * are byte-aligned although its payload may not be byte-aligned.
- */
-
-#pragma pack(1)
-typedef struct pjmedia_frame_ext {
- pjmedia_frame base; /**< Base frame info */
- pj_uint16_t samples_cnt; /**< Number of samples in this frame */
- pj_uint16_t subframe_cnt; /**< Number of (sub)frames in this frame */
-
- /* Zero or more (sub)frames follows immediately after this,
- * each will be represented by pjmedia_frame_ext_subframe
- */
-} pjmedia_frame_ext;
-#pragma pack()
+ pjmedia_coord coord; /**< The position. */
+ pjmedia_rect_size size; /**< The size. */
+} pjmedia_rect;
/**
- * This structure represents the individual subframes in the
- * pjmedia_frame_ext structure.
+ * Macro for packing format from a four character code, similar to FOURCC.
*/
-#pragma pack(1)
-typedef struct pjmedia_frame_ext_subframe {
- pj_uint16_t bitlen; /**< Number of bits in the data */
- pj_uint8_t data[1]; /**< Start of encoded data */
-} pjmedia_frame_ext_subframe;
-
-#pragma pack()
+#define PJMEDIA_FOURCC(C1, C2, C3, C4) ( C4<<24 | C3<<16 | C2<<8 | C1 )
/**
- * Append one subframe to #pjmedia_frame_ext.
- *
- * @param frm The #pjmedia_frame_ext.
- * @param src Subframe data.
- * @param bitlen Lenght of subframe, in bits.
- * @param samples_cnt Number of audio samples in subframe.
- */
-PJ_INLINE(void) pjmedia_frame_ext_append_subframe(pjmedia_frame_ext *frm,
- const void *src,
- unsigned bitlen,
- unsigned samples_cnt)
-{
- pjmedia_frame_ext_subframe *fsub;
- pj_uint8_t *p;
- unsigned i;
-
- p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
- for (i = 0; i < frm->subframe_cnt; ++i) {
- fsub = (pjmedia_frame_ext_subframe*) p;
- p += sizeof(fsub->bitlen) + ((fsub->bitlen+7) >> 3);
- }
-
- fsub = (pjmedia_frame_ext_subframe*) p;
- fsub->bitlen = (pj_uint16_t)bitlen;
- if (bitlen)
- pj_memcpy(fsub->data, src, (bitlen+7) >> 3);
-
- frm->subframe_cnt++;
- frm->samples_cnt = (pj_uint16_t)(frm->samples_cnt + samples_cnt);
-}
-
-/**
- * Get a subframe from #pjmedia_frame_ext.
- *
- * @param frm The #pjmedia_frame_ext.
- * @param n Subframe index, zero based.
- *
- * @return The n-th subframe, or NULL if n is out-of-range.
- */
-PJ_INLINE(pjmedia_frame_ext_subframe*)
-pjmedia_frame_ext_get_subframe(const pjmedia_frame_ext *frm, unsigned n)
-{
- pjmedia_frame_ext_subframe *sf = NULL;
-
- if (n < frm->subframe_cnt) {
- pj_uint8_t *p;
- unsigned i;
-
- p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
- for (i = 0; i < n; ++i) {
- sf = (pjmedia_frame_ext_subframe*) p;
- p += sizeof(sf->bitlen) + ((sf->bitlen+7) >> 3);
- }
-
- sf = (pjmedia_frame_ext_subframe*) p;
- }
-
- return sf;
-}
-
-/**
- * Extract all frame payload to the specified buffer.
+ * Utility function to return the string name for a pjmedia_type.
*
- * @param frm The frame.
- * @param dst Destination buffer.
- * @param maxlen Maximum size to copy (i.e. the size of the
- * destination buffer).
+ * @param t The media type.
*
- * @return Total size of payload copied.
+ * @return String.
*/
-PJ_INLINE(unsigned)
-pjmedia_frame_ext_copy_payload(const pjmedia_frame_ext *frm,
- void *dst,
- unsigned maxlen)
-{
- unsigned i, copied=0;
- for (i=0; i<frm->subframe_cnt; ++i) {
- pjmedia_frame_ext_subframe *sf;
- unsigned sz;
-
- sf = pjmedia_frame_ext_get_subframe(frm, i);
- if (!sf)
- continue;
-
- sz = ((sf->bitlen + 7) >> 3);
- if (sz + copied > maxlen)
- break;
-
- pj_memcpy(((pj_uint8_t*)dst) + copied, sf->data, sz);
- copied += sz;
- }
- return copied;
-}
-
+PJ_DECL(const char*) pjmedia_type_name(pjmedia_type t);
/**
- * Pop out first n subframes from #pjmedia_frame_ext.
+ * A utility function to convert fourcc type of value to four letters string.
*
- * @param frm The #pjmedia_frame_ext.
- * @param n Number of first subframes to be popped out.
+ * @param sig The fourcc value.
+ * @param buf Buffer to store the string, which MUST be at least
+ * five bytes long.
*
- * @return PJ_SUCCESS when successful.
+ * @return The string.
*/
-PJ_INLINE(pj_status_t)
-pjmedia_frame_ext_pop_subframes(pjmedia_frame_ext *frm, unsigned n)
+PJ_INLINE(const char*) pjmedia_fourcc_name(pj_uint32_t sig, char buf[])
{
- pjmedia_frame_ext_subframe *sf;
- pj_uint8_t *move_src;
- unsigned move_len;
-
- if (frm->subframe_cnt <= n) {
- frm->subframe_cnt = 0;
- frm->samples_cnt = 0;
- return PJ_SUCCESS;
- }
-
- move_src = (pj_uint8_t*)pjmedia_frame_ext_get_subframe(frm, n);
- sf = pjmedia_frame_ext_get_subframe(frm, frm->subframe_cnt-1);
- move_len = (pj_uint8_t*)sf - move_src + sizeof(sf->bitlen) +
- ((sf->bitlen+7) >> 3);
- pj_memmove((pj_uint8_t*)frm+sizeof(pjmedia_frame_ext),
- move_src, move_len);
-
- frm->samples_cnt = (pj_uint16_t)
- (frm->samples_cnt - n*frm->samples_cnt/frm->subframe_cnt);
- frm->subframe_cnt = (pj_uint16_t) (frm->subframe_cnt - n);
-
- return PJ_SUCCESS;
+ buf[3] = (char)((sig >> 24) & 0xFF);
+ buf[2] = (char)((sig >> 16) & 0xFF);
+ buf[1] = (char)((sig >> 8) & 0xFF);
+ buf[0] = (char)((sig >> 0) & 0xFF);
+ buf[4] = '\0';
+ return buf;
}
diff --git a/pjmedia/include/pjmedia/vid_codec.h b/pjmedia/include/pjmedia/vid_codec.h
new file mode 100644
index 00000000..40adcdcd
--- /dev/null
+++ b/pjmedia/include/pjmedia/vid_codec.h
@@ -0,0 +1,978 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_VID_CODEC_H__
+#define __PJMEDIA_VID_CODEC_H__
+
+
+/**
+ * @file vid_codec.h
+ * @brief Video codec framework.
+ */
+
+#include <pjmedia/codec.h>
+#include <pjmedia/event.h>
+#include <pjmedia/format.h>
+#include <pjmedia/types.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+
+PJ_BEGIN_DECL
+
+#define PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT 8
+#define PJMEDIA_VID_CODEC_MAX_FPS_CNT 16
+
+/**
+ * Identification used to search for codec factory that supports specific
+ * codec specification.
+ */
+typedef struct pjmedia_vid_codec_info
+{
+ pjmedia_format_id fmt_id; /**< Encoded format ID */
+ unsigned pt; /**< Payload type */
+ pj_str_t encoding_name; /**< Encoding name */
+ pj_str_t encoding_desc; /**< Encoding desc */
+ unsigned clock_rate; /**< Clock rate */
+ pjmedia_dir dir; /**< Direction */
+ unsigned dec_fmt_id_cnt; /**< # of supported encoding source
+ format IDs */
+ pjmedia_format_id dec_fmt_id[PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT];
+ /**< Supported encoding source
+ format IDs */
+ unsigned fps_cnt; /**< # of supported frame-rates, can be
+ zero (support any frame-rate) */
+ pjmedia_ratio fps[PJMEDIA_VID_CODEC_MAX_FPS_CNT];
+ /**< Supported frame-rates */
+ pj_bool_t has_rtp_pack; /**< Support RTP packetization */
+} pjmedia_vid_codec_info;
+
+
+/**
+ * Detailed codec attributes used in configuring a codec and in querying
+ * the capability of codec factories. Default attributes of any codecs could
+ * be queried using #pjmedia_vid_codec_mgr_get_default_param() and modified
+ * using #pjmedia_vid_codec_mgr_set_default_param().
+ *
+ * Please note that codec parameter also contains SDP specific setting,
+ * #dec_fmtp and #enc_fmtp, which may need to be set appropriately based on
+ * the effective setting. See each codec documentation for more detail.
+ */
+typedef struct pjmedia_vid_codec_param
+{
+ pjmedia_dir dir; /**< Direction */
+ pjmedia_format enc_fmt; /**< Encoded format */
+ pjmedia_format dec_fmt; /**< Decoded format */
+
+ pjmedia_codec_fmtp enc_fmtp; /**< Encoder fmtp params */
+ pjmedia_codec_fmtp dec_fmtp; /**< Decoder fmtp params */
+
+ unsigned enc_mtu; /**< MTU or max payload size setting*/
+} pjmedia_vid_codec_param;
+
+
+/**
+ * Duplicate video codec parameter.
+ *
+ * @param pool The pool.
+ * @param src The video codec parameter to be duplicated.
+ *
+ * @return Duplicated codec parameter.
+ */
+PJ_DECL(pjmedia_vid_codec_param*) pjmedia_vid_codec_param_clone(
+ pj_pool_t *pool,
+ const pjmedia_vid_codec_param *src);
+
+/**
+ * Forward declaration for video codec.
+ */
+typedef struct pjmedia_vid_codec pjmedia_vid_codec;
+
+
+/**
+ * This structure describes codec operations. Each codec MUST implement
+ * all of these functions.
+ */
+typedef struct pjmedia_vid_codec_op
+{
+ /**
+ * Initialize codec using the specified attribute.
+ *
+ * Application should call #pjmedia_vid_codec_init() instead of
+ * calling this function directly.
+ *
+ * @param codec The codec instance.
+ * @param pool Pool to use when the codec needs to allocate
+ * some memory.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+ pj_status_t (*init)(pjmedia_vid_codec *codec,
+ pj_pool_t *pool );
+
+ /**
+ * Open the codec and initialize with the specified parameter.
+ * Upon successful initialization, the codec may modify the parameter
+ * and fills in the unspecified values (such as size or frame rate of
+ * the encoder format, as it may need to be negotiated with remote
+ * preferences via SDP fmtp).
+ *
+ * Application should call #pjmedia_vid_codec_open() instead of
+ * calling this function directly.
+ *
+ * @param codec The codec instance.
+ * @param param Codec initialization parameter.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+ pj_status_t (*open)(pjmedia_vid_codec *codec,
+ pjmedia_vid_codec_param *param );
+
+ /**
+ * Close and shutdown codec, releasing all resources allocated by
+ * this codec, if any.
+ *
+ * Application should call #pjmedia_vid_codec_close() instead of
+ * calling this function directly.
+ *
+ * @param codec The codec instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+ pj_status_t (*close)(pjmedia_vid_codec *codec);
+
+ /**
+ * Modify the codec parameter after the codec is open.
+ * Note that not all codec parameters can be modified during run-time.
+ * When the parameter cannot be changed, this function will return
+ * non-PJ_SUCCESS, and the original parameters will not be changed.
+ *
+ * Application should call #pjmedia_vid_codec_modify() instead of
+ * calling this function directly.
+ *
+ * @param codec The codec instance.
+ * @param param The new codec parameter.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+ pj_status_t (*modify)(pjmedia_vid_codec *codec,
+ const pjmedia_vid_codec_param *param);
+
+ /**
+ * Get the codec parameter after the codec is opened.
+ *
+ * Application should call #pjmedia_vid_codec_get_param() instead of
+ * calling this function directly.
+ *
+ * @param codec The codec instance.
+ * @param param The codec parameter.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+ pj_status_t (*get_param)(pjmedia_vid_codec *codec,
+ pjmedia_vid_codec_param *param);
+
+ /**
+ * Instruct the codec to generate a payload/packet from a picture
+ * bitstream to be sent (via network). The maximum payload size or
+ * MTU is configurable via enc_mtu field of #pjmedia_vid_codec_param.
+ * For a long bitstream, application usually need to call this function
+ * multiple times until the whole bitstream is sent. Note that, for
+ * performance reason, the packetization will be done in-place, so the
+ * original bitstream may be modified by this function.
+ *
+ * Application should call #pjmedia_vid_codec_packetize() instead of
+ * calling this function directly.
+ *
+ * @param codec The codec instance
+ * @param bits The picture bitstream.
+ * @param bits_len The length of the bitstream.
+ * @param bits_pos On input, the start position of the bitstream
+ * to be packetized. On output, the next position for
+ * next packet.
+ * @param pkt The pointer of the generated payload.
+ * @param pkt_len The payload length.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+ pj_status_t (*packetize) (pjmedia_vid_codec *codec,
+ pj_uint8_t *bits,
+ pj_size_t bits_len,
+ unsigned *bits_pos,
+ const pj_uint8_t **pkt,
+ pj_size_t *pkt_len);
+
+ /**
+ * Instruct the codec to parse a payload and append it into a picture
+ * bitstream. A picture bitstreams may need to be reconstructed from
+ * one or more payloads. Note that this function will not provide the
+ * detection of picture boundary, so application should manage the
+ * picture boundary detection by itself, e.g: for RTP delivery, payloads
+ * belong to the same picture will share the same RTP timestamp and also
+ * there is marker bit in the RTP header that is usually reserved for
+ * end-of-picture flag. Also note that in case of noticing packet lost,
+ * application should keep calling this function with payload pointer
+ * set to NULL, as the packetizer need to update its internal state.
+ *
+ * Application should call #pjmedia_vid_codec_unpacketize() instead of
+ * calling this function directly.
+ *
+ * @param codec The codec instance
+ * @param pkt The input packet.
+ * @param pkt_size Size of the packet.
+ * @param timestamp The timestamp of the first sample in the packet.
+ * @param frame_cnt On input, specifies the maximum number of frames
+ * in the array. On output, the codec must fill
+ * with number of frames detected in the packet.
+ * @param frames On output, specifies the frames that have been
+ * detected in the packet.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+ pj_status_t (*unpacketize)(pjmedia_vid_codec *codec,
+ const pj_uint8_t *payload,
+ pj_size_t payload_len,
+ pj_uint8_t *bits,
+ pj_size_t bits_len,
+ unsigned *bits_pos);
+
+ /**
+ * Instruct the codec to encode the specified input frame. The input
+ * MUST contain only one picture with appropriate format as specified
+ * in opening the codec.
+ *
+ * Application should call #pjmedia_vid_codec_encode() instead of
+ * calling this function directly.
+ *
+ * @param codec The codec instance.
+ * @param input The input frame.
+ * @param out_size The length of buffer in the output frame.
+ * @param output The output frame.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+ pj_status_t (*encode)(pjmedia_vid_codec *codec,
+ const pjmedia_frame *input,
+ unsigned out_size,
+ pjmedia_frame *output);
+
+ /**
+ * Instruct the codec to decode the specified input frame. The input
+ * frame MUST contain exactly one picture. Note that the decoded picture
+ * format may different to the current setting, e.g: the format specified
+ * in the #pjmedia_vid_codec_param when opening the codec, in this case the
+ * PJMEDIA_EVENT_FMT_CHANGED event will be emitted by the codec. The codec
+ * parameter will also be updated, and application can query the format by
+ * using #get_param().
+ *
+ * Application should call #pjmedia_vid_codec_decode() instead of
+ * calling this function directly.
+ *
+ * @param codec The codec instance.
+ * @param input The input frame.
+ * @param out_size The length of buffer in the output frame.
+ * @param output The output frame.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+ pj_status_t (*decode)(pjmedia_vid_codec *codec,
+ const pjmedia_frame *input,
+ unsigned out_size,
+ pjmedia_frame *output);
+
+ /**
+ * Instruct the codec to recover a missing frame.
+ *
+ * Application should call #pjmedia_vid_codec_recover() instead of
+ * calling this function directly.
+ *
+ * @param codec The codec instance.
+ * @param out_size The length of buffer in the output frame.
+ * @param output The output frame where generated signal
+ * will be placed.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+ pj_status_t (*recover)(pjmedia_vid_codec *codec,
+ unsigned out_size,
+ pjmedia_frame *output);
+
+} pjmedia_vid_codec_op;
+
+
+
+/*
+ * Forward declaration for pjmedia_vid_codec_factory.
+ */
+typedef struct pjmedia_vid_codec_factory pjmedia_vid_codec_factory;
+
+
+/**
+ * This structure describes a video codec instance. Codec implementers
+ * should use #pjmedia_vid_codec_init() to initialize this structure with
+ * default values.
+ */
+struct pjmedia_vid_codec
+{
+ /** Entries to put this codec instance in codec factory's list. */
+ PJ_DECL_LIST_MEMBER(struct pjmedia_vid_codec);
+
+ /** Codec's private data. */
+ void *codec_data;
+
+ /** Codec factory where this codec was allocated. */
+ pjmedia_vid_codec_factory *factory;
+
+ /** Operations to codec. */
+ pjmedia_vid_codec_op *op;
+
+ /** Event publisher object */
+ pjmedia_event_publisher epub;
+};
+
+
+
+/**
+ * This structure describes operations that must be supported by codec
+ * factories.
+ */
+typedef struct pjmedia_vid_codec_factory_op
+{
+ /**
+ * Check whether the factory can create codec with the specified
+ * codec info.
+ *
+ * @param factory The codec factory.
+ * @param info The codec info.
+ *
+ * @return PJ_SUCCESS if this factory is able to create an
+ * instance of codec with the specified info.
+ */
+ pj_status_t (*test_alloc)(pjmedia_vid_codec_factory *factory,
+ const pjmedia_vid_codec_info *info );
+
+ /**
+ * Create default attributes for the specified codec ID. This function
+ * can be called by application to get the capability of the codec.
+ *
+ * @param factory The codec factory.
+ * @param info The codec info.
+ * @param attr The attribute to be initialized.
+ *
+ * @return PJ_SUCCESS if success.
+ */
+ pj_status_t (*default_attr)(pjmedia_vid_codec_factory *factory,
+ const pjmedia_vid_codec_info *info,
+ pjmedia_vid_codec_param *attr );
+
+ /**
+ * Enumerate supported codecs that can be created using this factory.
+ *
+ * @param factory The codec factory.
+ * @param count On input, specifies the number of elements in
+ * the array. On output, the value will be set to
+ * the number of elements that have been initialized
+ * by this function.
+ * @param info The codec info array, which contents will be
+ * initialized upon return.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+ pj_status_t (*enum_info)(pjmedia_vid_codec_factory *factory,
+ unsigned *count,
+ pjmedia_vid_codec_info codecs[]);
+
+ /**
+ * Create one instance of the codec with the specified codec info.
+ *
+ * @param factory The codec factory.
+ * @param info The codec info.
+ * @param p_codec Pointer to receive the codec instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+ pj_status_t (*alloc_codec)(pjmedia_vid_codec_factory *factory,
+ const pjmedia_vid_codec_info *info,
+ pjmedia_vid_codec **p_codec);
+
+ /**
+ * This function is called by codec manager to return a particular
+ * instance of codec back to the codec factory.
+ *
+ * @param factory The codec factory.
+ * @param codec The codec instance to be returned.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+ pj_status_t (*dealloc_codec)(pjmedia_vid_codec_factory *factory,
+ pjmedia_vid_codec *codec );
+
+} pjmedia_vid_codec_factory_op;
+
+
+
+/**
+ * Codec factory describes a module that is able to create codec with specific
+ * capabilities. These capabilities can be queried by codec manager to create
+ * instances of codec.
+ */
+struct pjmedia_vid_codec_factory
+{
+ /** Entries to put this structure in the codec manager list. */
+ PJ_DECL_LIST_MEMBER(struct pjmedia_vid_codec_factory);
+
+ /** The factory's private data. */
+ void *factory_data;
+
+ /** Operations to the factory. */
+ pjmedia_vid_codec_factory_op *op;
+
+};
+
+
+/**
+ * Opaque declaration for codec manager.
+ */
+typedef struct pjmedia_vid_codec_mgr pjmedia_vid_codec_mgr;
+
+/**
+ * Declare maximum codecs
+ */
+#define PJMEDIA_VID_CODEC_MGR_MAX_CODECS 32
+
+
+/**
+ * Initialize pjmedia_vid_codec structure with default values.
+ *
+ * @param codec The codec to be initialized.
+ * @param sig Codec's object signature (see signatures.h)
+ */
+PJ_DECL(void) pjmedia_vid_codec_reset(pjmedia_vid_codec *codec,
+ pjmedia_obj_sig sig);
+
+/**
+ * Initialize codec manager. If there is no the default video codec manager,
+ * this function will automatically set the default video codec manager to
+ * the new codec manager instance. Normally this function is called by pjmedia
+ * endpoint's initialization code.
+ *
+ * @param pool The pool instance.
+ * @param mgr The pointer to the new codec manager instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_create(pj_pool_t *pool,
+ pjmedia_vid_codec_mgr **mgr);
+
+
+/**
+ * Destroy codec manager. Normally this function is called by pjmedia
+ * endpoint's deinitialization code.
+ *
+ * @param mgr Codec manager instance. If NULL, it is the default codec
+ * manager instance will be destroyed.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_destroy(pjmedia_vid_codec_mgr *mgr);
+
+
+/**
+ * Get the default codec manager instance.
+ *
+ * @return The default codec manager instance or NULL if none.
+ */
+PJ_DECL(pjmedia_vid_codec_mgr*) pjmedia_vid_codec_mgr_instance(void);
+
+
+/**
+ * Set the default codec manager instance.
+ *
+ * @param mgr The codec manager instance.
+ */
+PJ_DECL(void) pjmedia_vid_codec_mgr_set_instance(pjmedia_vid_codec_mgr* mgr);
+
+
+/**
+ * Register codec factory to codec manager. This will also register
+ * all supported codecs in the factory to the codec manager.
+ *
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
+ * @param factory The codec factory to be registered.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_codec_mgr_register_factory( pjmedia_vid_codec_mgr *mgr,
+ pjmedia_vid_codec_factory *factory);
+
+/**
+ * Unregister codec factory from the codec manager. This will also
+ * remove all the codecs registered by the codec factory from the
+ * codec manager's list of supported codecs.
+ *
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
+ * @param factory The codec factory to be unregistered.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_codec_mgr_unregister_factory( pjmedia_vid_codec_mgr *mgr,
+ pjmedia_vid_codec_factory *factory);
+
+/**
+ * Enumerate all supported codecs that have been registered to the
+ * codec manager by codec factories.
+ *
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
+ * @param count On input, specifies the number of elements in
+ * the array. On output, the value will be set to
+ * the number of elements that have been initialized
+ * by this function.
+ * @param info The codec info array, which contents will be
+ * initialized upon return.
+ * @param prio Optional pointer to receive array of codec priorities.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_enum_codecs(
+ pjmedia_vid_codec_mgr *mgr,
+ unsigned *count,
+ pjmedia_vid_codec_info info[],
+ unsigned *prio);
+
+
+/**
+ * Get codec info for the specified payload type. The payload type must be
+ * static or locally defined in #pjmedia_video_pt.
+ *
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
+ * @param pt The payload type/number.
+ * @param info Pointer to receive codec info.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_codec_mgr_get_codec_info( pjmedia_vid_codec_mgr *mgr,
+ unsigned pt,
+ const pjmedia_vid_codec_info **info);
+
+
+/**
+ * Get codec info for the specified format ID.
+ *
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
+ * @param fmt_id Format ID. See #pjmedia_format_id
+ * @param info Pointer to receive codec info.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_codec_mgr_get_codec_info2(pjmedia_vid_codec_mgr *mgr,
+ pjmedia_format_id fmt_id,
+ const pjmedia_vid_codec_info **info);
+
+
+/**
+ * Convert codec info struct into a unique codec identifier.
+ * A codec identifier looks something like "H263/90000".
+ *
+ * @param info The codec info
+ * @param id Buffer to put the codec info string.
+ * @param max_len The length of the buffer.
+ *
+ * @return The null terminated codec info string, or NULL if
+ * the buffer is not long enough.
+ */
+PJ_DECL(char*) pjmedia_vid_codec_info_to_id(
+ const pjmedia_vid_codec_info *info,
+ char *id, unsigned max_len );
+
+
+/**
+ * Find codecs by the unique codec identifier. This function will find
+ * all codecs that match the codec identifier prefix. For example, if
+ * "H26" is specified, then it will find "H263/90000", "H264/90000",
+ * and so on, up to the maximum count specified in the argument.
+ *
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
+ * @param codec_id The full codec ID or codec ID prefix. If an empty
+ * string is given, it will match all codecs.
+ * @param count Maximum number of codecs to find. On return, it
+ * contains the actual number of codecs found.
+ * @param p_info Array of pointer to codec info to be filled. This
+ * argument may be NULL, which in this case, only
+ * codec count will be returned.
+ * @param prio Optional array of codec priorities.
+ *
+ * @return PJ_SUCCESS if at least one codec info is found.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_codec_mgr_find_codecs_by_id(pjmedia_vid_codec_mgr *mgr,
+ const pj_str_t *codec_id,
+ unsigned *count,
+ const pjmedia_vid_codec_info *p_info[],
+ unsigned prio[]);
+
+
+/**
+ * Set codec priority. The codec priority determines the order of
+ * the codec in the SDP created by the endpoint. If more than one codecs
+ * are found with the same codec_id prefix, then the function sets the
+ * priorities of all those codecs.
+ *
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
+ * @param codec_id The full codec ID or codec ID prefix. If an empty
+ * string is given, it will match all codecs.
+ * @param prio Priority to be set. The priority can have any value
+ * between 1 to 255. When the priority is set to zero,
+ * the codec will be disabled.
+ *
+ * @return PJ_SUCCESS if at least one codec info is found.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_codec_mgr_set_codec_priority(pjmedia_vid_codec_mgr *mgr,
+ const pj_str_t *codec_id,
+ pj_uint8_t prio);
+
+
+/**
+ * Get default codec param for the specified codec info.
+ *
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
+ * @param info The codec info, which default parameter's is being
+ * queried.
+ * @param param On return, will be filled with the default codec
+ * parameter.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_codec_mgr_get_default_param(pjmedia_vid_codec_mgr *mgr,
+ const pjmedia_vid_codec_info *info,
+ pjmedia_vid_codec_param *param);
+
+
+/**
+ * Set default codec param for the specified codec info.
+ *
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
+ * @param pool The pool instance.
+ * @param info The codec info, which default parameter's is being
+ * updated.
+ * @param param The new default codec parameter. Set to NULL to reset
+ * codec parameter to library default settings.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_codec_mgr_set_default_param(pjmedia_vid_codec_mgr *mgr,
+ pj_pool_t *pool,
+ const pjmedia_vid_codec_info *info,
+ const pjmedia_vid_codec_param *param);
+
+
+/**
+ * Request the codec manager to allocate one instance of codec with the
+ * specified codec info. The codec will enumerate all codec factories
+ * until it finds factory that is able to create the specified codec.
+ *
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
+ * @param info The information about the codec to be created.
+ * @param p_codec Pointer to receive the codec instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_codec_mgr_alloc_codec( pjmedia_vid_codec_mgr *mgr,
+ const pjmedia_vid_codec_info *info,
+ pjmedia_vid_codec **p_codec);
+
+/**
+ * Deallocate the specified codec instance. The codec manager will return
+ * the instance of the codec back to its factory.
+ *
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
+ * @param codec The codec instance.
+ *
+ * @return PJ_SUCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_dealloc_codec(
+ pjmedia_vid_codec_mgr *mgr,
+ pjmedia_vid_codec *codec);
+
+
+
+/**
+ * Initialize codec using the specified attribute.
+ *
+ * @param codec The codec instance.
+ * @param pool Pool to use when the codec needs to allocate
+ * some memory.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_INLINE(pj_status_t) pjmedia_vid_codec_init( pjmedia_vid_codec *codec,
+ pj_pool_t *pool )
+{
+ return (*codec->op->init)(codec, pool);
+}
+
+
+/**
+ * Open the codec and initialize with the specified parameter.
+ * Upon successful initialization, the codec may modify the parameter
+ * and fills in the unspecified values (such as size or frame rate of
+ * the encoder format, as it may need to be negotiated with remote
+ * preferences via SDP fmtp).
+ *
+ * @param codec The codec instance.
+ * @param param Codec initialization parameter.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_INLINE(pj_status_t) pjmedia_vid_codec_open(
+ pjmedia_vid_codec *codec,
+ pjmedia_vid_codec_param *param )
+{
+ return (*codec->op->open)(codec, param);
+}
+
+
+/**
+ * Close and shutdown codec, releasing all resources allocated by
+ * this codec, if any.
+ *
+ * @param codec The codec instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_INLINE(pj_status_t) pjmedia_vid_codec_close( pjmedia_vid_codec *codec )
+{
+ return (*codec->op->close)(codec);
+}
+
+
+/**
+ * Modify the codec parameter after the codec is open.
+ * Note that not all codec parameters can be modified during run-time.
+ * When the parameter cannot be changed, this function will return
+ * non-PJ_SUCCESS, and the original parameters will not be changed.
+ *
+ * @param codec The codec instance.
+ * @param param The new codec parameter.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_INLINE(pj_status_t) pjmedia_vid_codec_modify(
+ pjmedia_vid_codec *codec,
+ const pjmedia_vid_codec_param *param)
+{
+ return (*codec->op->modify)(codec, param);
+}
+
+
+/**
+ * Get the codec parameter after the codec is opened.
+ *
+ * @param codec The codec instance.
+ * @param param The codec parameter.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_INLINE(pj_status_t) pjmedia_vid_codec_get_param(
+ pjmedia_vid_codec *codec,
+ pjmedia_vid_codec_param *param)
+{
+ return (*codec->op->get_param)(codec, param);
+}
+
+
+/**
+ * Instruct the codec to generate a payload/packet from a picture
+ * bitstream to be sent (via network). The maximum payload size or
+ * MTU is configurable via enc_mtu field of #pjmedia_vid_codec_param.
+ * For a long bitstream, application usually need to call this function
+ * multiple times until the whole bitstream is sent. Note that, for
+ * performance reason, the packetization will be done in-place, so the
+ * original bitstream may be modified by this function.
+ *
+ * @param codec The codec instance
+ * @param bits The picture bitstream.
+ * @param bits_len The length of the bitstream.
+ * @param bits_pos On input, the start position of the bitstream
+ * to be packetized. On output, the next position for
+ * next packet.
+ * @param pkt The pointer of the generated payload.
+ * @param pkt_len The payload length.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_INLINE(pj_status_t) pjmedia_vid_codec_packetize(
+ pjmedia_vid_codec *codec,
+ pj_uint8_t *bits,
+ pj_size_t bits_len,
+ unsigned *bits_pos,
+ const pj_uint8_t **pkt,
+ pj_size_t *pkt_len )
+{
+ return (*codec->op->packetize)(codec, bits, bits_len, bits_pos,
+ pkt, pkt_len);
+}
+
+
+/**
+ * Instruct the codec to parse a payload and append it into a picture
+ * bitstream. A picture bitstreams may need to be reconstructed from
+ * one or more payloads. Note that this function will not provide the
+ * detection of picture boundary, so application should manage the
+ * picture boundary detection by itself, e.g: for RTP delivery, payloads
+ * belong to the same picture will share the same RTP timestamp and also
+ * there is marker bit in the RTP header that is usually reserved for
+ * end-of-picture flag. Also note that in case of noticing packet lost,
+ * application should keep calling this function with payload pointer
+ * set to NULL, as the packetizer need to update its internal state.
+ *
+ * @param codec The codec instance
+ * @param pkt The input packet.
+ * @param pkt_size Size of the packet.
+ * @param timestamp The timestamp of the first sample in the packet.
+ * @param frame_cnt On input, specifies the maximum number of frames
+ * in the array. On output, the codec must fill
+ * with number of frames detected in the packet.
+ * @param frames On output, specifies the frames that have been
+ * detected in the packet.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_INLINE(pj_status_t) pjmedia_vid_codec_unpacketize(
+ pjmedia_vid_codec *codec,
+ const pj_uint8_t *payload,
+ pj_size_t payload_len,
+ pj_uint8_t *bits,
+ pj_size_t bits_len,
+ unsigned *bits_pos )
+{
+ return (*codec->op->unpacketize)(codec, payload, payload_len, bits,
+ bits_len, bits_pos);
+}
+
+
+/**
+ * Instruct the codec to encode the specified input frame. The input
+ * MUST contain only one picture with appropriate format as specified
+ * in opening the codec.
+ *
+ * @param codec The codec instance.
+ * @param input The input frame.
+ * @param out_size The length of buffer in the output frame.
+ * @param output The output frame.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+PJ_INLINE(pj_status_t) pjmedia_vid_codec_encode(
+ pjmedia_vid_codec *codec,
+ const pjmedia_frame *input,
+ unsigned out_size,
+ pjmedia_frame *output)
+{
+ return (*codec->op->encode)(codec, input, out_size, output);
+}
+
+
+/**
+ * Instruct the codec to decode the specified input frame. The input
+ * frame MUST contain exactly one picture. Note that the decoded picture
+ * format may different to the current setting, e.g: the format specified
+ * in the #pjmedia_vid_codec_param when opening the codec, in this case the
+ * PJMEDIA_EVENT_FMT_CHANGED event will be emitted by the codec. The codec
+ * parameter will also be updated, and application can query the format by
+ * using #get_param().
+ *
+ * @param codec The codec instance.
+ * @param input The input frame.
+ * @param out_size The length of buffer in the output frame.
+ * @param output The output frame.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+PJ_INLINE(pj_status_t) pjmedia_vid_codec_decode(
+ pjmedia_vid_codec *codec,
+ const pjmedia_frame *input,
+ unsigned out_size,
+ pjmedia_frame *output)
+{
+ return (*codec->op->decode)(codec, input, out_size, output);
+}
+
+
+/**
+ * Instruct the codec to recover a missing frame.
+ *
+ * @param codec The codec instance.
+ * @param out_size The length of buffer in the output frame.
+ * @param output The output frame where generated signal
+ * will be placed.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+PJ_INLINE(pj_status_t) pjmedia_vid_codec_recover(
+ pjmedia_vid_codec *codec,
+ unsigned out_size,
+ pjmedia_frame *output)
+{
+ if (codec->op && codec->op->recover)
+ return (*codec->op->recover)(codec, out_size, output);
+ else
+ return PJ_ENOTSUP;
+}
+
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup PJMEDIA_CODEC_VID_CODECS Supported video codecs
+ * @ingroup PJMEDIA_CODEC
+ * @brief Documentation about individual video codec supported by PJMEDIA
+ * @{
+ * Please see the APIs provided by the individual codecs below.
+ */
+/**
+ * @}
+ */
+
+
+
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_VID_CODEC_H__ */
diff --git a/pjmedia/include/pjmedia/vid_codec_util.h b/pjmedia/include/pjmedia/vid_codec_util.h
new file mode 100644
index 00000000..e34440a4
--- /dev/null
+++ b/pjmedia/include/pjmedia/vid_codec_util.h
@@ -0,0 +1,158 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_VID_CODEC_UTIL_H__
+#define __PJMEDIA_VID_CODEC_UTIL_H__
+
+
+/**
+ * @file vid_codec_util.h
+ * @brief Video codec utilities.
+ */
+
+#include <pjmedia/vid_codec.h>
+#include <pjmedia/sdp_neg.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Definition of H.263 parameters.
+ */
+typedef struct pjmedia_vid_codec_h263_fmtp
+{
+ unsigned mpi_cnt; /**< # of parsed MPI param */
+ struct mpi {
+ pjmedia_rect_size size; /**< Picture size/resolution */
+ unsigned val; /**< MPI value */
+ } mpi[32]; /**< Minimum Picture Interval parameter */
+
+} pjmedia_vid_codec_h263_fmtp;
+
+
+/**
+ * Parse SDP fmtp of H.263.
+ *
+ * @param fmtp The H.263 SDP fmtp to be parsed.
+ * @param h263_fmtp The parsing result.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_h263_parse_fmtp(
+ const pjmedia_codec_fmtp *fmtp,
+ pjmedia_vid_codec_h263_fmtp *h263_fmtp);
+
+
+/**
+ * Parse, negotiate, and apply the encoding and decoding SDP fmtp of H.263
+ * in the specified codec parameter.
+ *
+ * @param param The codec parameter.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_h263_apply_fmtp(
+ pjmedia_vid_codec_param *param);
+
+
+/**
+ * Definition of H.264 parameters.
+ */
+typedef struct pjmedia_vid_codec_h264_fmtp
+{
+ /* profile-level-id */
+ pj_uint8_t profile_idc; /**< Profile ID */
+ pj_uint8_t profile_iop; /**< Profile constraints bits */
+ pj_uint8_t level; /**< Level */
+
+ /* packetization-mode */
+ pj_uint8_t packetization_mode; /**< Packetization mode */
+
+ /* max-mbps, max-fs, max-cpb, max-dpb, and max-br */
+ unsigned max_mbps; /**< Max macroblock processing rate */
+ unsigned max_fs; /**< Max frame size (in macroblocks) */
+ unsigned max_cpb; /**< Max coded picture buffer size */
+ unsigned max_dpb; /**< Max decoded picture buffer size */
+ unsigned max_br; /**< Max video bit rate */
+
+ /* sprop-parameter-sets, in NAL units */
+ pj_size_t sprop_param_sets_len; /**< Parameter set length */
+ pj_uint8_t sprop_param_sets[256]; /**< Parameter set (SPS & PPS),
+ in NAL unit bitstream */
+
+} pjmedia_vid_codec_h264_fmtp;
+
+
+/**
+ * Parse SDP fmtp of H.264.
+ *
+ * @param fmtp The H.264 SDP fmtp to be parsed.
+ * @param h264_fmtp The parsing result.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp(
+ const pjmedia_codec_fmtp *fmtp,
+ pjmedia_vid_codec_h264_fmtp *h264_fmtp);
+
+
+/**
+ * Match H.264 format in the SDP media offer and answer. This will compare
+ * H.264 identifier parameters in SDP fmtp, i.e: "profile-level-id" and
+ * "packetization-mode" fields. For better interoperability, when the option
+ * #PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER is set, this function
+ * may update the answer so the parameters in the answer match to ones
+ * in the offer.
+ *
+ * @param pool The memory pool.
+ * @param offer The SDP media offer.
+ * @param o_fmt_idx Index of the H.264 format in the SDP media offer.
+ * @param answer The SDP media answer.
+ * @param a_fmt_idx Index of the H.264 format in the SDP media answer.
+ * @param option The format matching option, see
+ * #pjmedia_sdp_neg_fmt_match_flag.
+ *
+ * @return PJ_SUCCESS when the formats in offer and answer match.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_h264_match_sdp(
+ pj_pool_t *pool,
+ pjmedia_sdp_media *offer,
+ unsigned o_fmt_idx,
+ pjmedia_sdp_media *answer,
+ unsigned a_fmt_idx,
+ unsigned option);
+
+
+/**
+ * Parse and apply the encoding and decoding SDP fmtp of H.264 in the
+ * specified codec parameter. This will validate size and fps to conform
+ * to H.264 level specified in SDP fmtp "profile-level-id".
+ *
+ * @param param The codec parameter.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp(
+ pjmedia_vid_codec_param *param);
+
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_VID_CODEC_UTIL_H__ */
diff --git a/pjmedia/include/pjmedia/vid_port.h b/pjmedia/include/pjmedia/vid_port.h
new file mode 100644
index 00000000..692a4f9d
--- /dev/null
+++ b/pjmedia/include/pjmedia/vid_port.h
@@ -0,0 +1,241 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_VIDPORT_H__
+#define __PJMEDIA_VIDPORT_H__
+
+/**
+ * @file pjmedia/videoport.h Video media port
+ * @brief Video media port
+ */
+
+#include <pjmedia-videodev/videodev.h>
+#include <pjmedia/port.h>
+
+/**
+ * @defgroup PJMEDIA_VIDEO_PORT Video media port
+ * @ingroup PJMEDIA_PORT_CLOCK
+ * @brief Video media port
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+/**
+ * This structure describes the parameters to create a video port
+ */
+typedef struct pjmedia_vid_port_param
+{
+ /**
+ * Video stream parameter.
+ */
+ pjmedia_vid_dev_param vidparam;
+
+ /**
+ * Specify whether the video port should use active or passive interface.
+ * If active interface is selected, the video port will perform as
+ * a media clock, automatically calls pjmedia_port_get_frame() and
+ * pjmedia_port_put_frame() of its slave port (depending on the direction
+ * that is specified when opening the video stream). If passive interface
+ * is selected, application can retrieve the media port of this video
+ * port by calling pjmedia_vid_port_get_passive_port(), and subsequently
+ * calls pjmedia_port_put_frame() or pjmedia_port_get_frame() to that
+ * media port.
+ *
+ * Default: PJ_TRUE
+ */
+ pj_bool_t active;
+
+} pjmedia_vid_port_param;
+
+/**
+ * Opaque data type for video port.
+ */
+typedef struct pjmedia_vid_port pjmedia_vid_port;
+
+/**
+ * Initialize the parameter with the default values. Note that this typically
+ * would only fill the structure to zeroes unless they have different default
+ * values.
+ *
+ * @param prm The parameter.
+ */
+PJ_DECL(void) pjmedia_vid_port_param_default(pjmedia_vid_port_param *prm);
+
+/**
+ * Create a video port with the specified parameter.
+ *
+ * @param pool Pool to allocate memory from.
+ * @param prm The video port parameter.
+ * @param p_vp Pointer to receive the result.
+ *
+ * @return PJ_SUCCESS if video port has been created
+ * successfully, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_port_create(pj_pool_t *pool,
+ const pjmedia_vid_port_param *prm,
+ pjmedia_vid_port **p_vp);
+
+/**
+ * Set the callbacks of the video port's underlying video stream.
+ *
+ * @param vid_port The video port.
+ * @param cb Pointer to structure containing video stream
+ * callbacks.
+ * @param user_data Arbitrary user data, which will be given back in the
+ * callbacks.
+ */
+PJ_DECL(void) pjmedia_vid_port_set_cb(pjmedia_vid_port *vid_port,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data);
+
+/**
+ * Get the event publisher instance of the video port.
+ *
+ * @param vid_port The video port.
+ *
+ * @return The event publisher of the video port.
+ */
+PJ_DECL(pjmedia_event_publisher*)
+pjmedia_vid_port_get_event_publisher(pjmedia_vid_port *vid_port);
+
+/**
+ * Return the underlying video stream of the video port.
+ *
+ * @param vid_port The video port.
+ *
+ * @return The video stream.
+ */
+PJ_DECL(pjmedia_vid_dev_stream*)
+pjmedia_vid_port_get_stream(pjmedia_vid_port *vid_port);
+
+/**
+ * Return the (passive) media port of the video port. This operation
+ * is only valid for video ports created with passive interface selected.
+ * Retrieving the media port for active video ports may raise an
+ * assertion.
+ *
+ * @param vid_port The video port.
+ *
+ * @return The media port instance, or NULL.
+ */
+PJ_DECL(pjmedia_port*)
+pjmedia_vid_port_get_passive_port(pjmedia_vid_port *vid_port);
+
+/**
+ * Get a clock source from the video port.
+ *
+ * @param vid_port The video port.
+ *
+ * @return The clock source.
+ */
+PJ_DECL(pjmedia_clock_src *)
+pjmedia_vid_port_get_clock_src( pjmedia_vid_port *vid_port );
+
+/**
+ * Set a clock source for the video port.
+ *
+ * @param vid_port The video port.
+ * @param clocksrc The clock source.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_port_set_clock_src( pjmedia_vid_port *vid_port,
+ pjmedia_clock_src *clocksrc );
+
+/**
+ * Connect the video port to a downstream (slave) media port. This operation
+ * is only valid for video ports created with active interface selected.
+ * Connecting a passive video port may raise an assertion.
+ *
+ * @param vid_port The video port.
+ * @param port A downstream media port to be connected to
+ * this video port.
+ * @param destroy Specify if the downstream media port should also be
+ * destroyed by this video port when the video port
+ * is destroyed.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_port_connect(pjmedia_vid_port *vid_port,
+ pjmedia_port *port,
+ pj_bool_t destroy);
+
+/**
+ * Disconnect the video port from its downstream (slave) media port, if any.
+ * This operation is only valid for video ports created with active interface
+ * selected, and assertion may be triggered if this is invoked on a passive
+ * video port.
+ *
+ * @param vid_port The video port.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_port_disconnect(pjmedia_vid_port *vid_port);
+
+/**
+ * Retrieve the media port currently connected as downstream media port of the
+ * specified video port. This operation is only valid for video ports created
+ * with active interface selected, and assertion may be triggered if this is
+ * invoked on a passive video port.
+ *
+ * @param vid_port The video port.
+ *
+ * @return Media port currently connected to the video port,
+ * if any.
+ */
+PJ_DECL(pjmedia_port*)
+pjmedia_vid_port_get_connected_port(pjmedia_vid_port *vid_port);
+
+/**
+ * Start the video port.
+ *
+ * @param vid_port The video port.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_port_start(pjmedia_vid_port *vid_port);
+
+/**
+ * Stop the video port.
+ *
+ * @param vid_port The video port.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_port_stop(pjmedia_vid_port *vid_port);
+
+/**
+ * Destroy the video port, along with its video stream. If the video port is
+ * an active one, this may also destroy the downstream media port, if the
+ * destroy flag is set when the media port is connected.
+ *
+ * @param vid_port The video port.
+ */
+PJ_DECL(void) pjmedia_vid_port_destroy(pjmedia_vid_port *vid_port);
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif /* __PJMEDIA_VIDPORT_H__ */
+
diff --git a/pjmedia/include/pjmedia/vid_stream.h b/pjmedia/include/pjmedia/vid_stream.h
new file mode 100644
index 00000000..83df1167
--- /dev/null
+++ b/pjmedia/include/pjmedia/vid_stream.h
@@ -0,0 +1,319 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_VID_STREAM_H__
+#define __PJMEDIA_VID_STREAM_H__
+
+
+/**
+ * @file vid_stream.h
+ * @brief Video Stream.
+ */
+
+#include <pjmedia/endpoint.h>
+#include <pjmedia/jbuf.h>
+#include <pjmedia/port.h>
+#include <pjmedia/rtcp.h>
+#include <pjmedia/transport.h>
+#include <pjmedia/vid_codec.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_VID_STRM Video streams
+ * @ingroup PJMEDIA_PORT
+ * @brief Video communication via the network
+ * @{
+ *
+ * A video stream is a bidirectional video communication between two
+ * endpoints. It corresponds to a video media description ("m=video" line)
+ * in SDP session descriptor.
+ *
+ * A video stream consists of two unidirectional channels:
+ * - encoding channel, which transmits unidirectional video to remote, and
+ * - decoding channel, which receives unidirectional media from remote.
+ *
+ * A video stream exports two media port interface (see @ref PJMEDIA_PORT),
+ * one for each direction, and application normally uses this interface to
+ * interconnect the stream to other PJMEDIA components, e.g: the video
+ * capture port supplies frames to the encoding port and video renderer
+ * consumes frames from the decoding port.
+ *
+ * A video stream internally manages the following objects:
+ * - an instance of video codec (see @ref PJMEDIA_VID_CODEC),
+ * - an @ref PJMED_JBUF,
+ * - two instances of RTP sessions (#pjmedia_rtp_session, one for each
+ * direction),
+ * - one instance of RTCP session (#pjmedia_rtcp_session),
+ * - and a reference to video transport to send and receive packets
+ * to/from the network (see @ref PJMEDIA_TRANSPORT).
+ *
+ * Video streams are created by calling #pjmedia_vid_stream_create(),
+ * specifying #pjmedia_stream_info structure in the parameter. Application
+ * can construct the #pjmedia_vid_stream_info structure manually, or use
+ * #pjmedia_vid_stream_info_from_sdp() function to construct the
+ * #pjmedia_vid stream_info from local and remote SDP session descriptors.
+ */
+
+
+/**
+ * This structure describes video stream information. Each video stream
+ * corresponds to one "m=" line in SDP session descriptor, and it has
+ * its own RTP/RTCP socket pair.
+ */
+typedef struct pjmedia_vid_stream_info
+{
+ pjmedia_type type; /**< Media type (audio, video) */
+ pjmedia_tp_proto proto; /**< Transport protocol (RTP/AVP, etc.) */
+ pjmedia_dir dir; /**< Media direction. */
+ pj_sockaddr rem_addr; /**< Remote RTP address */
+ pj_sockaddr rem_rtcp; /**< Optional remote RTCP address. If
+ sin_family is zero, the RTP address
+ will be calculated from RTP. */
+ unsigned tx_pt; /**< Outgoing codec paylaod type. */
+ unsigned rx_pt; /**< Incoming codec paylaod type. */
+ pj_uint32_t ssrc; /**< RTP SSRC. */
+ pj_uint32_t rtp_ts; /**< Initial RTP timestamp. */
+ pj_uint16_t rtp_seq; /**< Initial RTP sequence number. */
+ pj_uint8_t rtp_seq_ts_set;
+ /**< Bitmask flags if initial RTP sequence
+ and/or timestamp for sender are set.
+ bit 0/LSB : sequence flag
+ bit 1 : timestamp flag */
+ int jb_init; /**< Jitter buffer init delay in msec.
+ (-1 for default). */
+ int jb_min_pre; /**< Jitter buffer minimum prefetch
+ delay in msec (-1 for default). */
+ int jb_max_pre; /**< Jitter buffer maximum prefetch
+ delay in msec (-1 for default). */
+ int jb_max; /**< Jitter buffer max delay in msec. */
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+ pj_bool_t use_ka; /**< Stream keep-alive and NAT hole punch
+ (see #PJMEDIA_STREAM_ENABLE_KA)
+ is enabled? */
+#endif
+
+ pjmedia_vid_codec_info codec_info; /**< Incoming codec format info. */
+ pjmedia_vid_codec_param *codec_param; /**< Optional codec param. */
+
+} pjmedia_vid_stream_info;
+
+
+/**
+ * This function will initialize the video stream info based on information
+ * in both SDP session descriptors for the specified stream index.
+ * The remaining information will be taken from default codec parameters.
+ * If socket info array is specified, the socket will be copied to the
+ * session info as well.
+ *
+ * @param si Stream info structure to be initialized.
+ * @param pool Pool to allocate memory.
+ * @param endpt PJMEDIA endpoint instance.
+ * @param local Local SDP session descriptor.
+ * @param remote Remote SDP session descriptor.
+ * @param stream_idx Media stream index in the session descriptor.
+ *
+ * @return PJ_SUCCESS if stream info is successfully initialized.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_stream_info_from_sdp(pjmedia_vid_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_endpt *endpt,
+ const pjmedia_sdp_session *local,
+ const pjmedia_sdp_session *remote,
+ unsigned stream_idx);
+
+
+/*
+ * Opaque declaration for video stream.
+ */
+typedef struct pjmedia_vid_stream pjmedia_vid_stream;
+
+
+/**
+ * Create a video stream based on the specified parameter. After the video
+ * stream has been created, application normally would want to get the media
+ * port interface of the stream, by calling pjmedia_vid_stream_get_port().
+ * The media port interface exports put_frame() and get_frame() function,
+ * used to transmit and receive media frames from the stream.
+ *
+ * Without application calling put_frame() and get_frame(), there will be
+ * no media frames transmitted or received by the stream.
+ *
+ * @param endpt Media endpoint.
+ * @param pool Optional pool to allocate memory for the stream. If
+ * this is not specified, one will be created internally.
+ * A large number of memory may be needed because jitter
+ * buffer needs to preallocate some storage.
+ * @param info Stream information to create the stream. Upon return,
+ * this info will be updated with the information from
+ * the instantiated codec. Note that if the "pool"
+ * argument is NULL, some fields in this "info" parameter
+ * will be allocated from the internal pool of the
+ * stream, which means that they will only remain valid
+ * as long as the stream is not destroyed.
+ * @param tp Media transport instance used to transmit and receive
+ * RTP/RTCP packets to/from the underlying network.
+ * @param user_data Arbitrary user data (for future callback feature).
+ * @param p_stream Pointer to receive the video stream.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_create(
+ pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ pjmedia_vid_stream_info *info,
+ pjmedia_transport *tp,
+ void *user_data,
+ pjmedia_vid_stream **p_stream);
+
+/**
+ * Destroy the video stream.
+ *
+ * @param stream The video stream.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_destroy(pjmedia_vid_stream *stream);
+
+
+/**
+ * Get the media port interface of the stream. The media port interface
+ * declares put_frame() and get_frame() function, which is the only
+ * way for application to transmit and receive media frames from the
+ * stream. As bidirectional video streaming may have different video
+ * formats in the encoding and decoding direction, there are two media
+ * ports exported by the video stream, one for each direction.
+ *
+ * @param stream The video stream.
+ * @param dir The video direction.
+ * @param p_port Pointer to receive the port interface.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_get_port(
+ pjmedia_vid_stream *stream,
+ pjmedia_dir dir,
+ pjmedia_port **p_port);
+
+
+/**
+ * Get the media transport object associated with this stream.
+ *
+ * @param st The video stream.
+ *
+ * @return The transport object being used by the stream.
+ */
+PJ_DECL(pjmedia_transport*) pjmedia_vid_stream_get_transport(
+ pjmedia_vid_stream *st);
+
+
+/**
+ * Get the stream statistics. See also #pjmedia_stream_get_stat_jbuf()
+ *
+ * @param stream The video stream.
+ * @param stat Media stream statistics.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_get_stat(
+ const pjmedia_vid_stream *stream,
+ pjmedia_rtcp_stat *stat);
+
+/**
+ * Reset the video stream statistics.
+ *
+ * @param stream The video stream.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_reset_stat(pjmedia_vid_stream *stream);
+
+
+/**
+ * Get current jitter buffer state. See also #pjmedia_stream_get_stat()
+ *
+ * @param stream The video stream.
+ * @param state Jitter buffer state.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_get_stat_jbuf(
+ const pjmedia_vid_stream *stream,
+ pjmedia_jb_state *state);
+
+
+/**
+ * Get the stream info.
+ *
+ * @param stream The video stream.
+ * @param info Video stream info.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_get_info(
+ const pjmedia_vid_stream *stream,
+ pjmedia_vid_stream_info *info);
+
+
+/**
+ * Start the video stream. This will start the appropriate channels
+ * in the video stream, depending on the video direction that was set
+ * when the stream was created.
+ *
+ * @param stream The video stream.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream);
+
+
+/**
+ * Pause the individual channel in the stream.
+ *
+ * @param stream The video channel.
+ * @param dir Which direction to pause.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream,
+ pjmedia_dir dir);
+
+/**
+ * Resume the individual channel in the stream.
+ *
+ * @param stream The video channel.
+ * @param dir Which direction to resume.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream,
+ pjmedia_dir dir);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_VID_STREAM_H__ */
diff --git a/pjmedia/include/pjmedia/vid_tee.h b/pjmedia/include/pjmedia/vid_tee.h
new file mode 100644
index 00000000..d9f67796
--- /dev/null
+++ b/pjmedia/include/pjmedia/vid_tee.h
@@ -0,0 +1,142 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_VID_TEE_H__
+#define __PJMEDIA_VID_TEE_H__
+
+/**
+ * @file vid_tee.h
+ * @brief Video tee (source duplicator).
+ */
+#include <pjmedia/port.h>
+
+/**
+ * @addtogroup PJMEDIA_VID_TEE Video source duplicator
+ * @ingroup PJMEDIA_PORT
+ * @brief Duplicate video data from a media port into multiple media port
+ * destinations
+ * @{
+ *
+ * This section describes media port to duplicate video data in the stream.
+ *
+ * A video tee branches video stream flow from one source port to multiple
+ * destination ports by simply duplicating the video data supplied by the
+ * source port and delivering the copy to all registered destinations.
+ *
+ * The video tee is a unidirectional port, i.e: data flows from source port
+ * to destination ports only. Also, the video source port MUST actively call
+ * pjmedia_port_put_frame() to the video tee and the video destination ports
+ * MUST NEVER call pjmedia_port_get_frame() to the video tee. Please note that
+ * there is no specific order of which destination port will receive a frame
+ * from the video tee.
+ *
+ * The video tee is not thread-safe, so it is application responsibility
+ * to synchronize video tee operations, e.g: make sure the source port is
+ * paused during adding or removing a destination port.
+ */
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Enumeration of video tee flags.
+ */
+typedef enum pjmedia_vid_tee_flag
+{
+ /**
+ * Tell the video tee that the destination port will do in-place
+ * processing, so the delivered data may be modified by this port.
+ * If this flag is used, buffer will be copied before being given to
+ * the destination port.
+ */
+ PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC = 4,
+
+} pjmedia_vid_tee_flag;
+
+
+/**
+ * Create a video tee port with the specified source media port. Application
+ * should destroy the tee with pjmedia_port_destroy() as usual. Note that
+ * destroying the tee does not destroy its destination ports.
+ *
+ * @param pool The pool.
+ * @param fmt The source media port's format.
+ * @param max_dst_cnt The maximum number of destination ports supported.
+ * @param p_vid_tee Pointer to receive the video tee port.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate
+ * error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_tee_create(pj_pool_t *pool,
+ const pjmedia_format *fmt,
+ unsigned max_dst_cnt,
+ pjmedia_port **p_vid_tee);
+
+/**
+ * Add a destination media port to the video tee. For this function, the
+ * destination port's media format must match the source format.
+ *
+ * @param vid_tee The video tee.
+ * @param option Video tee option, see @pjmedia_vid_tee_flag.
+ * @param port The destination media port.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error
+ * code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_tee_add_dst_port(pjmedia_port *vid_tee,
+ unsigned option,
+ pjmedia_port *port);
+
+
+/**
+ * Add a destination media port to the video tee. This function will also
+ * create a converter if the destination port's media format does not match
+ * the source format.
+ *
+ * @param vid_tee The video tee.
+ * @param option Video tee option, see @pjmedia_vid_tee_flag.
+ * @param port The destination media port.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error
+ * code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_tee_add_dst_port2(pjmedia_port *vid_tee,
+ unsigned option,
+ pjmedia_port *port);
+
+
+/**
+ * Remove a destination media port from the video tee.
+ *
+ * @param vid_tee The video tee.
+ * @param port The destination media port to be removed.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error
+ * code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_tee_remove_dst_port(pjmedia_port *vid_tee,
+ pjmedia_port *port);
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif /* __PJMEDIA_VID_TEE_H__ */
diff --git a/pjmedia/include/pjmedia_videodev.h b/pjmedia/include/pjmedia_videodev.h
new file mode 100644
index 00000000..cf22e61f
--- /dev/null
+++ b/pjmedia/include/pjmedia_videodev.h
@@ -0,0 +1,30 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_VIDEODEV_H__
+#define __PJMEDIA_VIDEODEV_H__
+
+/**
+ * @file pjmedia_videodev.h
+ * @brief PJMEDIA main header file.
+ */
+
+#include <pjmedia-videodev/videodev.h>
+#include <pjmedia-videodev/videodev_imp.h>
+
+#endif /* __PJMEDIA_VIDEODEV_H__ */
diff --git a/pjmedia/src/pjmedia-audiodev/audiodev.c b/pjmedia/src/pjmedia-audiodev/audiodev.c
index cb1ebbe2..437ee584 100644
--- a/pjmedia/src/pjmedia-audiodev/audiodev.c
+++ b/pjmedia/src/pjmedia-audiodev/audiodev.c
@@ -145,7 +145,7 @@ PJ_DEF(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,
break;
}
- if (i==32) {
+ if (i==PJ_ARRAY_SIZE(cap_infos)) {
*p_desc = "??";
return "??";
}
@@ -190,9 +190,11 @@ static pj_status_t get_cap_pointer(const pjmedia_aud_param *param,
case PJMEDIA_AUD_DEV_CAP_EC_TAIL:
FIELD_INFO(ec_tail_ms);
break;
+ /* vad is no longer in "fmt" in 2.0.
case PJMEDIA_AUD_DEV_CAP_VAD:
FIELD_INFO(ext_fmt.vad);
break;
+ */
case PJMEDIA_AUD_DEV_CAP_CNG:
FIELD_INFO(cng_enabled);
break;
diff --git a/pjmedia/src/pjmedia-audiodev/wmme_dev.c b/pjmedia/src/pjmedia-audiodev/wmme_dev.c
index 9c7d4452..3a010226 100644
--- a/pjmedia/src/pjmedia-audiodev/wmme_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/wmme_dev.c
@@ -372,12 +372,12 @@ static void build_dev_info(UINT deviceId, struct wmme_dev_info *wdi,
/* Extended formats */
wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_EXT_FORMAT;
wdi->info.ext_fmt_cnt = 2;
- wdi->info.ext_fmt[0].id = PJMEDIA_FORMAT_PCMU;
- wdi->info.ext_fmt[0].bitrate = 64000;
- wdi->info.ext_fmt[0].vad = 0;
- wdi->info.ext_fmt[1].id = PJMEDIA_FORMAT_PCMA;
- wdi->info.ext_fmt[1].bitrate = 64000;
- wdi->info.ext_fmt[1].vad = 0;
+ pjmedia_format_init_audio(&wdi->info.ext_fmt[0],
+ PJMEDIA_FORMAT_PCMU, 8000, 1, 8,
+ 20000, 64000, 64000);
+ pjmedia_format_init_audio(&wdi->info.ext_fmt[0],
+ PJMEDIA_FORMAT_PCMA, 8000, 1, 8,
+ 20000, 64000, 64000);
}
/* API: init factory */
diff --git a/pjmedia/src/pjmedia-codec/audio_codecs.c b/pjmedia/src/pjmedia-codec/audio_codecs.c
new file mode 100644
index 00000000..a9e0700a
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/audio_codecs.c
@@ -0,0 +1,112 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-codec.h>
+#include <pjmedia/g711.h>
+
+PJ_DEF(void) pjmedia_audio_codec_config_default(pjmedia_audio_codec_config*cfg)
+{
+ pj_bzero(cfg, sizeof(*cfg));
+ cfg->speex.option = 0;
+ cfg->speex.quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY;
+ cfg->speex.complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY;
+ cfg->ilbc.mode = 30;
+ cfg->passthrough.setting.ilbc_mode = cfg->ilbc.mode;
+}
+
+PJ_DEF(pj_status_t)
+pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt,
+ const pjmedia_audio_codec_config *c)
+{
+ pjmedia_audio_codec_config default_cfg;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
+ if (!c) {
+ pjmedia_audio_codec_config_default(&default_cfg);
+ c = &default_cfg;
+ }
+
+ PJ_ASSERT_RETURN(c->ilbc.mode==20 || c->ilbc.mode==30, PJ_EINVAL);
+
+#if PJMEDIA_HAS_PASSTHROUGH_CODECS
+ status = pjmedia_codec_passthrough_init2(endpt, &c->passthough.ilbc);
+ if (status != PJ_SUCCESS)
+ return status;
+#endif
+
+#if PJMEDIA_HAS_SPEEX_CODEC
+ /* Register speex. */
+ status = pjmedia_codec_speex_init(endpt, c->speex.option,
+ c->speex.quality,
+ c->speex.complexity);
+ if (status != PJ_SUCCESS)
+ return status;
+#endif
+
+#if PJMEDIA_HAS_ILBC_CODEC
+ /* Register iLBC. */
+ status = pjmedia_codec_ilbc_init( endpt, c->ilbc.mode);
+ if (status != PJ_SUCCESS)
+ return status;
+#endif /* PJMEDIA_HAS_ILBC_CODEC */
+
+#if PJMEDIA_HAS_GSM_CODEC
+ /* Register GSM */
+ status = pjmedia_codec_gsm_init(endpt);
+ if (status != PJ_SUCCESS)
+ return status;
+#endif /* PJMEDIA_HAS_GSM_CODEC */
+
+#if PJMEDIA_HAS_G711_CODEC
+ /* Register PCMA and PCMU */
+ status = pjmedia_codec_g711_init(endpt);
+ if (status != PJ_SUCCESS)
+ return status;
+#endif /* PJMEDIA_HAS_G711_CODEC */
+
+#if PJMEDIA_HAS_G722_CODEC
+ status = pjmedia_codec_g722_init(endpt );
+ if (status != PJ_SUCCESS)
+ return status;
+#endif /* PJMEDIA_HAS_G722_CODEC */
+
+#if PJMEDIA_HAS_INTEL_IPP
+ /* Register IPP codecs */
+ status = pjmedia_codec_ipp_init(endpt);
+ if (status != PJ_SUCCESS)
+ return status;
+#endif /* PJMEDIA_HAS_INTEL_IPP */
+
+#if PJMEDIA_HAS_G7221_CODEC
+ /* Register G722.1 codecs */
+ status = pjmedia_codec_g7221_init(endpt);
+ if (status != PJ_SUCCESS)
+ return status;
+#endif /* PJMEDIA_HAS_G7221_CODEC */
+
+#if PJMEDIA_HAS_L16_CODEC
+ /* Register L16 family codecs */
+ status = pjmedia_codec_l16_init(endpt, 0);
+ if (status != PJ_SUCCESS)
+ return status;
+#endif /* PJMEDIA_HAS_L16_CODEC */
+
+ return PJ_SUCCESS;
+}
+
diff --git a/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c b/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
new file mode 100644
index 00000000..ddf02c1a
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
@@ -0,0 +1,1492 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-codec/ffmpeg_codecs.h>
+#include <pjmedia-codec/h263_packetizer.h>
+#include <pjmedia-codec/h264_packetizer.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/vid_codec_util.h>
+#include <pj/assert.h>
+#include <pj/list.h>
+#include <pj/log.h>
+#include <pj/math.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+
+/*
+ * Only build this file if PJMEDIA_HAS_FFMPEG_CODEC != 0
+ */
+#if defined(PJMEDIA_HAS_FFMPEG_CODEC) && PJMEDIA_HAS_FFMPEG_CODEC != 0
+
+#define THIS_FILE "ffmpeg_codecs.c"
+
+#include "../pjmedia/ffmpeg_util.h"
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+
+
+/* Prototypes for FFMPEG codecs factory */
+static pj_status_t ffmpeg_test_alloc( pjmedia_vid_codec_factory *factory,
+ const pjmedia_vid_codec_info *id );
+static pj_status_t ffmpeg_default_attr( pjmedia_vid_codec_factory *factory,
+ const pjmedia_vid_codec_info *info,
+ pjmedia_vid_codec_param *attr );
+static pj_status_t ffmpeg_enum_codecs( pjmedia_vid_codec_factory *factory,
+ unsigned *count,
+ pjmedia_vid_codec_info codecs[]);
+static pj_status_t ffmpeg_alloc_codec( pjmedia_vid_codec_factory *factory,
+ const pjmedia_vid_codec_info *info,
+ pjmedia_vid_codec **p_codec);
+static pj_status_t ffmpeg_dealloc_codec( pjmedia_vid_codec_factory *factory,
+ pjmedia_vid_codec *codec );
+
+/* Prototypes for FFMPEG codecs implementation. */
+static pj_status_t ffmpeg_codec_init( pjmedia_vid_codec *codec,
+ pj_pool_t *pool );
+static pj_status_t ffmpeg_codec_open( pjmedia_vid_codec *codec,
+ pjmedia_vid_codec_param *attr );
+static pj_status_t ffmpeg_codec_close( pjmedia_vid_codec *codec );
+static pj_status_t ffmpeg_codec_modify(pjmedia_vid_codec *codec,
+ const pjmedia_vid_codec_param *attr );
+static pj_status_t ffmpeg_codec_get_param(pjmedia_vid_codec *codec,
+ pjmedia_vid_codec_param *param);
+static pj_status_t ffmpeg_packetize ( pjmedia_vid_codec *codec,
+ pj_uint8_t *buf,
+ pj_size_t buf_len,
+ unsigned *pos,
+ const pj_uint8_t **payload,
+ pj_size_t *payload_len);
+static pj_status_t ffmpeg_unpacketize(pjmedia_vid_codec *codec,
+ const pj_uint8_t *payload,
+ pj_size_t payload_len,
+ pj_uint8_t *buf,
+ pj_size_t buf_len,
+ unsigned *pos);
+static pj_status_t ffmpeg_codec_encode( pjmedia_vid_codec *codec,
+ const pjmedia_frame *input,
+ unsigned output_buf_len,
+ pjmedia_frame *output);
+static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec,
+ const pjmedia_frame *input,
+ unsigned output_buf_len,
+ pjmedia_frame *output);
+static pj_status_t ffmpeg_codec_recover( pjmedia_vid_codec *codec,
+ unsigned output_buf_len,
+ pjmedia_frame *output);
+
+/* Definition for FFMPEG codecs operations. */
+static pjmedia_vid_codec_op ffmpeg_op =
+{
+ &ffmpeg_codec_init,
+ &ffmpeg_codec_open,
+ &ffmpeg_codec_close,
+ &ffmpeg_codec_modify,
+ &ffmpeg_codec_get_param,
+ &ffmpeg_packetize,
+ &ffmpeg_unpacketize,
+ &ffmpeg_codec_encode,
+ &ffmpeg_codec_decode,
+ NULL //&ffmpeg_codec_recover
+};
+
+/* Definition for FFMPEG codecs factory operations. */
+static pjmedia_vid_codec_factory_op ffmpeg_factory_op =
+{
+ &ffmpeg_test_alloc,
+ &ffmpeg_default_attr,
+ &ffmpeg_enum_codecs,
+ &ffmpeg_alloc_codec,
+ &ffmpeg_dealloc_codec
+};
+
+
+/* FFMPEG codecs factory */
+static struct ffmpeg_factory {
+ pjmedia_vid_codec_factory base;
+ pjmedia_vid_codec_mgr *mgr;
+ pj_pool_factory *pf;
+ pj_pool_t *pool;
+ pj_mutex_t *mutex;
+} ffmpeg_factory;
+
+
+typedef struct ffmpeg_codec_desc ffmpeg_codec_desc;
+
+
+/* FFMPEG codecs private data. */
+typedef struct ffmpeg_private
+{
+ const ffmpeg_codec_desc *desc;
+ pjmedia_vid_codec_param param; /**< Codec param */
+ pj_pool_t *pool; /**< Pool for each instance */
+ pj_timestamp last_tx; /**< Timestamp of last
+ transmit */
+
+ /* Format info and apply format param */
+ const pjmedia_video_format_info *enc_vfi;
+ pjmedia_video_apply_fmt_param enc_vafp;
+ const pjmedia_video_format_info *dec_vfi;
+ pjmedia_video_apply_fmt_param dec_vafp;
+
+ /* The ffmpeg codec states. */
+ AVCodec *enc;
+ AVCodec *dec;
+ AVCodecContext *enc_ctx;
+ AVCodecContext *dec_ctx;
+
+ /* The ffmpeg decoder cannot set the output format, so format conversion
+ * may be needed for post-decoding.
+ */
+ enum PixelFormat expected_dec_fmt;
+ /**< Expected output format of
+ ffmpeg decoder */
+
+ void *data; /**< Codec specific data */
+} ffmpeg_private;
+
+
+/* Shortcuts for packetize & unpacketize function declaration,
+ * as it has long params and is reused many times!
+ */
+#define FUNC_PACKETIZE(name) \
+ pj_status_t(name)(ffmpeg_private *ff, pj_uint8_t *bits, \
+ pj_size_t bits_len, unsigned *bits_pos, \
+ const pj_uint8_t **payload, pj_size_t *payload_len)
+
+#define FUNC_UNPACKETIZE(name) \
+ pj_status_t(name)(ffmpeg_private *ff, const pj_uint8_t *payload, \
+ pj_size_t payload_len, pj_uint8_t *bits, \
+ pj_size_t bits_len, unsigned *bits_pos)
+
+#define FUNC_FMT_MATCH(name) \
+ pj_status_t(name)(pj_pool_t *pool, \
+ pjmedia_sdp_media *offer, unsigned o_fmt_idx, \
+ pjmedia_sdp_media *answer, unsigned a_fmt_idx, \
+ unsigned option)
+
+
+/* Type definition of codec specific functions */
+typedef FUNC_PACKETIZE(*func_packetize);
+typedef FUNC_UNPACKETIZE(*func_unpacketize);
+typedef pj_status_t (*func_preopen) (ffmpeg_private *ff);
+typedef pj_status_t (*func_postopen) (ffmpeg_private *ff);
+typedef FUNC_FMT_MATCH(*func_sdp_fmt_match);
+
+
+/* FFMPEG codec info */
+struct ffmpeg_codec_desc
+{
+ /* Predefined info */
+ pjmedia_vid_codec_info info;
+ pjmedia_format_id base_fmt_id; /**< Some codecs may be exactly
+ same or compatible with
+ another codec, base format
+ will tell the initializer
+ to copy this codec desc
+ from its base format */
+ pj_uint32_t avg_bps;
+ pj_uint32_t max_bps;
+ func_packetize packetize;
+ func_unpacketize unpacketize;
+ func_preopen preopen;
+ func_preopen postopen;
+ func_sdp_fmt_match sdp_fmt_match;
+ pjmedia_codec_fmtp dec_fmtp;
+
+ /* Init time defined info */
+ pj_bool_t enabled;
+ AVCodec *enc;
+ AVCodec *dec;
+};
+
+
+/* H264 constants */
+#define PROFILE_H264_BASELINE 66
+#define PROFILE_H264_MAIN 77
+
+/* Codec specific functions */
+static pj_status_t h264_preopen(ffmpeg_private *ff);
+static pj_status_t h264_postopen(ffmpeg_private *ff);
+static pj_status_t h263_preopen(ffmpeg_private *ff);
+static FUNC_PACKETIZE(h264_packetize);
+static FUNC_UNPACKETIZE(h264_unpacketize);
+static FUNC_PACKETIZE(h263_packetize);
+static FUNC_UNPACKETIZE(h263_unpacketize);
+
+
+/* Internal codec info */
+ffmpeg_codec_desc codec_desc[] =
+{
+ {
+ {PJMEDIA_FORMAT_H264, PJMEDIA_RTP_PT_H264, {"H264",4},
+ {"Constrained Baseline (level=30, pack=1)", 39}},
+ 0, 128000, 1000000,
+ &h264_packetize, &h264_unpacketize, &h264_preopen, &h264_postopen,
+ &pjmedia_vid_codec_h264_match_sdp,
+ /* Leading space for better compatibility (strange indeed!) */
+ {2, { {{"profile-level-id",16}, {"42e01e",6}},
+ {{" packetization-mode",19}, {"1",1}}, } },
+ },
+ {
+ {PJMEDIA_FORMAT_H264, PJMEDIA_RTP_PT_H264_RSV1, {"H264",4},
+ {"Baseline (level=30, pack=1)", 27}},
+ PJMEDIA_FORMAT_H264, 128000, 1000000,
+ &h264_packetize, &h264_unpacketize, &h264_preopen, &h264_postopen,
+ &pjmedia_vid_codec_h264_match_sdp,
+ {2, { {{"profile-level-id",16}, {"42001e",6}},
+ {{" packetization-mode",19}, {"1",1}}, } },
+ },
+ {
+ {PJMEDIA_FORMAT_H263P, PJMEDIA_RTP_PT_H263P, {"H263-1998",9}},
+ PJMEDIA_FORMAT_H263, 1000000, 2000000,
+ &h263_packetize, &h263_unpacketize, &h263_preopen, NULL, NULL,
+ {2, { {{"CIF",3}, {"1",1}},
+ {{"QCIF",4}, {"1",1}}, } },
+ },
+ {
+ {PJMEDIA_FORMAT_H263, PJMEDIA_RTP_PT_H263, {"H263",4}},
+ PJMEDIA_FORMAT_H263, 1000000, 2000000,
+ &h263_packetize, &h263_unpacketize, &h263_preopen, NULL, NULL,
+ {2, { {{"CIF",3}, {"1",1}},
+ {{"QCIF",4}, {"1",1}}, } },
+ },
+ {
+ {PJMEDIA_FORMAT_H263, PJMEDIA_RTP_PT_H263, {"H263",4}},
+ },
+ {
+ {PJMEDIA_FORMAT_H261, PJMEDIA_RTP_PT_H261, {"H261",4}},
+ },
+ {
+ {PJMEDIA_FORMAT_MJPEG, PJMEDIA_RTP_PT_JPEG, {"JPEG",4}},
+ },
+ {
+ {PJMEDIA_FORMAT_MPEG4, 0, {"MP4V",4}},
+ },
+ {
+ {PJMEDIA_FORMAT_XVID, 0, {"XVID",4}},
+ PJMEDIA_FORMAT_MPEG4,
+ },
+};
+
+
+typedef struct h264_data
+{
+ pjmedia_vid_codec_h264_fmtp fmtp;
+ pjmedia_h264_packetizer *pktz;
+} h264_data;
+
+
+static pj_status_t h264_preopen(ffmpeg_private *ff)
+{
+ h264_data *data;
+ pjmedia_h264_packetizer_cfg pktz_cfg;
+ pj_status_t status;
+
+ data = PJ_POOL_ZALLOC_T(ff->pool, h264_data);
+ ff->data = data;
+
+ /* Parse remote fmtp */
+ status = pjmedia_vid_codec_h264_parse_fmtp(&ff->param.enc_fmtp,
+ &data->fmtp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Create packetizer */
+ pktz_cfg.mtu = ff->param.enc_mtu;
+#if 0
+ if (data->fmtp.packetization_mode == 0)
+ pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL;
+ else if (data->fmtp.packetization_mode == 1)
+ pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED;
+ else
+ return PJ_ENOTSUP;
+#else
+ if (data->fmtp.packetization_mode!=
+ PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL &&
+ data->fmtp.packetization_mode!=
+ PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED)
+ {
+ return PJ_ENOTSUP;
+ }
+ /* Better always send in single NAL mode for better compatibility */
+ pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL;
+#endif
+
+ status = pjmedia_h264_packetizer_create(ff->pool, &pktz_cfg, &data->pktz);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Apply SDP fmtp to format in codec param */
+ status = pjmedia_vid_codec_h264_apply_fmtp(&ff->param);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if (ff->param.dir & PJMEDIA_DIR_ENCODING) {
+ AVCodecContext *ctx = ff->enc_ctx;
+
+ /* Apply profile. Note that, for x264 backend, ffmpeg doesn't seem to
+ * use this profile param field, so let's try to apply it "manually".
+ */
+ ctx->profile = data->fmtp.profile_idc;
+ if (ctx->profile == PROFILE_H264_BASELINE) {
+ /* Baseline profile settings (the most used profile in
+ * conversational/real-time communications).
+ */
+ ctx->coder_type = FF_CODER_TYPE_VLC;
+ ctx->max_b_frames = 0;
+ ctx->flags2 &= ~(CODEC_FLAG2_WPRED | CODEC_FLAG2_8X8DCT);
+ ctx->weighted_p_pred = 0;
+ } else if (ctx->profile == PROFILE_H264_MAIN) {
+ ctx->flags2 &= ~CODEC_FLAG2_8X8DCT;
+ }
+
+ /* Apply profile constraint bits. */
+ // The x264 doesn't seem to support non-constrained (baseline) profile
+ // so this shouldn't be a problem (for now).
+ //PJ_TODO(set_h264_constraint_bits_properly_in_ffmpeg);
+ if (data->fmtp.profile_iop) {
+#if defined(FF_PROFILE_H264_CONSTRAINED)
+ ctx->profile |= FF_PROFILE_H264_CONSTRAINED;
+#endif
+ }
+
+ /* Apply profile level. */
+ ctx->level = data->fmtp.level;
+
+ /* Libx264 rejects the "broken" ffmpeg defaults, so just change some */
+ ctx->me_range = 16;
+ ctx->max_qdiff = 4;
+ ctx->qmin = 20;
+ ctx->qmax = 32;
+ ctx->qcompress = 0.6f;
+
+ ctx->rtp_payload_size = ff->param.enc_mtu;
+ }
+
+ if (ff->param.dir & PJMEDIA_DIR_DECODING) {
+ AVCodecContext *ctx = ff->dec_ctx;
+
+ /* Apply the "sprop-parameter-sets" fmtp from remote SDP to
+ * extradata of ffmpeg codec context.
+ */
+ if (data->fmtp.sprop_param_sets_len) {
+ ctx->extradata_size = data->fmtp.sprop_param_sets_len;
+ ctx->extradata = data->fmtp.sprop_param_sets;
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t h264_postopen(ffmpeg_private *ff)
+{
+ h264_data *data = (h264_data*)ff->data;
+ PJ_UNUSED_ARG(data);
+ return PJ_SUCCESS;
+}
+
+
+static FUNC_PACKETIZE(h264_packetize)
+{
+ h264_data *data = (h264_data*)ff->data;
+ return pjmedia_h264_packetize(data->pktz, bits, bits_len, bits_pos,
+ payload, payload_len);
+}
+
+static FUNC_UNPACKETIZE(h264_unpacketize)
+{
+ h264_data *data = (h264_data*)ff->data;
+ return pjmedia_h264_unpacketize(data->pktz, payload, payload_len,
+ bits, bits_len, bits_pos);
+}
+
+
+typedef struct h263_data
+{
+ pjmedia_h263_packetizer *pktz;
+} h263_data;
+
+/* H263 pre-open */
+static pj_status_t h263_preopen(ffmpeg_private *ff)
+{
+ h263_data *data;
+ pjmedia_h263_packetizer_cfg pktz_cfg;
+ pj_status_t status;
+
+ data = PJ_POOL_ZALLOC_T(ff->pool, h263_data);
+ ff->data = data;
+
+ /* Create packetizer */
+ pktz_cfg.mtu = ff->param.enc_mtu;
+ pktz_cfg.mode = PJMEDIA_H263_PACKETIZER_MODE_RFC4629;
+ status = pjmedia_h263_packetizer_create(ff->pool, &pktz_cfg, &data->pktz);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Apply fmtp settings to codec param */
+ status = pjmedia_vid_codec_h263_apply_fmtp(&ff->param);
+
+ return status;
+}
+
+static FUNC_PACKETIZE(h263_packetize)
+{
+ h263_data *data = (h263_data*)ff->data;
+ return pjmedia_h263_packetize(data->pktz, bits, bits_len, bits_pos,
+ payload, payload_len);
+}
+
+static FUNC_UNPACKETIZE(h263_unpacketize)
+{
+ h263_data *data = (h263_data*)ff->data;
+ return pjmedia_h263_unpacketize(data->pktz, payload, payload_len,
+ bits, bits_len, bits_pos);
+}
+
+
+static const ffmpeg_codec_desc* find_codec_desc_by_info(
+ const pjmedia_vid_codec_info *info)
+{
+ int i;
+
+ for (i=0; i<PJ_ARRAY_SIZE(codec_desc); ++i) {
+ ffmpeg_codec_desc *desc = &codec_desc[i];
+
+ if (desc->enabled &&
+ (desc->info.fmt_id == info->fmt_id) &&
+ ((desc->info.dir & info->dir) == info->dir) &&
+ (desc->info.pt == info->pt))
+ {
+ return desc;
+ }
+ }
+
+ return NULL;
+}
+
+
+static int find_codec_idx_by_fmt_id(pjmedia_format_id fmt_id)
+{
+ int i;
+ for (i=0; i<PJ_ARRAY_SIZE(codec_desc); ++i) {
+ if (codec_desc[i].info.fmt_id == fmt_id)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/*
+ * Initialize and register FFMPEG codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_ffmpeg_init(pjmedia_vid_codec_mgr *mgr,
+ pj_pool_factory *pf)
+{
+ pj_pool_t *pool;
+ AVCodec *c;
+ pj_status_t status;
+ unsigned i;
+
+ if (ffmpeg_factory.pool != NULL) {
+ /* Already initialized. */
+ return PJ_SUCCESS;
+ }
+
+ if (!mgr) mgr = pjmedia_vid_codec_mgr_instance();
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ /* Create FFMPEG codec factory. */
+ ffmpeg_factory.base.op = &ffmpeg_factory_op;
+ ffmpeg_factory.base.factory_data = NULL;
+ ffmpeg_factory.mgr = mgr;
+ ffmpeg_factory.pf = pf;
+
+ pool = pj_pool_create(pf, "ffmpeg codec factory", 256, 256, NULL);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ /* Create mutex. */
+ status = pj_mutex_create_simple(pool, "ffmpeg codec factory",
+ &ffmpeg_factory.mutex);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ avcodec_init();
+ avcodec_register_all();
+ av_log_set_level(AV_LOG_ERROR);
+
+ /* Enum FFMPEG codecs */
+ for (c=av_codec_next(NULL); c; c=av_codec_next(c))
+ {
+ ffmpeg_codec_desc *desc;
+ pjmedia_format_id fmt_id;
+ int codec_info_idx;
+
+#if LIBAVCODEC_VERSION_MAJOR <= 52
+# define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
+#endif
+ if (c->type != AVMEDIA_TYPE_VIDEO)
+ continue;
+
+ /* Video encoder and decoder are usually implemented in separate
+ * AVCodec instances. While the codec attributes (e.g: raw formats,
+ * supported fps) are in the encoder.
+ */
+
+ //PJ_LOG(3, (THIS_FILE, "%s", c->name));
+ status = CodecID_to_pjmedia_format_id(c->id, &fmt_id);
+ /* Skip if format ID is unknown */
+ if (status != PJ_SUCCESS)
+ continue;
+
+ codec_info_idx = find_codec_idx_by_fmt_id(fmt_id);
+ /* Skip if codec is unwanted by this wrapper (not listed in
+ * the codec info array)
+ */
+ if (codec_info_idx < 0)
+ continue;
+
+ desc = &codec_desc[codec_info_idx];
+
+ /* Skip duplicated codec implementation */
+ if ((c->encode && (desc->info.dir & PJMEDIA_DIR_ENCODING)) ||
+ (c->decode && (desc->info.dir & PJMEDIA_DIR_DECODING)))
+ {
+ continue;
+ }
+
+ /* Get raw/decoded format ids in the encoder */
+ if (c->pix_fmts && c->encode) {
+ pjmedia_format_id raw_fmt[PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT];
+ unsigned raw_fmt_cnt = 0;
+ unsigned raw_fmt_cnt_should_be = 0;
+ const enum PixelFormat *p = c->pix_fmts;
+
+ for(;(p && *p != -1) &&
+ (raw_fmt_cnt < PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT);
+ ++p)
+ {
+ pjmedia_format_id fmt_id;
+
+ raw_fmt_cnt_should_be++;
+ status = PixelFormat_to_pjmedia_format_id(*p, &fmt_id);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(6, (THIS_FILE, "Unrecognized ffmpeg pixel "
+ "format %d", *p));
+ continue;
+ }
+ raw_fmt[raw_fmt_cnt++] = fmt_id;
+ }
+
+ if (raw_fmt_cnt == 0) {
+ PJ_LOG(5, (THIS_FILE, "No recognized raw format "
+ "for codec [%s/%s], codec ignored",
+ c->name, c->long_name));
+ /* Skip this encoder */
+ continue;
+ }
+
+ if (raw_fmt_cnt < raw_fmt_cnt_should_be) {
+ PJ_LOG(6, (THIS_FILE, "Codec [%s/%s] have %d raw formats, "
+ "recognized only %d raw formats",
+ c->name, c->long_name,
+ raw_fmt_cnt_should_be, raw_fmt_cnt));
+ }
+
+ desc->info.dec_fmt_id_cnt = raw_fmt_cnt;
+ pj_memcpy(desc->info.dec_fmt_id, raw_fmt,
+ sizeof(raw_fmt[0])*raw_fmt_cnt);
+ }
+
+ /* Get supported framerates */
+ if (c->supported_framerates) {
+ const AVRational *fr = c->supported_framerates;
+ while ((fr->num != 0 || fr->den != 0) &&
+ desc->info.fps_cnt < PJMEDIA_VID_CODEC_MAX_FPS_CNT)
+ {
+ desc->info.fps[desc->info.fps_cnt].num = fr->num;
+ desc->info.fps[desc->info.fps_cnt].denum = fr->den;
+ ++desc->info.fps_cnt;
+ ++fr;
+ }
+ }
+
+ /* Get ffmpeg encoder instance */
+ if (c->encode && !desc->enc) {
+ desc->info.dir |= PJMEDIA_DIR_ENCODING;
+ desc->enc = c;
+ }
+
+ /* Get ffmpeg decoder instance */
+ if (c->decode && !desc->dec) {
+ desc->info.dir |= PJMEDIA_DIR_DECODING;
+ desc->dec = c;
+ }
+
+ /* Enable this codec when any ffmpeg codec instance are recognized
+ * and the supported raw formats info has been collected.
+ */
+ if ((desc->dec || desc->enc) && desc->info.dec_fmt_id_cnt)
+ {
+ desc->enabled = PJ_TRUE;
+ }
+
+ /* Normalize default value of clock rate */
+ if (desc->info.clock_rate == 0)
+ desc->info.clock_rate = 90000;
+
+ /* Set RTP packetization support flag in the codec info */
+ desc->info.has_rtp_pack = (desc->packetize != NULL) &&
+ (desc->unpacketize != NULL);
+ }
+
+ /* Review all codecs for applying base format, registering format match for
+ * SDP negotiation, etc.
+ */
+ for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
+ ffmpeg_codec_desc *desc = &codec_desc[i];
+
+ /* Init encoder/decoder description from base format */
+ if (desc->base_fmt_id && (!desc->dec || !desc->enc)) {
+ ffmpeg_codec_desc *base_desc = NULL;
+ int base_desc_idx;
+ pjmedia_dir copied_dir = PJMEDIA_DIR_NONE;
+
+ base_desc_idx = find_codec_idx_by_fmt_id(desc->base_fmt_id);
+ if (base_desc_idx != -1)
+ base_desc = &codec_desc[base_desc_idx];
+ if (!base_desc || !base_desc->enabled)
+ continue;
+
+ /* Copy description from base codec */
+ if (!desc->info.dec_fmt_id_cnt) {
+ desc->info.dec_fmt_id_cnt = base_desc->info.dec_fmt_id_cnt;
+ pj_memcpy(desc->info.dec_fmt_id, base_desc->info.dec_fmt_id,
+ sizeof(pjmedia_format_id)*desc->info.dec_fmt_id_cnt);
+ }
+ if (!desc->info.fps_cnt) {
+ desc->info.fps_cnt = base_desc->info.fps_cnt;
+ pj_memcpy(desc->info.fps, base_desc->info.fps,
+ sizeof(desc->info.fps[0])*desc->info.fps_cnt);
+ }
+ if (!desc->info.clock_rate) {
+ desc->info.clock_rate = base_desc->info.clock_rate;
+ }
+ if (!desc->dec && base_desc->dec) {
+ copied_dir |= PJMEDIA_DIR_DECODING;
+ desc->dec = base_desc->dec;
+ }
+ if (!desc->enc && base_desc->enc) {
+ copied_dir |= PJMEDIA_DIR_ENCODING;
+ desc->enc = base_desc->enc;
+ }
+
+ desc->info.dir |= copied_dir;
+ desc->enabled = (desc->info.dir != PJMEDIA_DIR_NONE);
+ desc->info.has_rtp_pack = (desc->packetize != NULL) &&
+ (desc->unpacketize != NULL);
+
+ if (copied_dir != PJMEDIA_DIR_NONE) {
+ const char *dir_name[] = {NULL, "encoder", "decoder", "codec"};
+ PJ_LOG(5, (THIS_FILE, "The %.*s %s is using base codec (%.*s)",
+ desc->info.encoding_name.slen,
+ desc->info.encoding_name.ptr,
+ dir_name[copied_dir],
+ base_desc->info.encoding_name.slen,
+ base_desc->info.encoding_name.ptr));
+ }
+ }
+
+ /* Registering format match for SDP negotiation */
+ if (desc->sdp_fmt_match) {
+ status = pjmedia_sdp_neg_register_fmt_match_cb(
+ &desc->info.encoding_name,
+ desc->sdp_fmt_match);
+ pj_assert(status == PJ_SUCCESS);
+ }
+ }
+
+ /* Register codec factory to codec manager. */
+ status = pjmedia_vid_codec_mgr_register_factory(mgr,
+ &ffmpeg_factory.base);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ ffmpeg_factory.pool = pool;
+
+ /* Done. */
+ return PJ_SUCCESS;
+
+on_error:
+ pj_pool_release(pool);
+ return status;
+}
+
+/*
+ * Unregister FFMPEG codecs factory from pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_ffmpeg_deinit(void)
+{
+ pj_status_t status = PJ_SUCCESS;
+
+ if (ffmpeg_factory.pool == NULL) {
+ /* Already deinitialized */
+ return PJ_SUCCESS;
+ }
+
+ pj_mutex_lock(ffmpeg_factory.mutex);
+
+ /* Unregister FFMPEG codecs factory. */
+ status = pjmedia_vid_codec_mgr_unregister_factory(ffmpeg_factory.mgr,
+ &ffmpeg_factory.base);
+
+ /* Destroy mutex. */
+ pj_mutex_destroy(ffmpeg_factory.mutex);
+
+ /* Destroy pool. */
+ pj_pool_release(ffmpeg_factory.pool);
+ ffmpeg_factory.pool = NULL;
+
+ return status;
+}
+
+
+/*
+ * Check if factory can allocate the specified codec.
+ */
+static pj_status_t ffmpeg_test_alloc( pjmedia_vid_codec_factory *factory,
+ const pjmedia_vid_codec_info *info )
+{
+ const ffmpeg_codec_desc *desc;
+
+ PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
+ PJ_ASSERT_RETURN(info, PJ_EINVAL);
+
+ desc = find_codec_desc_by_info(info);
+ if (!desc) {
+ return PJMEDIA_CODEC_EUNSUP;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t ffmpeg_default_attr( pjmedia_vid_codec_factory *factory,
+ const pjmedia_vid_codec_info *info,
+ pjmedia_vid_codec_param *attr )
+{
+ const ffmpeg_codec_desc *desc;
+
+ PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
+ PJ_ASSERT_RETURN(info && attr, PJ_EINVAL);
+
+ desc = find_codec_desc_by_info(info);
+ if (!desc) {
+ return PJMEDIA_CODEC_EUNSUP;
+ }
+
+ pj_bzero(attr, sizeof(pjmedia_vid_codec_param));
+
+ /* Direction */
+ attr->dir = desc->info.dir;
+
+ /* Encoded format */
+ pjmedia_format_init_video(&attr->enc_fmt, desc->info.fmt_id,
+ 352, 288, 30000, 1001);
+
+ /* Decoded format */
+ pjmedia_format_init_video(&attr->dec_fmt, desc->info.dec_fmt_id[0],
+ //352, 288, 30000, 1001);
+ 720, 576, 30000, 1001);
+
+ /* Decoding fmtp */
+ attr->dec_fmtp = desc->dec_fmtp;
+
+ /* Bitrate */
+ attr->enc_fmt.det.vid.avg_bps = desc->avg_bps;
+ attr->enc_fmt.det.vid.max_bps = desc->max_bps;
+
+ /* MTU */
+ attr->enc_mtu = PJMEDIA_MAX_MTU;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Enum codecs supported by this factory.
+ */
+static pj_status_t ffmpeg_enum_codecs( pjmedia_vid_codec_factory *factory,
+ unsigned *count,
+ pjmedia_vid_codec_info codecs[])
+{
+ unsigned i, max_cnt;
+
+ PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+ PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
+
+ max_cnt = PJ_MIN(*count, PJ_ARRAY_SIZE(codec_desc));
+ *count = 0;
+
+ for (i=0; i<max_cnt; ++i) {
+ if (codec_desc[i].enabled) {
+ pj_memcpy(&codecs[*count], &codec_desc[i].info,
+ sizeof(pjmedia_vid_codec_info));
+ (*count)++;
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Allocate a new codec instance.
+ */
+static pj_status_t ffmpeg_alloc_codec( pjmedia_vid_codec_factory *factory,
+ const pjmedia_vid_codec_info *info,
+ pjmedia_vid_codec **p_codec)
+{
+ ffmpeg_private *ff;
+ const ffmpeg_codec_desc *desc;
+ pjmedia_vid_codec *codec;
+ pj_pool_t *pool = NULL;
+ pj_status_t status = PJ_SUCCESS;
+
+ PJ_ASSERT_RETURN(factory && info && p_codec, PJ_EINVAL);
+ PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
+
+ desc = find_codec_desc_by_info(info);
+ if (!desc) {
+ return PJMEDIA_CODEC_EUNSUP;
+ }
+
+ /* Create pool for codec instance */
+ pool = pj_pool_create(ffmpeg_factory.pf, "ffmpeg codec", 512, 512, NULL);
+ codec = PJ_POOL_ALLOC_T(pool, pjmedia_vid_codec);
+ if (!codec) {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+ pjmedia_vid_codec_reset(codec, PJMEDIA_SIG_VID_CODEC_FFMPEG);
+ codec->op = &ffmpeg_op;
+ codec->factory = factory;
+ ff = PJ_POOL_ZALLOC_T(pool, ffmpeg_private);
+ if (!ff) {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+ codec->codec_data = ff;
+ ff->pool = pool;
+ ff->enc = desc->enc;
+ ff->dec = desc->dec;
+ ff->desc = desc;
+
+ *p_codec = codec;
+ return PJ_SUCCESS;
+
+on_error:
+ if (pool)
+ pj_pool_release(pool);
+ return status;
+}
+
+/*
+ * Free codec.
+ */
+static pj_status_t ffmpeg_dealloc_codec( pjmedia_vid_codec_factory *factory,
+ pjmedia_vid_codec *codec )
+{
+ ffmpeg_private *ff;
+ pj_pool_t *pool;
+
+ PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+ PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
+
+ /* Close codec, if it's not closed. */
+ ff = (ffmpeg_private*) codec->codec_data;
+ pool = ff->pool;
+ codec->codec_data = NULL;
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Init codec.
+ */
+static pj_status_t ffmpeg_codec_init( pjmedia_vid_codec *codec,
+ pj_pool_t *pool )
+{
+ PJ_UNUSED_ARG(codec);
+ PJ_UNUSED_ARG(pool);
+ return PJ_SUCCESS;
+}
+
+static void print_ffmpeg_err(int err)
+{
+#if LIBAVCODEC_VERSION_MAJOR > 52 || \
+ (LIBAVCODEC_VERSION_MAJOR >= 52 && LIBAVCODEC_VERSION_MINOR >= 72)
+ char errbuf[512];
+ if (av_strerror(err, errbuf, sizeof(errbuf)) >= 0)
+ PJ_LOG(5, (THIS_FILE, "ffmpeg err %d: %s", err, errbuf));
+#else
+ PJ_LOG(5, (THIS_FILE, "ffmpeg err %d", err));
+#endif
+
+}
+
+static enum PixelFormat dec_get_format(struct AVCodecContext *s,
+ const enum PixelFormat * fmt)
+{
+ ffmpeg_private *ff = (ffmpeg_private*)s->opaque;
+ enum PixelFormat def_fmt = *fmt;
+
+ while (*fmt != -1) {
+ if (*fmt == ff->expected_dec_fmt)
+ return *fmt;
+ ++fmt;
+ }
+
+ pj_assert(!"Inconsistency in supported formats");
+ return def_fmt;
+}
+
+
+static pj_status_t open_ffmpeg_codec(ffmpeg_private *ff,
+ pj_mutex_t *ff_mutex)
+{
+ enum PixelFormat pix_fmt;
+ pjmedia_video_format_detail *vfd;
+ pj_bool_t enc_opened = PJ_FALSE, dec_opened = PJ_FALSE;
+ pj_status_t status;
+
+ /* Get decoded pixel format */
+ status = pjmedia_format_id_to_PixelFormat(ff->param.dec_fmt.id,
+ &pix_fmt);
+ if (status != PJ_SUCCESS)
+ return status;
+ ff->expected_dec_fmt = pix_fmt;
+
+ /* Get video format detail for shortcut access to encoded format */
+ vfd = pjmedia_format_get_video_format_detail(&ff->param.enc_fmt,
+ PJ_TRUE);
+
+ /* Allocate ffmpeg codec context */
+ if (ff->param.dir & PJMEDIA_DIR_ENCODING) {
+ ff->enc_ctx = avcodec_alloc_context();
+ if (ff->enc_ctx == NULL)
+ goto on_error;
+ }
+ if (ff->param.dir & PJMEDIA_DIR_DECODING) {
+ ff->dec_ctx = avcodec_alloc_context();
+ if (ff->dec_ctx == NULL)
+ goto on_error;
+ }
+
+ /* Let the codec apply specific settings before the codec opened */
+ if (ff->desc->preopen) {
+ status = (*ff->desc->preopen)(ff);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+
+ if (ff->param.dir & PJMEDIA_DIR_ENCODING) {
+ AVCodecContext *ctx = ff->enc_ctx;
+ int err;
+
+ /* Init common settings */
+ ctx->pix_fmt = pix_fmt;
+ ctx->width = vfd->size.w;
+ ctx->height = vfd->size.h;
+ ctx->time_base.num = vfd->fps.denum;
+ ctx->time_base.den = vfd->fps.num;
+ if (vfd->avg_bps) {
+ ctx->bit_rate = vfd->avg_bps;
+ if (vfd->max_bps > vfd->avg_bps)
+ ctx->bit_rate_tolerance = vfd->max_bps - vfd->avg_bps;
+ }
+ ctx->strict_std_compliance = FF_COMPLIANCE_STRICT;
+ ctx->workaround_bugs = FF_BUG_AUTODETECT;
+ ctx->opaque = ff;
+
+ /* Set no delay, note that this may cause some codec functionals
+ * not working (e.g: rate control).
+ */
+#if LIBAVCODEC_VERSION_MAJOR > 52 || \
+ (LIBAVCODEC_VERSION_MAJOR >= 52 && LIBAVCODEC_VERSION_MINOR >= 113)
+ ctx->rc_lookahead = 0;
+#endif
+
+ /* Open ffmpeg codec */
+ pj_mutex_lock(ff_mutex);
+ err = avcodec_open(ctx, ff->enc);
+ pj_mutex_unlock(ff_mutex);
+ if (err < 0) {
+ print_ffmpeg_err(err);
+ status = PJMEDIA_CODEC_EFAILED;
+ goto on_error;
+ }
+ enc_opened = PJ_TRUE;
+ }
+
+ if (ff->param.dir & PJMEDIA_DIR_DECODING) {
+ AVCodecContext *ctx = ff->dec_ctx;
+ int err;
+
+ /* Init common settings */
+ /* Width/height may be overriden by ffmpeg after first decoding. */
+ ctx->width = ctx->coded_width = ff->param.dec_fmt.det.vid.size.w;
+ ctx->height = ctx->coded_height = ff->param.dec_fmt.det.vid.size.h;
+ ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+ ctx->workaround_bugs = FF_BUG_AUTODETECT;
+ ctx->opaque = ff;
+ ctx->get_format = &dec_get_format;
+
+ /* Open ffmpeg codec */
+ pj_mutex_lock(ff_mutex);
+ err = avcodec_open(ctx, ff->dec);
+ pj_mutex_unlock(ff_mutex);
+ if (err < 0) {
+ print_ffmpeg_err(err);
+ status = PJMEDIA_CODEC_EFAILED;
+ goto on_error;
+ }
+ dec_opened = PJ_TRUE;
+ }
+
+ /* Let the codec apply specific settings after the codec opened */
+ if (ff->desc->postopen) {
+ status = (*ff->desc->postopen)(ff);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+
+ return PJ_SUCCESS;
+
+on_error:
+ if (ff->enc_ctx) {
+ if (enc_opened)
+ avcodec_close(ff->enc_ctx);
+ av_free(ff->enc_ctx);
+ ff->enc_ctx = NULL;
+ }
+ if (ff->dec_ctx) {
+ if (dec_opened)
+ avcodec_close(ff->dec_ctx);
+ av_free(ff->dec_ctx);
+ ff->dec_ctx = NULL;
+ }
+ return status;
+}
+
+/*
+ * Open codec.
+ */
+static pj_status_t ffmpeg_codec_open( pjmedia_vid_codec *codec,
+ pjmedia_vid_codec_param *attr )
+{
+ ffmpeg_private *ff;
+ pj_status_t status;
+ pj_mutex_t *ff_mutex;
+
+ PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
+ ff = (ffmpeg_private*)codec->codec_data;
+
+ pj_memcpy(&ff->param, attr, sizeof(*attr));
+
+ /* Open the codec */
+ ff_mutex = ((struct ffmpeg_factory*)codec->factory)->mutex;
+ status = open_ffmpeg_codec(ff, ff_mutex);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Init format info and apply-param of decoder */
+ ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+ if (!ff->dec_vfi) {
+ status = PJ_EINVAL;
+ goto on_error;
+ }
+ pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp));
+ ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size;
+ ff->dec_vafp.buffer = NULL;
+ status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Init format info and apply-param of encoder */
+ ff->enc_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+ if (!ff->enc_vfi) {
+ status = PJ_EINVAL;
+ goto on_error;
+ }
+ pj_bzero(&ff->enc_vafp, sizeof(ff->enc_vafp));
+ ff->enc_vafp.size = ff->param.enc_fmt.det.vid.size;
+ ff->enc_vafp.buffer = NULL;
+ status = (*ff->enc_vfi->apply_fmt)(ff->enc_vfi, &ff->enc_vafp);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Update codec attributes, e.g: encoding format may be changed by
+ * SDP fmtp negotiation.
+ */
+ pj_memcpy(attr, &ff->param, sizeof(*attr));
+
+ return PJ_SUCCESS;
+
+on_error:
+ ffmpeg_codec_close(codec);
+ return status;
+}
+
+/*
+ * Close codec.
+ */
+static pj_status_t ffmpeg_codec_close( pjmedia_vid_codec *codec )
+{
+ ffmpeg_private *ff;
+ pj_mutex_t *ff_mutex;
+
+ PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+ ff = (ffmpeg_private*)codec->codec_data;
+ ff_mutex = ((struct ffmpeg_factory*)codec->factory)->mutex;
+
+ pj_mutex_lock(ff_mutex);
+ if (ff->enc_ctx) {
+ avcodec_close(ff->enc_ctx);
+ av_free(ff->enc_ctx);
+ }
+ if (ff->dec_ctx && ff->dec_ctx!=ff->enc_ctx) {
+ avcodec_close(ff->dec_ctx);
+ av_free(ff->dec_ctx);
+ }
+ ff->enc_ctx = NULL;
+ ff->dec_ctx = NULL;
+ pj_mutex_unlock(ff_mutex);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t ffmpeg_codec_modify( pjmedia_vid_codec *codec,
+ const pjmedia_vid_codec_param *attr)
+{
+ ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+
+ PJ_UNUSED_ARG(attr);
+ PJ_UNUSED_ARG(ff);
+
+ return PJ_ENOTSUP;
+}
+
+static pj_status_t ffmpeg_codec_get_param(pjmedia_vid_codec *codec,
+ pjmedia_vid_codec_param *param)
+{
+ ffmpeg_private *ff;
+
+ PJ_ASSERT_RETURN(codec && param, PJ_EINVAL);
+
+ ff = (ffmpeg_private*)codec->codec_data;
+ pj_memcpy(param, &ff->param, sizeof(*param));
+
+ return PJ_SUCCESS;
+}
+
+
+static pj_status_t ffmpeg_packetize ( pjmedia_vid_codec *codec,
+ pj_uint8_t *bits,
+ pj_size_t bits_len,
+ unsigned *bits_pos,
+ const pj_uint8_t **payload,
+ pj_size_t *payload_len)
+{
+ ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+
+ if (ff->desc->packetize) {
+ return (*ff->desc->packetize)(ff, bits, bits_len, bits_pos,
+ payload, payload_len);
+ }
+
+ return PJ_ENOTSUP;
+}
+
+static pj_status_t ffmpeg_unpacketize(pjmedia_vid_codec *codec,
+ const pj_uint8_t *payload,
+ pj_size_t payload_len,
+ pj_uint8_t *bits,
+ pj_size_t bits_len,
+ unsigned *bits_pos)
+{
+ ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+
+ if (ff->desc->unpacketize) {
+ return (*ff->desc->unpacketize)(ff, payload, payload_len,
+ bits, bits_len, bits_pos);
+ }
+
+ return PJ_ENOTSUP;
+}
+
+
+/*
+ * Encode frames.
+ */
+static pj_status_t ffmpeg_codec_encode( pjmedia_vid_codec *codec,
+ const pjmedia_frame *input,
+ unsigned output_buf_len,
+ pjmedia_frame *output)
+{
+ ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+ pj_uint8_t *p = (pj_uint8_t*)input->buf;
+ AVFrame avframe;
+ pj_uint8_t *out_buf = (pj_uint8_t*)output->buf;
+ int out_buf_len = output_buf_len;
+ int err;
+ //AVRational src_timebase;
+ /* For some reasons (e.g: SSE/MMX usage), the avcodec_encode_video() must
+ * have stack aligned to 16 bytes. Let's try to be safe by preparing the
+ * 16-bytes aligned stack here, in case it's not managed by the ffmpeg.
+ */
+ PJ_ALIGN_DATA(pj_uint32_t i[4], 16);
+
+ if ((long)i & 0xF) {
+ PJ_LOG(2,(THIS_FILE, "Stack alignment fails"));
+ }
+
+ /* Check if encoder has been opened */
+ PJ_ASSERT_RETURN(ff->enc_ctx, PJ_EINVALIDOP);
+
+ avcodec_get_frame_defaults(&avframe);
+
+ // Let ffmpeg manage the timestamps
+ /*
+ src_timebase.num = 1;
+ src_timebase.den = ff->desc->info.clock_rate;
+ avframe.pts = av_rescale_q(input->timestamp.u64, src_timebase,
+ ff->enc_ctx->time_base);
+ */
+
+ for (i[0] = 0; i[0] < ff->enc_vfi->plane_cnt; ++i[0]) {
+ avframe.data[i[0]] = p;
+ avframe.linesize[i[0]] = ff->enc_vafp.strides[i[0]];
+ p += ff->enc_vafp.plane_bytes[i[0]];
+ }
+
+ err = avcodec_encode_video(ff->enc_ctx, out_buf, out_buf_len, &avframe);
+ if (err < 0) {
+ print_ffmpeg_err(err);
+ return PJMEDIA_CODEC_EFAILED;
+ } else {
+ output->size = err;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Decode frame.
+ */
+static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec,
+ const pjmedia_frame *input,
+ unsigned output_buf_len,
+ pjmedia_frame *output)
+{
+ ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+ AVFrame avframe;
+ AVPacket avpacket;
+ int err, got_picture;
+
+ /* Check if decoder has been opened */
+ PJ_ASSERT_RETURN(ff->dec_ctx, PJ_EINVALIDOP);
+
+ /* Reset output frame bit info */
+ output->bit_info = 0;
+
+ /* Validate output buffer size */
+ // Do this validation later after getting decoding result, where the real
+ // decoded size will be assured.
+ //if (ff->dec_vafp.framebytes > output_buf_len)
+ //return PJ_ETOOSMALL;
+
+ /* Init frame to receive the decoded data, the ffmpeg codec context will
+ * automatically provide the decoded buffer (single buffer used for the
+ * whole decoding session, and seems to be freed when the codec context
+ * closed).
+ */
+ avcodec_get_frame_defaults(&avframe);
+
+ /* Init packet, the container of the encoded data */
+ av_init_packet(&avpacket);
+ avpacket.data = (pj_uint8_t*)input->buf;
+ avpacket.size = input->size;
+
+ /* ffmpeg warns:
+ * - input buffer padding, at least FF_INPUT_BUFFER_PADDING_SIZE
+ * - null terminated
+ * Normally, encoded buffer is allocated more than needed, so lets just
+ * bzero the input buffer end/pad, hope it will be just fine.
+ */
+ pj_bzero(avpacket.data+avpacket.size, FF_INPUT_BUFFER_PADDING_SIZE);
+
+ output->bit_info = 0;
+ output->timestamp = input->timestamp;
+
+#if LIBAVCODEC_VERSION_MAJOR > 52 || \
+ (LIBAVCODEC_VERSION_MAJOR >= 52 && LIBAVCODEC_VERSION_MINOR >= 72)
+ //avpacket.flags = AV_PKT_FLAG_KEY;
+#else
+ avpacket.flags = 0;
+#endif
+
+#if LIBAVCODEC_VERSION_MAJOR > 52 || \
+ (LIBAVCODEC_VERSION_MAJOR >= 52 && LIBAVCODEC_VERSION_MINOR >= 72)
+ err = avcodec_decode_video2(ff->dec_ctx, &avframe,
+ &got_picture, &avpacket);
+#else
+ err = avcodec_decode_video(ff->dec_ctx, &avframe,
+ &got_picture, avpacket.data, avpacket.size);
+#endif
+ if (err < 0) {
+ output->type = PJMEDIA_FRAME_TYPE_NONE;
+ output->size = 0;
+ print_ffmpeg_err(err);
+ return PJMEDIA_CODEC_EFAILED;
+ } else if (got_picture) {
+ pjmedia_video_apply_fmt_param *vafp = &ff->dec_vafp;
+ pj_uint8_t *q = (pj_uint8_t*)output->buf;
+ unsigned i;
+
+ /* Decoder output format is set by libavcodec, in case it is different
+ * to the configured param.
+ */
+ if (ff->dec_ctx->pix_fmt != ff->expected_dec_fmt ||
+ ff->dec_ctx->width != (int)vafp->size.w ||
+ ff->dec_ctx->height != (int)vafp->size.h)
+ {
+ pjmedia_format_id new_fmt_id;
+ pj_status_t status;
+
+ /* Get current raw format id from ffmpeg decoder context */
+ status = PixelFormat_to_pjmedia_format_id(ff->dec_ctx->pix_fmt,
+ &new_fmt_id);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Update decoder format in param */
+ ff->param.dec_fmt.id = new_fmt_id;
+ ff->param.dec_fmt.det.vid.size.w = ff->dec_ctx->width;
+ ff->param.dec_fmt.det.vid.size.h = ff->dec_ctx->height;
+
+ /* Re-init format info and apply-param of decoder */
+ ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+ if (!ff->dec_vfi)
+ return PJ_ENOTSUP;
+ pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp));
+ ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size;
+ ff->dec_vafp.buffer = NULL;
+ status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Broadcast event */
+ if (pjmedia_event_publisher_has_sub(&codec->epub)) {
+ pjmedia_event event;
+
+ pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED,
+ &input->timestamp, &codec->epub);
+ event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING;
+ pj_memcpy(&event.data.fmt_changed.new_fmt, &ff->param.dec_fmt,
+ sizeof(ff->param.dec_fmt));
+ pjmedia_event_publish(&codec->epub, &event);
+ }
+ }
+
+ /* Check provided buffer size */
+ if (vafp->framebytes > output_buf_len)
+ return PJ_ETOOSMALL;
+
+ /* Get the decoded data */
+ for (i = 0; i < ff->dec_vfi->plane_cnt; ++i) {
+ pj_uint8_t *p = avframe.data[i];
+
+ /* The decoded data may contain padding */
+ if (avframe.linesize[i]!=vafp->strides[i]) {
+ /* Padding exists, copy line by line */
+ pj_uint8_t *q_end;
+
+ q_end = q+vafp->plane_bytes[i];
+ while(q < q_end) {
+ pj_memcpy(q, p, vafp->strides[i]);
+ q += vafp->strides[i];
+ p += avframe.linesize[i];
+ }
+ } else {
+ /* No padding, copy the whole plane */
+ pj_memcpy(q, p, vafp->plane_bytes[i]);
+ q += vafp->plane_bytes[i];
+ }
+ }
+
+ output->type = PJMEDIA_FRAME_TYPE_VIDEO;
+ output->size = vafp->framebytes;
+
+ /* Check if we got key frame */
+ if (avframe.key_frame && pjmedia_event_publisher_has_sub(&codec->epub))
+ {
+ pjmedia_event event;
+
+ pjmedia_event_init(&event, PJMEDIA_EVENT_KEY_FRAME_FOUND,
+ &output->timestamp, &codec->epub);
+ pjmedia_event_publish(&codec->epub, &event);
+ }
+ } else {
+ output->type = PJMEDIA_FRAME_TYPE_NONE;
+ output->size = 0;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Recover lost frame.
+ */
+static pj_status_t ffmpeg_codec_recover( pjmedia_vid_codec *codec,
+ unsigned output_buf_len,
+ pjmedia_frame *output)
+{
+ PJ_UNUSED_ARG(codec);
+ PJ_UNUSED_ARG(output_buf_len);
+ PJ_UNUSED_ARG(output);
+
+ return PJ_ENOTSUP;
+}
+
+#ifdef _MSC_VER
+# pragma comment( lib, "avcodec.lib")
+#endif
+
+#endif /* PJMEDIA_HAS_FFMPEG_CODEC */
+
diff --git a/pjmedia/src/pjmedia-codec/g722.c b/pjmedia/src/pjmedia-codec/g722.c
index 8cd332d8..acddc142 100644
--- a/pjmedia/src/pjmedia-codec/g722.c
+++ b/pjmedia/src/pjmedia-codec/g722.c
@@ -122,7 +122,8 @@ static pjmedia_codec_factory_op g722_factory_op =
&g722_default_attr,
&g722_enum_codecs,
&g722_alloc_codec,
- &g722_dealloc_codec
+ &g722_dealloc_codec,
+ &pjmedia_codec_g722_deinit
};
/* G722 factory */
diff --git a/pjmedia/src/pjmedia-codec/g7221.c b/pjmedia/src/pjmedia-codec/g7221.c
index f1aa8ab8..44ecabc9 100644
--- a/pjmedia/src/pjmedia-codec/g7221.c
+++ b/pjmedia/src/pjmedia-codec/g7221.c
@@ -115,7 +115,8 @@ static pjmedia_codec_factory_op codec_factory_op =
&default_attr,
&enum_codecs,
&alloc_codec,
- &dealloc_codec
+ &dealloc_codec,
+ &pjmedia_codec_g7221_deinit
};
diff --git a/pjmedia/src/pjmedia-codec/gsm.c b/pjmedia/src/pjmedia-codec/gsm.c
index ea4bb63a..43149b78 100644
--- a/pjmedia/src/pjmedia-codec/gsm.c
+++ b/pjmedia/src/pjmedia-codec/gsm.c
@@ -117,7 +117,8 @@ static pjmedia_codec_factory_op gsm_factory_op =
&gsm_default_attr,
&gsm_enum_codecs,
&gsm_alloc_codec,
- &gsm_dealloc_codec
+ &gsm_dealloc_codec,
+ &pjmedia_codec_gsm_deinit
};
/* GSM factory */
diff --git a/pjmedia/src/pjmedia-codec/h263_packetizer.c b/pjmedia/src/pjmedia-codec/h263_packetizer.c
new file mode 100644
index 00000000..149c2e64
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/h263_packetizer.c
@@ -0,0 +1,287 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-codec/h263_packetizer.h>
+#include <pjmedia/types.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/string.h>
+
+#define THIS_FILE "h263_packetizer.c"
+
+
+/* H.263 packetizer definition */
+struct pjmedia_h263_packetizer {
+ /* Current settings */
+ pjmedia_h263_packetizer_cfg cfg;
+
+ /* Unpacketizer state */
+ unsigned unpack_last_sync_pos;
+ pj_bool_t unpack_prev_lost;
+};
+
+
+/*
+ * Find synchronization point (PSC, slice, GSBC, EOS, EOSBS) in H.263
+ * bitstream.
+ */
+static pj_uint8_t* find_sync_point(pj_uint8_t *data,
+ pj_size_t data_len)
+{
+ pj_uint8_t *p = data, *end = data+data_len-1;
+
+ while (p < end && (*p || *(p+1)))
+ ++p;
+
+ if (p == end)
+ return NULL;
+
+ return p;
+}
+
+
+/*
+ * Find synchronization point (PSC, slice, GSBC, EOS, EOSBS) in H.263
+ * bitstream, in reversed manner.
+ */
+static pj_uint8_t* find_sync_point_rev(pj_uint8_t *data,
+ pj_size_t data_len)
+{
+ pj_uint8_t *p = data+data_len-2;
+
+ while (p >= data && (*p || *(p+1)))
+ --p;
+
+ if (p < data)
+ return (data + data_len);
+
+ return p;
+}
+
+
+/*
+ * Create H263 packetizer.
+ */
+PJ_DEF(pj_status_t) pjmedia_h263_packetizer_create(
+ pj_pool_t *pool,
+ const pjmedia_h263_packetizer_cfg *cfg,
+ pjmedia_h263_packetizer **p)
+{
+ pjmedia_h263_packetizer *p_;
+
+ PJ_ASSERT_RETURN(pool && p, PJ_EINVAL);
+
+ if (cfg && cfg->mode != PJMEDIA_H263_PACKETIZER_MODE_RFC4629)
+ return PJ_ENOTSUP;
+
+ p_ = PJ_POOL_ZALLOC_T(pool, pjmedia_h263_packetizer);
+ if (cfg) {
+ pj_memcpy(&p_->cfg, cfg, sizeof(*cfg));
+ } else {
+ p_->cfg.mode = PJMEDIA_H263_PACKETIZER_MODE_RFC4629;
+ p_->cfg.mtu = PJMEDIA_MAX_MTU;
+ }
+
+ *p = p_;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Generate an RTP payload from H.263 frame bitstream, in-place processing.
+ */
+PJ_DEF(pj_status_t) pjmedia_h263_packetize(pjmedia_h263_packetizer *pktz,
+ pj_uint8_t *bits,
+ pj_size_t bits_len,
+ unsigned *pos,
+ const pj_uint8_t **payload,
+ pj_size_t *payload_len)
+{
+ pj_uint8_t *p, *end;
+
+ pj_assert(pktz && bits && pos && payload && payload_len);
+ pj_assert(*pos <= bits_len);
+
+ p = bits + *pos;
+ end = bits + bits_len;
+
+ /* Put two octets payload header */
+ if ((end-p > 2) && *p==0 && *(p+1)==0) {
+ /* The bitstream starts with synchronization point, just override
+ * the two zero octets (sync point mark) for payload header.
+ */
+ *p = 0x04;
+ } else {
+ /* Not started in synchronization point, we will use two octets
+ * preceeding the bitstream for payload header!
+ */
+
+ if (*pos < 2) {
+ /* Invalid H263 bitstream, it's not started with PSC */
+ return PJ_EINVAL;
+ }
+
+ p -= 2;
+ *p = 0;
+ }
+ *(p+1) = 0;
+
+ /* When bitstream truncation needed because of payload length/MTU
+ * limitation, try to use sync point for the payload boundary.
+ */
+ if (end-p > pktz->cfg.mtu) {
+ end = find_sync_point_rev(p+2, pktz->cfg.mtu-2);
+ }
+
+ *payload = p;
+ *payload_len = end-p;
+ *pos = end - bits;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Append an RTP payload to a H.263 picture bitstream.
+ */
+PJ_DEF(pj_status_t) pjmedia_h263_unpacketize (pjmedia_h263_packetizer *pktz,
+ const pj_uint8_t *payload,
+ pj_size_t payload_len,
+ pj_uint8_t *bits,
+ pj_size_t bits_size,
+ unsigned *pos)
+{
+ pj_uint8_t P, V, PLEN;
+ const pj_uint8_t *p = payload;
+ pj_uint8_t *q;
+
+ q = bits + *pos;
+
+ /* Check if this is a missing/lost packet */
+ if (payload == NULL) {
+ pktz->unpack_prev_lost = PJ_TRUE;
+ return PJ_SUCCESS;
+ }
+
+ /* H263 payload header size is two octets */
+ if (payload_len < 2) {
+ /* Invalid bitstream, discard this payload */
+ pktz->unpack_prev_lost = PJ_TRUE;
+ return PJ_EINVAL;
+ }
+
+ /* Reset last sync point for every new picture bitstream */
+ if (*pos == 0)
+ pktz->unpack_last_sync_pos = 0;
+
+ /* Get payload header info */
+ P = *p & 0x04;
+ V = *p & 0x02;
+ PLEN = ((*p & 0x01) << 5) + ((*(p+1) & 0xF8)>>3);
+
+ /* Get start bitstream pointer */
+ p += 2; /* Skip payload header */
+ if (V)
+ p += 1; /* Skip VRC data */
+ if (PLEN)
+ p += PLEN; /* Skip extra picture header data */
+
+ /* Get bitstream length */
+ if (payload_len > (pj_size_t)(p - payload)) {
+ payload_len -= (p - payload);
+ } else {
+ /* Invalid bitstream, discard this payload */
+ pktz->unpack_prev_lost = PJ_TRUE;
+ return PJ_EINVAL;
+ }
+
+ /* Validate bitstream length */
+ if (bits_size < *pos + payload_len + 2) {
+ /* Insufficient bistream buffer, discard this payload */
+ pj_assert(!"Insufficient H.263 bitstream buffer");
+ pktz->unpack_prev_lost = PJ_TRUE;
+ return PJ_ETOOSMALL;
+ }
+
+ /* Start writing bitstream */
+
+ /* No sync point flag */
+ if (!P) {
+ if (*pos == 0) {
+ /* Previous packet must be lost */
+ pktz->unpack_prev_lost = PJ_TRUE;
+
+ /* If there is extra picture header, let's use it. */
+ if (PLEN) {
+ /* Write two zero octets for PSC */
+ *q++ = 0;
+ *q++ = 0;
+ /* Copy the picture header */
+ p -= PLEN;
+ pj_memcpy(q, p, PLEN);
+ p += PLEN;
+ q += PLEN;
+ }
+ } else if (pktz->unpack_prev_lost) {
+ /* If prev packet was lost, revert the bitstream pointer to
+ * the last sync point.
+ */
+ pj_assert(pktz->unpack_last_sync_pos <= *pos);
+ q = bits + pktz->unpack_last_sync_pos;
+ }
+
+ /* There was packet lost, see if this payload contain sync point
+ * (usable data).
+ */
+ if (pktz->unpack_prev_lost) {
+ pj_uint8_t *sync;
+ sync = find_sync_point((pj_uint8_t*)p, payload_len);
+ if (sync) {
+ /* Got sync point, update P/sync-point flag */
+ P = 1;
+ /* Skip the two zero octets */
+ sync += 2;
+ /* Update payload length and start bitstream pointer */
+ payload_len -= (sync - p);
+ p = sync;
+ } else {
+ /* No sync point in it, just discard this payload */
+ return PJ_EIGNORED;
+ }
+ }
+ }
+
+ /* Write two zero octets when payload flagged with sync point */
+ if (P) {
+ pktz->unpack_last_sync_pos = q - bits;
+ *q++ = 0;
+ *q++ = 0;
+ }
+
+ /* Write the payload to the bitstream */
+ pj_memcpy(q, p, payload_len);
+ q += payload_len;
+
+ /* Update the bitstream writing offset */
+ *pos = q - bits;
+
+ pktz->unpack_prev_lost = PJ_FALSE;
+
+ return PJ_SUCCESS;
+}
diff --git a/pjmedia/src/pjmedia-codec/h264_packetizer.c b/pjmedia/src/pjmedia-codec/h264_packetizer.c
new file mode 100644
index 00000000..8eb22d6f
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/h264_packetizer.c
@@ -0,0 +1,530 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-codec/h264_packetizer.h>
+#include <pjmedia/types.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+#define THIS_FILE "h264_packetizer.c"
+
+#define DBG_PACKETIZE 0
+#define DBG_UNPACKETIZE 0
+
+
+/* H.264 packetizer definition */
+struct pjmedia_h264_packetizer
+{
+ /* Current settings */
+ pjmedia_h264_packetizer_cfg cfg;
+
+ /* Unpacketizer state */
+ unsigned unpack_last_sync_pos;
+ pj_bool_t unpack_prev_lost;
+};
+
+
+/* Enumeration of H.264 NAL unit types */
+enum
+{
+ NAL_TYPE_SINGLE_NAL_MIN = 1,
+ NAL_TYPE_SINGLE_NAL_MAX = 23,
+ NAL_TYPE_STAP_A = 24,
+ NAL_TYPE_FU_A = 28,
+};
+
+
+/*
+ * Find next NAL unit from the specified H.264 bitstream data.
+ */
+static pj_uint8_t* find_next_nal_unit(pj_uint8_t *start,
+ pj_uint8_t *end)
+{
+ pj_uint8_t *p = start;
+
+ /* Simply lookup "0x000001" pattern */
+ while (p <= end-3 && (p[0] || p[1] || p[2]!=1))
+ ++p;
+
+ if (p > end-3)
+ /* No more NAL unit in this bitstream */
+ return NULL;
+
+ /* Include 8 bits leading zero */
+ if (p>start && *(p-1)==0)
+ return (p-1);
+
+ return p;
+}
+
+
+/*
+ * Create H264 packetizer.
+ */
+PJ_DEF(pj_status_t) pjmedia_h264_packetizer_create(
+ pj_pool_t *pool,
+ const pjmedia_h264_packetizer_cfg *cfg,
+ pjmedia_h264_packetizer **p)
+{
+ pjmedia_h264_packetizer *p_;
+
+ PJ_ASSERT_RETURN(pool && p, PJ_EINVAL);
+
+ if (cfg &&
+ cfg->mode != PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED &&
+ cfg->mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL)
+ {
+ return PJ_ENOTSUP;
+ }
+
+ p_ = PJ_POOL_ZALLOC_T(pool, pjmedia_h264_packetizer);
+ if (cfg) {
+ pj_memcpy(&p_->cfg, cfg, sizeof(*cfg));
+ } else {
+ p_->cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED;
+ p_->cfg.mtu = PJMEDIA_MAX_MTU;
+ }
+
+ *p = p_;
+
+ return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Generate an RTP payload from H.264 frame bitstream, in-place processing.
+ */
+PJ_DEF(pj_status_t) pjmedia_h264_packetize(pjmedia_h264_packetizer *pktz,
+ pj_uint8_t *buf,
+ pj_size_t buf_len,
+ unsigned *pos,
+ const pj_uint8_t **payload,
+ pj_size_t *payload_len)
+{
+ pj_uint8_t *nal_start = NULL, *nal_end = NULL, *nal_octet = NULL;
+ pj_uint8_t *p, *end;
+ enum {
+ HEADER_SIZE_FU_A = 2,
+ HEADER_SIZE_STAP_A = 3,
+ };
+ enum { MAX_NALS_IN_AGGR = 32 };
+
+#if DBG_PACKETIZE
+ if (*pos == 0 && buf_len) {
+ PJ_LOG(3, ("h264pack", "<< Start packing new frame >>"));
+ }
+#endif
+
+ p = buf + *pos;
+ end = buf + buf_len;
+
+ /* Find NAL unit startcode */
+ if (end-p >= 4)
+ nal_start = find_next_nal_unit(p, p+4);
+ if (nal_start) {
+ /* Get NAL unit octet pointer */
+ while (*nal_start++ == 0);
+ nal_octet = nal_start;
+ } else {
+ /* This NAL unit is being fragmented */
+ nal_start = p;
+ }
+
+ /* Get end of NAL unit */
+ p = nal_start+pktz->cfg.mtu+1;
+ if (p > end || pktz->cfg.mode==PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL)
+ p = end;
+ nal_end = find_next_nal_unit(nal_start, p);
+ if (!nal_end)
+ nal_end = p;
+
+ /* Validate MTU vs NAL length on single NAL unit packetization */
+ if ((pktz->cfg.mode==PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) &&
+ nal_end - nal_start > pktz->cfg.mtu)
+ {
+ //pj_assert(!"MTU too small for H.264 single NAL packetization mode");
+ PJ_LOG(2,("h264_packetizer.c",
+ "MTU too small for H.264 (required=%u, MTU=%u)",
+ nal_end - nal_start, pktz->cfg.mtu));
+ return PJ_ETOOSMALL;
+ }
+
+ /* Evaluate the proper payload format structure */
+
+ /* Fragmentation (FU-A) packet */
+ if ((pktz->cfg.mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) &&
+ (!nal_octet || nal_end-nal_start > pktz->cfg.mtu))
+ {
+ pj_uint8_t NRI, TYPE;
+
+ if (nal_octet) {
+ /* We have NAL unit octet, so this is the first fragment */
+ NRI = (*nal_octet & 0x60) >> 5;
+ TYPE = *nal_octet & 0x1F;
+
+ /* Skip nal_octet in nal_start to be overriden by FU header */
+ ++nal_start;
+ } else {
+ /* Not the first fragment, get NRI and NAL unit type
+ * from the previous fragment.
+ */
+ p = nal_start - pktz->cfg.mtu;
+ NRI = (*p & 0x60) >> 5;
+ TYPE = *(p+1) & 0x1F;
+ }
+
+ /* Init FU indicator (one octet: F+NRI+TYPE) */
+ p = nal_start - HEADER_SIZE_FU_A;
+ *p = (NRI << 5) | NAL_TYPE_FU_A;
+ ++p;
+
+ /* Init FU header (one octed: S+E+R+TYPE) */
+ *p = TYPE;
+ if (nal_octet)
+ *p |= (1 << 7); /* S bit flag = start of fragmentation */
+ if (nal_end-nal_start+HEADER_SIZE_FU_A <= pktz->cfg.mtu)
+ *p |= (1 << 6); /* E bit flag = end of fragmentation */
+
+ /* Set payload, payload length */
+ *payload = nal_start - HEADER_SIZE_FU_A;
+ if (nal_end-nal_start+HEADER_SIZE_FU_A > pktz->cfg.mtu)
+ *payload_len = pktz->cfg.mtu;
+ else
+ *payload_len = nal_end - nal_start + HEADER_SIZE_FU_A;
+ *pos = *payload + *payload_len - buf;
+
+#if DBG_PACKETIZE
+ PJ_LOG(3, ("h264pack", "Packetized fragmented H264 NAL unit "
+ "(pos=%d, type=%d, NRI=%d, S=%d, E=%d, len=%d/%d)",
+ *payload-buf, TYPE, NRI, *p>>7, (*p>>6)&1, *payload_len,
+ buf_len));
+#endif
+
+ return PJ_SUCCESS;
+ }
+
+ /* Aggregation (STAP-A) packet */
+ if ((pktz->cfg.mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) &&
+ (nal_end != end) &&
+ (nal_end - nal_start + HEADER_SIZE_STAP_A) < pktz->cfg.mtu)
+ {
+ int total_size;
+ unsigned nal_cnt = 1;
+ pj_uint8_t *nal[MAX_NALS_IN_AGGR];
+ pj_size_t nal_size[MAX_NALS_IN_AGGR];
+ pj_uint8_t NRI;
+
+ pj_assert(nal_octet);
+
+ /* Init the first NAL unit in the packet */
+ nal[0] = nal_start;
+ nal_size[0] = nal_end - nal_start;
+ total_size = nal_size[0] + HEADER_SIZE_STAP_A;
+ NRI = (*nal_octet & 0x60) >> 5;
+
+ /* Populate next NAL units */
+ while (nal_cnt < MAX_NALS_IN_AGGR) {
+ pj_uint8_t *tmp_end;
+
+ /* Find start address of the next NAL unit */
+ p = nal[nal_cnt-1] + nal_size[nal_cnt-1];
+ while (*p++ == 0);
+ nal[nal_cnt] = p;
+
+ /* Find end address of the next NAL unit */
+ tmp_end = p + (pktz->cfg.mtu - total_size);
+ if (tmp_end > end)
+ tmp_end = end;
+ p = find_next_nal_unit(p+1, tmp_end);
+ if (p) {
+ nal_size[nal_cnt] = p - nal[nal_cnt];
+ } else {
+ break;
+ }
+
+ /* Update total payload size (2 octet NAL size + NAL) */
+ total_size += (2 + nal_size[nal_cnt]);
+ if (total_size <= pktz->cfg.mtu) {
+ pj_uint8_t tmp_nri;
+
+ /* Get maximum NRI of the aggregated NAL units */
+ tmp_nri = (*(nal[nal_cnt]-1) & 0x60) >> 5;
+ if (tmp_nri > NRI)
+ NRI = tmp_nri;
+ } else {
+ break;
+ }
+
+ ++nal_cnt;
+ }
+
+ /* Only use STAP-A when we found more than one NAL units */
+ if (nal_cnt > 1) {
+ unsigned i;
+
+ /* Init STAP-A NAL header (F+NRI+TYPE) */
+ p = nal[0] - HEADER_SIZE_STAP_A;
+ *p++ = (NRI << 5) | NAL_TYPE_STAP_A;
+
+ /* Append all populated NAL units into payload (SIZE+NAL) */
+ for (i = 0; i < nal_cnt; ++i) {
+ /* Put size (2 octets in network order) */
+ pj_assert(nal_size[i] <= 0xFFFF);
+ *p++ = (pj_uint8_t)(nal_size[i] >> 8);
+ *p++ = (pj_uint8_t)(nal_size[i] & 0xFF);
+
+ /* Append NAL unit, watchout memmove()-ing bitstream! */
+ if (p != nal[i])
+ pj_memmove(p, nal[i], nal_size[i]);
+ p += nal_size[i];
+ }
+
+ /* Set payload, payload length, and pos */
+ *payload = nal[0] - HEADER_SIZE_STAP_A;
+ pj_assert(*payload >= buf+*pos);
+ *payload_len = p - *payload;
+ *pos = nal[nal_cnt-1] + nal_size[nal_cnt-1] - buf;
+
+#if DBG_PACKETIZE
+ PJ_LOG(3, ("h264pack", "Packetized aggregation of "
+ "%d H264 NAL units (pos=%d, NRI=%d len=%d/%d)",
+ nal_cnt, *payload-buf, NRI, *payload_len, buf_len));
+#endif
+
+ return PJ_SUCCESS;
+ }
+ }
+
+ /* Single NAL unit packet */
+ pj_assert(nal_octet);
+
+ *payload = nal_start;
+ *payload_len = nal_end - nal_start;
+ *pos = nal_end - buf;
+
+#if DBG_PACKETIZE
+ PJ_LOG(3, ("h264pack", "Packetized single H264 NAL unit "
+ "(pos=%d, type=%d, NRI=%d, len=%d/%d)",
+ nal_start-buf, *nal_octet&0x1F, (*nal_octet&0x60)>>5,
+ *payload_len, buf_len));
+#endif
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Append RTP payload to a H.264 picture bitstream. Note that the only
+ * payload format that cares about packet lost is the NAL unit
+ * fragmentation format (FU-A/B), so we will only manage the "prev_lost"
+ * state for the FU-A/B packets.
+ */
+PJ_DEF(pj_status_t) pjmedia_h264_unpacketize(pjmedia_h264_packetizer *pktz,
+ const pj_uint8_t *payload,
+ pj_size_t payload_len,
+ pj_uint8_t *bits,
+ pj_size_t bits_len,
+ unsigned *bits_pos)
+{
+ const pj_uint8_t nal_start_code[3] = {0, 0, 1};
+ enum { MIN_PAYLOAD_SIZE = 2 };
+ pj_uint8_t nal_type;
+
+ PJ_UNUSED_ARG(pktz);
+
+#if DBG_UNPACKETIZE
+ if (*bits_pos == 0 && payload_len) {
+ PJ_LOG(3, ("h264unpack", ">> Start unpacking new frame <<"));
+ }
+#endif
+
+ /* Check if this is a missing/lost packet */
+ if (payload == NULL) {
+ pktz->unpack_prev_lost = PJ_TRUE;
+ return PJ_SUCCESS;
+ }
+
+ /* H264 payload size */
+ if (payload_len < MIN_PAYLOAD_SIZE) {
+ /* Invalid bitstream, discard this payload */
+ pktz->unpack_prev_lost = PJ_TRUE;
+ return PJ_EINVAL;
+ }
+
+ /* Reset last sync point for every new picture bitstream */
+ if (*bits_pos == 0)
+ pktz->unpack_last_sync_pos = 0;
+
+ nal_type = *payload & 0x1F;
+ if (nal_type >= NAL_TYPE_SINGLE_NAL_MIN &&
+ nal_type <= NAL_TYPE_SINGLE_NAL_MAX)
+ {
+ /* Single NAL unit packet */
+ pj_uint8_t *p = bits + *bits_pos;
+
+ /* Validate bitstream length */
+ if (bits_len-*bits_pos < payload_len+PJ_ARRAY_SIZE(nal_start_code)) {
+ /* Insufficient bistream buffer, discard this payload */
+ pj_assert(!"Insufficient H.263 bitstream buffer");
+ return PJ_ETOOSMALL;
+ }
+
+ /* Write NAL unit start code */
+ pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code));
+ p += PJ_ARRAY_SIZE(nal_start_code);
+
+ /* Write NAL unit */
+ pj_memcpy(p, payload, payload_len);
+ p += payload_len;
+
+ /* Update the bitstream writing offset */
+ *bits_pos = p - bits;
+ pktz->unpack_last_sync_pos = *bits_pos;
+
+#if DBG_UNPACKETIZE
+ PJ_LOG(3, ("h264unpack", "Unpacked single H264 NAL unit "
+ "(type=%d, NRI=%d, len=%d)",
+ nal_type, (*payload&0x60)>>5, payload_len));
+#endif
+
+ }
+ else if (nal_type == NAL_TYPE_STAP_A)
+ {
+ /* Aggregation packet */
+ pj_uint8_t *p, *p_end;
+ const pj_uint8_t *q, *q_end;
+ unsigned cnt = 0;
+
+ /* Validate bitstream length */
+ if (bits_len - *bits_pos < payload_len + 32) {
+ /* Insufficient bistream buffer, discard this payload */
+ pj_assert(!"Insufficient H.263 bitstream buffer");
+ return PJ_ETOOSMALL;
+ }
+
+ /* Fill bitstream */
+ p = bits + *bits_pos;
+ p_end = bits + bits_len;
+ q = payload + 1;
+ q_end = payload + payload_len;
+ while (q < q_end && p < p_end) {
+ pj_uint16_t tmp_nal_size;
+
+ /* Write NAL unit start code */
+ pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code));
+ p += PJ_ARRAY_SIZE(nal_start_code);
+
+ /* Get NAL unit size */
+ tmp_nal_size = (*q << 8) | *(q+1);
+ q += 2;
+ if (q + tmp_nal_size > q_end) {
+ /* Invalid bitstream, discard the rest of the payload */
+ return PJ_EINVAL;
+ }
+
+ /* Write NAL unit */
+ pj_memcpy(p, q, tmp_nal_size);
+ p += tmp_nal_size;
+ q += tmp_nal_size;
+ ++cnt;
+
+ /* Update the bitstream writing offset */
+ *bits_pos = p - bits;
+ pktz->unpack_last_sync_pos = *bits_pos;
+ }
+
+#if DBG_UNPACKETIZE
+ PJ_LOG(3, ("h264unpack", "Unpacked %d H264 NAL units (len=%d)",
+ cnt, payload_len));
+#endif
+
+ }
+ else if (nal_type == NAL_TYPE_FU_A)
+ {
+ /* Fragmentation packet */
+ pj_uint8_t *p;
+ const pj_uint8_t *q = payload;
+ pj_uint8_t NRI, TYPE, S, E;
+
+ p = bits + *bits_pos;
+
+ /* Validate bitstream length */
+ if (bits_len-*bits_pos < payload_len+PJ_ARRAY_SIZE(nal_start_code)) {
+ /* Insufficient bistream buffer, drop this packet */
+ pj_assert(!"Insufficient H.263 bitstream buffer");
+ pktz->unpack_prev_lost = PJ_TRUE;
+ return PJ_ETOOSMALL;
+ }
+
+ /* Get info */
+ S = *(q+1) & 0x80; /* Start bit flag */
+ E = *(q+1) & 0x40; /* End bit flag */
+ TYPE = *(q+1) & 0x1f;
+ NRI = (*q & 0x60) >> 5;
+
+ /* Fill bitstream */
+ if (S) {
+ /* This is the first part, write NAL unit start code */
+ pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code));
+ p += PJ_ARRAY_SIZE(nal_start_code);
+
+ /* Write NAL unit octet */
+ *p++ = (NRI << 5) | TYPE;
+ } else if (pktz->unpack_prev_lost) {
+ /* If prev packet was lost, revert the bitstream pointer to
+ * the last sync point.
+ */
+ pj_assert(pktz->unpack_last_sync_pos <= *bits_pos);
+ *bits_pos = pktz->unpack_last_sync_pos;
+ /* And discard this payload (and the following fragmentation
+ * payloads carrying this same NAL unit.
+ */
+ return PJ_EIGNORED;
+ }
+ q += 2;
+
+ /* Write NAL unit */
+ pj_memcpy(p, q, payload_len - 2);
+ p += (payload_len - 2);
+
+ /* Update the bitstream writing offset */
+ *bits_pos = p - bits;
+ if (E) {
+ /* Update the sync pos only if the end bit flag is set */
+ pktz->unpack_last_sync_pos = *bits_pos;
+ }
+
+#if DBG_UNPACKETIZE
+ PJ_LOG(3, ("h264unpack", "Unpacked fragmented H264 NAL unit "
+ "(type=%d, NRI=%d, len=%d)",
+ TYPE, NRI, payload_len));
+#endif
+
+ } else {
+ *bits_pos = 0;
+ return PJ_ENOTSUP;
+ }
+
+ pktz->unpack_prev_lost = PJ_FALSE;
+
+ return PJ_SUCCESS;
+}
diff --git a/pjmedia/src/pjmedia-codec/ilbc.c b/pjmedia/src/pjmedia-codec/ilbc.c
index f30b5f09..c595477f 100644
--- a/pjmedia/src/pjmedia-codec/ilbc.c
+++ b/pjmedia/src/pjmedia-codec/ilbc.c
@@ -113,7 +113,8 @@ static pjmedia_codec_factory_op ilbc_factory_op =
&ilbc_default_attr,
&ilbc_enum_codecs,
&ilbc_alloc_codec,
- &ilbc_dealloc_codec
+ &ilbc_dealloc_codec,
+ &pjmedia_codec_ilbc_deinit
};
/* iLBC factory */
diff --git a/pjmedia/src/pjmedia-codec/ipp_codecs.c b/pjmedia/src/pjmedia-codec/ipp_codecs.c
index 023cdc7a..f5690eee 100644
--- a/pjmedia/src/pjmedia-codec/ipp_codecs.c
+++ b/pjmedia/src/pjmedia-codec/ipp_codecs.c
@@ -103,7 +103,8 @@ static pjmedia_codec_factory_op ipp_factory_op =
&ipp_default_attr,
&ipp_enum_codecs,
&ipp_alloc_codec,
- &ipp_dealloc_codec
+ &ipp_dealloc_codec,
+ &pjmedia_codec_ipp_deinit
};
/* IPP codecs factory */
diff --git a/pjmedia/src/pjmedia-codec/l16.c b/pjmedia/src/pjmedia-codec/l16.c
index 3be8a1b6..71b72009 100644
--- a/pjmedia/src/pjmedia-codec/l16.c
+++ b/pjmedia/src/pjmedia-codec/l16.c
@@ -112,7 +112,8 @@ static pjmedia_codec_factory_op l16_factory_op =
&l16_default_attr,
&l16_enum_codecs,
&l16_alloc_codec,
- &l16_dealloc_codec
+ &l16_dealloc_codec,
+ &pjmedia_codec_l16_deinit
};
/* L16 factory private data */
diff --git a/pjmedia/src/pjmedia-codec/passthrough.c b/pjmedia/src/pjmedia-codec/passthrough.c
index 73eed891..8ded8e2a 100644
--- a/pjmedia/src/pjmedia-codec/passthrough.c
+++ b/pjmedia/src/pjmedia-codec/passthrough.c
@@ -98,7 +98,8 @@ static pjmedia_codec_factory_op codec_factory_op =
&default_attr,
&enum_codecs,
&alloc_codec,
- &dealloc_codec
+ &dealloc_codec,
+ &pjmedia_codec_passthrough_deinit
};
/* Passthrough codecs factory */
diff --git a/pjmedia/src/pjmedia-codec/speex_codec.c b/pjmedia/src/pjmedia-codec/speex_codec.c
index eff88235..d5cc0a57 100644
--- a/pjmedia/src/pjmedia-codec/speex_codec.c
+++ b/pjmedia/src/pjmedia-codec/speex_codec.c
@@ -99,7 +99,8 @@ static pjmedia_codec_factory_op spx_factory_op =
&spx_default_attr,
&spx_enum_codecs,
&spx_alloc_codec,
- &spx_dealloc_codec
+ &spx_dealloc_codec,
+ &pjmedia_codec_speex_deinit
};
/* Index to Speex parameter. */
diff --git a/pjmedia/src/pjmedia-videodev/colorbar_dev.c b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
new file mode 100644
index 00000000..7d6c348f
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
@@ -0,0 +1,622 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/rand.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC
+
+#define THIS_FILE "colorbar_dev.c"
+#define DEFAULT_CLOCK_RATE 90000
+#define DEFAULT_WIDTH 352 //640
+#define DEFAULT_HEIGHT 288 //480
+#define DEFAULT_FPS 25
+
+/* cbar_ device info */
+struct cbar_dev_info
+{
+ pjmedia_vid_dev_info info;
+};
+
+/* cbar_ factory */
+struct cbar_factory
+{
+ pjmedia_vid_dev_factory base;
+ pj_pool_t *pool;
+ pj_pool_factory *pf;
+
+ unsigned dev_count;
+ struct cbar_dev_info *dev_info;
+};
+
+struct cbar_fmt_info {
+ pjmedia_format_id fmt_id; /* Format ID */
+
+ /* Info for packed formats. */
+ unsigned c_offset[3]; /* Color component offset,
+ in bytes */
+ unsigned c_stride[3]; /* Color component stride,
+ or distance between two
+ consecutive same color
+ components, in bytes */
+};
+
+/* Colorbar video source supports */
+static struct cbar_fmt_info cbar_fmts[] =
+{
+ /* Packed formats */
+ { PJMEDIA_FORMAT_YUY2, {0, 1, 3}, {2, 4, 4} },
+ { PJMEDIA_FORMAT_UYVY, {1, 0, 2}, {2, 4, 4} },
+ { PJMEDIA_FORMAT_YVYU, {0, 3, 1}, {2, 4, 4} },
+ { PJMEDIA_FORMAT_RGBA, {0, 1, 2}, {4, 4, 4} },
+ { PJMEDIA_FORMAT_RGB24, {0, 1, 2}, {3, 3, 3} },
+ { PJMEDIA_FORMAT_BGRA, {2, 1, 0}, {4, 4, 4} },
+
+ /* Planar formats */
+ { PJMEDIA_FORMAT_YV12 },
+ { PJMEDIA_FORMAT_I420 },
+ { PJMEDIA_FORMAT_I420JPEG },
+ { PJMEDIA_FORMAT_I422JPEG },
+};
+
+/* Video stream. */
+struct cbar_stream
+{
+ pjmedia_vid_dev_stream base; /**< Base stream */
+ pjmedia_vid_dev_param param; /**< Settings */
+ pj_pool_t *pool; /**< Memory pool. */
+
+ pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */
+ void *user_data; /**< Application data. */
+
+ const struct cbar_fmt_info *cbfi;
+ const pjmedia_video_format_info *vfi;
+ pjmedia_video_apply_fmt_param vafp;
+ pj_uint8_t *first_line[PJMEDIA_MAX_VIDEO_PLANES];
+ pj_timestamp ts;
+ unsigned ts_inc;
+};
+
+
+/* Prototypes */
+static pj_status_t cbar_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t cbar_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t cbar_factory_refresh(pjmedia_vid_dev_factory *f);
+static unsigned cbar_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t cbar_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info);
+static pj_status_t cbar_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param);
+static pj_status_t cbar_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm);
+
+static pj_status_t cbar_stream_get_param(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_param *param);
+static pj_status_t cbar_stream_get_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
+static pj_status_t cbar_stream_set_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
+static pj_status_t cbar_stream_get_frame(pjmedia_vid_dev_stream *strm,
+ pjmedia_frame *frame);
+static pj_status_t cbar_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t cbar_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t cbar_stream_destroy(pjmedia_vid_dev_stream *strm);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+ &cbar_factory_init,
+ &cbar_factory_destroy,
+ &cbar_factory_get_dev_count,
+ &cbar_factory_get_dev_info,
+ &cbar_factory_default_param,
+ &cbar_factory_create_stream,
+ &cbar_factory_refresh
+};
+
+static pjmedia_vid_dev_stream_op stream_op =
+{
+ &cbar_stream_get_param,
+ &cbar_stream_get_cap,
+ &cbar_stream_set_cap,
+ &cbar_stream_start,
+ &cbar_stream_get_frame,
+ NULL,
+ &cbar_stream_stop,
+ &cbar_stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init cbar_ video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_cbar_factory(pj_pool_factory *pf)
+{
+ struct cbar_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, "cbar video", 512, 512, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, struct cbar_factory);
+ f->pf = pf;
+ f->pool = pool;
+ f->base.op = &factory_op;
+
+ return &f->base;
+}
+
+
+/* API: init factory */
+static pj_status_t cbar_factory_init(pjmedia_vid_dev_factory *f)
+{
+ struct cbar_factory *cf = (struct cbar_factory*)f;
+ struct cbar_dev_info *ddi;
+ unsigned i;
+
+ cf->dev_count = 1;
+ cf->dev_info = (struct cbar_dev_info*)
+ pj_pool_calloc(cf->pool, cf->dev_count,
+ sizeof(struct cbar_dev_info));
+
+ ddi = &cf->dev_info[0];
+ pj_bzero(ddi, sizeof(*ddi));
+ pj_ansi_strncpy(ddi->info.name, "Colorbar generator",
+ sizeof(ddi->info.name));
+ ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
+ pj_ansi_strncpy(ddi->info.driver, "Colorbar", sizeof(ddi->info.driver));
+ ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
+ ddi->info.dir = PJMEDIA_DIR_CAPTURE;
+ ddi->info.has_callback = PJ_FALSE;
+
+ ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+ ddi->info.fmt_cnt = sizeof(cbar_fmts)/sizeof(cbar_fmts[0]);
+ for (i = 0; i < ddi->info.fmt_cnt; i++) {
+ pjmedia_format *fmt = &ddi->info.fmt[i];
+ pjmedia_format_init_video(fmt, cbar_fmts[i].fmt_id,
+ DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ DEFAULT_FPS, 1);
+ }
+
+ PJ_LOG(4, (THIS_FILE, "Colorbar video src initialized with %d device(s):",
+ cf->dev_count));
+ for (i = 0; i < cf->dev_count; i++) {
+ PJ_LOG(4, (THIS_FILE, "%2d: %s", i, cf->dev_info[i].info.name));
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t cbar_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+ struct cbar_factory *cf = (struct cbar_factory*)f;
+ pj_pool_t *pool = cf->pool;
+
+ cf->pool = NULL;
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/* API: refresh the list of devices */
+static pj_status_t cbar_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+ PJ_UNUSED_ARG(f);
+ return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned cbar_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+ struct cbar_factory *cf = (struct cbar_factory*)f;
+ return cf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t cbar_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info)
+{
+ struct cbar_factory *cf = (struct cbar_factory*)f;
+
+ PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV);
+
+ pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info));
+
+ return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t cbar_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param)
+{
+ struct cbar_factory *cf = (struct cbar_factory*)f;
+ struct cbar_dev_info *di = &cf->dev_info[index];
+
+ PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV);
+
+ PJ_UNUSED_ARG(pool);
+
+ pj_bzero(param, sizeof(*param));
+ param->dir = PJMEDIA_DIR_CAPTURE;
+ param->cap_id = index;
+ param->rend_id = PJMEDIA_VID_INVALID_DEV;
+ param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+ param->clock_rate = DEFAULT_CLOCK_RATE;
+ pj_memcpy(&param->fmt, &di->info.fmt[0], sizeof(param->fmt));
+
+ return PJ_SUCCESS;
+}
+
+static const struct cbar_fmt_info* get_cbar_fmt_info(pjmedia_format_id id)
+{
+ unsigned i;
+
+ for (i = 0; i < sizeof(cbar_fmts)/sizeof(cbar_fmts[0]); i++) {
+ if (cbar_fmts[i].fmt_id == id)
+ return &cbar_fmts[i];
+ }
+
+ return NULL;
+}
+
+static void fill_first_line(pj_uint8_t *first_lines[],
+ const struct cbar_fmt_info *cbfi,
+ const pjmedia_video_format_info *vfi,
+ const pjmedia_video_apply_fmt_param *vafp)
+{
+ typedef pj_uint8_t color_comp_t[3];
+ color_comp_t rgb_colors[] =
+ {
+ {255,255,255}, {255,255,0}, {0,255,255}, {0,255,0},
+ {255,0,255}, {255,0,0}, {0,0,255}, {0,0,0}
+ };
+ color_comp_t yuv_colors[] =
+ {
+ //{235,128,128}, {162,44,142}, {131,156,44}, {112,72,58},
+ //{84,184,198}, {65,100,212}, {35,212,114}, {16,128,128}
+ {235,128,128}, {210,16,146}, {170,166,16}, {145,54,34},
+ {106,202,222}, {81,90,240}, {41,240,110}, {16,128,128}
+ };
+
+ unsigned i, j, k;
+
+ if (vfi->plane_cnt == 1) {
+ /* Packed */
+
+ for (i = 0; i < 8; ++i) {
+ /* iterate bars */
+ for (j = 0; j < 3; ++j) {
+ /* iterate color components */
+ pj_uint8_t *p = NULL, c;
+ unsigned bar_width, inc_p;
+
+ if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB)
+ c = rgb_colors[i][j];
+ else
+ c = yuv_colors[i][j];
+
+ bar_width = vafp->size.w/8;
+ bar_width /= (cbfi->c_stride[j] * 8 / vfi->bpp);
+ inc_p = cbfi->c_stride[j];
+ p = first_lines[0] + bar_width*i*inc_p + cbfi->c_offset[j];
+
+ /* draw this color */
+ for (k = 0; k < bar_width; ++k) {
+ *p = c;
+ p += inc_p;
+ }
+ }
+ }
+
+ } else if (vfi->plane_cnt == 3) {
+
+ for (i = 0; i < 8; ++i) {
+ /* iterate bars */
+ for (j = 0; j < 3; ++j) {
+ /* iterate planes/color components */
+ pj_uint8_t *p = NULL, c;
+ unsigned bar_width;
+
+ if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB)
+ c = rgb_colors[i][j];
+ else
+ c = yuv_colors[i][j];
+
+ bar_width = vafp->strides[j]/8;
+ p = first_lines[j] + bar_width*i;
+
+ /* draw this plane/color */
+ for (k = 0; k < bar_width; ++k)
+ *p++ = c;
+ }
+ }
+ }
+}
+
+/* API: create stream */
+static pj_status_t cbar_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
+{
+ struct cbar_factory *cf = (struct cbar_factory*)f;
+ pj_pool_t *pool;
+ struct cbar_stream *strm;
+ const pjmedia_video_format_detail *vfd;
+ const pjmedia_video_format_info *vfi;
+ pjmedia_video_apply_fmt_param vafp;
+ const struct cbar_fmt_info *cbfi;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
+ PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
+ param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
+ param->dir == PJMEDIA_DIR_CAPTURE,
+ PJ_EINVAL);
+
+ pj_bzero(&vafp, sizeof(vafp));
+
+ vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
+ vfi = pjmedia_get_video_format_info(NULL, param->fmt.id);
+ cbfi = get_cbar_fmt_info(param->fmt.id);
+ if (!vfi || !cbfi)
+ return PJMEDIA_EVID_BADFORMAT;
+
+ vafp.size = param->fmt.det.vid.size;
+ if (vfi->apply_fmt(vfi, &vafp) != PJ_SUCCESS)
+ return PJMEDIA_EVID_BADFORMAT;
+
+ /* Create and Initialize stream descriptor */
+ pool = pj_pool_create(cf->pf, "cbar-dev", 512, 512, NULL);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+ strm = PJ_POOL_ZALLOC_T(pool, struct cbar_stream);
+ pj_memcpy(&strm->param, param, sizeof(*param));
+ strm->pool = pool;
+ pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
+ strm->user_data = user_data;
+ strm->vfi = vfi;
+ strm->cbfi = cbfi;
+ pj_memcpy(&strm->vafp, &vafp, sizeof(vafp));
+ strm->ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1);
+ pjmedia_event_publisher_init(&strm->base.epub, PJMEDIA_SIG_VID_DEV_COLORBAR);
+
+ for (i = 0; i < vfi->plane_cnt; ++i) {
+ strm->first_line[i] = pj_pool_alloc(pool, vafp.strides[i]);
+ pj_memset(strm->first_line[i], 255, vafp.strides[i]);
+ }
+
+ fill_first_line(strm->first_line, strm->cbfi, vfi, &strm->vafp);
+
+ /* Apply the remaining settings */
+/* if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) {
+ cbar_stream_set_cap(&strm->base,
+ PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
+ &param->fmt);
+ }
+*/
+ /* Done */
+ strm->base.op = &stream_op;
+ *p_vid_strm = &strm->base;
+
+ return PJ_SUCCESS;
+}
+
+/* API: Get stream info. */
+static pj_status_t cbar_stream_get_param(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_param *pi)
+{
+ struct cbar_stream *strm = (struct cbar_stream*)s;
+
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+/* if (cbar_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
+ &pi->fmt.info_size) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_INPUT_SCALE;
+ }
+*/
+ return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t cbar_stream_get_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ void *pval)
+{
+ struct cbar_stream *strm = (struct cbar_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+ {
+ return PJMEDIA_EVID_INVCAP;
+// return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_EVID_INVCAP;
+ }
+}
+
+/* API: set capability */
+static pj_status_t cbar_stream_set_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ const void *pval)
+{
+ struct cbar_stream *strm = (struct cbar_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+ {
+ return PJ_SUCCESS;
+ }
+
+ return PJMEDIA_EVID_INVCAP;
+}
+
+static pj_status_t spectrum_run(struct cbar_stream *d, pj_uint8_t *p,
+ pj_size_t size)
+{
+ unsigned i;
+ pj_uint8_t *ptr = p;
+ pj_time_val tv;
+
+ PJ_UNUSED_ARG(size);
+
+ /* Subsequent lines */
+ for (i=0; i<d->vfi->plane_cnt; ++i) {
+ pj_uint8_t *plane_end;
+
+ plane_end = ptr + d->vafp.plane_bytes[i];
+ while (ptr < plane_end) {
+ pj_memcpy(ptr, d->first_line[i], d->vafp.strides[i]);
+ ptr += d->vafp.strides[i];
+ }
+ }
+
+ /* blinking dot */
+ pj_gettimeofday(&tv);
+ if (tv.msec < 660) {
+ enum { DOT_SIZE = 8 };
+ pj_uint8_t dot_clr_rgb[3] = {255, 255, 255};
+ pj_uint8_t dot_clr_yuv[3] = {235, 128, 128};
+
+ if (d->vfi->plane_cnt == 1) {
+ for (i = 0; i < 3; ++i) {
+ pj_uint8_t *ptr;
+ unsigned j, k, inc_ptr;
+ pj_size_t dot_size = DOT_SIZE;
+
+ dot_size /= (d->cbfi->c_stride[i] * 8 / d->vfi->bpp);
+ inc_ptr = d->cbfi->c_stride[i];
+ for (j = 0; j < dot_size; ++j) {
+ ptr = p + d->vafp.strides[0]*(dot_size+j+1) -
+ 2*dot_size*inc_ptr + d->cbfi->c_offset[i];
+ for (k = 0; k < dot_size; ++k) {
+ if (d->vfi->color_model == PJMEDIA_COLOR_MODEL_RGB)
+ *ptr = dot_clr_rgb[i];
+ else
+ *ptr = dot_clr_yuv[i];
+ ptr += inc_ptr;
+ }
+ }
+ }
+ } else {
+ pj_size_t offset_p = 0;
+
+ for (i = 0; i < 3; ++i) {
+ pj_uint8_t *ptr, c;
+ unsigned j;
+ pj_size_t dot_size = DOT_SIZE;
+
+ if (d->vfi->color_model == PJMEDIA_COLOR_MODEL_RGB)
+ c = dot_clr_rgb[i];
+ else
+ c = dot_clr_yuv[i];
+
+ dot_size /= (d->vafp.size.w / d->vafp.strides[i]);
+ ptr = p + offset_p + d->vafp.strides[i]*(dot_size+1) -
+ 2*dot_size;
+ for (j = 0; j < dot_size; ++j) {
+ pj_memset(ptr, c, dot_size);
+ ptr += d->vafp.strides[i];
+ }
+ offset_p += d->vafp.plane_bytes[i];
+ }
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: Get frame from stream */
+static pj_status_t cbar_stream_get_frame(pjmedia_vid_dev_stream *strm,
+ pjmedia_frame *frame)
+{
+ struct cbar_stream *stream = (struct cbar_stream*)strm;
+
+ frame->timestamp = stream->ts;
+ stream->ts.u64 += stream->ts_inc;
+ return spectrum_run(stream, frame->buf, frame->size);
+}
+
+/* API: Start stream. */
+static pj_status_t cbar_stream_start(pjmedia_vid_dev_stream *strm)
+{
+ struct cbar_stream *stream = (struct cbar_stream*)strm;
+
+ PJ_UNUSED_ARG(stream);
+
+ PJ_LOG(4, (THIS_FILE, "Starting cbar video stream"));
+
+ return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t cbar_stream_stop(pjmedia_vid_dev_stream *strm)
+{
+ struct cbar_stream *stream = (struct cbar_stream*)strm;
+
+ PJ_UNUSED_ARG(stream);
+
+ PJ_LOG(4, (THIS_FILE, "Stopping cbar video stream"));
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t cbar_stream_destroy(pjmedia_vid_dev_stream *strm)
+{
+ struct cbar_stream *stream = (struct cbar_stream*)strm;
+
+ PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+ cbar_stream_stop(strm);
+
+ pj_pool_release(stream->pool);
+
+ return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC */
diff --git a/pjmedia/src/pjmedia-videodev/dshow_dev.c b/pjmedia/src/pjmedia-videodev/dshow_dev.c
new file mode 100644
index 00000000..c69393fa
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/dshow_dev.c
@@ -0,0 +1,1016 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/unicode.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_DSHOW
+
+#ifdef _MSC_VER
+# pragma warning(push, 3)
+#endif
+
+#include <windows.h>
+#define COBJMACROS
+#include <DShow.h>
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+#pragma comment(lib, "Strmiids.lib")
+#pragma comment(lib, "Rpcrt4.lib")
+#pragma comment(lib, "Quartz.lib")
+
+#define THIS_FILE "dshow_dev.c"
+#define DEFAULT_CLOCK_RATE 90000
+#define DEFAULT_WIDTH 640
+#define DEFAULT_HEIGHT 480
+#define DEFAULT_FPS 25
+
+/* Temporarily disable DirectShow renderer (VMR) */
+#define HAS_VMR 0
+
+typedef void (*input_callback)(void *user_data, IMediaSample *pMediaSample);
+typedef struct NullRenderer NullRenderer;
+IBaseFilter* NullRenderer_Create(input_callback input_cb,
+ void *user_data);
+typedef struct SourceFilter SourceFilter;
+IBaseFilter* SourceFilter_Create(SourceFilter **pSrc);
+HRESULT SourceFilter_Deliver(SourceFilter *src, void *buf, long size);
+void SourceFilter_SetMediaType(SourceFilter *src, AM_MEDIA_TYPE *pmt);
+
+typedef struct dshow_fmt_info
+{
+ pjmedia_format_id pjmedia_format;
+ const GUID *dshow_format;
+} dshow_fmt_info;
+
+static dshow_fmt_info dshow_fmts[] =
+{
+ {PJMEDIA_FORMAT_YUY2, &MEDIASUBTYPE_YUY2} ,
+ {PJMEDIA_FORMAT_RGB24, &MEDIASUBTYPE_RGB24} ,
+ {PJMEDIA_FORMAT_RGB32, &MEDIASUBTYPE_RGB32} ,
+ {PJMEDIA_FORMAT_IYUV, &MEDIASUBTYPE_IYUV} ,
+};
+
+/* dshow_ device info */
+struct dshow_dev_info
+{
+ pjmedia_vid_dev_info info;
+ unsigned dev_id;
+ WCHAR display_name[192];
+};
+
+/* dshow_ factory */
+struct dshow_factory
+{
+ pjmedia_vid_dev_factory base;
+ pj_pool_t *pool;
+ pj_pool_t *dev_pool;
+ pj_pool_factory *pf;
+
+ unsigned dev_count;
+ struct dshow_dev_info *dev_info;
+};
+
+/* Video stream. */
+struct dshow_stream
+{
+ pjmedia_vid_dev_stream base; /**< Base stream */
+ pjmedia_vid_dev_param param; /**< Settings */
+ pj_pool_t *pool; /**< Memory pool. */
+
+ pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */
+ void *user_data; /**< Application data. */
+
+ pj_bool_t quit_flag;
+ pj_bool_t rend_thread_exited;
+ pj_bool_t cap_thread_exited;
+ pj_bool_t cap_thread_initialized;
+ pj_thread_desc cap_thread_desc;
+ pj_thread_t *cap_thread;
+
+ struct dshow_graph
+ {
+ IFilterGraph *filter_graph;
+ IMediaFilter *media_filter;
+ SourceFilter *csource_filter;
+ IBaseFilter *source_filter;
+ IBaseFilter *rend_filter;
+ AM_MEDIA_TYPE *mediatype;
+ } dgraph;
+
+ pj_timestamp cap_ts;
+ unsigned cap_ts_inc;
+};
+
+
+/* Prototypes */
+static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t dshow_factory_refresh(pjmedia_vid_dev_factory *f);
+static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info);
+static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param);
+static pj_status_t dshow_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm);
+
+static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_param *param);
+static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
+static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
+static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame);
+static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+ &dshow_factory_init,
+ &dshow_factory_destroy,
+ &dshow_factory_get_dev_count,
+ &dshow_factory_get_dev_info,
+ &dshow_factory_default_param,
+ &dshow_factory_create_stream,
+ &dshow_factory_refresh
+};
+
+static pjmedia_vid_dev_stream_op stream_op =
+{
+ &dshow_stream_get_param,
+ &dshow_stream_get_cap,
+ &dshow_stream_set_cap,
+ &dshow_stream_start,
+ NULL,
+ &dshow_stream_put_frame,
+ &dshow_stream_stop,
+ &dshow_stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init dshow_ video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf)
+{
+ struct dshow_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, "dshow video", 1000, 1000, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, struct dshow_factory);
+ f->pf = pf;
+ f->pool = pool;
+ f->base.op = &factory_op;
+
+ return &f->base;
+}
+
+/* API: init factory */
+static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f)
+{
+ CoInitializeEx(NULL, COINIT_MULTITHREADED);
+
+ return dshow_factory_refresh(f);
+}
+
+/* API: destroy factory */
+static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+ struct dshow_factory *df = (struct dshow_factory*)f;
+ pj_pool_t *pool = df->pool;
+
+ df->pool = NULL;
+ if (df->dev_pool)
+ pj_pool_release(df->dev_pool);
+ if (pool)
+ pj_pool_release(pool);
+
+ CoUninitialize();
+
+ return PJ_SUCCESS;
+}
+
+static HRESULT get_cap_device(struct dshow_factory *df,
+ unsigned id,
+ IBaseFilter **filter)
+{
+ IBindCtx *pbc;
+ HRESULT hr;
+
+ hr = CreateBindCtx(0, &pbc);
+ if (SUCCEEDED (hr)) {
+ IMoniker *moniker;
+ DWORD pchEaten;
+
+ hr = MkParseDisplayName(pbc, df->dev_info[id].display_name,
+ &pchEaten, &moniker);
+ if (SUCCEEDED(hr)) {
+ hr = IMoniker_BindToObject(moniker, pbc, NULL,
+ &IID_IBaseFilter,
+ (LPVOID *)filter);
+ IMoniker_Release(moniker);
+ }
+ IBindCtx_Release(pbc);
+ }
+
+ return hr;
+}
+
+static void enum_dev_cap(IBaseFilter *filter,
+ pjmedia_dir dir,
+ const GUID *dshow_format,
+ AM_MEDIA_TYPE **pMediatype,
+ IPin **pSrcpin,
+ pj_bool_t *sup_fmt)
+{
+ IEnumPins *pEnum;
+ AM_MEDIA_TYPE *mediatype = NULL;
+ HRESULT hr;
+
+ if (pSrcpin)
+ *pSrcpin = NULL;
+ hr = IBaseFilter_EnumPins(filter, &pEnum);
+ if (SUCCEEDED(hr)) {
+ /* Loop through all the pins. */
+ IPin *pPin = NULL;
+
+ while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
+ PIN_DIRECTION pindirtmp;
+
+ hr = IPin_QueryDirection(pPin, &pindirtmp);
+ if (hr != S_OK || pindirtmp != PINDIR_OUTPUT) {
+ if (SUCCEEDED(hr))
+ IPin_Release(pPin);
+ continue;
+ }
+
+ if (dir == PJMEDIA_DIR_CAPTURE) {
+ IAMStreamConfig *streamcaps;
+
+ hr = IPin_QueryInterface(pPin, &IID_IAMStreamConfig,
+ (LPVOID *)&streamcaps);
+ if (SUCCEEDED(hr)) {
+ VIDEO_STREAM_CONFIG_CAPS vscc;
+ int i, isize, icount;
+
+ IAMStreamConfig_GetNumberOfCapabilities(streamcaps,
+ &icount, &isize);
+
+ for (i = 0; i < icount; i++) {
+ unsigned j, nformat;
+ RPC_STATUS rpcstatus, rpcstatus2;
+
+ hr = IAMStreamConfig_GetStreamCaps(streamcaps, i,
+ &mediatype,
+ (BYTE *)&vscc);
+ if (FAILED (hr))
+ continue;
+
+ nformat = (dshow_format? 1:
+ sizeof(dshow_fmts)/sizeof(dshow_fmts[0]));
+ for (j = 0; j < nformat; j++) {
+ if (!dshow_format || j > 0)
+ dshow_format = dshow_fmts[j].dshow_format;
+ if (UuidCompare(&mediatype->subtype,
+ (UUID*)dshow_format,
+ &rpcstatus) == 0 &&
+ rpcstatus == RPC_S_OK &&
+ UuidCompare(&mediatype->formattype,
+ (UUID*)&FORMAT_VideoInfo,
+ &rpcstatus2) == 0 &&
+ rpcstatus2 == RPC_S_OK)
+ {
+ if (sup_fmt)
+ sup_fmt[j] = PJ_TRUE;
+ if (pSrcpin) {
+ *pSrcpin = pPin;
+ *pMediatype = mediatype;
+ }
+ }
+ }
+ if (pSrcpin && *pSrcpin)
+ break;
+ }
+ IAMStreamConfig_Release(streamcaps);
+ }
+ } else {
+ *pSrcpin = pPin;
+ }
+ if (pSrcpin && *pSrcpin)
+ break;
+ IPin_Release(pPin);
+ }
+ IEnumPins_Release(pEnum);
+ }
+}
+
+/* API: refresh the list of devices */
+static pj_status_t dshow_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+ struct dshow_factory *df = (struct dshow_factory*)f;
+ struct dshow_dev_info *ddi;
+ int dev_count = 0;
+ unsigned c;
+ ICreateDevEnum *dev_enum = NULL;
+ IEnumMoniker *enum_cat = NULL;
+ IMoniker *moniker = NULL;
+ HRESULT hr;
+ ULONG fetched;
+
+ if (df->dev_pool) {
+ pj_pool_release(df->dev_pool);
+ df->dev_pool = NULL;
+ }
+
+ df->dev_count = 0;
+ df->dev_pool = pj_pool_create(df->pf, "dshow video", 500, 500, NULL);
+
+ hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL,
+ CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
+ (void**)&dev_enum);
+ if (FAILED(hr) ||
+ ICreateDevEnum_CreateClassEnumerator(dev_enum,
+ &CLSID_VideoInputDeviceCategory, &enum_cat, 0) != S_OK)
+ {
+ PJ_LOG(4,(THIS_FILE, "Windows found no video input devices"));
+ if (dev_enum)
+ ICreateDevEnum_Release(dev_enum);
+ dev_count = 0;
+ } else {
+ while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
+ dev_count++;
+ }
+ }
+
+ /* Add renderer device */
+ dev_count += 1;
+ df->dev_info = (struct dshow_dev_info*)
+ pj_pool_calloc(df->dev_pool, dev_count,
+ sizeof(struct dshow_dev_info));
+
+ if (dev_count > 1) {
+ IEnumMoniker_Reset(enum_cat);
+ while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
+ IPropertyBag *prop_bag;
+
+ hr = IMoniker_BindToStorage(moniker, 0, 0, &IID_IPropertyBag,
+ (void**)&prop_bag);
+ if (SUCCEEDED(hr)) {
+ VARIANT var_name;
+
+ VariantInit(&var_name);
+ hr = IPropertyBag_Read(prop_bag, L"FriendlyName",
+ &var_name, NULL);
+ if (SUCCEEDED(hr) && var_name.bstrVal) {
+ WCHAR *wszDisplayName = NULL;
+ IBaseFilter *filter;
+
+ ddi = &df->dev_info[df->dev_count++];
+ pj_bzero(ddi, sizeof(*ddi));
+ pj_unicode_to_ansi(var_name.bstrVal,
+ wcslen(var_name.bstrVal),
+ ddi->info.name,
+ sizeof(ddi->info.name));
+
+ hr = IMoniker_GetDisplayName(moniker, NULL, NULL,
+ &wszDisplayName);
+ if (hr == S_OK && wszDisplayName) {
+ pj_memcpy(ddi->display_name, wszDisplayName,
+ (wcslen(wszDisplayName)+1) * sizeof(WCHAR));
+ CoTaskMemFree(wszDisplayName);
+ }
+
+ strncpy(ddi->info.driver, "dshow",
+ sizeof(ddi->info.driver));
+ ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
+ ddi->info.dir = PJMEDIA_DIR_CAPTURE;
+ ddi->info.has_callback = PJ_TRUE;
+
+ /* Set the device capabilities here */
+ ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+
+ hr = get_cap_device(df, df->dev_count-1, &filter);
+ if (SUCCEEDED(hr)) {
+ unsigned j;
+ pj_bool_t sup_fmt[sizeof(dshow_fmts)/sizeof(dshow_fmts[0])];
+
+ pj_bzero(sup_fmt, sizeof(sup_fmt));
+ enum_dev_cap(filter, ddi->info.dir, NULL, NULL, NULL, sup_fmt);
+
+ ddi->info.fmt_cnt = 0;
+ for (j = 0;
+ j < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]);
+ j++)
+ {
+ if (!sup_fmt[j])
+ continue;
+ pjmedia_format_init_video(
+ &ddi->info.fmt[ddi->info.fmt_cnt++],
+ dshow_fmts[j].pjmedia_format,
+ DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ DEFAULT_FPS, 1);
+ }
+ }
+ }
+ VariantClear(&var_name);
+
+ IPropertyBag_Release(prop_bag);
+ }
+ IMoniker_Release(moniker);
+ }
+
+ IEnumMoniker_Release(enum_cat);
+ ICreateDevEnum_Release(dev_enum);
+ }
+
+#if HAS_VMR
+ ddi = &df->dev_info[df->dev_count++];
+ pj_bzero(ddi, sizeof(*ddi));
+ pj_ansi_strncpy(ddi->info.name, "Video Mixing Renderer",
+ sizeof(ddi->info.name));
+ ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
+ pj_ansi_strncpy(ddi->info.driver, "dshow", sizeof(ddi->info.driver));
+ ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
+ ddi->info.dir = PJMEDIA_DIR_RENDER;
+ ddi->info.has_callback = PJ_FALSE;
+ ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+// TODO:
+// ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+
+ ddi->info.fmt_cnt = 1;
+ pjmedia_format_init_video(&ddi->info.fmt[0], dshow_fmts[0].pjmedia_format,
+ DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ DEFAULT_FPS, 1);
+#endif
+
+ PJ_LOG(4, (THIS_FILE, "DShow has %d devices:",
+ df->dev_count));
+ for (c = 0; c < df->dev_count; ++c) {
+ PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (%s)",
+ c,
+ df->dev_info[c].info.name,
+ df->dev_info[c].info.dir & PJMEDIA_DIR_CAPTURE ?
+ "capture" : "render"));
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+ struct dshow_factory *df = (struct dshow_factory*)f;
+ return df->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info)
+{
+ struct dshow_factory *df = (struct dshow_factory*)f;
+
+ PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
+
+ pj_memcpy(info, &df->dev_info[index].info, sizeof(*info));
+
+ return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param)
+{
+ struct dshow_factory *df = (struct dshow_factory*)f;
+ struct dshow_dev_info *di = &df->dev_info[index];
+
+ PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
+
+ PJ_UNUSED_ARG(pool);
+
+ pj_bzero(param, sizeof(*param));
+ if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
+ param->dir = PJMEDIA_DIR_CAPTURE;
+ param->cap_id = index;
+ param->rend_id = PJMEDIA_VID_INVALID_DEV;
+ } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
+ param->dir = PJMEDIA_DIR_RENDER;
+ param->rend_id = index;
+ param->cap_id = PJMEDIA_VID_INVALID_DEV;
+ } else {
+ return PJMEDIA_EVID_INVDEV;
+ }
+
+ /* Set the device capabilities here */
+ param->clock_rate = DEFAULT_CLOCK_RATE;
+ param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+
+ pjmedia_format_copy(&param->fmt, &di->info.fmt[0]);
+
+ return PJ_SUCCESS;
+}
+
+static void input_cb(void *user_data, IMediaSample *pMediaSample)
+{
+ struct dshow_stream *strm = (struct dshow_stream*)user_data;
+ unsigned char *buffer;
+ pjmedia_frame frame = {0};
+
+ if (strm->quit_flag) {
+ strm->cap_thread_exited = PJ_TRUE;
+ return;
+ }
+
+ if (strm->cap_thread_initialized == 0 || !pj_thread_is_registered())
+ {
+ pj_status_t status;
+
+ status = pj_thread_register("ds_cap", strm->cap_thread_desc,
+ &strm->cap_thread);
+ if (status != PJ_SUCCESS)
+ return;
+ strm->cap_thread_initialized = 1;
+ PJ_LOG(5,(THIS_FILE, "Capture thread started"));
+ }
+
+ IMediaSample_GetPointer(pMediaSample, &buffer);
+
+ frame.type = PJMEDIA_TYPE_VIDEO;
+ IMediaSample_GetPointer(pMediaSample, (BYTE **)&frame.buf);
+ frame.size = IMediaSample_GetActualDataLength(pMediaSample);
+ frame.bit_info = 0;
+ frame.timestamp = strm->cap_ts;
+ strm->cap_ts.u64 += strm->cap_ts_inc;
+ if (strm->vid_cb.capture_cb)
+ (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame);
+}
+
+/* API: Put frame from stream */
+static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame)
+{
+ struct dshow_stream *stream = (struct dshow_stream*)strm;
+ HRESULT hr;
+
+ if (stream->quit_flag) {
+ stream->rend_thread_exited = PJ_TRUE;
+ return PJ_SUCCESS;
+ }
+
+ hr = SourceFilter_Deliver(stream->dgraph.csource_filter,
+ frame->buf, frame->size);
+ if (FAILED(hr))
+ return hr;
+
+ return PJ_SUCCESS;
+}
+
+static dshow_fmt_info* get_dshow_format_info(pjmedia_format_id id)
+{
+ unsigned i;
+
+ for (i = 0; i < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]); i++) {
+ if (dshow_fmts[i].pjmedia_format == id)
+ return &dshow_fmts[i];
+ }
+
+ return NULL;
+}
+
+static pj_status_t create_filter_graph(pjmedia_dir dir,
+ unsigned id,
+ pj_bool_t use_def_size,
+ pj_bool_t use_def_fps,
+ struct dshow_factory *df,
+ struct dshow_stream *strm,
+ struct dshow_graph *graph)
+{
+ HRESULT hr;
+ IEnumPins *pEnum;
+ IPin *srcpin = NULL;
+ IPin *sinkpin = NULL;
+ AM_MEDIA_TYPE *mediatype= NULL, mtype;
+ VIDEOINFOHEADER *video_info, *vi = NULL;
+ pjmedia_video_format_detail *vfd;
+ const pjmedia_video_format_info *vfi;
+
+ vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
+ strm->param.fmt.id);
+ if (!vfi)
+ return PJMEDIA_EVID_BADFORMAT;
+
+ hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC,
+ &IID_IFilterGraph, (LPVOID *)&graph->filter_graph);
+ if (FAILED(hr)) {
+ goto on_error;
+ }
+
+ hr = IFilterGraph_QueryInterface(graph->filter_graph, &IID_IMediaFilter,
+ (LPVOID *)&graph->media_filter);
+ if (FAILED(hr)) {
+ goto on_error;
+ }
+
+ if (dir == PJMEDIA_DIR_CAPTURE) {
+ hr = get_cap_device(df, id, &graph->source_filter);
+ if (FAILED(hr)) {
+ goto on_error;
+ }
+ } else {
+ graph->source_filter = SourceFilter_Create(&graph->csource_filter);
+ }
+
+ hr = IFilterGraph_AddFilter(graph->filter_graph, graph->source_filter,
+ L"capture");
+ if (FAILED(hr)) {
+ goto on_error;
+ }
+
+ if (dir == PJMEDIA_DIR_CAPTURE) {
+ graph->rend_filter = NullRenderer_Create(input_cb, strm);
+ } else {
+ hr = CoCreateInstance(&CLSID_VideoMixingRenderer, NULL,
+ CLSCTX_INPROC, &IID_IBaseFilter,
+ (LPVOID *)&graph->rend_filter);
+ if (FAILED (hr)) {
+ goto on_error;
+ }
+ }
+
+ IBaseFilter_EnumPins(graph->rend_filter, &pEnum);
+ if (SUCCEEDED(hr)) {
+ // Loop through all the pins
+ IPin *pPin = NULL;
+
+ while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
+ PIN_DIRECTION pindirtmp;
+
+ hr = IPin_QueryDirection(pPin, &pindirtmp);
+ if (hr == S_OK && pindirtmp == PINDIR_INPUT) {
+ sinkpin = pPin;
+ break;
+ }
+ IPin_Release(pPin);
+ }
+ IEnumPins_Release(pEnum);
+ }
+
+ vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
+
+ enum_dev_cap(graph->source_filter, dir,
+ get_dshow_format_info(strm->param.fmt.id)->dshow_format,
+ &mediatype, &srcpin, NULL);
+ graph->mediatype = mediatype;
+
+ if (srcpin && dir == PJMEDIA_DIR_RENDER) {
+ mediatype = graph->mediatype = &mtype;
+
+ memset (mediatype, 0, sizeof(AM_MEDIA_TYPE));
+ mediatype->majortype = MEDIATYPE_Video;
+ mediatype->subtype = *(get_dshow_format_info(strm->param.fmt.id)->
+ dshow_format);
+ mediatype->bFixedSizeSamples = TRUE;
+ mediatype->bTemporalCompression = FALSE;
+
+ vi = (VIDEOINFOHEADER *)
+ CoTaskMemAlloc(sizeof(VIDEOINFOHEADER));
+ memset (vi, 0, sizeof(VIDEOINFOHEADER));
+ mediatype->formattype = FORMAT_VideoInfo;
+ mediatype->cbFormat = sizeof(VIDEOINFOHEADER);
+ mediatype->pbFormat = (BYTE *)vi;
+
+ vi->rcSource.bottom = vfd->size.h;
+ vi->rcSource.right = vfd->size.w;
+ vi->rcTarget.bottom = vfd->size.h;
+ vi->rcTarget.right = vfd->size.w;
+
+ vi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ vi->bmiHeader.biPlanes = 1;
+ vi->bmiHeader.biBitCount = vfi->bpp;
+ vi->bmiHeader.biCompression = strm->param.fmt.id;
+ }
+
+ if (!srcpin || !sinkpin || !mediatype) {
+ hr = VFW_E_TYPE_NOT_ACCEPTED;
+ goto on_error;
+ }
+ video_info = (VIDEOINFOHEADER *) mediatype->pbFormat;
+ if (!use_def_size) {
+ video_info->bmiHeader.biWidth = vfd->size.w;
+ video_info->bmiHeader.biHeight = vfd->size.h;
+ }
+ if (!use_def_fps && vfd->fps.num != 0)
+ video_info->AvgTimePerFrame = (LONGLONG) (10000000 *
+ (double)vfd->fps.denum /
+ vfd->fps.num);
+ video_info->bmiHeader.biSizeImage = DIBSIZE(video_info->bmiHeader);
+ mediatype->lSampleSize = DIBSIZE(video_info->bmiHeader);
+ if (graph->csource_filter)
+ SourceFilter_SetMediaType(graph->csource_filter,
+ mediatype);
+
+ hr = IFilterGraph_AddFilter(graph->filter_graph,
+ (IBaseFilter *)graph->rend_filter,
+ L"renderer");
+ if (FAILED(hr))
+ goto on_error;
+
+ hr = IFilterGraph_ConnectDirect(graph->filter_graph, srcpin, sinkpin,
+ mediatype);
+ if (SUCCEEDED(hr) && (use_def_size || use_def_fps)) {
+ pjmedia_format_init_video(&strm->param.fmt, strm->param.fmt.id,
+ video_info->bmiHeader.biWidth,
+ video_info->bmiHeader.biHeight,
+ 10000000,
+ (unsigned)video_info->AvgTimePerFrame);
+ }
+
+on_error:
+ if (srcpin)
+ IPin_Release(srcpin);
+ if (sinkpin)
+ IPin_Release(sinkpin);
+ if (vi)
+ CoTaskMemFree(vi);
+ if (FAILED(hr)) {
+ char msg[80];
+ if (AMGetErrorText(hr, msg, sizeof(msg))) {
+ PJ_LOG(4,(THIS_FILE, "Error creating filter graph: %s (hr=0x%x)",
+ msg, hr));
+ }
+ return PJ_EUNKNOWN;
+ }
+
+ return PJ_SUCCESS;
+}
+
+static void destroy_filter_graph(struct dshow_stream * stream)
+{
+ if (stream->dgraph.source_filter) {
+ IBaseFilter_Release(stream->dgraph.source_filter);
+ stream->dgraph.source_filter = NULL;
+ }
+ if (stream->dgraph.rend_filter) {
+ IBaseFilter_Release(stream->dgraph.rend_filter);
+ stream->dgraph.rend_filter = NULL;
+ }
+ if (stream->dgraph.media_filter) {
+ IMediaFilter_Release(stream->dgraph.media_filter);
+ stream->dgraph.media_filter = NULL;
+ }
+ if (stream->dgraph.filter_graph) {
+ IFilterGraph_Release(stream->dgraph.filter_graph);
+ stream->dgraph.filter_graph = NULL;
+ }
+}
+
+/* API: create stream */
+static pj_status_t dshow_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
+{
+ struct dshow_factory *df = (struct dshow_factory*)f;
+ pj_pool_t *pool;
+ struct dshow_stream *strm;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_CAPTURE ||
+ param->dir == PJMEDIA_DIR_RENDER, PJ_EINVAL);
+
+ if (!get_dshow_format_info(param->fmt.id))
+ return PJMEDIA_EVID_BADFORMAT;
+
+ /* Create and Initialize stream descriptor */
+ pool = pj_pool_create(df->pf, "dshow-dev", 1000, 1000, NULL);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+ strm = PJ_POOL_ZALLOC_T(pool, struct dshow_stream);
+ pj_memcpy(&strm->param, param, sizeof(*param));
+ strm->pool = pool;
+ pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
+ strm->user_data = user_data;
+ pjmedia_event_publisher_init(&strm->base.epub, PJMEDIA_SIG_VID_DEV_DSHOW);
+
+ if (param->dir & PJMEDIA_DIR_CAPTURE) {
+ const pjmedia_video_format_detail *vfd;
+
+ /* Create capture stream here */
+ status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id,
+ PJ_FALSE, PJ_FALSE, df, strm,
+ &strm->dgraph);
+ if (status != PJ_SUCCESS) {
+ destroy_filter_graph(strm);
+ /* Try to use default fps */
+ PJ_LOG(4,(THIS_FILE, "Trying to open dshow dev with default fps"));
+ status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id,
+ PJ_FALSE, PJ_TRUE, df, strm,
+ &strm->dgraph);
+
+ if (status != PJ_SUCCESS) {
+ /* Still failed, now try to use default fps and size */
+ destroy_filter_graph(strm);
+ /* Try to use default fps */
+ PJ_LOG(4,(THIS_FILE, "Trying to open dshow dev with default "
+ "size & fps"));
+ status = create_filter_graph(PJMEDIA_DIR_CAPTURE,
+ param->cap_id,
+ PJ_TRUE, PJ_TRUE, df, strm,
+ &strm->dgraph);
+ }
+
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ pj_memcpy(param, &strm->param, sizeof(*param));
+ }
+
+ vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
+ strm->cap_ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1);
+ } else if (param->dir & PJMEDIA_DIR_RENDER) {
+ /* Create render stream here */
+ status = create_filter_graph(PJMEDIA_DIR_RENDER, param->rend_id,
+ PJ_FALSE, PJ_FALSE, df, strm,
+ &strm->dgraph);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+
+ /* Apply the remaining settings */
+ if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
+ dshow_stream_set_cap(&strm->base,
+ PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
+ &param->window);
+ }
+
+ /* Done */
+ strm->base.op = &stream_op;
+ *p_vid_strm = &strm->base;
+
+ return PJ_SUCCESS;
+
+on_error:
+ dshow_stream_destroy((pjmedia_vid_dev_stream *)strm);
+ return status;
+}
+
+/* API: Get stream info. */
+static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_param *pi)
+{
+ struct dshow_stream *strm = (struct dshow_stream*)s;
+
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+ if (dshow_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
+ &pi->window) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ void *pval)
+{
+ struct dshow_stream *strm = (struct dshow_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
+ {
+ *(unsigned*)pval = 0;
+ return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_EVID_INVCAP;
+ }
+}
+
+/* API: set capability */
+static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ const void *pval)
+{
+ struct dshow_stream *strm = (struct dshow_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
+ {
+ // set renderer's window here
+ return PJ_SUCCESS;
+ }
+
+ return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: Start stream. */
+static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm)
+{
+ struct dshow_stream *stream = (struct dshow_stream*)strm;
+ HRESULT hr;
+
+ stream->quit_flag = PJ_FALSE;
+ stream->cap_thread_exited = PJ_FALSE;
+ stream->rend_thread_exited = PJ_FALSE;
+
+ hr = IMediaFilter_Run(stream->dgraph.media_filter, 0);
+ if (FAILED(hr)) {
+ char msg[80];
+ if (AMGetErrorText(hr, msg, sizeof(msg))) {
+ PJ_LOG(4,(THIS_FILE, "Error starting media: %s", msg));
+ }
+ return PJ_EUNKNOWN;
+ }
+
+ PJ_LOG(4, (THIS_FILE, "Starting dshow video stream"));
+
+ return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm)
+{
+ struct dshow_stream *stream = (struct dshow_stream*)strm;
+ unsigned i;
+
+ stream->quit_flag = PJ_TRUE;
+ if (stream->cap_thread) {
+ for (i=0; !stream->cap_thread_exited && i<100; ++i)
+ pj_thread_sleep(10);
+ }
+ for (i=0; !stream->rend_thread_exited && i<100; ++i)
+ pj_thread_sleep(10);
+
+ IMediaFilter_Stop(stream->dgraph.media_filter);
+
+ PJ_LOG(4, (THIS_FILE, "Stopping dshow video stream"));
+
+ return PJ_SUCCESS;
+}
+
+/* API: Destroy stream. */
+static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm)
+{
+ struct dshow_stream *stream = (struct dshow_stream*)strm;
+
+ PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+ dshow_stream_stop(strm);
+ destroy_filter_graph(stream);
+
+ pj_pool_release(stream->pool);
+
+ return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */
diff --git a/pjmedia/src/pjmedia-videodev/dshowclasses.cpp b/pjmedia/src/pjmedia-videodev/dshowclasses.cpp
new file mode 100644
index 00000000..bdddd01f
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/dshowclasses.cpp
@@ -0,0 +1,245 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjmedia-videodev/config.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_DSHOW
+
+#include <assert.h>
+#include <streams.h>
+
+#if PJ_DEBUG
+# pragma comment(lib, "Strmbasd.lib")
+#else
+# pragma comment(lib, "Strmbase.lib")
+#endif
+
+typedef void (*input_callback)(void *user_data, IMediaSample *pMediaSample);
+
+const GUID CLSID_NullRenderer = {0xF9168C5E, 0xCEB2, 0x4FAA, {0xB6, 0xBF,
+ 0x32, 0x9B, 0xF3, 0x9F, 0xA1, 0xE4}};
+
+const GUID CLSID_SourceFilter = {0xF9168C5E, 0xCEB2, 0x4FAA, {0xB6, 0xBF,
+ 0x32, 0x9B, 0xF3, 0x9F, 0xA1, 0xE5}};
+
+class NullRenderer: public CBaseRenderer
+{
+public:
+ NullRenderer(HRESULT *pHr);
+ virtual ~NullRenderer();
+
+ virtual HRESULT CheckMediaType(const CMediaType *pmt);
+ virtual HRESULT DoRenderSample(IMediaSample *pMediaSample);
+
+ input_callback input_cb;
+ void *user_data;
+};
+
+class OutputPin: public CBaseOutputPin
+{
+public:
+ OutputPin(CBaseFilter *pFilter, CCritSec *pLock, HRESULT *pHr);
+ ~OutputPin();
+
+ HRESULT Push(void *buf, long size);
+
+ virtual HRESULT CheckMediaType(const CMediaType *pmt);
+ virtual HRESULT DecideBufferSize(IMemAllocator *pAlloc,
+ ALLOCATOR_PROPERTIES *ppropInputRequest);
+
+ CMediaType mediaType;
+ long bufSize;
+};
+
+class SourceFilter: public CBaseFilter
+{
+public:
+ SourceFilter();
+ ~SourceFilter();
+
+ int GetPinCount();
+ CBasePin* GetPin(int n);
+
+protected:
+ CCritSec lock;
+ OutputPin* outPin;
+};
+
+OutputPin::OutputPin(CBaseFilter *pFilter, CCritSec *pLock, HRESULT *pHr):
+ CBaseOutputPin("OutputPin", pFilter, pLock, pHr, L"OutputPin")
+{
+}
+
+OutputPin::~OutputPin()
+{
+}
+
+HRESULT OutputPin::CheckMediaType(const CMediaType *pmt)
+{
+ return S_OK;
+}
+
+HRESULT OutputPin::DecideBufferSize(IMemAllocator *pAlloc,
+ ALLOCATOR_PROPERTIES *ppropInputRequest)
+{
+ ALLOCATOR_PROPERTIES properties;
+
+ ppropInputRequest->cbBuffer = bufSize;
+ ppropInputRequest->cBuffers = 1;
+
+ /* First set the buffer descriptions we're interested in */
+ pAlloc->SetProperties(ppropInputRequest, &properties);
+
+ return S_OK;
+}
+
+HRESULT OutputPin::Push(void *buf, long size)
+{
+ HRESULT hr;
+ IMediaSample *pSample;
+ VIDEOINFOHEADER *vi;
+ AM_MEDIA_TYPE *pmt;
+ BYTE *dst_buf;
+
+ /**
+ * Hold the critical section here as the pin might get disconnected
+ * during the Deliver() method call.
+ */
+ m_pLock->Lock();
+
+ hr = GetDeliveryBuffer(&pSample, NULL, NULL, 0);
+ if (FAILED(hr))
+ goto on_error;
+
+ pSample->GetMediaType(&pmt);
+ if (pmt) {
+ mediaType.Set(*pmt);
+ bufSize = pmt->lSampleSize;
+ }
+
+ pSample->GetPointer(&dst_buf);
+ vi = (VIDEOINFOHEADER *)mediaType.pbFormat;
+ if (vi->rcSource.right == vi->bmiHeader.biWidth) {
+ assert(pSample->GetSize() >= size);
+ memcpy(dst_buf, buf, size);
+ } else {
+ unsigned i, bpp;
+ unsigned dststride, srcstride;
+ BYTE *src_buf = (BYTE *)buf;
+
+ bpp = size / abs(vi->bmiHeader.biHeight) / vi->rcSource.right;
+ dststride = vi->bmiHeader.biWidth * bpp;
+ srcstride = vi->rcSource.right * bpp;
+ for (i = abs(vi->bmiHeader.biHeight); i > 0; i--) {
+ memcpy(dst_buf, src_buf, srcstride);
+ dst_buf += dststride;
+ src_buf += srcstride;
+ }
+ }
+ pSample->SetActualDataLength(size);
+
+ hr = Deliver(pSample);
+
+ pSample->Release();
+
+on_error:
+ m_pLock->Unlock();
+ return hr;
+}
+
+SourceFilter::SourceFilter(): CBaseFilter("SourceFilter", NULL, &lock,
+ CLSID_SourceFilter)
+{
+ HRESULT hr;
+ outPin = new OutputPin(this, &lock, &hr);
+}
+
+SourceFilter::~SourceFilter()
+{
+}
+
+int SourceFilter::GetPinCount()
+{
+ return 1;
+}
+
+CBasePin* SourceFilter::GetPin(int n)
+{
+ return outPin;
+}
+
+NullRenderer::NullRenderer(HRESULT *pHr): CBaseRenderer(CLSID_NullRenderer,
+ "NullRenderer",
+ NULL, pHr)
+{
+ input_cb = NULL;
+}
+
+NullRenderer::~NullRenderer()
+{
+}
+
+HRESULT NullRenderer::CheckMediaType(const CMediaType *pmt)
+{
+ return S_OK;
+}
+
+HRESULT NullRenderer::DoRenderSample(IMediaSample *pMediaSample)
+{
+ if (input_cb)
+ input_cb(user_data, pMediaSample);
+
+ return S_OK;
+}
+
+extern "C" IBaseFilter* NullRenderer_Create(input_callback input_cb,
+ void *user_data)
+{
+ HRESULT hr;
+ NullRenderer *renderer = new NullRenderer(&hr);
+ renderer->AddRef();
+ renderer->input_cb = input_cb;
+ renderer->user_data = user_data;
+
+ return (CBaseFilter *)renderer;
+}
+
+extern "C" IBaseFilter* SourceFilter_Create(SourceFilter **pSrc)
+{
+ SourceFilter *src = new SourceFilter();
+ src->AddRef();
+ *pSrc = src;
+
+ return (CBaseFilter *)src;
+}
+
+extern "C" HRESULT SourceFilter_Deliver(SourceFilter *src,
+ void *buf, long size)
+{
+ return ((OutputPin *)src->GetPin(0))->Push(buf, size);
+}
+
+extern "C" void SourceFilter_SetMediaType(SourceFilter *src,
+ AM_MEDIA_TYPE *pmt)
+{
+ ((OutputPin *)src->GetPin(0))->mediaType.Set(*pmt);
+ ((OutputPin *)src->GetPin(0))->bufSize = pmt->lSampleSize;
+}
+
+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */
diff --git a/pjmedia/src/pjmedia-videodev/errno.c b/pjmedia/src/pjmedia-videodev/errno.c
new file mode 100644
index 00000000..c0729ca0
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/errno.c
@@ -0,0 +1,112 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-videodev/errno.h>
+#include <pj/string.h>
+#include <pj/unicode.h>
+
+/* PJMEDIA-videodev's own error codes/messages
+ * MUST KEEP THIS ARRAY SORTED!!
+ * Message must be limited to 64 chars!
+ */
+
+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
+
+static const struct
+{
+ int code;
+ const char *msg;
+} err_str[] =
+{
+ PJ_BUILD_ERR( PJMEDIA_EVID_ERR, "Unspecified video device error" ),
+ PJ_BUILD_ERR( PJMEDIA_EVID_SYSERR, "Unknown error from video driver" ),
+ PJ_BUILD_ERR( PJMEDIA_EVID_INIT, "video subsystem not initialized" ),
+ PJ_BUILD_ERR( PJMEDIA_EVID_INVDEV, "Invalid video device" ),
+ PJ_BUILD_ERR( PJMEDIA_EVID_NODEV, "Found no video devices" ),
+ PJ_BUILD_ERR( PJMEDIA_EVID_NODEFDEV, "Unable to find default video device" ),
+ PJ_BUILD_ERR( PJMEDIA_EVID_NOTREADY, "video device not ready" ),
+ PJ_BUILD_ERR( PJMEDIA_EVID_INVCAP, "Invalid or unsupported video capability" ),
+ PJ_BUILD_ERR( PJMEDIA_EVID_INVOP, "Invalid or unsupported video device operation" ),
+ PJ_BUILD_ERR( PJMEDIA_EVID_BADFORMAT, "Bad or invalid video device format" ),
+ PJ_BUILD_ERR( PJMEDIA_EVID_SAMPFORMAT, "Invalid video device sample format"),
+ PJ_BUILD_ERR( PJMEDIA_EVID_BADLATENCY, "Bad video latency setting")
+
+};
+
+#endif /* PJ_HAS_ERROR_STRING */
+
+
+
+/*
+ * pjmedia_videodev_strerror()
+ */
+PJ_DEF(pj_str_t) pjmedia_videodev_strerror(pj_status_t statcode,
+ char *buf, pj_size_t bufsize )
+{
+ pj_str_t errstr;
+
+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
+
+ /* videodev error */
+ if (statcode >= PJMEDIA_VIDEODEV_ERRNO_START &&
+ statcode < PJMEDIA_VIDEODEV_ERRNO_END)
+ {
+ /* Find the error in the table.
+ * Use binary search!
+ */
+ int first = 0;
+ int n = PJ_ARRAY_SIZE(err_str);
+
+ while (n > 0) {
+ int half = n/2;
+ int mid = first + half;
+
+ if (err_str[mid].code < statcode) {
+ first = mid+1;
+ n -= (half+1);
+ } else if (err_str[mid].code > statcode) {
+ n = half;
+ } else {
+ first = mid;
+ break;
+ }
+ }
+
+
+ if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) {
+ pj_str_t msg;
+
+ msg.ptr = (char*)err_str[first].msg;
+ msg.slen = pj_ansi_strlen(err_str[first].msg);
+
+ errstr.ptr = buf;
+ pj_strncpy_with_null(&errstr, &msg, bufsize);
+ return errstr;
+
+ }
+ }
+#endif /* PJ_HAS_ERROR_STRING */
+
+ /* Error not found. */
+ errstr.ptr = buf;
+ errstr.slen = pj_ansi_snprintf(buf, bufsize,
+ "Unknown pjmedia-videodev error %d",
+ statcode);
+
+ return errstr;
+}
diff --git a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
new file mode 100644
index 00000000..aa02c87b
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
@@ -0,0 +1,514 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Video device with ffmpeg backend, currently only capture devices are
+ * implemented.
+ *
+ * Issues:
+ * - no device enumeration (ffmpeg limitation), so this uses "host API" enum
+ * instead
+ * - need stricter filter on "host API" enum, currently audio capture devs are
+ * still listed.
+ * - no format enumeration, currently hardcoded to PJMEDIA_FORMAT_RGB24 only
+ * - tested on Vista only (vfw backend) with virtual cam
+ * - vfw backend produce bottom up pictures
+ * - using VS IDE, this cannot run under debugger!
+ */
+
+#include <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/unicode.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG
+
+#define THIS_FILE "ffmpeg.c"
+
+#include "../pjmedia/ffmpeg_util.h"
+#include <libavdevice/avdevice.h>
+#include <libavformat/avformat.h>
+
+#define MAX_DEV_CNT 8
+
+typedef struct ffmpeg_dev_info
+{
+ pjmedia_vid_dev_info base;
+ AVInputFormat *host_api;
+ const char *def_devname;
+} ffmpeg_dev_info;
+
+
+typedef struct ffmpeg_factory
+{
+ pjmedia_vid_dev_factory base;
+ pj_pool_factory *pf;
+ pj_pool_t *pool;
+ pj_pool_t *dev_pool;
+ unsigned dev_count;
+ ffmpeg_dev_info dev_info[MAX_DEV_CNT];
+} ffmpeg_factory;
+
+
+typedef struct ffmpeg_stream
+{
+ pjmedia_vid_dev_stream base;
+ ffmpeg_factory *factory;
+ pj_pool_t *pool;
+ pjmedia_vid_dev_param param;
+ AVFormatContext *ff_fmt_ctx;
+} ffmpeg_stream;
+
+
+/* Prototypes */
+static pj_status_t ffmpeg_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t ffmpeg_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t ffmpeg_factory_refresh(pjmedia_vid_dev_factory *f);
+static unsigned ffmpeg_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t ffmpeg_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info);
+static pj_status_t ffmpeg_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param);
+static pj_status_t ffmpeg_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm);
+
+static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_param *param);
+static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
+static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
+static pj_status_t ffmpeg_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_dev_stream *s,
+ pjmedia_frame *frame);
+static pj_status_t ffmpeg_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_dev_stream *strm);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+ &ffmpeg_factory_init,
+ &ffmpeg_factory_destroy,
+ &ffmpeg_factory_get_dev_count,
+ &ffmpeg_factory_get_dev_info,
+ &ffmpeg_factory_default_param,
+ &ffmpeg_factory_create_stream,
+ &ffmpeg_factory_refresh
+};
+
+static pjmedia_vid_dev_stream_op stream_op =
+{
+ &ffmpeg_stream_get_param,
+ &ffmpeg_stream_get_cap,
+ &ffmpeg_stream_set_cap,
+ &ffmpeg_stream_start,
+ &ffmpeg_stream_get_frame,
+ NULL,
+ &ffmpeg_stream_stop,
+ &ffmpeg_stream_destroy
+};
+
+
+static void print_ffmpeg_err(int err)
+{
+ char errbuf[512];
+ if (av_strerror(err, errbuf, sizeof(errbuf)) >= 0)
+ PJ_LOG(1, (THIS_FILE, "ffmpeg err %d: %s", err, errbuf));
+
+}
+
+static void print_ffmpeg_log(void* ptr, int level, const char* fmt, va_list vl)
+{
+ PJ_UNUSED_ARG(ptr);
+ PJ_UNUSED_ARG(level);
+ vfprintf(stdout, fmt, vl);
+}
+
+
+static pj_status_t ffmpeg_capture_open(AVFormatContext **ctx,
+ AVInputFormat *ifmt,
+ const char *dev_name,
+ const pjmedia_vid_dev_param *param)
+{
+ AVFormatParameters fp;
+ pjmedia_video_format_detail *vfd;
+ int err;
+
+ PJ_ASSERT_RETURN(ctx && ifmt && dev_name && param, PJ_EINVAL);
+ PJ_ASSERT_RETURN(param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO,
+ PJ_EINVAL);
+
+ vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
+
+ /* Init ffmpeg format context */
+ *ctx = avformat_alloc_context();
+
+ /* Init ffmpeg format param */
+ pj_bzero(&fp, sizeof(fp));
+ fp.prealloced_context = 1;
+ fp.width = vfd->size.w;
+ fp.height = vfd->size.h;
+ fp.pix_fmt = PIX_FMT_BGR24;
+ fp.time_base.num = vfd->fps.denum;
+ fp.time_base.den = vfd->fps.num;
+
+ /* Open capture stream */
+ err = av_open_input_stream(ctx, NULL, dev_name, ifmt, &fp);
+ if (err < 0) {
+ *ctx = NULL; /* ffmpeg freed its states on failure, do we must too */
+ print_ffmpeg_err(err);
+ return PJ_EUNKNOWN;
+ }
+
+ return PJ_SUCCESS;
+}
+
+static void ffmpeg_capture_close(AVFormatContext *ctx)
+{
+ if (ctx)
+ av_close_input_stream(ctx);
+}
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init ffmpeg_ video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_ffmpeg_factory(pj_pool_factory *pf)
+{
+ ffmpeg_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, "ffmpeg_cap_dev", 1000, 1000, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, ffmpeg_factory);
+
+ f->pool = pool;
+ f->pf = pf;
+ f->base.op = &factory_op;
+
+ avdevice_register_all();
+
+ return &f->base;
+}
+
+
+/* API: init factory */
+static pj_status_t ffmpeg_factory_init(pjmedia_vid_dev_factory *f)
+{
+ return ffmpeg_factory_refresh(f);
+}
+
+/* API: destroy factory */
+static pj_status_t ffmpeg_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+ ffmpeg_factory *ff = (ffmpeg_factory*)f;
+ pj_pool_t *pool = ff->pool;
+
+ ff->dev_count = 0;
+ ff->pool = NULL;
+ if (ff->dev_pool)
+ pj_pool_release(ff->dev_pool);
+ if (pool)
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/* API: refresh the list of devices */
+static pj_status_t ffmpeg_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+ ffmpeg_factory *ff = (ffmpeg_factory*)f;
+ AVInputFormat *p;
+ ffmpeg_dev_info *info;
+
+ av_log_set_callback(&print_ffmpeg_log);
+ av_log_set_level(AV_LOG_DEBUG);
+
+ if (ff->dev_pool) {
+ pj_pool_release(ff->dev_pool);
+ ff->dev_pool = NULL;
+ }
+
+ /* TODO: this should enumerate devices, now it enumerates host APIs */
+ ff->dev_count = 0;
+ ff->dev_pool = pj_pool_create(ff->pf, "ffmpeg_cap_dev", 500, 500, NULL);
+
+ p = av_iformat_next(NULL);
+ while (p) {
+ if (p->flags & AVFMT_NOFILE) {
+ unsigned i;
+
+ info = &ff->dev_info[ff->dev_count++];
+ pj_bzero(info, sizeof(*info));
+ pj_ansi_strncpy(info->base.name, "default",
+ sizeof(info->base.name));
+ pj_ansi_snprintf(info->base.driver, sizeof(info->base.driver),
+ "%s (ffmpeg)", p->name);
+ info->base.dir = PJMEDIA_DIR_CAPTURE;
+ info->base.has_callback = PJ_FALSE;
+
+ info->host_api = p;
+
+#if defined(PJ_WIN32) && PJ_WIN32!=0
+ info->def_devname = "0";
+#elif defined(PJ_LINUX) && PJ_LINUX!=0
+ info->def_devname = "/dev/video0";
+#endif
+
+ /* Set supported formats, currently hardcoded to RGB24 only */
+ info->base.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+ info->base.fmt_cnt = 1;
+ for (i = 0; i < info->base.fmt_cnt; ++i) {
+ pjmedia_format *fmt = &info->base.fmt[i];
+
+ fmt->id = PJMEDIA_FORMAT_RGB24;
+ fmt->type = PJMEDIA_TYPE_VIDEO;
+ fmt->detail_type = PJMEDIA_FORMAT_DETAIL_NONE;
+ }
+ }
+ p = av_iformat_next(p);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned ffmpeg_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+ ffmpeg_factory *ff = (ffmpeg_factory*)f;
+ return ff->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t ffmpeg_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info)
+{
+ ffmpeg_factory *ff = (ffmpeg_factory*)f;
+
+ PJ_ASSERT_RETURN(index < ff->dev_count, PJMEDIA_EVID_INVDEV);
+
+ pj_memcpy(info, &ff->dev_info[index].base, sizeof(*info));
+
+ return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t ffmpeg_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param)
+{
+ ffmpeg_factory *ff = (ffmpeg_factory*)f;
+ ffmpeg_dev_info *info;
+
+ PJ_ASSERT_RETURN(index < ff->dev_count, PJMEDIA_EVID_INVDEV);
+
+ PJ_UNUSED_ARG(pool);
+
+ info = &ff->dev_info[index];
+
+ pj_bzero(param, sizeof(*param));
+ param->dir = PJMEDIA_DIR_CAPTURE;
+ param->cap_id = index;
+ param->rend_id = PJMEDIA_VID_INVALID_DEV;
+ param->clock_rate = 0;
+
+ /* Set the device capabilities here */
+ param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+ param->clock_rate = 90000;
+ pjmedia_format_init_video(&param->fmt, 0, 320, 240, 25, 1);
+ param->fmt.id = info->base.fmt[0].id;
+
+ return PJ_SUCCESS;
+}
+
+
+
+/* API: create stream */
+static pj_status_t ffmpeg_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
+{
+ ffmpeg_factory *ff = (ffmpeg_factory*)f;
+ pj_pool_t *pool;
+ ffmpeg_stream *strm;
+
+ PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
+ PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL);
+ PJ_ASSERT_RETURN((unsigned)param->cap_id < ff->dev_count, PJ_EINVAL);
+ PJ_ASSERT_RETURN(param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO,
+ PJ_EINVAL);
+
+ PJ_UNUSED_ARG(cb);
+ PJ_UNUSED_ARG(user_data);
+
+ /* Create and Initialize stream descriptor */
+ pool = pj_pool_create(ff->pf, "ffmpeg-dev", 1000, 1000, NULL);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+ strm = PJ_POOL_ZALLOC_T(pool, struct ffmpeg_stream);
+ strm->factory = (ffmpeg_factory*)f;
+ strm->pool = pool;
+ pj_memcpy(&strm->param, param, sizeof(*param));
+ pjmedia_event_publisher_init(&strm->base.epub);
+
+ /* Done */
+ strm->base.op = &stream_op;
+ *p_vid_strm = &strm->base;
+
+ return PJ_SUCCESS;
+}
+
+/* API: Get stream info. */
+static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_param *pi)
+{
+ ffmpeg_stream *strm = (ffmpeg_stream*)s;
+
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+ return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ void *pval)
+{
+ ffmpeg_stream *strm = (ffmpeg_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+ PJ_UNUSED_ARG(cap);
+ PJ_UNUSED_ARG(pval);
+
+ return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: set capability */
+static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ const void *pval)
+{
+ ffmpeg_stream *strm = (ffmpeg_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+ PJ_UNUSED_ARG(cap);
+ PJ_UNUSED_ARG(pval);
+
+ return PJMEDIA_EVID_INVCAP;
+}
+
+
+/* API: Start stream. */
+static pj_status_t ffmpeg_stream_start(pjmedia_vid_dev_stream *s)
+{
+ ffmpeg_stream *strm = (ffmpeg_stream*)s;
+ ffmpeg_dev_info *info;
+ pj_status_t status;
+
+ info = &strm->factory->dev_info[strm->param.cap_id];
+
+ PJ_LOG(4, (THIS_FILE, "Starting ffmpeg capture stream"));
+
+ status = ffmpeg_capture_open(&strm->ff_fmt_ctx, info->host_api,
+ info->def_devname, &strm->param);
+ if (status != PJ_SUCCESS) {
+ /* must set ffmpeg states to NULL on any failure */
+ strm->ff_fmt_ctx = NULL;
+ }
+
+ return status;
+}
+
+
+/* API: Get frame from stream */
+static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_dev_stream *s,
+ pjmedia_frame *frame)
+{
+ ffmpeg_stream *strm = (ffmpeg_stream*)s;
+ AVPacket p;
+ int err;
+
+ err = av_read_frame(strm->ff_fmt_ctx, &p);
+ if (err < 0) {
+ print_ffmpeg_err(err);
+ return PJ_EUNKNOWN;
+ }
+
+ pj_bzero(frame, sizeof(*frame));
+ frame->type = PJMEDIA_FRAME_TYPE_VIDEO;
+ frame->buf = p.data;
+ frame->size = p.size;
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Stop stream. */
+static pj_status_t ffmpeg_stream_stop(pjmedia_vid_dev_stream *s)
+{
+ ffmpeg_stream *strm = (ffmpeg_stream*)s;
+
+ PJ_LOG(4, (THIS_FILE, "Stopping ffmpeg capture stream"));
+
+ ffmpeg_capture_close(strm->ff_fmt_ctx);
+ strm->ff_fmt_ctx = NULL;
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_dev_stream *s)
+{
+ ffmpeg_stream *strm = (ffmpeg_stream*)s;
+
+ PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
+
+ ffmpeg_stream_stop(s);
+
+ pj_pool_release(strm->pool);
+
+ return PJ_SUCCESS;
+}
+
+#ifdef _MSC_VER
+# pragma comment( lib, "avdevice.lib")
+# pragma comment( lib, "avformat.lib")
+# pragma comment( lib, "avutil.lib")
+#endif
+
+#endif /* PJMEDIA_VIDEO_DEV_HAS_FFMPEG */
diff --git a/pjmedia/src/pjmedia-videodev/ios_dev.m b/pjmedia/src/pjmedia-videodev/ios_dev.m
new file mode 100644
index 00000000..cafa57ad
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/ios_dev.m
@@ -0,0 +1,685 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_IOS
+#include "Availability.h"
+#ifdef __IPHONE_4_0
+
+#import <UIKit/UIKit.h>
+#import <AVFoundation/AVFoundation.h>
+
+#define THIS_FILE "ios_dev.c"
+#define DEFAULT_CLOCK_RATE 9000
+#define DEFAULT_WIDTH 480
+#define DEFAULT_HEIGHT 360
+#define DEFAULT_FPS 15
+
+typedef struct ios_fmt_info
+{
+ pjmedia_format_id pjmedia_format;
+ UInt32 ios_format;
+} ios_fmt_info;
+
+static ios_fmt_info ios_fmts[] =
+{
+ {PJMEDIA_FORMAT_BGRA, kCVPixelFormatType_32BGRA} ,
+};
+
+/* qt device info */
+struct ios_dev_info
+{
+ pjmedia_vid_dev_info info;
+};
+
+/* qt factory */
+struct ios_factory
+{
+ pjmedia_vid_dev_factory base;
+ pj_pool_t *pool;
+ pj_pool_factory *pf;
+
+ unsigned dev_count;
+ struct ios_dev_info *dev_info;
+};
+
+@interface VOutDelegate: NSObject
+ <AVCaptureVideoDataOutputSampleBufferDelegate>
+{
+@public
+ struct ios_stream *stream;
+}
+@end
+
+/* Video stream. */
+struct ios_stream
+{
+ pjmedia_vid_dev_stream base; /**< Base stream */
+ pjmedia_vid_dev_param param; /**< Settings */
+ pj_pool_t *pool; /**< Memory pool */
+
+ pjmedia_vid_dev_cb vid_cb; /**< Stream callback */
+ void *user_data; /**< Application data */
+
+ pjmedia_rect_size size;
+ pj_uint8_t bpp;
+ unsigned bytes_per_row;
+ unsigned frame_size;
+
+ AVCaptureSession *cap_session;
+ AVCaptureDeviceInput *dev_input;
+ AVCaptureVideoDataOutput *video_output;
+ VOutDelegate *vout_delegate;
+
+ UIImageView *imgView;
+ void *buf;
+
+ pj_timestamp frame_ts;
+ unsigned ts_inc;
+};
+
+
+/* Prototypes */
+static pj_status_t ios_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t ios_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t ios_factory_refresh(pjmedia_vid_dev_factory *f);
+static unsigned ios_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t ios_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info);
+static pj_status_t ios_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param);
+static pj_status_t ios_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm);
+
+static pj_status_t ios_stream_get_param(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_param *param);
+static pj_status_t ios_stream_get_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
+static pj_status_t ios_stream_set_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
+static pj_status_t ios_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t ios_stream_put_frame(pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame);
+static pj_status_t ios_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t ios_stream_destroy(pjmedia_vid_dev_stream *strm);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+ &ios_factory_init,
+ &ios_factory_destroy,
+ &ios_factory_get_dev_count,
+ &ios_factory_get_dev_info,
+ &ios_factory_default_param,
+ &ios_factory_create_stream,
+ &ios_factory_refresh
+};
+
+static pjmedia_vid_dev_stream_op stream_op =
+{
+ &ios_stream_get_param,
+ &ios_stream_get_cap,
+ &ios_stream_set_cap,
+ &ios_stream_start,
+ NULL,
+ &ios_stream_put_frame,
+ &ios_stream_stop,
+ &ios_stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init ios_ video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_ios_factory(pj_pool_factory *pf)
+{
+ struct ios_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, "ios video", 512, 512, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, struct ios_factory);
+ f->pf = pf;
+ f->pool = pool;
+ f->base.op = &factory_op;
+
+ return &f->base;
+}
+
+
+/* API: init factory */
+static pj_status_t ios_factory_init(pjmedia_vid_dev_factory *f)
+{
+ struct ios_factory *qf = (struct ios_factory*)f;
+ struct ios_dev_info *qdi;
+ unsigned i, l;
+
+ /* Initialize input and output devices here */
+ qf->dev_info = (struct ios_dev_info*)
+ pj_pool_calloc(qf->pool, 2,
+ sizeof(struct ios_dev_info));
+
+ qf->dev_count = 0;
+ qdi = &qf->dev_info[qf->dev_count++];
+ pj_bzero(qdi, sizeof(*qdi));
+ strcpy(qdi->info.name, "iOS UIView");
+ strcpy(qdi->info.driver, "iOS");
+ qdi->info.dir = PJMEDIA_DIR_RENDER;
+ qdi->info.has_callback = PJ_FALSE;
+ qdi->info.caps = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+
+ if (NSClassFromString(@"AVCaptureSession")) {
+ qdi = &qf->dev_info[qf->dev_count++];
+ pj_bzero(qdi, sizeof(*qdi));
+ strcpy(qdi->info.name, "iOS AVCapture");
+ strcpy(qdi->info.driver, "iOS");
+ qdi->info.dir = PJMEDIA_DIR_CAPTURE;
+ qdi->info.has_callback = PJ_TRUE;
+ }
+
+ for (i = 0; i < qf->dev_count; i++) {
+ qdi = &qf->dev_info[i];
+ qdi->info.fmt_cnt = PJ_ARRAY_SIZE(ios_fmts);
+ qdi->info.caps |= PJMEDIA_VID_DEV_CAP_FORMAT;
+
+ for (l = 0; l < PJ_ARRAY_SIZE(ios_fmts); l++) {
+ pjmedia_format *fmt = &qdi->info.fmt[l];
+ pjmedia_format_init_video(fmt,
+ ios_fmts[l].pjmedia_format,
+ DEFAULT_WIDTH,
+ DEFAULT_HEIGHT,
+ DEFAULT_FPS, 1);
+ }
+ }
+
+ PJ_LOG(4, (THIS_FILE, "iOS video initialized with %d devices",
+ qf->dev_count));
+
+ return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t ios_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+ struct ios_factory *qf = (struct ios_factory*)f;
+ pj_pool_t *pool = qf->pool;
+
+ qf->pool = NULL;
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/* API: refresh the list of devices */
+static pj_status_t ios_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+ PJ_UNUSED_ARG(f);
+ return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned ios_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+ struct ios_factory *qf = (struct ios_factory*)f;
+ return qf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t ios_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info)
+{
+ struct ios_factory *qf = (struct ios_factory*)f;
+
+ PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
+
+ pj_memcpy(info, &qf->dev_info[index].info, sizeof(*info));
+
+ return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t ios_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param)
+{
+ struct ios_factory *qf = (struct ios_factory*)f;
+ struct ios_dev_info *di = &qf->dev_info[index];
+
+ PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
+
+ PJ_UNUSED_ARG(pool);
+
+ pj_bzero(param, sizeof(*param));
+ if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
+ param->dir = PJMEDIA_DIR_CAPTURE;
+ param->cap_id = index;
+ param->rend_id = PJMEDIA_VID_INVALID_DEV;
+ } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
+ param->dir = PJMEDIA_DIR_RENDER;
+ param->rend_id = index;
+ param->cap_id = PJMEDIA_VID_INVALID_DEV;
+ } else {
+ return PJMEDIA_EVID_INVDEV;
+ }
+
+ param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+ param->clock_rate = DEFAULT_CLOCK_RATE;
+ pj_memcpy(&param->fmt, &di->info.fmt[0], sizeof(param->fmt));
+
+ return PJ_SUCCESS;
+}
+
+@implementation VOutDelegate
+- (void)update_image
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ /* Create a device-dependent RGB color space */
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+
+ /* Create a bitmap graphics context with the sample buffer data */
+ CGContextRef context =
+ CGBitmapContextCreate(stream->buf, stream->size.w, stream->size.h, 8,
+ stream->bytes_per_row, colorSpace,
+ kCGBitmapByteOrder32Little |
+ kCGImageAlphaPremultipliedFirst);
+
+ /**
+ * Create a Quartz image from the pixel data in the bitmap graphics
+ * context
+ */
+ CGImageRef quartzImage = CGBitmapContextCreateImage(context);
+
+ /* Free up the context and color space */
+ CGContextRelease(context);
+ CGColorSpaceRelease(colorSpace);
+
+ /* Create an image object from the Quartz image */
+ UIImage *image = [UIImage imageWithCGImage:quartzImage scale:1.0
+ orientation:UIImageOrientationRight];
+
+ /* Release the Quartz image */
+ CGImageRelease(quartzImage);
+
+ [stream->imgView performSelectorOnMainThread:@selector(setImage:)
+ withObject:image waitUntilDone:NO];
+
+ [pool release];
+}
+
+- (void)captureOutput:(AVCaptureOutput *)captureOutput
+ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
+ fromConnection:(AVCaptureConnection *)connection
+{
+ pjmedia_frame frame;
+ CVImageBufferRef imageBuffer;
+
+ if (!sampleBuffer)
+ return;
+
+ /* Get a CMSampleBuffer's Core Video image buffer for the media data */
+ imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
+
+ /* Lock the base address of the pixel buffer */
+ CVPixelBufferLockBaseAddress(imageBuffer, 0);
+
+ frame.type = PJMEDIA_TYPE_VIDEO;
+ frame.buf = CVPixelBufferGetBaseAddress(imageBuffer);
+ frame.size = stream->frame_size;
+ frame.bit_info = 0;
+ frame.timestamp.u64 = stream->frame_ts.u64;
+
+ if (stream->vid_cb.capture_cb)
+ (*stream->vid_cb.capture_cb)(&stream->base, stream->user_data, &frame);
+
+ stream->frame_ts.u64 += stream->ts_inc;
+
+ /* Unlock the pixel buffer */
+ CVPixelBufferUnlockBaseAddress(imageBuffer,0);
+}
+@end
+
+static ios_fmt_info* get_ios_format_info(pjmedia_format_id id)
+{
+ unsigned i;
+
+ for (i = 0; i < PJ_ARRAY_SIZE(ios_fmts); i++) {
+ if (ios_fmts[i].pjmedia_format == id)
+ return &ios_fmts[i];
+ }
+
+ return NULL;
+}
+
+/* API: create stream */
+static pj_status_t ios_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
+{
+ struct ios_factory *qf = (struct ios_factory*)f;
+ pj_pool_t *pool;
+ struct ios_stream *strm;
+ const pjmedia_video_format_detail *vfd;
+ const pjmedia_video_format_info *vfi;
+ pj_status_t status = PJ_SUCCESS;
+ ios_fmt_info *ifi = get_ios_format_info(param->fmt.id);
+ NSError *error;
+
+ PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
+ PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
+ param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
+ (param->dir == PJMEDIA_DIR_CAPTURE ||
+ param->dir == PJMEDIA_DIR_RENDER),
+ PJ_EINVAL);
+
+ if (!(ifi = get_ios_format_info(param->fmt.id)))
+ return PJMEDIA_EVID_BADFORMAT;
+
+ vfi = pjmedia_get_video_format_info(NULL, param->fmt.id);
+ if (!vfi)
+ return PJMEDIA_EVID_BADFORMAT;
+
+ /* Create and Initialize stream descriptor */
+ pool = pj_pool_create(qf->pf, "ios-dev", 4000, 4000, NULL);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+ strm = PJ_POOL_ZALLOC_T(pool, struct ios_stream);
+ pj_memcpy(&strm->param, param, sizeof(*param));
+ strm->pool = pool;
+ pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
+ strm->user_data = user_data;
+ pjmedia_event_publisher_init(&strm->base.epub, PJMEDIA_SIG_VID_DEV_IOS);
+
+ vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
+ pj_memcpy(&strm->size, &vfd->size, sizeof(vfd->size));
+ strm->bpp = vfi->bpp;
+ strm->bytes_per_row = strm->size.w * strm->bpp / 8;
+ strm->frame_size = strm->bytes_per_row * strm->size.h;
+ strm->ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1);
+
+ if (param->dir & PJMEDIA_DIR_CAPTURE) {
+ /* Create capture stream here */
+ strm->cap_session = [[AVCaptureSession alloc] init];
+ if (!strm->cap_session) {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+ strm->cap_session.sessionPreset = AVCaptureSessionPresetMedium;
+
+ /* Open video device */
+ AVCaptureDevice *videoDevice =
+ [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
+ if (!videoDevice) {
+ status = PJMEDIA_EVID_SYSERR;
+ goto on_error;
+ }
+
+ /* Add the video device to the session as a device input */
+ strm->dev_input = [AVCaptureDeviceInput
+ deviceInputWithDevice:videoDevice
+ error: &error];
+ if (!strm->dev_input) {
+ status = PJMEDIA_EVID_SYSERR;
+ goto on_error;
+ }
+ [strm->cap_session addInput:strm->dev_input];
+
+ strm->video_output = [[[AVCaptureVideoDataOutput alloc] init]
+ autorelease];
+ if (!strm->video_output) {
+ status = PJMEDIA_EVID_SYSERR;
+ goto on_error;
+ }
+ [strm->cap_session addOutput:strm->video_output];
+
+ /* Configure the video output */
+ strm->vout_delegate = [VOutDelegate alloc];
+ strm->vout_delegate->stream = strm;
+ dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);
+ [strm->video_output setSampleBufferDelegate:strm->vout_delegate
+ queue:queue];
+ dispatch_release(queue);
+
+ strm->video_output.videoSettings =
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInt:ifi->ios_format],
+ kCVPixelBufferPixelFormatTypeKey,
+ [NSNumber numberWithInt: vfd->size.w],
+ kCVPixelBufferWidthKey,
+ [NSNumber numberWithInt: vfd->size.h],
+ kCVPixelBufferHeightKey, nil];
+ strm->video_output.minFrameDuration = CMTimeMake(vfd->fps.denum,
+ vfd->fps.num);
+ } else if (param->dir & PJMEDIA_DIR_RENDER) {
+ /* Create renderer stream here */
+ /* Get the main window */
+ UIWindow *window = [[UIApplication sharedApplication] keyWindow];
+
+ if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW && param->window)
+ window = (UIWindow *)param->window;
+
+ pj_assert(window);
+ strm->imgView = [[UIImageView alloc] initWithFrame:[window bounds]];
+ if (!strm->imgView) {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+ [window addSubview:strm->imgView];
+
+ if (!strm->vout_delegate) {
+ strm->vout_delegate = [VOutDelegate alloc];
+ strm->vout_delegate->stream = strm;
+ }
+
+ strm->buf = pj_pool_alloc(pool, strm->frame_size);
+ }
+
+ /* Apply the remaining settings */
+ /*
+ if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) {
+ ios_stream_set_cap(&strm->base,
+ PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
+ &param->fmt);
+ }
+ */
+ /* Done */
+ strm->base.op = &stream_op;
+ *p_vid_strm = &strm->base;
+
+ return PJ_SUCCESS;
+
+on_error:
+ ios_stream_destroy((pjmedia_vid_dev_stream *)strm);
+
+ return status;
+}
+
+/* API: Get stream info. */
+static pj_status_t ios_stream_get_param(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_param *pi)
+{
+ struct ios_stream *strm = (struct ios_stream*)s;
+
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+/* if (ios_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
+ &pi->fmt.info_size) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_INPUT_SCALE;
+ }
+*/
+ return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t ios_stream_get_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ void *pval)
+{
+ struct ios_stream *strm = (struct ios_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+ {
+ return PJMEDIA_EVID_INVCAP;
+// return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_EVID_INVCAP;
+ }
+}
+
+/* API: set capability */
+static pj_status_t ios_stream_set_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ const void *pval)
+{
+ struct ios_stream *strm = (struct ios_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+ {
+ return PJ_SUCCESS;
+ }
+
+ return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: Start stream. */
+static pj_status_t ios_stream_start(pjmedia_vid_dev_stream *strm)
+{
+ struct ios_stream *stream = (struct ios_stream*)strm;
+
+ PJ_UNUSED_ARG(stream);
+
+ PJ_LOG(4, (THIS_FILE, "Starting qt video stream"));
+
+ if (stream->cap_session) {
+ [stream->cap_session startRunning];
+
+ if (![stream->cap_session isRunning])
+ return PJ_EUNKNOWN;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Put frame from stream */
+static pj_status_t ios_stream_put_frame(pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame)
+{
+ struct ios_stream *stream = (struct ios_stream*)strm;
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ pj_assert(stream->frame_size >= frame->size);
+ pj_memcpy(stream->buf, frame->buf, frame->size);
+ /* Perform video display in a background thread */
+// [stream->vout_delegate update_image];
+ [NSThread detachNewThreadSelector:@selector(update_image)
+ toTarget:stream->vout_delegate withObject:nil];
+
+ [pool release];
+
+ return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t ios_stream_stop(pjmedia_vid_dev_stream *strm)
+{
+ struct ios_stream *stream = (struct ios_stream*)strm;
+
+ PJ_UNUSED_ARG(stream);
+
+ PJ_LOG(4, (THIS_FILE, "Stopping qt video stream"));
+
+ if (stream->cap_session && [stream->cap_session isRunning])
+ [stream->cap_session stopRunning];
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t ios_stream_destroy(pjmedia_vid_dev_stream *strm)
+{
+ struct ios_stream *stream = (struct ios_stream*)strm;
+
+ PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+ ios_stream_stop(strm);
+
+ if (stream->imgView) {
+ [stream->imgView removeFromSuperview];
+ [stream->imgView release];
+ stream->imgView = NULL;
+ }
+
+ if (stream->cap_session) {
+ [stream->cap_session release];
+ stream->cap_session = NULL;
+ }
+/* if (stream->dev_input) {
+ [stream->dev_input release];
+ stream->dev_input = NULL;
+ }
+*/
+ if (stream->vout_delegate) {
+ [stream->vout_delegate release];
+ stream->vout_delegate = NULL;
+ }
+/* if (stream->video_output) {
+ [stream->video_output release];
+ stream->video_output = NULL;
+ }
+*/
+
+ pj_pool_release(stream->pool);
+
+ return PJ_SUCCESS;
+}
+
+#endif
+#endif /* PJMEDIA_VIDEO_DEV_HAS_IOS */
diff --git a/pjmedia/src/pjmedia-videodev/qt_dev.m b/pjmedia/src/pjmedia-videodev/qt_dev.m
new file mode 100644
index 00000000..80546a5c
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/qt_dev.m
@@ -0,0 +1,636 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_QT
+
+#include <Foundation/NSAutoreleasePool.h>
+#include <QTKit/QTKit.h>
+
+#define THIS_FILE "qt_dev.c"
+#define DEFAULT_CLOCK_RATE 9000
+#define DEFAULT_WIDTH 640
+#define DEFAULT_HEIGHT 480
+#define DEFAULT_FPS 15
+
+#define kCVPixelFormatType_422YpCbCr8_yuvs 'yuvs'
+
+typedef struct qt_fmt_info
+{
+ pjmedia_format_id pjmedia_format;
+ unsigned qt_format;
+} qt_fmt_info;
+
+static qt_fmt_info qt_fmts[] =
+{
+ {PJMEDIA_FORMAT_YUY2, kCVPixelFormatType_422YpCbCr8_yuvs},
+ {PJMEDIA_FORMAT_UYVY, kCVPixelFormatType_422YpCbCr8},
+};
+
+/* qt device info */
+struct qt_dev_info
+{
+ pjmedia_vid_dev_info info;
+ char dev_id[192];
+};
+
+/* qt factory */
+struct qt_factory
+{
+ pjmedia_vid_dev_factory base;
+ pj_pool_t *pool;
+ pj_pool_t *dev_pool;
+ pj_pool_factory *pf;
+
+ unsigned dev_count;
+ struct qt_dev_info *dev_info;
+};
+
+@interface VOutDelegate: NSObject
+{
+@public
+ struct qt_stream *stream;
+}
+@end
+
+/* Video stream. */
+struct qt_stream
+{
+ pjmedia_vid_dev_stream base; /**< Base stream */
+ pjmedia_vid_dev_param param; /**< Settings */
+ pj_pool_t *pool; /**< Memory pool. */
+
+ pj_timestamp cap_frame_ts; /**< Captured frame tstamp */
+ unsigned cap_ts_inc; /**< Increment */
+
+ pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */
+ void *user_data; /**< Application data. */
+
+ pj_bool_t cap_thread_exited;
+ pj_bool_t cap_thread_initialized;
+ pj_thread_desc cap_thread_desc;
+ pj_thread_t *cap_thread;
+
+ NSAutoreleasePool *apool;
+ QTCaptureSession *cap_session;
+ QTCaptureDeviceInput *dev_input;
+ QTCaptureDecompressedVideoOutput *video_output;
+ VOutDelegate *vout_delegate;
+};
+
+
+/* Prototypes */
+static pj_status_t qt_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t qt_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t qt_factory_refresh(pjmedia_vid_dev_factory *f);
+static unsigned qt_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t qt_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info);
+static pj_status_t qt_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param);
+static pj_status_t qt_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm);
+
+static pj_status_t qt_stream_get_param(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_param *param);
+static pj_status_t qt_stream_get_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
+static pj_status_t qt_stream_set_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
+static pj_status_t qt_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t qt_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t qt_stream_destroy(pjmedia_vid_dev_stream *strm);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+ &qt_factory_init,
+ &qt_factory_destroy,
+ &qt_factory_get_dev_count,
+ &qt_factory_get_dev_info,
+ &qt_factory_default_param,
+ &qt_factory_create_stream,
+ &qt_factory_refresh
+};
+
+static pjmedia_vid_dev_stream_op stream_op =
+{
+ &qt_stream_get_param,
+ &qt_stream_get_cap,
+ &qt_stream_set_cap,
+ &qt_stream_start,
+ NULL,
+ NULL,
+ &qt_stream_stop,
+ &qt_stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init qt_ video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_qt_factory(pj_pool_factory *pf)
+{
+ struct qt_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, "qt video", 4000, 4000, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, struct qt_factory);
+ f->pf = pf;
+ f->pool = pool;
+ f->base.op = &factory_op;
+
+ return &f->base;
+}
+
+
+/* API: init factory */
+static pj_status_t qt_factory_init(pjmedia_vid_dev_factory *f)
+{
+ return qt_factory_refresh(f);
+}
+
+/* API: destroy factory */
+static pj_status_t qt_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+ struct qt_factory *qf = (struct qt_factory*)f;
+ pj_pool_t *pool = qf->pool;
+
+ if (qf->dev_pool)
+ pj_pool_release(qf->dev_pool);
+ qf->pool = NULL;
+ if (pool)
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/* API: refresh the list of devices */
+static pj_status_t qt_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+ struct qt_factory *qf = (struct qt_factory*)f;
+ struct qt_dev_info *qdi;
+ unsigned i, dev_count = 0;
+ NSAutoreleasePool *apool = [[NSAutoreleasePool alloc]init];
+ NSArray *dev_array;
+
+ if (qf->dev_pool) {
+ pj_pool_release(qf->dev_pool);
+ qf->dev_pool = NULL;
+ }
+
+ dev_array = [QTCaptureDevice inputDevices];
+ for (i = 0; i < [dev_array count]; i++) {
+ QTCaptureDevice *dev = [dev_array objectAtIndex:i];
+ if ([dev hasMediaType:QTMediaTypeVideo] ||
+ [dev hasMediaType:QTMediaTypeMuxed])
+ {
+ dev_count++;
+ }
+ }
+
+ /* Initialize input and output devices here */
+ qf->dev_count = 0;
+ qf->dev_pool = pj_pool_create(qf->pf, "qt video", 500, 500, NULL);
+
+ qf->dev_info = (struct qt_dev_info*)
+ pj_pool_calloc(qf->dev_pool, dev_count,
+ sizeof(struct qt_dev_info));
+ for (i = 0; i < [dev_array count]; i++) {
+ QTCaptureDevice *dev = [dev_array objectAtIndex:i];
+ if ([dev hasMediaType:QTMediaTypeVideo] ||
+ [dev hasMediaType:QTMediaTypeMuxed])
+ {
+ unsigned k;
+
+ qdi = &qf->dev_info[qf->dev_count++];
+ pj_bzero(qdi, sizeof(*qdi));
+ [[dev localizedDisplayName] getCString:qdi->info.name
+ maxLength:sizeof(qdi->info.name)
+ encoding:
+ [NSString defaultCStringEncoding]];
+ [[dev uniqueID] getCString:qdi->dev_id
+ maxLength:sizeof(qdi->dev_id)
+ encoding:[NSString defaultCStringEncoding]];
+ strcpy(qdi->info.driver, "QT");
+ qdi->info.dir = PJMEDIA_DIR_CAPTURE;
+ qdi->info.has_callback = PJ_TRUE;
+
+ qdi->info.fmt_cnt = 0;
+ qdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+ for (k = 0; k < [[dev formatDescriptions] count]; k++) {
+ unsigned l;
+ QTFormatDescription *desc = [[dev formatDescriptions]
+ objectAtIndex:k];
+ for (l = 0; l < PJ_ARRAY_SIZE(qt_fmts); l++) {
+ if ([desc formatType] == qt_fmts[l].qt_format) {
+ pjmedia_format *fmt =
+ &qdi->info.fmt[qdi->info.fmt_cnt++];
+ pjmedia_format_init_video(fmt,
+ qt_fmts[l].pjmedia_format,
+ DEFAULT_WIDTH,
+ DEFAULT_HEIGHT,
+ DEFAULT_FPS, 1);
+ break;
+ }
+ }
+ }
+
+ PJ_LOG(4, (THIS_FILE, " dev_id %d: %s", i, qdi->info.name));
+ }
+ }
+
+ [apool release];
+
+ PJ_LOG(4, (THIS_FILE, "qt video has %d devices",
+ qf->dev_count));
+
+ return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned qt_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+ struct qt_factory *qf = (struct qt_factory*)f;
+ return qf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t qt_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info)
+{
+ struct qt_factory *qf = (struct qt_factory*)f;
+
+ PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
+
+ pj_memcpy(info, &qf->dev_info[index].info, sizeof(*info));
+
+ return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t qt_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param)
+{
+ struct qt_factory *qf = (struct qt_factory*)f;
+ struct qt_dev_info *di = &qf->dev_info[index];
+
+ PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
+
+ PJ_UNUSED_ARG(pool);
+
+ pj_bzero(param, sizeof(*param));
+ param->dir = PJMEDIA_DIR_CAPTURE;
+ param->cap_id = index;
+ param->rend_id = PJMEDIA_VID_INVALID_DEV;
+ param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+ param->clock_rate = DEFAULT_CLOCK_RATE;
+ pj_memcpy(&param->fmt, &di->info.fmt[0], sizeof(param->fmt));
+
+ return PJ_SUCCESS;
+}
+
+@implementation VOutDelegate
+- (void)captureOutput:(QTCaptureOutput *)captureOutput
+ didOutputVideoFrame:(CVImageBufferRef)videoFrame
+ withSampleBuffer:(QTSampleBuffer *)sampleBuffer
+ fromConnection:(QTCaptureConnection *)connection
+{
+ unsigned size = [sampleBuffer lengthForAllSamples];
+ pjmedia_frame frame;
+
+ if (stream->cap_thread_initialized == 0 || !pj_thread_is_registered())
+ {
+ pj_thread_register("qt_cap", stream->cap_thread_desc,
+ &stream->cap_thread);
+ stream->cap_thread_initialized = 1;
+ PJ_LOG(5,(THIS_FILE, "Capture thread started"));
+ }
+
+ if (!videoFrame)
+ return;
+
+ frame.type = PJMEDIA_TYPE_VIDEO;
+ frame.buf = [sampleBuffer bytesForAllSamples];
+ frame.size = size;
+ frame.bit_info = 0;
+ frame.timestamp.u64 = stream->cap_frame_ts.u64;
+
+ if (stream->vid_cb.capture_cb)
+ (*stream->vid_cb.capture_cb)(&stream->base, stream->user_data,
+ &frame);
+
+ stream->cap_frame_ts.u64 += stream->cap_ts_inc;
+}
+@end
+
+static qt_fmt_info* get_qt_format_info(pjmedia_format_id id)
+{
+ unsigned i;
+
+ for (i = 0; i < PJ_ARRAY_SIZE(qt_fmts); i++) {
+ if (qt_fmts[i].pjmedia_format == id)
+ return &qt_fmts[i];
+ }
+
+ return NULL;
+}
+
+/* API: create stream */
+static pj_status_t qt_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
+{
+ struct qt_factory *qf = (struct qt_factory*)f;
+ pj_pool_t *pool;
+ struct qt_stream *strm;
+ const pjmedia_video_format_info *vfi;
+ pj_status_t status = PJ_SUCCESS;
+ BOOL success = NO;
+ NSError *error;
+
+ PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
+ PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
+ param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
+ param->dir == PJMEDIA_DIR_CAPTURE,
+ PJ_EINVAL);
+
+ vfi = pjmedia_get_video_format_info(NULL, param->fmt.id);
+ if (!vfi)
+ return PJMEDIA_EVID_BADFORMAT;
+
+ /* Create and Initialize stream descriptor */
+ pool = pj_pool_create(qf->pf, "qt-dev", 4000, 4000, NULL);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+ strm = PJ_POOL_ZALLOC_T(pool, struct qt_stream);
+ pj_memcpy(&strm->param, param, sizeof(*param));
+ strm->pool = pool;
+ pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
+ strm->user_data = user_data;
+ strm->apool = [[NSAutoreleasePool alloc]init];
+ pjmedia_event_publisher_init(&strm->base.epub, PJMEDIA_SIG_VID_DEV_COLORBAR);
+
+ /* Create capture stream here */
+ if (param->dir & PJMEDIA_DIR_CAPTURE) {
+ const pjmedia_video_format_detail *vfd;
+ qt_fmt_info *qfi = get_qt_format_info(param->fmt.id);
+
+ if (!qfi) {
+ status = PJMEDIA_EVID_BADFORMAT;
+ goto on_error;
+ }
+
+ strm->cap_session = [[QTCaptureSession alloc] init];
+ if (!strm->cap_session) {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+
+ /* Open video device */
+ QTCaptureDevice *videoDevice =
+ [QTCaptureDevice deviceWithUniqueID:
+ [NSString stringWithCString:
+ qf->dev_info[param->cap_id].dev_id
+ encoding:
+ [NSString defaultCStringEncoding]]];
+ if (!videoDevice || ![videoDevice open:&error]) {
+ status = PJMEDIA_EVID_SYSERR;
+ goto on_error;
+ }
+
+ /* Add the video device to the session as a device input */
+ strm->dev_input = [[QTCaptureDeviceInput alloc]
+ initWithDevice:videoDevice];
+ success = [strm->cap_session addInput:strm->dev_input error:&error];
+ if (!success) {
+ status = PJMEDIA_EVID_SYSERR;
+ goto on_error;
+ }
+
+ strm->video_output = [[QTCaptureDecompressedVideoOutput alloc] init];
+ success = [strm->cap_session addOutput:strm->video_output
+ error:&error];
+ if (!success) {
+ status = PJMEDIA_EVID_SYSERR;
+ goto on_error;
+ }
+
+ vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt,
+ PJ_TRUE);
+ [strm->video_output setPixelBufferAttributes:
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInt:
+ qfi->qt_format],
+ kCVPixelBufferPixelFormatTypeKey,
+ [NSNumber numberWithInt:
+ vfd->size.w],
+ kCVPixelBufferWidthKey,
+ [NSNumber numberWithInt:
+ vfd->size.h],
+ kCVPixelBufferHeightKey, nil]];
+
+ pj_assert(vfd->fps.num);
+ strm->cap_ts_inc = PJMEDIA_SPF2(strm->param.clock_rate, &vfd->fps, 1);
+
+ if ([strm->video_output
+ respondsToSelector:@selector(setMinimumVideoFrameInterval)])
+ {
+ [strm->video_output setMinimumVideoFrameInterval:
+ (1.0f * vfd->fps.denum /
+ (double)vfd->fps.num)];
+ }
+
+ strm->vout_delegate = [[VOutDelegate alloc]init];
+ strm->vout_delegate->stream = strm;
+ [strm->video_output setDelegate:strm->vout_delegate];
+ }
+
+ /* Apply the remaining settings */
+ /*
+ if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) {
+ qt_stream_set_cap(&strm->base,
+ PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
+ &param->fmt);
+ }
+ */
+ /* Done */
+ strm->base.op = &stream_op;
+ *p_vid_strm = &strm->base;
+
+ return PJ_SUCCESS;
+
+on_error:
+ qt_stream_destroy((pjmedia_vid_dev_stream *)strm);
+
+ return status;
+}
+
+/* API: Get stream info. */
+static pj_status_t qt_stream_get_param(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_param *pi)
+{
+ struct qt_stream *strm = (struct qt_stream*)s;
+
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+/* if (qt_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
+ &pi->fmt.info_size) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_INPUT_SCALE;
+ }
+*/
+ return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t qt_stream_get_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ void *pval)
+{
+ struct qt_stream *strm = (struct qt_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+ {
+ return PJMEDIA_EVID_INVCAP;
+// return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_EVID_INVCAP;
+ }
+}
+
+/* API: set capability */
+static pj_status_t qt_stream_set_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ const void *pval)
+{
+ struct qt_stream *strm = (struct qt_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+ {
+ return PJ_SUCCESS;
+ }
+
+ return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: Start stream. */
+static pj_status_t qt_stream_start(pjmedia_vid_dev_stream *strm)
+{
+ struct qt_stream *stream = (struct qt_stream*)strm;
+
+ PJ_UNUSED_ARG(stream);
+
+ PJ_LOG(4, (THIS_FILE, "Starting qt video stream"));
+
+ if (stream->cap_session) {
+ [stream->cap_session startRunning];
+
+ if (![stream->cap_session isRunning])
+ return PJ_EUNKNOWN;
+
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t qt_stream_stop(pjmedia_vid_dev_stream *strm)
+{
+ struct qt_stream *stream = (struct qt_stream*)strm;
+
+ PJ_UNUSED_ARG(stream);
+
+ PJ_LOG(4, (THIS_FILE, "Stopping qt video stream"));
+
+ if (stream->cap_session && [stream->cap_session isRunning])
+ [stream->cap_session stopRunning];
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t qt_stream_destroy(pjmedia_vid_dev_stream *strm)
+{
+ struct qt_stream *stream = (struct qt_stream*)strm;
+
+ PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+ qt_stream_stop(strm);
+
+ if (stream->dev_input && [[stream->dev_input device] isOpen])
+ [[stream->dev_input device] close];
+
+ if (stream->cap_session) {
+ [stream->cap_session release];
+ stream->cap_session = NULL;
+ }
+ if (stream->dev_input) {
+ [stream->dev_input release];
+ stream->dev_input = NULL;
+ }
+ if (stream->vout_delegate) {
+ [stream->vout_delegate release];
+ stream->vout_delegate = NULL;
+ }
+ if (stream->video_output) {
+ [stream->video_output release];
+ stream->video_output = NULL;
+ }
+
+// [stream->apool release];
+ pj_pool_release(stream->pool);
+
+ return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_VIDEO_DEV_HAS_QT */
diff --git a/pjmedia/src/pjmedia-videodev/sdl_dev.c b/pjmedia/src/pjmedia-videodev/sdl_dev.c
new file mode 100644
index 00000000..115b362e
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/sdl_dev.c
@@ -0,0 +1,1291 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/converter.h>
+#include <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_SDL
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+# include <Foundation/Foundation.h>
+#endif
+
+#include <SDL.h>
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+# include "SDL_opengl.h"
+# define OPENGL_DEV_IDX 1
+#else
+# define OPENGL_DEV_IDX -999
+#endif
+
+#define THIS_FILE "sdl_dev.c"
+#define DEFAULT_CLOCK_RATE 90000
+#define DEFAULT_WIDTH 640
+#define DEFAULT_HEIGHT 480
+#define DEFAULT_FPS 25
+
+#if !(SDL_VERSION_ATLEAST(1,3,0))
+# define SDL_PIXELFORMAT_RGBA8888 0
+# define SDL_PIXELFORMAT_RGB24 0
+# define SDL_PIXELFORMAT_BGRA8888 0
+# define SDL_PIXELFORMAT_ABGR8888 0
+# define SDL_PIXELFORMAT_BGR24 0
+# define SDL_PIXELFORMAT_ARGB8888 0
+# define SDL_PIXELFORMAT_RGB24 0
+#endif
+
+typedef struct sdl_fmt_info
+{
+ pjmedia_format_id fmt_id;
+ Uint32 sdl_format;
+ Uint32 Rmask;
+ Uint32 Gmask;
+ Uint32 Bmask;
+ Uint32 Amask;
+} sdl_fmt_info;
+
+static sdl_fmt_info sdl_fmts[] =
+{
+#if PJ_IS_BIG_ENDIAN
+ {PJMEDIA_FORMAT_RGBA, (Uint32)SDL_PIXELFORMAT_RGBA8888,
+ 0xFF000000, 0xFF0000, 0xFF00, 0xFF} ,
+ {PJMEDIA_FORMAT_RGB24, (Uint32)SDL_PIXELFORMAT_RGB24,
+ 0xFF0000, 0xFF00, 0xFF, 0} ,
+ {PJMEDIA_FORMAT_BGRA, (Uint32)SDL_PIXELFORMAT_BGRA8888,
+ 0xFF00, 0xFF0000, 0xFF000000, 0xFF} ,
+#else
+ {PJMEDIA_FORMAT_RGBA, (Uint32)SDL_PIXELFORMAT_ABGR8888,
+ 0xFF, 0xFF00, 0xFF0000, 0xFF000000} ,
+ {PJMEDIA_FORMAT_RGB24, (Uint32)SDL_PIXELFORMAT_BGR24,
+ 0xFF, 0xFF00, 0xFF0000, 0} ,
+ {PJMEDIA_FORMAT_BGRA, (Uint32)SDL_PIXELFORMAT_ARGB8888,
+ 0xFF0000, 0xFF00, 0xFF, 0xFF000000} ,
+#endif
+
+ {PJMEDIA_FORMAT_DIB , (Uint32)SDL_PIXELFORMAT_RGB24,
+ 0xFF0000, 0xFF00, 0xFF, 0} ,
+
+ {PJMEDIA_FORMAT_YUY2, SDL_YUY2_OVERLAY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_UYVY, SDL_UYVY_OVERLAY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_YVYU, SDL_YVYU_OVERLAY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_I420, SDL_IYUV_OVERLAY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_YV12, SDL_YV12_OVERLAY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_I420JPEG, SDL_IYUV_OVERLAY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_I422JPEG, SDL_YV12_OVERLAY, 0, 0, 0, 0} ,
+};
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+@interface SDLDelegate: NSObject
+{
+ @public
+ struct sdl_stream *strm;
+}
+
+- (void)sdl_init;
+- (void)sdl_quit;
+- (void)detect_new_fmt;
+- (int)sdl_create;
+- (void)sdl_destroy;
+- (int)handle_event;
+- (pj_status_t)put_frame;
+@end
+#endif
+
+/* sdl_ device info */
+struct sdl_dev_info
+{
+ pjmedia_vid_dev_info info;
+};
+
+/* sdl_ factory */
+struct sdl_factory
+{
+ pjmedia_vid_dev_factory base;
+ pj_pool_t *pool;
+ pj_pool_factory *pf;
+
+ unsigned dev_count;
+ struct sdl_dev_info *dev_info;
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ NSAutoreleasePool *apool;
+ SDLDelegate *delegate;
+#endif
+};
+
+/* Video stream. */
+struct sdl_stream
+{
+ pjmedia_vid_dev_stream base; /**< Base stream */
+ pjmedia_vid_dev_param param; /**< Settings */
+ pj_pool_t *pool; /**< Memory pool. */
+
+ pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */
+ void *user_data; /**< Application data. */
+
+ pj_thread_t *sdl_thread; /**< SDL thread. */
+ pj_bool_t is_quitting;
+ pj_bool_t is_running;
+ pj_bool_t render_exited;
+ pj_status_t status;
+ pjmedia_format *new_fmt;
+ pjmedia_rect_size *new_disp_size;
+ pj_timestamp last_ts;
+
+#if SDL_VERSION_ATLEAST(1,3,0)
+ SDL_Window *window; /**< Display window. */
+ SDL_Renderer *renderer; /**< Display renderer. */
+ SDL_Texture *scr_tex; /**< Screen texture. */
+ int pitch; /**< Pitch value. */
+#endif
+ SDL_Rect rect; /**< Frame rectangle. */
+ SDL_Rect dstrect; /**< Display rectangle. */
+ SDL_Surface *screen; /**< Display screen. */
+ SDL_Surface *surf; /**< RGB surface. */
+ SDL_Overlay *overlay; /**< YUV overlay. */
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+#if SDL_VERSION_ATLEAST(1,3,0)
+ SDL_GLContext *gl_context;
+#endif
+ GLuint texture;
+ void *tex_buf;
+ pj_size_t tex_buf_size;
+#endif
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ NSAutoreleasePool *apool;
+ SDLDelegate *delegate;
+ const pjmedia_frame *frame;
+#endif
+
+ /* For frame conversion */
+ pjmedia_converter *conv;
+ pjmedia_conversion_param conv_param;
+ pjmedia_frame conv_buf;
+
+ pjmedia_video_apply_fmt_param vafp;
+};
+
+
+/* Prototypes */
+static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t sdl_factory_refresh(pjmedia_vid_dev_factory *f);
+static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info);
+static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param);
+static pj_status_t sdl_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm);
+
+static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_param *param);
+static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
+static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame);
+static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm);
+
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+static void draw_gl(struct sdl_stream *stream, void *tex_buf);
+#endif
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+ &sdl_factory_init,
+ &sdl_factory_destroy,
+ &sdl_factory_get_dev_count,
+ &sdl_factory_get_dev_info,
+ &sdl_factory_default_param,
+ &sdl_factory_create_stream,
+ &sdl_factory_refresh
+};
+
+static pjmedia_vid_dev_stream_op stream_op =
+{
+ &sdl_stream_get_param,
+ &sdl_stream_get_cap,
+ &sdl_stream_set_cap,
+ &sdl_stream_start,
+ NULL,
+ &sdl_stream_put_frame,
+ &sdl_stream_stop,
+ &sdl_stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init sdl_ video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf)
+{
+ struct sdl_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, "sdl video", 1000, 1000, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, struct sdl_factory);
+ f->pf = pf;
+ f->pool = pool;
+ f->base.op = &factory_op;
+
+ return &f->base;
+}
+
+
+/* API: init factory */
+static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+ struct sdl_dev_info *ddi;
+ unsigned i, j;
+
+#if SDL_VERSION_ATLEAST(1,3,0)
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ sf->apool = [[NSAutoreleasePool alloc] init];
+ sf->delegate = [[SDLDelegate alloc] init];
+ [sf->delegate performSelectorOnMainThread:@selector(sdl_init)
+ withObject:nil waitUntilDone:YES];
+#else
+ /* Initialize the SDL library */
+ if (SDL_Init(SDL_INIT_VIDEO))
+ return PJMEDIA_EVID_INIT;
+#endif
+#endif
+
+ sf->dev_count = 1;
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ sf->dev_count++;
+#endif
+ sf->dev_info = (struct sdl_dev_info*)
+ pj_pool_calloc(sf->pool, sf->dev_count,
+ sizeof(struct sdl_dev_info));
+
+ ddi = &sf->dev_info[0];
+ pj_bzero(ddi, sizeof(*ddi));
+ strncpy(ddi->info.name, "SDL renderer", sizeof(ddi->info.name));
+ ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
+ ddi->info.fmt_cnt = PJ_ARRAY_SIZE(sdl_fmts);
+
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ ddi = &sf->dev_info[OPENGL_DEV_IDX];
+ pj_bzero(ddi, sizeof(*ddi));
+ strncpy(ddi->info.name, "SDL openGL renderer", sizeof(ddi->info.name));
+ ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
+ ddi->info.fmt_cnt = 1;
+#endif
+
+ for (i = 0; i < sf->dev_count; i++) {
+ ddi = &sf->dev_info[i];
+ strncpy(ddi->info.driver, "SDL", sizeof(ddi->info.driver));
+ ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
+ ddi->info.dir = PJMEDIA_DIR_RENDER;
+ ddi->info.has_callback = PJ_FALSE;
+ ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT |
+ PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
+#if SDL_VERSION_ATLEAST(1,3,0)
+ ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+#endif
+
+ for (j = 0; j < ddi->info.fmt_cnt; j++) {
+ pjmedia_format *fmt = &ddi->info.fmt[j];
+ pjmedia_format_init_video(fmt, sdl_fmts[j].fmt_id,
+ DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ DEFAULT_FPS, 1);
+ }
+ }
+
+ PJ_LOG(4, (THIS_FILE, "SDL initialized"));
+
+ return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+ pj_pool_t *pool = sf->pool;
+
+ sf->pool = NULL;
+ pj_pool_release(pool);
+
+#if SDL_VERSION_ATLEAST(1,3,0)
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ [sf->delegate performSelectorOnMainThread:@selector(sdl_quit)
+ withObject:nil waitUntilDone:YES];
+ [sf->delegate release];
+ [sf->apool release];
+#else
+ SDL_Quit();
+#endif
+#endif
+
+ return PJ_SUCCESS;
+}
+
+/* API: refresh the list of devices */
+static pj_status_t sdl_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+ PJ_UNUSED_ARG(f);
+ return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+ return sf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+
+ PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
+
+ pj_memcpy(info, &sf->dev_info[index].info, sizeof(*info));
+
+ return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+ struct sdl_dev_info *di = &sf->dev_info[index];
+
+ PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
+
+ PJ_UNUSED_ARG(pool);
+
+ pj_bzero(param, sizeof(*param));
+ param->dir = PJMEDIA_DIR_RENDER;
+ param->rend_id = index;
+ param->cap_id = PJMEDIA_VID_INVALID_DEV;
+
+ /* Set the device capabilities here */
+ param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+ param->fmt.type = PJMEDIA_TYPE_VIDEO;
+ param->clock_rate = DEFAULT_CLOCK_RATE;
+ pj_memcpy(&param->fmt, &di->info.fmt[0], sizeof(param->fmt));
+
+ return PJ_SUCCESS;
+}
+
+static sdl_fmt_info* get_sdl_format_info(pjmedia_format_id id)
+{
+ unsigned i;
+
+ for (i = 0; i < sizeof(sdl_fmts)/sizeof(sdl_fmts[0]); i++) {
+ if (sdl_fmts[i].fmt_id == id)
+ return &sdl_fmts[i];
+ }
+
+ return NULL;
+}
+
+static void destroy_sdl(struct sdl_stream *strm, pj_bool_t destroy_win)
+{
+ PJ_UNUSED_ARG(destroy_win);
+
+ if (strm->surf) {
+ SDL_FreeSurface(strm->surf);
+ strm->surf = NULL;
+ }
+ if (strm->overlay) {
+ SDL_FreeYUVOverlay(strm->overlay);
+ strm->overlay = NULL;
+ }
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ if (strm->texture) {
+ glDeleteTextures(1, &strm->texture);
+ strm->texture = 0;
+ }
+#endif
+#if SDL_VERSION_ATLEAST(1,3,0)
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ if (strm->gl_context) {
+ SDL_GL_DeleteContext(strm->gl_context);
+ strm->gl_context = NULL;
+ }
+#endif
+ if (strm->scr_tex) {
+ SDL_DestroyTexture(strm->scr_tex);
+ strm->scr_tex = NULL;
+ }
+ if (strm->renderer) {
+ SDL_DestroyRenderer(strm->renderer);
+ strm->renderer = NULL;
+ }
+#ifndef __IPHONEOS__
+ if (destroy_win) {
+ if (strm->window &&
+ !(strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW))
+ {
+ SDL_DestroyWindow(strm->window);
+ }
+ strm->window = NULL;
+ }
+#endif
+#endif
+}
+
+static pj_status_t init_sdl(struct sdl_stream *strm, pjmedia_format *fmt)
+{
+ sdl_fmt_info *sdl_info = get_sdl_format_info(fmt->id);
+ const pjmedia_video_format_info *vfi;
+ pjmedia_video_format_detail *vfd;
+
+ vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
+ fmt->id);
+ if (!vfi || !sdl_info)
+ return PJMEDIA_EVID_BADFORMAT;
+
+ strm->vafp.size = fmt->det.vid.size;
+ strm->vafp.buffer = NULL;
+ if (vfi->apply_fmt(vfi, &strm->vafp) != PJ_SUCCESS)
+ return PJMEDIA_EVID_BADFORMAT;
+
+ vfd = pjmedia_format_get_video_format_detail(fmt, PJ_TRUE);
+ strm->rect.x = strm->rect.y = 0;
+ strm->rect.w = (Uint16)vfd->size.w;
+ strm->rect.h = (Uint16)vfd->size.h;
+ if (strm->param.disp_size.w == 0)
+ strm->param.disp_size.w = strm->rect.w;
+ if (strm->param.disp_size.h == 0)
+ strm->param.disp_size.h = strm->rect.h;
+ strm->dstrect.x = strm->dstrect.y = 0;
+ strm->dstrect.w = (Uint16)strm->param.disp_size.w;
+ strm->dstrect.h = (Uint16)strm->param.disp_size.h;
+
+ destroy_sdl(strm, PJ_FALSE);
+
+#if SDL_VERSION_ATLEAST(1,3,0)
+ if (!strm->window) {
+ Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
+
+ if (strm->param.rend_id == OPENGL_DEV_IDX)
+ flags |= SDL_WINDOW_OPENGL;
+
+ if (strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
+ /* Use the window supplied by the application. */
+ strm->window = SDL_CreateWindowFrom(strm->param.window);
+ } else {
+ /* Create the window where we will draw. */
+ strm->window = SDL_CreateWindow("pjmedia-SDL video",
+ SDL_WINDOWPOS_CENTERED,
+ SDL_WINDOWPOS_CENTERED,
+ strm->param.disp_size.w,
+ strm->param.disp_size.h,
+ flags);
+ }
+ if (!strm->window)
+ return PJMEDIA_EVID_SYSERR;
+ }
+
+ SDL_SetWindowSize(strm->window, strm->param.disp_size.w,
+ strm->param.disp_size.h);
+
+ /**
+ * We must call SDL_CreateRenderer in order for draw calls to
+ * affect this window.
+ */
+ strm->renderer = SDL_CreateRenderer(strm->window, -1, 0);
+ if (!strm->renderer)
+ return PJMEDIA_EVID_SYSERR;
+
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ if (strm->param.rend_id == OPENGL_DEV_IDX) {
+ strm->gl_context = SDL_GL_CreateContext(strm->window);
+ if (!strm->gl_context)
+ return PJMEDIA_EVID_SYSERR;
+ SDL_GL_MakeCurrent(strm->window, strm->gl_context);
+ }
+#endif
+
+ strm->screen = SDL_GetWindowSurface(strm->window);
+
+#else
+ /* Initialize the display */
+ strm->screen = SDL_SetVideoMode(strm->param.disp_size.w,
+ strm->param.disp_size.h, 0, (
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ strm->param.rend_id == OPENGL_DEV_IDX?
+ SDL_OPENGL | SDL_RESIZABLE:
+#endif
+ SDL_RESIZABLE | SDL_SWSURFACE));
+ if (strm->screen == NULL)
+ return PJMEDIA_EVID_SYSERR;
+
+ SDL_WM_SetCaption("pjmedia-SDL video", NULL);
+
+#endif
+
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ if (strm->param.rend_id == OPENGL_DEV_IDX) {
+ /* Init some OpenGL settings */
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glEnable(GL_TEXTURE_2D);
+
+ /* Init the viewport */
+ glViewport(0, 0, strm->param.disp_size.w, strm->param.disp_size.h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+
+ glOrtho(0.0, (GLdouble)strm->param.disp_size.w,
+ (GLdouble)strm->param.disp_size.h, 0.0, 0.0, 1.0);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ /* Create a texture */
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+ glGenTextures(1, &strm->texture);
+
+ if (!strm->texture)
+ return PJMEDIA_EVID_SYSERR;
+
+#if defined(PJ_WIN32) && PJ_WIN32 != 0
+ /**
+ * On Win32 platform, the OpenGL drawing must be in the same
+ * thread that calls SDL_SetVideoMode(), hence we need a buffer
+ * for the frame from sdl_stream_put_frame()
+ */
+ if (strm->vafp.framebytes > strm->tex_buf_size) {
+ strm->tex_buf_size = strm->vafp.framebytes;
+ strm->tex_buf = pj_pool_alloc(strm->pool, strm->vafp.framebytes);
+ }
+#endif
+ } else
+#endif
+#if SDL_VERSION_ATLEAST(1,3,0)
+ {
+ strm->scr_tex = SDL_CreateTexture(strm->renderer, sdl_info->sdl_format,
+ SDL_TEXTUREACCESS_STREAMING,
+ strm->rect.w, strm->rect.h);
+ if (strm->scr_tex == NULL)
+ return PJMEDIA_EVID_SYSERR;
+
+ strm->pitch = strm->rect.w * SDL_BYTESPERPIXEL(sdl_info->sdl_format);
+ }
+#else
+ if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB) {
+ strm->surf = SDL_CreateRGBSurface(SDL_SWSURFACE,
+ strm->rect.w, strm->rect.h,
+ vfi->bpp,
+ sdl_info->Rmask,
+ sdl_info->Gmask,
+ sdl_info->Bmask,
+ sdl_info->Amask);
+ if (strm->surf == NULL)
+ return PJMEDIA_EVID_SYSERR;
+ } else if (vfi->color_model == PJMEDIA_COLOR_MODEL_YUV) {
+ strm->overlay = SDL_CreateYUVOverlay(strm->rect.w, strm->rect.h,
+ sdl_info->sdl_format,
+ strm->screen);
+ if (strm->overlay == NULL)
+ return PJMEDIA_EVID_SYSERR;
+ }
+#endif
+
+ return PJ_SUCCESS;
+}
+
+static void detect_fmt_change(struct sdl_stream *strm)
+{
+ if (strm->new_fmt || strm->new_disp_size) {
+ /* Stop the stream */
+ sdl_stream_stop((pjmedia_vid_dev_stream *)strm);
+
+ if (strm->new_disp_size)
+ pj_memcpy(&strm->param.disp_size, strm->new_disp_size,
+ sizeof(strm->param.disp_size));
+
+ /* Re-initialize SDL */
+ strm->status = init_sdl(strm, (strm->new_fmt? strm->new_fmt :
+ &strm->param.fmt));
+
+ if (strm->status == PJ_SUCCESS) {
+ if (strm->new_fmt)
+ pjmedia_format_copy(&strm->param.fmt, strm->new_fmt);
+ /* Restart the stream */
+ sdl_stream_start((pjmedia_vid_dev_stream *)strm);
+ }
+ strm->new_fmt = NULL;
+ strm->new_disp_size = NULL;
+ }
+}
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+@implementation SDLDelegate
+- (void)sdl_init
+{
+ if (SDL_Init(SDL_INIT_VIDEO)) {
+ PJ_LOG(4, (THIS_FILE, "Cannot initialize SDL"));
+ }
+}
+
+- (void)sdl_quit
+{
+ SDL_Quit();
+}
+
+- (void)detect_new_fmt
+{
+ detect_fmt_change(strm);
+}
+
+- (int)sdl_create
+{
+#else
+static int sdlthread(void * data)
+{
+ struct sdl_stream *strm = (struct sdl_stream*)data;
+ pj_bool_t notify_wnd_closed_event = PJ_FALSE;
+ pj_status_t saved_stream_status;
+#endif
+
+#if !(SDL_VERSION_ATLEAST(1,3,0))
+ if (SDL_Init(SDL_INIT_VIDEO)) {
+ strm->status = PJMEDIA_EVID_INIT;
+ goto on_return;
+ }
+#endif
+
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ if (strm->param.rend_id == OPENGL_DEV_IDX) {
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
+ }
+#endif
+
+ strm->status = init_sdl(strm, &strm->param.fmt);
+ if (strm->status != PJ_SUCCESS)
+ goto on_return;
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+on_return:
+ if (strm->status != PJ_SUCCESS) {
+ destroy_sdl(strm, PJ_TRUE);
+#if !(SDL_VERSION_ATLEAST(1,3,0))
+ SDL_Quit();
+#endif
+ strm->screen = NULL;
+ }
+
+ return strm->status;
+}
+
+- (void)sdl_destroy
+{
+ destroy_sdl(strm, PJ_TRUE);
+}
+
+- (int)handle_event
+{
+ const pjmedia_video_format_info *vfi;
+ pjmedia_video_format_detail *vfd;
+ pj_bool_t notify_wnd_closed_event = PJ_FALSE;
+ pj_status_t saved_stream_status;
+
+ vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
+ strm->param.fmt.id);
+ vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
+#else
+ while(!strm->is_quitting)
+#endif
+ {
+ SDL_Event sevent;
+ pjmedia_event pevent;
+
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+#if defined(PJ_WIN32) && PJ_WIN32 != 0
+ if (strm->param.rend_id == OPENGL_DEV_IDX) {
+ draw_gl(strm, strm->tex_buf);
+ }
+#endif
+#endif
+
+ detect_fmt_change(strm);
+
+ /**
+ * The event polling must be placed in the same thread that
+ * call SDL_SetVideoMode(). Please consult the official doc of
+ * SDL_PumpEvents().
+ */
+ while (SDL_PollEvent(&sevent)) {
+ pjmedia_event_init(&pevent, PJMEDIA_EVENT_NONE, &strm->last_ts,
+ &strm->base.epub);
+
+ switch(sevent.type) {
+ case SDL_MOUSEBUTTONDOWN:
+ pevent.type = PJMEDIA_EVENT_MOUSE_BTN_DOWN;
+ break;
+#if SDL_VERSION_ATLEAST(1,3,0)
+ case SDL_WINDOWEVENT:
+ switch (sevent.window.event) {
+ case SDL_WINDOWEVENT_RESIZED:
+ pevent.type = PJMEDIA_EVENT_WND_RESIZED;
+ pevent.data.wnd_resized.new_size.w =
+ sevent.window.data1;
+ pevent.data.wnd_resized.new_size.h =
+ sevent.window.data2;
+ break;
+ }
+ break;
+#else
+ case SDL_VIDEORESIZE:
+ pevent.type = PJMEDIA_EVENT_WND_RESIZED;
+ pevent.data.wnd_resized.new_size.w = sevent.resize.w;
+ pevent.data.wnd_resized.new_size.h = sevent.resize.h;
+ break;
+ case SDL_QUIT:
+ pevent.type = PJMEDIA_EVENT_WND_CLOSING;
+ break;
+#endif
+ }
+
+ if (pevent.type != PJMEDIA_EVENT_NONE) {
+ pj_status_t status;
+
+ status = pjmedia_event_publish(&strm->base.epub, &pevent);
+
+ switch (pevent.type) {
+ case PJMEDIA_EVENT_WND_RESIZED:
+ strm->new_disp_size = &pevent.data.wnd_resized.new_size;
+ detect_fmt_change(strm);
+ break;
+
+ case PJMEDIA_EVENT_WND_CLOSING:
+ if (pevent.data.wnd_closing.cancel) {
+ /* Cancel the closing operation */
+ break;
+ }
+
+ /* Proceed to cleanup SDL. App must still call
+ * pjmedia_dev_stream_destroy() when getting WND_CLOSED
+ * event
+ */
+ strm->is_quitting = PJ_TRUE;
+ notify_wnd_closed_event = PJ_TRUE;
+ sdl_stream_stop(&strm->base);
+ goto on_return;
+
+ default:
+ /* Just to prevent gcc warning about unused enums */
+ break;
+ }
+ }
+ }
+ }
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ return 0;
+#endif
+on_return:
+ destroy_sdl(strm, PJ_TRUE);
+#if !(SDL_VERSION_ATLEAST(1,3,0))
+ SDL_Quit();
+#endif
+ strm->screen = NULL;
+ saved_stream_status = strm->status;
+
+ if (notify_wnd_closed_event) {
+ pjmedia_event pevent;
+
+ pjmedia_event_init(&pevent, PJMEDIA_EVENT_WND_CLOSED, &strm->last_ts,
+ &strm->base.epub);
+ pjmedia_event_publish(&strm->base.epub, &pevent);
+ }
+
+ /*
+ * Note: don't access the stream after this point, it might have
+ * been destroyed
+ */
+
+ return saved_stream_status;
+}
+
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+static void draw_gl(struct sdl_stream *stream, void *tex_buf)
+{
+ if (stream->texture) {
+ glBindTexture(GL_TEXTURE_2D, stream->texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+ stream->rect.w, stream->rect.h, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, tex_buf);
+ glBegin(GL_TRIANGLE_STRIP);
+ glTexCoord2f(0, 0); glVertex2i(0, 0);
+ glTexCoord2f(1, 0); glVertex2i(stream->param.disp_size.w, 0);
+ glTexCoord2f(0, 1); glVertex2i(0, stream->param.disp_size.h);
+ glTexCoord2f(1, 1);
+ glVertex2i(stream->param.disp_size.w, stream->param.disp_size.h);
+ glEnd();
+#if SDL_VERSION_ATLEAST(1,3,0)
+ SDL_GL_SwapWindow(stream->window);
+#else
+ SDL_GL_SwapBuffers();
+#endif
+ }
+}
+#endif
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+- (pj_status_t)put_frame
+{
+ const pjmedia_frame *frame = strm->frame;
+#else
+/* API: Put frame from stream */
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame)
+{
+#endif
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+ pj_status_t status = PJ_SUCCESS;
+
+ stream->last_ts.u64 = frame->timestamp.u64;
+
+ if (!stream->is_running) {
+ stream->render_exited = PJ_TRUE;
+ goto on_return;
+ }
+
+ if (frame->size==0 || frame->buf==NULL ||
+ frame->size < stream->vafp.framebytes)
+ goto on_return;
+
+ if (stream->surf) {
+ if (SDL_MUSTLOCK(stream->surf)) {
+ if (SDL_LockSurface(stream->surf) < 0) {
+ PJ_LOG(3, (THIS_FILE, "Unable to lock SDL surface"));
+ status = PJMEDIA_EVID_NOTREADY;
+ goto on_return;
+ }
+ }
+
+ pj_memcpy(stream->surf->pixels, frame->buf,
+ stream->vafp.framebytes);
+
+ if (SDL_MUSTLOCK(stream->surf)) {
+ SDL_UnlockSurface(stream->surf);
+ }
+ SDL_BlitSurface(stream->surf, NULL, stream->screen, &stream->dstrect);
+#if SDL_VERSION_ATLEAST(1,3,0)
+ SDL_UpdateWindowSurface(stream->window);
+#else
+ SDL_UpdateRect(stream->screen, 0, 0, 0, 0);
+#endif
+ } else if (stream->overlay) {
+ int i, sz, offset;
+
+ if (SDL_LockYUVOverlay(stream->overlay) < 0) {
+ PJ_LOG(3, (THIS_FILE, "Unable to lock SDL overlay"));
+ status = PJMEDIA_EVID_NOTREADY;
+ goto on_return;
+ }
+
+ for (i = 0, offset = 0; i < stream->overlay->planes; i++) {
+ sz = stream->vafp.plane_bytes[i];
+ pj_memcpy(stream->overlay->pixels[i],
+ (char *)frame->buf + offset, sz);
+ offset += sz;
+ }
+
+ SDL_UnlockYUVOverlay(stream->overlay);
+ SDL_DisplayYUVOverlay(stream->overlay, &stream->dstrect);
+ }
+#if SDL_VERSION_ATLEAST(1,3,0)
+ else if (stream->scr_tex) {
+ SDL_UpdateTexture(stream->scr_tex, NULL, frame->buf, stream->pitch);
+ SDL_RenderClear(stream->renderer);
+ SDL_RenderCopy(stream->renderer, stream->scr_tex, NULL, NULL);
+ SDL_RenderPresent(stream->renderer);
+ }
+#endif
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ else if (stream->param.rend_id == OPENGL_DEV_IDX) {
+#if defined(PJ_WIN32) && PJ_WIN32 != 0
+ pj_memcpy(stream->tex_buf, frame->buf, stream->vafp.framebytes);
+#else
+ draw_gl(stream, frame->buf);
+#endif
+ }
+#endif
+
+on_return:
+ return status;
+}
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+@end
+
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame)
+{
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+ stream->frame = frame;
+ [stream->delegate performSelectorOnMainThread:@selector(put_frame)
+ withObject:nil waitUntilDone:YES];
+
+ return PJ_SUCCESS;
+}
+
+static int sdlthread(void * data)
+{
+ struct sdl_stream *strm = (struct sdl_stream*)data;
+
+ while(!strm->is_quitting) {
+ [strm->delegate performSelectorOnMainThread:@selector(handle_event)
+ withObject:nil waitUntilDone:YES];
+ }
+
+ return 0;
+}
+
+#endif
+
+/* API: create stream */
+static pj_status_t sdl_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+ pj_pool_t *pool;
+ struct sdl_stream *strm;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_RENDER, PJ_EINVAL);
+
+ /* Create and Initialize stream descriptor */
+ pool = pj_pool_create(sf->pf, "sdl-dev", 1000, 1000, NULL);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+ strm = PJ_POOL_ZALLOC_T(pool, struct sdl_stream);
+ pj_memcpy(&strm->param, param, sizeof(*param));
+ strm->pool = pool;
+ pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
+ strm->user_data = user_data;
+ pjmedia_event_publisher_init(&strm->base.epub, PJMEDIA_SIG_VID_DEV_SDL);
+
+ /* Create render stream here */
+ if (param->dir & PJMEDIA_DIR_RENDER) {
+ strm->status = PJ_SUCCESS;
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ strm->apool = [[NSAutoreleasePool alloc] init];
+ strm->delegate = [[SDLDelegate alloc]init];
+ strm->delegate->strm = strm;
+ /* On Darwin OS, we need to call SDL functions in the main thread */
+ [strm->delegate performSelectorOnMainThread:@selector(sdl_create)
+ withObject:nil waitUntilDone:YES];
+ if ((status = strm->status) != PJ_SUCCESS) {
+ goto on_error;
+ }
+#endif
+ status = pj_thread_create(pool, "sdl_thread", sdlthread,
+ strm, 0, 0, &strm->sdl_thread);
+
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ while(strm->status == PJ_SUCCESS && !strm->surf && !strm->overlay
+#if SDL_VERSION_ATLEAST(1,3,0)
+ && !strm->scr_tex
+#endif
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ && !strm->texture
+#endif
+ )
+ {
+ pj_thread_sleep(10);
+ }
+ if ((status = strm->status) != PJ_SUCCESS) {
+ goto on_error;
+ }
+ }
+
+ /* Apply the remaining settings */
+ if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) {
+ sdl_stream_set_cap(&strm->base,
+ PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION,
+ &param->window_pos);
+ }
+ if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) {
+ sdl_stream_set_cap(&strm->base,
+ PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
+ &param->window_hide);
+ }
+
+ /* Done */
+ strm->base.op = &stream_op;
+ *p_vid_strm = &strm->base;
+
+ return PJ_SUCCESS;
+
+on_error:
+ sdl_stream_destroy(&strm->base);
+ return status;
+}
+
+/* API: Get stream info. */
+static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_param *pi)
+{
+ struct sdl_stream *strm = (struct sdl_stream*)s;
+
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+ if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
+ &pi->window) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+ }
+ if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION,
+ &pi->window_pos) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION;
+ }
+ if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE,
+ &pi->disp_size) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
+ }
+ if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
+ &pi->window_hide) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ void *pval)
+{
+ struct sdl_stream *strm = (struct sdl_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+#if SDL_VERSION_ATLEAST(1,3,0)
+ if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
+ {
+ *((void **)pval) = strm->window;
+ return PJ_SUCCESS;
+ } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) {
+ SDL_GetWindowPosition(strm->window, &((pjmedia_coord *)pval)->x,
+ &((pjmedia_coord *)pval)->y);
+ return PJ_SUCCESS;
+ } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE) {
+ SDL_GetWindowSize(strm->window, (int *)&((pjmedia_rect_size *)pval)->w,
+ (int *)&((pjmedia_rect_size *)pval)->h);
+ return PJ_SUCCESS;
+ } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) {
+ Uint32 flag = SDL_GetWindowFlags(strm->window);
+ *((pj_bool_t *)pval) = (flag | SDL_WINDOW_HIDDEN)? PJ_TRUE: PJ_FALSE;
+ return PJ_SUCCESS;
+ }
+#endif
+
+ return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: set capability */
+static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ const void *pval)
+{
+ struct sdl_stream *strm = (struct sdl_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+#if SDL_VERSION_ATLEAST(1,3,0)
+ if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) {
+ SDL_SetWindowPosition(strm->window, ((pjmedia_coord *)pval)->x,
+ ((pjmedia_coord *)pval)->y);
+ return PJ_SUCCESS;
+ } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) {
+ if (*(pj_bool_t *)pval)
+ SDL_HideWindow(strm->window);
+ else
+ SDL_ShowWindow(strm->window);
+ return PJ_SUCCESS;
+ } else
+#endif
+ if (cap == PJMEDIA_VID_DEV_CAP_FORMAT) {
+ strm->new_fmt = (pjmedia_format *)pval;
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ [strm->delegate performSelectorOnMainThread:@selector(detect_new_fmt)
+ withObject:nil waitUntilDone:YES];
+#endif
+ while (strm->new_fmt)
+ pj_thread_sleep(10);
+
+ if (strm->status != PJ_SUCCESS) {
+ pj_status_t status = strm->status;
+
+ /**
+ * Failed to change the output format. Try to revert
+ * to its original format.
+ */
+ strm->new_fmt = &strm->param.fmt;
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ [strm->delegate performSelectorOnMainThread:
+ @selector(detect_new_fmt)
+ withObject:nil waitUntilDone:YES];
+#endif
+ while (strm->new_fmt)
+ pj_thread_sleep(10);
+
+ if (strm->status != PJ_SUCCESS) {
+ /**
+ * This means that we failed to revert to our
+ * original state!
+ */
+ status = PJMEDIA_EVID_ERR;
+ }
+
+ strm->status = status;
+ }
+
+ return strm->status;
+ } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE) {
+ strm->new_disp_size = (pjmedia_rect_size *)pval;
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ [strm->delegate performSelectorOnMainThread:
+ @selector(detect_new_fmt)
+ withObject:nil waitUntilDone:YES];
+#endif
+ while (strm->new_disp_size)
+ pj_thread_sleep(10);
+
+ return strm->status;
+ }
+
+ return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: Start stream. */
+static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm)
+{
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+
+ PJ_LOG(4, (THIS_FILE, "Starting sdl video stream"));
+
+ stream->is_running = PJ_TRUE;
+ stream->render_exited = PJ_FALSE;
+
+ return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm)
+{
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+ unsigned i;
+
+ PJ_LOG(4, (THIS_FILE, "Stopping sdl video stream"));
+
+ /* Wait for renderer put_frame() to finish */
+ stream->is_running = PJ_FALSE;
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ if (![NSThread isMainThread])
+#endif
+ for (i=0; !stream->render_exited && i<50; ++i)
+ pj_thread_sleep(10);
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm)
+{
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+
+ PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+ sdl_stream_stop(strm);
+
+ if (!stream->is_quitting) {
+ stream->is_quitting = PJ_TRUE;
+ if (stream->sdl_thread)
+ pj_thread_join(stream->sdl_thread);
+ }
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ if (stream->delegate) {
+ [stream->delegate performSelectorOnMainThread:@selector(sdl_destroy)
+ withObject:nil waitUntilDone:YES];
+ [stream->delegate release];
+ stream->delegate = NULL;
+ }
+ if (stream->apool) {
+ [stream->apool release];
+ stream->apool = NULL;
+ }
+#endif
+ pj_pool_release(stream->pool);
+
+
+ return PJ_SUCCESS;
+}
+
+#ifdef _MSC_VER
+# pragma comment( lib, "sdl.lib")
+# if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+# pragma comment(lib, "OpenGL32.lib")
+# endif
+#endif
+
+#endif /* PJMEDIA_VIDEO_DEV_HAS_SDL */
diff --git a/pjmedia/src/pjmedia-videodev/sdl_dev_m.m b/pjmedia/src/pjmedia-videodev/sdl_dev_m.m
new file mode 100644
index 00000000..566bf31e
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/sdl_dev_m.m
@@ -0,0 +1,20 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "sdl_dev.c"
diff --git a/pjmedia/src/pjmedia-videodev/v4l2_dev.c b/pjmedia/src/pjmedia-videodev/v4l2_dev.c
new file mode 100644
index 00000000..545c9975
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/v4l2_dev.c
@@ -0,0 +1,820 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-videodev/videodev_imp.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/file_access.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/rand.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_V4L2
+
+#include <linux/videodev2.h>
+#include <libv4l2.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#define THIS_FILE "v4l2_dev.c"
+#define DRIVER_NAME "v4l2"
+#define V4L2_MAX_DEVS 4
+#define DEFAULT_WIDTH 640
+#define DEFAULT_HEIGHT 480
+#define DEFAULT_FPS 25
+#define DEFAULT_CLOCK_RATE 90000
+#define INVALID_FD -1
+#define BUFFER_CNT 2
+#define MAX_IOCTL_RETRY 20
+
+
+/* mapping between pjmedia_fmt_id and v4l2 pixel format */
+typedef struct vid4lin_fmt_map
+{
+ pj_uint32_t pjmedia_fmt_id;
+ pj_uint32_t v4l2_fmt_id;
+} vid4lin_fmt_map;
+
+/* I/O type being used */
+enum vid4lin_io_type
+{
+ IO_TYPE_NONE,
+ IO_TYPE_READ,
+ IO_TYPE_MMAP,
+ IO_TYPE_MMAP_USER
+};
+
+/* descriptor for each mmap-ed buffer */
+typedef struct vid4lin_buffer
+{
+ void *start;
+ size_t length;
+} vid4lin_buffer;
+
+/* v4l2 device info */
+typedef struct vid4lin_dev_info
+{
+ pjmedia_vid_dev_info info;
+ char dev_name[32];
+ struct v4l2_capability v4l2_cap;
+} vid4lin_dev_info;
+
+/* v4l2 factory */
+typedef struct vid4lin_factory
+{
+ pjmedia_vid_dev_factory base;
+ pj_pool_t *pool;
+ pj_pool_t *dev_pool;
+ pj_pool_factory *pf;
+
+ unsigned dev_count;
+ vid4lin_dev_info *dev_info;
+} vid4lin_factory;
+
+/* Video stream. */
+typedef struct vid4lin_stream
+{
+ pjmedia_vid_dev_stream base; /**< Base stream */
+ pjmedia_vid_dev_param param; /**< Settings */
+ pj_pool_t *pool; /**< Memory pool. */
+
+ int fd; /**< Video fd. */
+ char name[64]; /**< Name for log */
+ enum vid4lin_io_type io_type; /**< I/O method. */
+ unsigned buf_cnt; /**< MMap buf cnt. */
+ vid4lin_buffer *buffers; /**< MMap buffers. */
+ pj_time_val start_time; /**< Time when started */
+
+ pjmedia_vid_dev_cb vid_cb; /**< Stream callback */
+ void *user_data; /**< Application data */
+} vid4lin_stream;
+
+/* Use this to convert between pjmedia_format_id and V4L2 fourcc */
+static vid4lin_fmt_map v4l2_fmt_maps[] =
+{
+ { PJMEDIA_FORMAT_RGB24, V4L2_PIX_FMT_BGR24 },
+ { PJMEDIA_FORMAT_RGBA, V4L2_PIX_FMT_BGR32 },
+ { PJMEDIA_FORMAT_RGB32, V4L2_PIX_FMT_BGR32 },
+ { PJMEDIA_FORMAT_AYUV, V4L2_PIX_FMT_YUV32 },
+ { PJMEDIA_FORMAT_YUY2, V4L2_PIX_FMT_YUYV },
+ { PJMEDIA_FORMAT_UYVY, V4L2_PIX_FMT_UYVY }
+};
+
+/* Prototypes */
+static pj_status_t vid4lin_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t vid4lin_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t vid4lin_factory_refresh(pjmedia_vid_dev_factory *f);
+static unsigned vid4lin_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t vid4lin_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info);
+static pj_status_t vid4lin_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param);
+static pj_status_t vid4lin_factory_create_stream(pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *prm,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p);
+
+static pj_status_t vid4lin_stream_get_param(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_param *param);
+static pj_status_t vid4lin_stream_get_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
+static pj_status_t vid4lin_stream_set_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
+static pj_status_t vid4lin_stream_get_frame(pjmedia_vid_dev_stream *strm,
+ pjmedia_frame *frame);
+static pj_status_t vid4lin_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t vid4lin_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t vid4lin_stream_destroy(pjmedia_vid_dev_stream *strm);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+ &vid4lin_factory_init,
+ &vid4lin_factory_destroy,
+ &vid4lin_factory_get_dev_count,
+ &vid4lin_factory_get_dev_info,
+ &vid4lin_factory_default_param,
+ &vid4lin_factory_create_stream,
+ &vid4lin_factory_refresh
+};
+
+static pjmedia_vid_dev_stream_op stream_op =
+{
+ &vid4lin_stream_get_param,
+ &vid4lin_stream_get_cap,
+ &vid4lin_stream_set_cap,
+ &vid4lin_stream_start,
+ &vid4lin_stream_get_frame,
+ NULL,
+ &vid4lin_stream_stop,
+ &vid4lin_stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Factory creation function.
+ */
+pjmedia_vid_dev_factory* pjmedia_v4l2_factory(pj_pool_factory *pf)
+{
+ vid4lin_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, DRIVER_NAME, 512, 512, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, vid4lin_factory);
+ f->pf = pf;
+ f->pool = pool;
+ f->base.op = &factory_op;
+
+ return &f->base;
+}
+
+/* util: ioctl that tries harder. */
+static pj_status_t xioctl(int fh, int request, void *arg)
+{
+ enum { RETRY = MAX_IOCTL_RETRY };
+ int r, c=0;
+
+ do {
+ r = v4l2_ioctl(fh, request, arg);
+ } while (r==-1 && c++<RETRY && ((errno==EINTR) || (errno==EAGAIN)));
+
+ return (r == -1) ? pj_get_os_error() : PJ_SUCCESS;
+}
+
+/* Scan V4L2 devices */
+static pj_status_t v4l2_scan_devs(vid4lin_factory *f)
+{
+ vid4lin_dev_info vdi[V4L2_MAX_DEVS];
+ char dev_name[32];
+ unsigned i, old_count;
+ pj_status_t status;
+
+ if (f->dev_pool) {
+ pj_pool_release(f->dev_pool);
+ f->dev_pool = NULL;
+ }
+
+ pj_bzero(vdi, sizeof(vdi));
+ old_count = f->dev_count;
+ f->dev_count = 0;
+ f->dev_pool = pj_pool_create(f->pf, DRIVER_NAME, 500, 500, NULL);
+
+ for (i=0; i<V4L2_MAX_DEVS && f->dev_count < V4L2_MAX_DEVS; ++i) {
+ int fd;
+ vid4lin_dev_info *pdi;
+ pj_pool_t *pool = f->dev_pool;
+ pj_uint32_t fmt_cap[8];
+ int j, fmt_cnt=0;
+
+ pdi = &vdi[f->dev_count];
+
+ snprintf(dev_name, sizeof(dev_name), "/dev/video%d", i);
+ if (!pj_file_exists(dev_name))
+ continue;
+
+ fd = v4l2_open(dev_name, O_RDWR, 0);
+ if (fd == -1)
+ continue;
+
+ status = xioctl(fd, VIDIOC_QUERYCAP, &pdi->v4l2_cap);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(4,(THIS_FILE, status, "Error querying %s", dev_name));
+ v4l2_close(fd);
+ continue;
+ }
+
+ if ((pdi->v4l2_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
+ v4l2_close(fd);
+ continue;
+ }
+
+ PJ_LOG(5,(THIS_FILE, "Found capture device %s", pdi->v4l2_cap.card));
+ PJ_LOG(5,(THIS_FILE, " Enumerating formats:"));
+ for (j=0; fmt_cnt<PJ_ARRAY_SIZE(fmt_cap); ++j) {
+ struct v4l2_fmtdesc fdesc;
+ unsigned k;
+
+ pj_bzero(&fdesc, sizeof(fdesc));
+ fdesc.index = j;
+ fdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ status = xioctl(fd, VIDIOC_ENUM_FMT, &fdesc);
+ if (status != PJ_SUCCESS)
+ break;
+
+ for (k=0; k<PJ_ARRAY_SIZE(v4l2_fmt_maps); ++k) {
+ if (v4l2_fmt_maps[k].v4l2_fmt_id == fdesc.pixelformat) {
+ fmt_cap[fmt_cnt++] = v4l2_fmt_maps[k].pjmedia_fmt_id;
+ PJ_LOG(5,(THIS_FILE, " Supported: %s",
+ fdesc.description));
+ break;
+ }
+ }
+ if (k==PJ_ARRAY_SIZE(v4l2_fmt_maps)) {
+ PJ_LOG(5,(THIS_FILE, " Unsupported: %s", fdesc.description));
+ }
+ }
+
+ v4l2_close(fd);
+
+ if (fmt_cnt==0) {
+ PJ_LOG(5,(THIS_FILE, " Found no common format"));
+ continue;
+ }
+
+ strncpy(pdi->dev_name, dev_name, sizeof(pdi->dev_name));
+ pdi->dev_name[sizeof(pdi->dev_name)-1] = '\0';
+ strncpy(pdi->info.name, (char*)pdi->v4l2_cap.card,
+ sizeof(pdi->info.name));
+ pdi->info.name[sizeof(pdi->info.name)-1] = '\0';
+ strncpy(pdi->info.driver, DRIVER_NAME, sizeof(pdi->info.driver));
+ pdi->info.driver[sizeof(pdi->info.driver)-1] = '\0';
+ pdi->info.dir = PJMEDIA_DIR_CAPTURE;
+ pdi->info.has_callback = PJ_FALSE;
+ pdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+
+ pdi->info.fmt_cnt = fmt_cnt;
+ for (j=0; j<fmt_cnt; ++j) {
+ pjmedia_format_init_video(&pdi->info.fmt[j],
+ fmt_cap[j],
+ DEFAULT_WIDTH,
+ DEFAULT_HEIGHT,
+ DEFAULT_FPS, 1);
+ }
+ if (j < fmt_cnt)
+ continue;
+
+ f->dev_count++;
+ }
+
+ if (f->dev_count == 0)
+ return PJ_SUCCESS;
+
+ if (f->dev_count > old_count || f->dev_info == NULL) {
+ f->dev_info = (vid4lin_dev_info*)
+ pj_pool_calloc(f->dev_pool,
+ f->dev_count,
+ sizeof(vid4lin_dev_info));
+ }
+ pj_memcpy(f->dev_info, vdi, f->dev_count * sizeof(vid4lin_dev_info));
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: init factory */
+static pj_status_t vid4lin_factory_init(pjmedia_vid_dev_factory *f)
+{
+ return vid4lin_factory_refresh(f);
+}
+
+/* API: destroy factory */
+static pj_status_t vid4lin_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+ vid4lin_factory *cf = (vid4lin_factory*)f;
+ pj_pool_t *pool = cf->pool;
+
+ if (cf->dev_pool)
+ pj_pool_release(cf->dev_pool);
+ if (cf->pool) {
+ cf->pool = NULL;
+ pj_pool_release(pool);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: refresh the list of devices */
+static pj_status_t vid4lin_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+ vid4lin_factory *cf = (vid4lin_factory*)f;
+ pj_status_t status;
+
+ status = v4l2_scan_devs(cf);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ PJ_LOG(4, (THIS_FILE, "Video4Linux2 has %d devices",
+ cf->dev_count));
+
+ return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned vid4lin_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+ vid4lin_factory *cf = (vid4lin_factory*)f;
+ return cf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t vid4lin_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info)
+{
+ vid4lin_factory *cf = (vid4lin_factory*)f;
+
+ PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV);
+
+ pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info));
+
+ return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t vid4lin_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param)
+{
+ vid4lin_factory *cf = (vid4lin_factory*)f;
+
+ PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV);
+
+ pj_bzero(param, sizeof(*param));
+ param->dir = PJMEDIA_DIR_CAPTURE;
+ param->cap_id = index;
+ param->rend_id = PJMEDIA_VID_INVALID_DEV;
+ param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+ param->clock_rate = DEFAULT_CLOCK_RATE;
+ pjmedia_format_copy(&param->fmt, &cf->dev_info[index].info.fmt[0]);
+
+ return PJ_SUCCESS;
+}
+
+static vid4lin_fmt_map* get_v4l2_format_info(pjmedia_format_id id)
+{
+ unsigned i;
+
+ for (i = 0; i < PJ_ARRAY_SIZE(v4l2_fmt_maps); i++) {
+ if (v4l2_fmt_maps[i].pjmedia_fmt_id == id)
+ return &v4l2_fmt_maps[i];
+ }
+
+ return NULL;
+}
+
+/* util: setup format */
+static pj_status_t vid4lin_stream_init_fmt(vid4lin_stream *stream,
+ const pjmedia_vid_dev_param *param,
+ pj_uint32_t pix_fmt)
+{
+ pjmedia_video_format_detail *vfd;
+ struct v4l2_format v4l2_fmt;
+ pj_status_t status;
+
+ vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
+ if (vfd == NULL)
+ return PJMEDIA_EVID_BADFORMAT;
+
+ pj_bzero(&v4l2_fmt, sizeof(v4l2_fmt));
+ v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_fmt.fmt.pix.width = vfd->size.w;
+ v4l2_fmt.fmt.pix.height = vfd->size.h;
+ v4l2_fmt.fmt.pix.pixelformat = pix_fmt;
+ v4l2_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
+ status = xioctl(stream->fd, VIDIOC_S_FMT, &v4l2_fmt);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if (v4l2_fmt.fmt.pix.pixelformat != pix_fmt) {
+ status = PJMEDIA_EVID_BADFORMAT;
+ return status;
+ }
+
+ if ((v4l2_fmt.fmt.pix.width != vfd->size.w) ||
+ (v4l2_fmt.fmt.pix.height != vfd->size.h))
+ {
+ /* Size has changed */
+ vfd->size.w = v4l2_fmt.fmt.pix.width;
+ vfd->size.h = v4l2_fmt.fmt.pix.height;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* Util: initiate v4l2 streaming via mmap */
+static pj_status_t vid4lin_stream_init_streaming(vid4lin_stream *stream)
+{
+ struct v4l2_requestbuffers req;
+ unsigned i;
+ pj_status_t status;
+
+ pj_bzero(&req, sizeof(req));
+ req.count = BUFFER_CNT;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_MMAP;
+ status = xioctl(stream->fd, VIDIOC_REQBUFS, &req);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ stream->buffers = pj_pool_calloc(stream->pool, req.count,
+ sizeof(*stream->buffers));
+ stream->buf_cnt = 0;
+
+ for (i = 0; i < req.count; ++i) {
+ struct v4l2_buffer buf;
+
+ pj_bzero(&buf, sizeof(buf));
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = i;
+
+ status = xioctl(stream->fd, VIDIOC_QUERYBUF, &buf);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ stream->buffers[i].length = buf.length;
+ stream->buffers[i].start = v4l2_mmap(NULL, buf.length,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, stream->fd,
+ buf.m.offset);
+
+ if (MAP_FAILED == stream->buffers[i].start) {
+ status = pj_get_os_error();
+ goto on_error;
+ }
+
+ stream->buf_cnt++;
+ }
+
+ PJ_LOG(5,(THIS_FILE, " mmap streaming initialized"));
+
+ stream->io_type = IO_TYPE_MMAP;
+ return PJ_SUCCESS;
+
+on_error:
+ return status;
+}
+
+/* init streaming with user pointer */
+static pj_status_t vid4lin_stream_init_streaming_user(vid4lin_stream *stream)
+{
+ return PJ_ENOTSUP;
+}
+
+/* init streaming with read() */
+static pj_status_t vid4lin_stream_init_read_write(vid4lin_stream *stream)
+{
+ return PJ_ENOTSUP;
+}
+
+/* API: create stream */
+static pj_status_t vid4lin_factory_create_stream(pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
+{
+ vid4lin_factory *cf = (vid4lin_factory*)f;
+ pj_pool_t *pool;
+ vid4lin_stream *stream;
+ vid4lin_dev_info *vdi;
+ const vid4lin_fmt_map *fmt_map;
+ const pjmedia_video_format_info *fmt_info;
+ pjmedia_video_format_detail *vfd;
+ pj_status_t status = PJ_SUCCESS;
+
+
+ PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
+ PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
+ param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
+ param->dir == PJMEDIA_DIR_CAPTURE,
+ PJ_EINVAL);
+ PJ_ASSERT_RETURN(param->cap_id >= 0 && param->cap_id < cf->dev_count,
+ PJMEDIA_EVID_INVDEV);
+
+ fmt_info = pjmedia_get_video_format_info(NULL, param->fmt.id);
+ if (!fmt_info || (fmt_map=get_v4l2_format_info(param->fmt.id))==NULL)
+ return PJMEDIA_EVID_BADFORMAT;
+
+ vdi = &cf->dev_info[param->cap_id];
+ vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
+
+ /* Create and Initialize stream descriptor */
+ pool = pj_pool_create(cf->pf, vdi->info.name, 512, 512, NULL);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+ stream = PJ_POOL_ZALLOC_T(pool, vid4lin_stream);
+ pj_memcpy(&stream->param, param, sizeof(*param));
+ stream->pool = pool;
+ pj_memcpy(&stream->vid_cb, cb, sizeof(*cb));
+ strncpy(stream->name, vdi->info.name, sizeof(stream->name));
+ stream->name[sizeof(stream->name)-1] = '\0';
+ stream->user_data = user_data;
+ stream->fd = INVALID_FD;
+ pjmedia_event_publisher_init(&stream->base.epub, PJMEDIA_SIG_VID_DEV_V4L2);
+
+ stream->fd = v4l2_open(vdi->dev_name, O_RDWR | O_NONBLOCK, 0);
+ if (stream->fd < 0)
+ goto on_error;
+
+ status = vid4lin_stream_init_fmt(stream, param, fmt_map->v4l2_fmt_id);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ if (vdi->v4l2_cap.capabilities & V4L2_CAP_STREAMING)
+ status = vid4lin_stream_init_streaming(stream);
+
+ if (status!=PJ_SUCCESS && vdi->v4l2_cap.capabilities & V4L2_CAP_STREAMING)
+ status = vid4lin_stream_init_streaming_user(stream);
+
+ if (status!=PJ_SUCCESS && vdi->v4l2_cap.capabilities & V4L2_CAP_READWRITE)
+ status = vid4lin_stream_init_read_write(stream);
+
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(1,(THIS_FILE, "Error: unable to initiate I/O on %s",
+ stream->name));
+ goto on_error;
+ }
+
+ /* Done */
+ stream->base.op = &stream_op;
+ *p_vid_strm = &stream->base;
+
+ return PJ_SUCCESS;
+
+on_error:
+ if (status == PJ_SUCCESS)
+ status = PJ_RETURN_OS_ERROR(errno);
+
+ vid4lin_stream_destroy(&stream->base);
+ return status;
+}
+
+/* API: Get stream info. */
+static pj_status_t vid4lin_stream_get_param(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_param *pi)
+{
+ vid4lin_stream *strm = (vid4lin_stream*)s;
+
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+ return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t vid4lin_stream_get_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ void *pval)
+{
+ vid4lin_stream *strm = (vid4lin_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+ {
+ return PJMEDIA_EVID_INVCAP;
+// return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_EVID_INVCAP;
+ }
+}
+
+/* API: set capability */
+static pj_status_t vid4lin_stream_set_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ const void *pval)
+{
+ vid4lin_stream *strm = (vid4lin_stream*)s;
+
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ /*
+ if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+ {
+ return PJ_SUCCESS;
+ }
+ */
+ PJ_UNUSED_ARG(strm);
+ PJ_UNUSED_ARG(cap);
+ PJ_UNUSED_ARG(pval);
+
+ return PJMEDIA_EVID_INVCAP;
+}
+
+/* get frame from mmap */
+static pj_status_t vid4lin_stream_get_frame_mmap(vid4lin_stream *stream,
+ pjmedia_frame *frame)
+{
+ struct v4l2_buffer buf;
+ pj_time_val time;
+ pj_status_t status = PJ_SUCCESS;
+
+ pj_bzero(&buf, sizeof(buf));
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ status = xioctl(stream->fd, VIDIOC_DQBUF, &buf);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if (frame->size < buf.bytesused) {
+ /* supplied buffer is too small */
+ pj_assert(!"frame buffer is too small for v4l2");
+ status = PJ_ETOOSMALL;
+ goto on_return;
+ }
+
+ time.sec = buf.timestamp.tv_sec;
+ time.msec = buf.timestamp.tv_usec / 1000;
+ PJ_TIME_VAL_SUB(time, stream->start_time);
+
+ frame->type = PJMEDIA_FRAME_TYPE_VIDEO;
+ frame->size = buf.bytesused;
+ frame->timestamp.u64 = PJ_UINT64(1) * PJ_TIME_VAL_MSEC(time) *
+ stream->param.clock_rate / PJ_UINT64(1000);
+ pj_memcpy(frame->buf, stream->buffers[buf.index].start, buf.bytesused);
+
+on_return:
+ pj_bzero(&buf, sizeof(buf));
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ xioctl(stream->fd, VIDIOC_QBUF, &buf);
+
+ return status;
+}
+
+/* API: Get frame from stream */
+static pj_status_t vid4lin_stream_get_frame(pjmedia_vid_dev_stream *strm,
+ pjmedia_frame *frame)
+{
+ vid4lin_stream *stream = (vid4lin_stream*)strm;
+
+ if (stream->io_type == IO_TYPE_MMAP)
+ return vid4lin_stream_get_frame_mmap(stream, frame);
+ else {
+ pj_assert(!"Unsupported i/o type");
+ return PJ_EINVALIDOP;
+ }
+}
+
+/* API: Start stream. */
+static pj_status_t vid4lin_stream_start(pjmedia_vid_dev_stream *strm)
+{
+ vid4lin_stream *stream = (vid4lin_stream*)strm;
+ struct v4l2_buffer buf;
+ enum v4l2_buf_type type;
+ unsigned i;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(stream->fd != -1, PJ_EINVALIDOP);
+
+ PJ_LOG(4, (THIS_FILE, "Starting v4l2 video stream %s", stream->name));
+
+ pj_gettimeofday(&stream->start_time);
+
+ for (i = 0; i < stream->buf_cnt; ++i) {
+ pj_bzero(&buf, sizeof(buf));
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = i;
+ status = xioctl(stream->fd, VIDIOC_QBUF, &buf);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ status = xioctl(stream->fd, VIDIOC_STREAMON, &type);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ return PJ_SUCCESS;
+
+on_error:
+ if (i > 0) {
+ /* Dequeue already enqueued buffers. Can we do this while streaming
+ * is not started?
+ */
+ unsigned n = i;
+ for (i=0; i<n; ++i) {
+ pj_bzero(&buf, sizeof(buf));
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ xioctl(stream->fd, VIDIOC_DQBUF, &buf);
+ }
+ }
+ return status;
+}
+
+/* API: Stop stream. */
+static pj_status_t vid4lin_stream_stop(pjmedia_vid_dev_stream *strm)
+{
+ vid4lin_stream *stream = (vid4lin_stream*)strm;
+ enum v4l2_buf_type type;
+ pj_status_t status;
+
+ if (stream->fd < 0)
+ return PJ_SUCCESS;
+
+ PJ_LOG(4, (THIS_FILE, "Stopping v4l2 video stream %s", stream->name));
+
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ status = xioctl(stream->fd, VIDIOC_STREAMOFF, &type);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t vid4lin_stream_destroy(pjmedia_vid_dev_stream *strm)
+{
+ vid4lin_stream *stream = (vid4lin_stream*)strm;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+ vid4lin_stream_stop(strm);
+
+ PJ_LOG(4, (THIS_FILE, "Destroying v4l2 video stream %s", stream->name));
+
+ for (i=0; i<stream->buf_cnt; ++i) {
+ if (stream->buffers[i].start != MAP_FAILED) {
+ v4l2_munmap(stream->buffers[i].start, stream->buffers[i].length);
+ stream->buffers[i].start = MAP_FAILED;
+ }
+ }
+
+ if (stream->fd >= 0) {
+ v4l2_close(stream->fd);
+ stream->fd = -1;
+ }
+ pj_pool_release(stream->pool);
+
+ return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_VIDEO_DEV_HAS_V4L2 */
diff --git a/pjmedia/src/pjmedia-videodev/videodev.c b/pjmedia/src/pjmedia-videodev/videodev.c
new file mode 100644
index 00000000..833608e8
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/videodev.c
@@ -0,0 +1,806 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+#define THIS_FILE "videodev.c"
+
+#define DEFINE_CAP(name, info) {name, info}
+
+/* Capability names */
+static struct cap_info
+{
+ const char *name;
+ const char *info;
+} cap_infos[] =
+{
+ DEFINE_CAP("format", "Video format"),
+ DEFINE_CAP("scale", "Input dimension"),
+ DEFINE_CAP("window", "Renderer window"),
+ DEFINE_CAP("resize", "Renderer resize"),
+ DEFINE_CAP("position", "Renderer position"),
+ DEFINE_CAP("hide", "Renderer hide"),
+};
+
+
+/*
+ * The device index seen by application and driver is different.
+ *
+ * At application level, device index is index to global list of device.
+ * At driver level, device index is index to device list on that particular
+ * factory only.
+ */
+#define MAKE_DEV_ID(f_id, index) (((f_id & 0xFFFF) << 16) | (index & 0xFFFF))
+#define GET_INDEX(dev_id) ((dev_id) & 0xFFFF)
+#define GET_FID(dev_id) ((dev_id) >> 16)
+#define DEFAULT_DEV_ID 0
+
+
+/* extern functions to create factories */
+#if PJMEDIA_VIDEO_DEV_HAS_NULL_VIDEO
+pjmedia_vid_dev_factory* pjmedia_null_video_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_DSHOW
+pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC
+pjmedia_vid_dev_factory* pjmedia_cbar_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_SDL
+pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG
+pjmedia_vid_dev_factory* pjmedia_ffmpeg_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_V4L2
+pjmedia_vid_dev_factory* pjmedia_v4l2_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_QT
+pjmedia_vid_dev_factory* pjmedia_qt_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_IOS
+pjmedia_vid_dev_factory* pjmedia_ios_factory(pj_pool_factory *pf);
+#endif
+
+#define MAX_DRIVERS 16
+#define MAX_DEVS 64
+
+
+/* driver structure */
+struct driver
+{
+ /* Creation function */
+ pjmedia_vid_dev_factory_create_func_ptr create;
+ /* Factory instance */
+ pjmedia_vid_dev_factory *f;
+ char name[32]; /* Driver name */
+ unsigned dev_cnt; /* Number of devices */
+ unsigned start_idx; /* Start index in global list */
+ int cap_dev_idx; /* Default capture device. */
+ int rend_dev_idx; /* Default render device */
+};
+
+/* The video device subsystem */
+static struct vid_subsys
+{
+ unsigned init_count; /* How many times init() is called */
+ pj_pool_factory *pf; /* The pool factory. */
+
+ unsigned drv_cnt; /* Number of drivers. */
+ struct driver drv[MAX_DRIVERS]; /* Array of drivers. */
+
+ unsigned dev_cnt; /* Total number of devices. */
+ pj_uint32_t dev_list[MAX_DEVS];/* Array of device IDs. */
+
+} vid_subsys;
+
+/* API: get capability name/info */
+PJ_DEF(const char*) pjmedia_vid_dev_cap_name(pjmedia_vid_dev_cap cap,
+ const char **p_desc)
+{
+ const char *desc;
+ unsigned i;
+
+ if (p_desc==NULL) p_desc = &desc;
+
+ for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
+ if ((1 << i)==cap)
+ break;
+ }
+
+ if (i==PJ_ARRAY_SIZE(cap_infos)) {
+ *p_desc = "??";
+ return "??";
+ }
+
+ *p_desc = cap_infos[i].info;
+ return cap_infos[i].name;
+}
+
+static pj_status_t get_cap_pointer(const pjmedia_vid_dev_param *param,
+ pjmedia_vid_dev_cap cap,
+ void **ptr,
+ unsigned *size)
+{
+#define FIELD_INFO(name) *ptr = (void*)&param->name; \
+ *size = sizeof(param->name)
+
+ switch (cap) {
+ case PJMEDIA_VID_DEV_CAP_FORMAT:
+ FIELD_INFO(fmt);
+ break;
+ case PJMEDIA_VID_DEV_CAP_INPUT_SCALE:
+ FIELD_INFO(disp_size);
+ break;
+ case PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW:
+ FIELD_INFO(window);
+ break;
+ case PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE:
+ FIELD_INFO(disp_size);
+ break;
+ case PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION:
+ FIELD_INFO(window_pos);
+ break;
+ case PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE:
+ FIELD_INFO(window_hide);
+ break;
+ default:
+ return PJMEDIA_EVID_INVCAP;
+ }
+
+#undef FIELD_INFO
+
+ return PJ_SUCCESS;
+}
+
+/* API: set cap value to param */
+PJ_DEF(pj_status_t)
+pjmedia_vid_dev_param_set_cap( pjmedia_vid_dev_param *param,
+ pjmedia_vid_dev_cap cap,
+ const void *pval)
+{
+ void *cap_ptr;
+ unsigned cap_size;
+ pj_status_t status;
+
+ status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_memcpy(cap_ptr, pval, cap_size);
+ param->flags |= cap;
+
+ return PJ_SUCCESS;
+}
+
+/* API: get cap value from param */
+PJ_DEF(pj_status_t)
+pjmedia_vid_dev_param_get_cap( const pjmedia_vid_dev_param *param,
+ pjmedia_vid_dev_cap cap,
+ void *pval)
+{
+ void *cap_ptr;
+ unsigned cap_size;
+ pj_status_t status;
+
+ status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if ((param->flags & cap) == 0) {
+ pj_bzero(cap_ptr, cap_size);
+ return PJMEDIA_EVID_INVCAP;
+ }
+
+ pj_memcpy(pval, cap_ptr, cap_size);
+ return PJ_SUCCESS;
+}
+
+/* Internal: init driver */
+static pj_status_t init_driver(unsigned drv_idx, pj_bool_t refresh)
+{
+ struct driver *drv = &vid_subsys.drv[drv_idx];
+ pjmedia_vid_dev_factory *f;
+ unsigned i, dev_cnt;
+ pj_status_t status;
+
+ if (!refresh) {
+ /* Create the factory */
+ f = (*drv->create)(vid_subsys.pf);
+ if (!f)
+ return PJ_EUNKNOWN;
+
+ /* Call factory->init() */
+ status = f->op->init(f);
+ if (status != PJ_SUCCESS) {
+ f->op->destroy(f);
+ return status;
+ }
+ } else {
+ f = drv->f;
+ }
+
+ /* Get number of devices */
+ dev_cnt = f->op->get_dev_count(f);
+ if (dev_cnt + vid_subsys.dev_cnt > MAX_DEVS) {
+ PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
+ " there are too many devices",
+ vid_subsys.dev_cnt + dev_cnt - MAX_DEVS));
+ dev_cnt = MAX_DEVS - vid_subsys.dev_cnt;
+ }
+
+ /* enabling this will cause pjsua-lib initialization to fail when there
+ * is no video device installed in the system, even when pjsua has been
+ * run with --null-video
+ *
+ if (dev_cnt == 0) {
+ f->op->destroy(f);
+ return PJMEDIA_EVID_NODEV;
+ }
+ */
+
+ /* Fill in default devices */
+ drv->rend_dev_idx = drv->cap_dev_idx = -1;
+ for (i=0; i<dev_cnt; ++i) {
+ pjmedia_vid_dev_info info;
+
+ status = f->op->get_dev_info(f, i, &info);
+ if (status != PJ_SUCCESS) {
+ f->op->destroy(f);
+ return status;
+ }
+
+ if (drv->name[0]=='\0') {
+ /* Set driver name */
+ pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
+ drv->name[sizeof(drv->name)-1] = '\0';
+ }
+
+ if (drv->rend_dev_idx < 0 && (info.dir & PJMEDIA_DIR_RENDER)) {
+ /* Set default render device */
+ drv->rend_dev_idx = i;
+ }
+ if (drv->cap_dev_idx < 0 && (info.dir & PJMEDIA_DIR_CAPTURE)) {
+ /* Set default capture device */
+ drv->cap_dev_idx = i;
+ }
+
+ if (drv->rend_dev_idx >= 0 && drv->cap_dev_idx >= 0) {
+ /* Done. */
+ break;
+ }
+ }
+
+ /* Register the factory */
+ drv->f = f;
+ drv->f->sys.drv_idx = drv_idx;
+ drv->start_idx = vid_subsys.dev_cnt;
+ drv->dev_cnt = dev_cnt;
+
+ /* Register devices to global list */
+ for (i=0; i<dev_cnt; ++i) {
+ vid_subsys.dev_list[vid_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* Internal: deinit driver */
+static void deinit_driver(unsigned drv_idx)
+{
+ struct driver *drv = &vid_subsys.drv[drv_idx];
+
+ if (drv->f) {
+ drv->f->op->destroy(drv->f);
+ drv->f = NULL;
+ }
+
+ drv->dev_cnt = 0;
+ drv->rend_dev_idx = drv->cap_dev_idx = -1;
+}
+
+/* API: Initialize the video device subsystem. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_subsys_init(pj_pool_factory *pf)
+{
+ unsigned i;
+ pj_status_t status = PJ_SUCCESS;
+
+ /* Allow init() to be called multiple times as long as there is matching
+ * number of shutdown().
+ */
+ if (vid_subsys.init_count++ != 0) {
+ return PJ_SUCCESS;
+ }
+
+ /* Register error subsystem */
+ pj_register_strerror(PJMEDIA_VIDEODEV_ERRNO_START,
+ PJ_ERRNO_SPACE_SIZE,
+ &pjmedia_videodev_strerror);
+
+ /* Init */
+ vid_subsys.pf = pf;
+ vid_subsys.drv_cnt = 0;
+ vid_subsys.dev_cnt = 0;
+
+ /* Register creation functions */
+#if PJMEDIA_VIDEO_DEV_HAS_V4L2
+ vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_v4l2_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_QT
+ vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_qt_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_IOS
+ vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_ios_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_DSHOW
+ vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_dshow_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG
+ vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_ffmpeg_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC
+ vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_cbar_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_SDL
+ vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_sdl_factory;
+#endif
+
+ /* Initialize each factory and build the device ID list */
+ for (i=0; i<vid_subsys.drv_cnt; ++i) {
+ status = init_driver(i, PJ_FALSE);
+ if (status != PJ_SUCCESS) {
+ deinit_driver(i);
+ continue;
+ }
+ }
+
+ return vid_subsys.dev_cnt ? PJ_SUCCESS : status;
+}
+
+/* API: register a video device factory to the video device subsystem. */
+PJ_DEF(pj_status_t)
+pjmedia_vid_register_factory(pjmedia_vid_dev_factory_create_func_ptr adf)
+{
+ pj_status_t status;
+
+ if (vid_subsys.init_count == 0)
+ return PJMEDIA_EVID_INIT;
+
+ vid_subsys.drv[vid_subsys.drv_cnt].create = adf;
+ status = init_driver(vid_subsys.drv_cnt, PJ_FALSE);
+ if (status == PJ_SUCCESS) {
+ vid_subsys.drv_cnt++;
+ } else {
+ deinit_driver(vid_subsys.drv_cnt);
+ }
+
+ return status;
+}
+
+/* API: unregister a video device factory from the video device subsystem. */
+PJ_DEF(pj_status_t)
+pjmedia_vid_unregister_factory(pjmedia_vid_dev_factory_create_func_ptr adf)
+{
+ unsigned i, j;
+
+ if (vid_subsys.init_count == 0)
+ return PJMEDIA_EVID_INIT;
+
+ for (i=0; i<vid_subsys.drv_cnt; ++i) {
+ struct driver *drv = &vid_subsys.drv[i];
+
+ if (drv->create == adf) {
+ for (j = drv->start_idx; j < drv->start_idx + drv->dev_cnt; j++)
+ {
+ vid_subsys.dev_list[j] = (pj_uint32_t)PJMEDIA_VID_INVALID_DEV;
+ }
+
+ deinit_driver(i);
+ pj_bzero(drv, sizeof(*drv));
+ return PJ_SUCCESS;
+ }
+ }
+
+ return PJMEDIA_EVID_ERR;
+}
+
+/* API: get the pool factory registered to the video device subsystem. */
+PJ_DEF(pj_pool_factory*) pjmedia_vid_dev_subsys_get_pool_factory(void)
+{
+ return vid_subsys.pf;
+}
+
+/* API: Shutdown the video device subsystem. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_subsys_shutdown(void)
+{
+ unsigned i;
+
+ /* Allow shutdown() to be called multiple times as long as there is
+ * matching number of init().
+ */
+ if (vid_subsys.init_count == 0) {
+ return PJ_SUCCESS;
+ }
+ --vid_subsys.init_count;
+
+ if (vid_subsys.init_count == 0) {
+ for (i=0; i<vid_subsys.drv_cnt; ++i) {
+ deinit_driver(i);
+ }
+
+ vid_subsys.pf = NULL;
+ }
+ return PJ_SUCCESS;
+}
+
+/* API: Refresh the list of video devices installed in the system. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_refresh(void)
+{
+ unsigned i;
+
+ vid_subsys.dev_cnt = 0;
+ for (i=0; i<vid_subsys.drv_cnt; ++i) {
+ struct driver *drv = &vid_subsys.drv[i];
+
+ if (drv->f && drv->f->op->refresh) {
+ pj_status_t status = drv->f->op->refresh(drv->f);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(4, (THIS_FILE, status, "Unable to refresh device "
+ "list for %s", drv->name));
+ }
+ }
+ init_driver(i, PJ_TRUE);
+ }
+ return PJ_SUCCESS;
+}
+
+/* API: Get the number of video devices installed in the system. */
+PJ_DEF(unsigned) pjmedia_vid_dev_count(void)
+{
+ return vid_subsys.dev_cnt;
+}
+
+/* Internal: convert local index to global device index */
+static pj_status_t make_global_index(unsigned drv_idx,
+ pjmedia_vid_dev_index *id)
+{
+ if (*id < 0) {
+ return PJ_SUCCESS;
+ }
+
+ /* Check that factory still exists */
+ PJ_ASSERT_RETURN(vid_subsys.drv[drv_idx].f, PJ_EBUG);
+
+ /* Check that device index is valid */
+ PJ_ASSERT_RETURN(*id>=0 && *id<(int)vid_subsys.drv[drv_idx].dev_cnt,
+ PJ_EBUG);
+
+ *id += vid_subsys.drv[drv_idx].start_idx;
+ return PJ_SUCCESS;
+}
+
+/* Internal: lookup device id */
+static pj_status_t lookup_dev(pjmedia_vid_dev_index id,
+ pjmedia_vid_dev_factory **p_f,
+ unsigned *p_local_index)
+{
+ int f_id, index;
+
+ if (id < 0) {
+ unsigned i;
+
+ if (id <= PJMEDIA_VID_INVALID_DEV)
+ return PJMEDIA_EVID_INVDEV;
+
+ for (i=0; i<vid_subsys.drv_cnt; ++i) {
+ struct driver *drv = &vid_subsys.drv[i];
+ if (id==PJMEDIA_VID_DEFAULT_CAPTURE_DEV &&
+ drv->cap_dev_idx >= 0)
+ {
+ id = drv->cap_dev_idx;
+ make_global_index(i, &id);
+ break;
+ } else if (id==PJMEDIA_VID_DEFAULT_RENDER_DEV &&
+ drv->rend_dev_idx >= 0)
+ {
+ id = drv->rend_dev_idx;
+ make_global_index(i, &id);
+ break;
+ }
+ }
+
+ if (id < 0) {
+ return PJMEDIA_EVID_NODEFDEV;
+ }
+ }
+
+ f_id = GET_FID(vid_subsys.dev_list[id]);
+ index = GET_INDEX(vid_subsys.dev_list[id]);
+
+ if (f_id < 0 || f_id >= (int)vid_subsys.drv_cnt)
+ return PJMEDIA_EVID_INVDEV;
+
+ if (index < 0 || index >= (int)vid_subsys.drv[f_id].dev_cnt)
+ return PJMEDIA_EVID_INVDEV;
+
+ *p_f = vid_subsys.drv[f_id].f;
+ *p_local_index = (unsigned)index;
+
+ return PJ_SUCCESS;
+
+}
+
+/* API: Get device information. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_get_info(pjmedia_vid_dev_index id,
+ pjmedia_vid_dev_info *info)
+{
+ pjmedia_vid_dev_factory *f;
+ unsigned index;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(info, PJ_EINVAL);
+ PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+
+ if (id <= PJMEDIA_VID_INVALID_DEV)
+ return PJMEDIA_EVID_INVDEV;
+
+ status = lookup_dev(id, &f, &index);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = f->op->get_dev_info(f, index, info);
+
+ /* Make sure device ID is the real ID (not PJMEDIA_VID_DEFAULT_*_DEV) */
+ info->id = index;
+ make_global_index(f->sys.drv_idx, &info->id);
+
+ return status;
+}
+
+/* API: find device */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_lookup( const char *drv_name,
+ const char *dev_name,
+ pjmedia_vid_dev_index *id)
+{
+ pjmedia_vid_dev_factory *f = NULL;
+ unsigned drv_idx, dev_idx;
+
+ PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
+ PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+
+ for (drv_idx=0; drv_idx<vid_subsys.drv_cnt; ++drv_idx) {
+ if (!pj_ansi_stricmp(drv_name, vid_subsys.drv[drv_idx].name))
+ {
+ f = vid_subsys.drv[drv_idx].f;
+ break;
+ }
+ }
+
+ if (!f)
+ return PJ_ENOTFOUND;
+
+ for (dev_idx=0; dev_idx<vid_subsys.drv[drv_idx].dev_cnt; ++dev_idx)
+ {
+ pjmedia_vid_dev_info info;
+ pj_status_t status;
+
+ status = f->op->get_dev_info(f, dev_idx, &info);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if (!pj_ansi_stricmp(dev_name, info.name))
+ break;
+ }
+
+ if (dev_idx==vid_subsys.drv[drv_idx].dev_cnt)
+ return PJ_ENOTFOUND;
+
+ *id = dev_idx;
+ make_global_index(drv_idx, id);
+
+ return PJ_SUCCESS;
+}
+
+/* API: Initialize the video device parameters with default values for the
+ * specified device.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_index id,
+ pjmedia_vid_dev_param *param)
+{
+ pjmedia_vid_dev_factory *f;
+ unsigned index;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(param, PJ_EINVAL);
+ PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+
+ if (id <= PJMEDIA_VID_INVALID_DEV)
+ return PJMEDIA_EVID_INVDEV;
+
+ status = lookup_dev(id, &f, &index);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = f->op->default_param(pool, f, index, param);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Normalize device IDs */
+ make_global_index(f->sys.drv_idx, &param->cap_id);
+ make_global_index(f->sys.drv_idx, &param->rend_id);
+
+ return PJ_SUCCESS;
+}
+
+/* API: Open video stream object using the specified parameters. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_create(
+ pjmedia_vid_dev_param *prm,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
+{
+ pjmedia_vid_dev_factory *cap_f=NULL, *rend_f=NULL, *f=NULL;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(prm && prm->dir && p_vid_strm, PJ_EINVAL);
+ PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+ PJ_ASSERT_RETURN(prm->dir==PJMEDIA_DIR_CAPTURE ||
+ prm->dir==PJMEDIA_DIR_RENDER ||
+ prm->dir==PJMEDIA_DIR_CAPTURE_RENDER,
+ PJ_EINVAL);
+
+ /* Normalize cap_id */
+ if (prm->dir & PJMEDIA_DIR_CAPTURE) {
+ unsigned index;
+
+ if (prm->cap_id < 0)
+ prm->cap_id = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
+
+ status = lookup_dev(prm->cap_id, &cap_f, &index);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ prm->cap_id = index;
+ f = cap_f;
+ }
+
+ /* Normalize rend_id */
+ if (prm->dir & PJMEDIA_DIR_RENDER) {
+ unsigned index;
+
+ if (prm->rend_id < 0)
+ prm->rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
+
+ status = lookup_dev(prm->rend_id, &rend_f, &index);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ prm->rend_id = index;
+ f = rend_f;
+ }
+
+ PJ_ASSERT_RETURN(f != NULL, PJ_EBUG);
+
+ /* For now, cap_id and rend_id must belong to the same factory */
+ PJ_ASSERT_RETURN((prm->dir != PJMEDIA_DIR_CAPTURE_RENDER) ||
+ (cap_f == rend_f),
+ PJMEDIA_EVID_INVDEV);
+
+ /* Create the stream */
+ status = f->op->create_stream(f, prm, cb,
+ user_data, p_vid_strm);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Assign factory id to the stream */
+ (*p_vid_strm)->sys.drv_idx = f->sys.drv_idx;
+ return PJ_SUCCESS;
+}
+
+/* API: Get the running parameters for the specified video stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_param(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_param *param)
+{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
+ PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+
+ status = strm->op->get_param(strm, param);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Normalize device id's */
+ make_global_index(strm->sys.drv_idx, &param->cap_id);
+ make_global_index(strm->sys.drv_idx, &param->rend_id);
+
+ return PJ_SUCCESS;
+}
+
+/* API: Get the value of a specific capability of the video stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_cap(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value)
+{
+ return strm->op->get_cap(strm, cap, value);
+}
+
+/* API: Set the value of a specific capability of the video stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_set_cap(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value)
+{
+ return strm->op->set_cap(strm, cap, value);
+}
+
+PJ_DEF(pjmedia_event_publisher*)
+pjmedia_vid_dev_stream_get_event_publisher(pjmedia_vid_dev_stream *strm)
+{
+ return &strm->epub;
+}
+
+/* API: Start the stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_start(pjmedia_vid_dev_stream *strm)
+{
+ return strm->op->start(strm);
+}
+
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_frame(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_frame *frame)
+{
+ pj_assert(strm->op->get_frame);
+ return strm->op->get_frame(strm, frame);
+}
+
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_put_frame(
+ pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame)
+{
+ pj_assert(strm->op->put_frame);
+ return strm->op->put_frame(strm, frame);
+}
+
+/* API: Stop the stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_stop(pjmedia_vid_dev_stream *strm)
+{
+ return strm->op->stop(strm);
+}
+
+/* API: Destroy the stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_destroy(
+ pjmedia_vid_dev_stream *strm)
+{
+ return strm->op->destroy(strm);
+}
diff --git a/pjmedia/src/pjmedia/avi_player.c b/pjmedia/src/pjmedia/avi_player.c
new file mode 100644
index 00000000..ce8d914a
--- /dev/null
+++ b/pjmedia/src/pjmedia/avi_player.c
@@ -0,0 +1,711 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * Default file player/writer buffer size.
+ */
+#include <pjmedia/avi_stream.h>
+#include <pjmedia/avi.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/wave.h>
+#include <pj/assert.h>
+#include <pj/file_access.h>
+#include <pj/file_io.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE "avi_player.c"
+
+#define AVIF_MUSTUSEINDEX 0x00000020
+#define AVIF_ISINTERLEAVED 0x00000100
+#define AVISF_DISABLED 0x00000001
+#define AVISF_VIDEO_PALCHANGES 0x00010000
+
+#define AVI_EOF 0xFFEEFFEE
+
+#define COMPARE_TAG(doc_tag, tag) (doc_tag == *((pj_uint32_t *)avi_tags[tag]))
+
+#define SIGNATURE PJMEDIA_SIG_PORT_VID_AVI_PLAYER
+
+#if 0
+# define TRACE_(x) PJ_LOG(4,x)
+#else
+# define TRACE_(x)
+#endif
+
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+ static void data_to_host(void *data, pj_uint8_t bits, unsigned count)
+ {
+ unsigned i;
+ pj_int32_t *data32 = (pj_int32_t *)data;
+ pj_int16_t *data16 = (pj_int16_t *)data;
+ count /= (bits == 32? 4 : 2);
+ for (i=0; i<count; ++i) {
+ if (bits == 32)
+ data32[i] = pj_swap32(data32[i]);
+ else
+ data16[i] = pj_swap16(data16[i]);
+ }
+ }
+ static void data_to_host2(void *data, pj_uint8_t nsizes,
+ pj_uint8_t *sizes)
+ {
+ unsigned i;
+ pj_int8_t *datap = (pj_int8_t *)data;
+ for (i = 0; i < nsizes; i++) {
+ data_to_host(datap, 32, sizes[i]);
+ datap += sizes[i++];
+ if (i >= nsizes)
+ break;
+ data_to_host(datap, 16, sizes[i]);
+ datap += sizes[i];
+ }
+ }
+#else
+# define data_to_host(data, bits, count)
+# define data_to_host2(data, nsizes, sizes)
+#endif
+
+struct pjmedia_avi_streams
+{
+ unsigned num_streams;
+ pjmedia_port **streams;
+};
+
+struct avi_reader_port
+{
+ pjmedia_port base;
+ unsigned stream_id;
+ unsigned options;
+ pj_uint16_t bits_per_sample;
+ pj_bool_t eof;
+ pj_off_t fsize;
+ pj_off_t start_data;
+ pj_uint8_t pad;
+ pj_oshandle_t fd;
+ pj_ssize_t size_left;
+
+ pj_status_t (*cb)(pjmedia_port*, void*);
+};
+
+
+static pj_status_t avi_get_frame(pjmedia_port *this_port,
+ pjmedia_frame *frame);
+static pj_status_t avi_on_destroy(pjmedia_port *this_port);
+
+static struct avi_reader_port *create_avi_port(pj_pool_t *pool)
+{
+ const pj_str_t name = pj_str("file");
+ struct avi_reader_port *port;
+
+ port = PJ_POOL_ZALLOC_T(pool, struct avi_reader_port);
+ if (!port)
+ return NULL;
+
+ /* Put in default values.
+ * These will be overriden once the file is read.
+ */
+ pjmedia_port_info_init(&port->base.info, &name, SIGNATURE,
+ 8000, 1, 16, 80);
+
+ port->fd = (pj_oshandle_t)-1;
+ port->base.get_frame = &avi_get_frame;
+ port->base.on_destroy = &avi_on_destroy;
+
+ return port;
+}
+
+#define file_read(fd, data, size) file_read2(fd, data, size, 32)
+#define file_read2(fd, data, size, bits) file_read3(fd, data, size, bits, NULL)
+
+static pj_status_t file_read3(pj_oshandle_t fd, void *data, pj_ssize_t size,
+ pj_uint16_t bits, pj_ssize_t *psz_read)
+{
+ pj_ssize_t size_read = size, size_to_read = size;
+ pj_status_t status = pj_file_read(fd, data, &size_read);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Normalize AVI header fields values from little-endian to host
+ * byte order.
+ */
+ if (bits > 0)
+ data_to_host(data, bits, size_read);
+
+ if (size_read != size_to_read) {
+ if (psz_read)
+ *psz_read = size_read;
+ return AVI_EOF;
+ }
+
+ return status;
+}
+
+/*
+ * Create AVI player port.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_avi_player_create_streams(pj_pool_t *pool,
+ const char *filename,
+ unsigned options,
+ pjmedia_avi_streams **p_streams)
+{
+ pjmedia_avi_hdr avi_hdr;
+ struct avi_reader_port *fport[PJMEDIA_AVI_MAX_NUM_STREAMS];
+ pj_off_t pos;
+ unsigned i, nstr = 0;
+ pj_status_t status = PJ_SUCCESS;
+
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(pool && filename && p_streams, PJ_EINVAL);
+
+ /* Check the file really exists. */
+ if (!pj_file_exists(filename)) {
+ return PJ_ENOTFOUND;
+ }
+
+ /* Create fport instance. */
+ fport[0] = create_avi_port(pool);
+ if (!fport[0]) {
+ return PJ_ENOMEM;
+ }
+
+ /* Get the file size. */
+ fport[0]->fsize = pj_file_size(filename);
+
+ /* Size must be more than AVI header size */
+ if (fport[0]->fsize <= sizeof(riff_hdr_t) + sizeof(avih_hdr_t) +
+ sizeof(strl_hdr_t))
+ {
+ return PJMEDIA_EINVALIMEDIATYPE;
+ }
+
+ /* Open file. */
+ status = pj_file_open(pool, filename, PJ_O_RDONLY, &fport[0]->fd);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Read the RIFF + AVIH header. */
+ status = file_read(fport[0]->fd, &avi_hdr,
+ sizeof(riff_hdr_t) + sizeof(avih_hdr_t));
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Validate AVI file. */
+ if (!COMPARE_TAG(avi_hdr.riff_hdr.riff, PJMEDIA_AVI_RIFF_TAG) ||
+ !COMPARE_TAG(avi_hdr.riff_hdr.avi, PJMEDIA_AVI_AVI_TAG) ||
+ !COMPARE_TAG(avi_hdr.avih_hdr.list_tag, PJMEDIA_AVI_LIST_TAG) ||
+ !COMPARE_TAG(avi_hdr.avih_hdr.hdrl_tag, PJMEDIA_AVI_HDRL_TAG) ||
+ !COMPARE_TAG(avi_hdr.avih_hdr.avih, PJMEDIA_AVI_AVIH_TAG))
+ {
+ status = PJMEDIA_EINVALIMEDIATYPE;
+ goto on_error;
+ }
+
+ PJ_LOG(5, (THIS_FILE, "The AVI file has %d streams.",
+ avi_hdr.avih_hdr.num_streams));
+
+ /* Unsupported AVI format. */
+ if (avi_hdr.avih_hdr.num_streams > PJMEDIA_AVI_MAX_NUM_STREAMS) {
+ status = PJMEDIA_EAVIUNSUPP;
+ goto on_error;
+ }
+
+ /**
+ * TODO: Possibly unsupported AVI format.
+ * If you encounter this warning, verify whether the avi player
+ * is working properly.
+ */
+ if (avi_hdr.avih_hdr.flags & AVIF_MUSTUSEINDEX ||
+ avi_hdr.avih_hdr.pad > 1)
+ {
+ PJ_LOG(3, (THIS_FILE, "Warning!!! Possibly unsupported AVI format: "
+ "flags:%d, pad:%d", avi_hdr.avih_hdr.flags,
+ avi_hdr.avih_hdr.pad));
+ }
+
+ /* Read the headers of each stream. */
+ for (i = 0; i < avi_hdr.avih_hdr.num_streams; i++) {
+ pj_size_t elem = 0;
+ pj_ssize_t size_to_read;
+
+ /* Read strl header */
+ status = file_read(fport[0]->fd, &avi_hdr.strl_hdr[i],
+ sizeof(strl_hdr_t));
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ elem = COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
+ PJMEDIA_AVI_VIDS_TAG) ?
+ sizeof(strf_video_hdr_t) :
+ COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
+ PJMEDIA_AVI_AUDS_TAG) ?
+ sizeof(strf_audio_hdr_t) : 0;
+
+ /* Read strf header */
+ status = file_read2(fport[0]->fd, &avi_hdr.strf_hdr[i],
+ elem, 0);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Normalize the endian */
+ if (elem == sizeof(strf_video_hdr_t))
+ data_to_host2(&avi_hdr.strf_hdr[i],
+ sizeof(strf_video_hdr_sizes)/
+ sizeof(strf_video_hdr_sizes[0]),
+ strf_video_hdr_sizes);
+ else if (elem == sizeof(strf_audio_hdr_t))
+ data_to_host2(&avi_hdr.strf_hdr[i],
+ sizeof(strf_audio_hdr_sizes)/
+ sizeof(strf_audio_hdr_sizes[0]),
+ strf_audio_hdr_sizes);
+
+ /* Skip the remainder of the header */
+ size_to_read = avi_hdr.strl_hdr[i].list_sz - (sizeof(strl_hdr_t) -
+ 8) - elem;
+ status = pj_file_setpos(fport[0]->fd, size_to_read, PJ_SEEK_CUR);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+ }
+
+ /* Finish reading the AVIH header */
+ status = pj_file_setpos(fport[0]->fd, avi_hdr.avih_hdr.list_sz +
+ sizeof(riff_hdr_t) + 8, PJ_SEEK_SET);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Skip any JUNK or LIST INFO until we get MOVI tag */
+ do {
+ pjmedia_avi_subchunk ch;
+ int read = 0;
+
+ status = file_read(fport[0]->fd, &ch, sizeof(pjmedia_avi_subchunk));
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ if (COMPARE_TAG(ch.id, PJMEDIA_AVI_LIST_TAG))
+ {
+ read = 4;
+ status = file_read(fport[0]->fd, &ch, read);
+ if (COMPARE_TAG(ch.id, PJMEDIA_AVI_MOVI_TAG))
+ break;
+ }
+
+ status = pj_file_setpos(fport[0]->fd, ch.len-read, PJ_SEEK_CUR);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+ } while(1);
+
+ status = pj_file_getpos(fport[0]->fd, &pos);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ for (i = 0, nstr = 0; i < avi_hdr.avih_hdr.num_streams; i++) {
+ /* Skip non-audio, non-video, or disabled streams) */
+ if ((!COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
+ PJMEDIA_AVI_VIDS_TAG) &&
+ !COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
+ PJMEDIA_AVI_AUDS_TAG)) ||
+ avi_hdr.strl_hdr[i].flags & AVISF_DISABLED)
+ {
+ continue;
+ }
+
+ if (COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
+ PJMEDIA_AVI_VIDS_TAG))
+ {
+ /* Check supported video formats here */
+ if (avi_hdr.strl_hdr[i].flags & AVISF_VIDEO_PALCHANGES ||
+ (avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_MJPEG &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_XVID &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_UYVY &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_YUY2 &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_IYUV &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_I420 &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_DIB &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_RGB24 &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_RGB32))
+ {
+ PJ_LOG(4, (THIS_FILE, "Unsupported video stream"));
+ continue;
+ }
+ } else {
+ /* Check supported audio formats here */
+ if ((avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_PCM &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_ALAW &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_ULAW &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_WAVE_FMT_TAG_PCM) ||
+ avi_hdr.strf_hdr[i].strf_audio_hdr.bits_per_sample != 16)
+ {
+ PJ_LOG(4, (THIS_FILE, "Unsupported audio stream"));
+ continue;
+ }
+ }
+
+ if (nstr == 0) {
+ fport[0]->stream_id = i;
+ nstr++;
+ continue;
+ }
+
+ /* Create fport instance. */
+ fport[nstr] = create_avi_port(pool);
+ if (!fport[nstr]) {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+
+ fport[nstr]->stream_id = i;
+
+ /* Open file. */
+ status = pj_file_open(pool, filename, PJ_O_RDONLY, &fport[nstr]->fd);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Set the file position */
+ status = pj_file_setpos(fport[nstr]->fd, pos, PJ_SEEK_SET);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ nstr++;
+ }
+
+ if (nstr == 0) {
+ status = PJMEDIA_EAVIUNSUPP;
+ goto on_error;
+ }
+
+ for (i = 0; i < nstr; i++) {
+ strl_hdr_t *strl_hdr = &avi_hdr.strl_hdr[fport[i]->stream_id];
+
+ /* Initialize */
+ fport[i]->options = options;
+ fport[i]->fsize = fport[0]->fsize;
+ /* Current file position now points to start of data */
+ fport[i]->start_data = pos;
+
+ if (COMPARE_TAG(strl_hdr->data_type, PJMEDIA_AVI_VIDS_TAG)) {
+ strf_video_hdr_t *strf_hdr =
+ &avi_hdr.strf_hdr[fport[i]->stream_id].strf_video_hdr;
+ const pjmedia_video_format_info *vfi;
+
+ vfi = pjmedia_get_video_format_info(
+ pjmedia_video_format_mgr_instance(),
+ strl_hdr->codec);
+
+ fport[i]->bits_per_sample = (vfi ? vfi->bpp : 0);
+ pjmedia_format_init_video(&fport[i]->base.info.fmt,
+ strl_hdr->codec,
+ strf_hdr->biWidth,
+ strf_hdr->biHeight,
+ strl_hdr->rate,
+ strl_hdr->scale);
+
+ } else {
+ strf_audio_hdr_t *strf_hdr =
+ &avi_hdr.strf_hdr[fport[i]->stream_id].strf_audio_hdr;
+
+ fport[i]->bits_per_sample = strf_hdr->bits_per_sample;
+ pjmedia_format_init_audio(&fport[i]->base.info.fmt,
+ strl_hdr->codec,
+ strf_hdr->sample_rate,
+ strf_hdr->nchannels,
+ strf_hdr->bits_per_sample,
+ 20000,
+ strf_hdr->bytes_per_sec,
+ strf_hdr->bytes_per_sec);
+ }
+
+ pj_strdup2(pool, &fport[i]->base.info.name, filename);
+ }
+
+ /* Done. */
+ *p_streams = pj_pool_alloc(pool, sizeof(pjmedia_avi_streams));
+ (*p_streams)->num_streams = nstr;
+ (*p_streams)->streams = pj_pool_calloc(pool, (*p_streams)->num_streams,
+ sizeof(pjmedia_port *));
+ for (i = 0; i < nstr; i++)
+ (*p_streams)->streams[i] = &fport[i]->base;
+
+ PJ_LOG(4,(THIS_FILE,
+ "AVI file player '%.*s' created with "
+ "%d media ports",
+ (int)fport[0]->base.info.name.slen,
+ fport[0]->base.info.name.ptr,
+ (*p_streams)->num_streams));
+
+ return PJ_SUCCESS;
+
+on_error:
+ fport[0]->base.on_destroy(&fport[0]->base);
+ for (i = 1; i < nstr; i++)
+ fport[i]->base.on_destroy(&fport[i]->base);
+ if (status == AVI_EOF)
+ return PJMEDIA_EINVALIMEDIATYPE;
+ return status;
+}
+
+PJ_DEF(unsigned)
+pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams)
+{
+ pj_assert(streams);
+ return streams->num_streams;
+}
+
+PJ_DEF(pjmedia_avi_stream *)
+pjmedia_avi_streams_get_stream(pjmedia_avi_streams *streams,
+ unsigned idx)
+{
+ pj_assert(streams);
+ return (idx >=0 && idx < streams->num_streams ?
+ streams->streams[idx] : NULL);
+}
+
+PJ_DEF(pjmedia_avi_stream *)
+pjmedia_avi_streams_get_stream_by_media(pjmedia_avi_streams *streams,
+ unsigned start_idx,
+ pjmedia_type media_type)
+{
+ unsigned i;
+
+ pj_assert(streams);
+ for (i = start_idx; i < streams->num_streams; i++)
+ if (streams->streams[i]->info.fmt.type == media_type)
+ return streams->streams[i];
+ return NULL;
+}
+
+
+/*
+ * Get the data length, in bytes.
+ */
+PJ_DEF(pj_ssize_t) pjmedia_avi_stream_get_len(pjmedia_avi_stream *stream)
+{
+ struct avi_reader_port *fport;
+
+ /* Sanity check */
+ PJ_ASSERT_RETURN(stream, -PJ_EINVAL);
+
+ /* Check that this is really a player port */
+ PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP);
+
+ fport = (struct avi_reader_port*) stream;
+
+ return (pj_ssize_t)(fport->fsize - fport->start_data);
+}
+
+
+/*
+ * Register a callback to be called when the file reading has reached the
+ * end of file.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_avi_stream_set_eof_cb( pjmedia_avi_stream *stream,
+ void *user_data,
+ pj_status_t (*cb)(pjmedia_avi_stream *stream,
+ void *usr_data))
+{
+ struct avi_reader_port *fport;
+
+ /* Sanity check */
+ PJ_ASSERT_RETURN(stream, -PJ_EINVAL);
+
+ /* Check that this is really a player port */
+ PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP);
+
+ fport = (struct avi_reader_port*) stream;
+
+ fport->base.port_data.pdata = user_data;
+ fport->cb = cb;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get frame from file.
+ */
+static pj_status_t avi_get_frame(pjmedia_port *this_port,
+ pjmedia_frame *frame)
+{
+ struct avi_reader_port *fport = (struct avi_reader_port*)this_port;
+ pj_status_t status;
+ pj_ssize_t size_read = 0, size_to_read = 0;
+
+ pj_assert(fport->base.info.signature == SIGNATURE);
+
+ /* We encountered end of file */
+ if (fport->eof) {
+ pj_status_t status = PJ_SUCCESS;
+
+ PJ_LOG(5,(THIS_FILE, "File port %.*s EOF",
+ (int)fport->base.info.name.slen,
+ fport->base.info.name.ptr));
+
+ /* Call callback, if any */
+ if (fport->cb)
+ status = (*fport->cb)(this_port, fport->base.port_data.pdata);
+
+ /* If callback returns non PJ_SUCCESS or 'no loop' is specified,
+ * return immediately (and don't try to access player port since
+ * it might have been destroyed by the callback).
+ */
+ if ((status != PJ_SUCCESS) ||
+ (fport->options & PJMEDIA_AVI_FILE_NO_LOOP))
+ {
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ frame->size = 0;
+ return PJ_EEOF;
+ }
+
+ /* Rewind file */
+ PJ_LOG(5,(THIS_FILE, "File port %.*s rewinding..",
+ (int)fport->base.info.name.slen,
+ fport->base.info.name.ptr));
+ fport->eof = PJ_FALSE;
+ pj_file_setpos(fport->fd, fport->start_data, PJ_SEEK_SET);
+ }
+
+ /* Fill frame buffer. */
+ size_to_read = frame->size;
+ do {
+ pjmedia_avi_subchunk ch = {0, 0};
+ char *cid;
+ unsigned stream_id;
+
+ /* We need to read data from the file past the chunk boundary */
+ if (fport->size_left > 0 && fport->size_left < size_to_read) {
+ status = file_read3(fport->fd, frame->buf, fport->size_left,
+ fport->bits_per_sample, &size_read);
+ if (status != PJ_SUCCESS)
+ goto on_error2;
+ size_to_read -= fport->size_left;
+ fport->size_left = 0;
+ }
+
+ /* Read new chunk data */
+ if (fport->size_left == 0) {
+ pj_off_t pos;
+ pj_file_getpos(fport->fd, &pos);
+
+ /* Data is padded to the nearest WORD boundary */
+ if (fport->pad) {
+ status = pj_file_setpos(fport->fd, fport->pad, PJ_SEEK_CUR);
+ fport->pad = 0;
+ }
+
+ status = file_read(fport->fd, &ch, sizeof(pjmedia_avi_subchunk));
+ if (status != PJ_SUCCESS) {
+ size_read = 0;
+ goto on_error2;
+ }
+
+ cid = (char *)&ch.id;
+ if (cid[0] >= '0' && cid[0] <= '9' &&
+ cid[1] >= '0' && cid[1] <= '9')
+ {
+ stream_id = (cid[0] - '0') * 10 + (cid[1] - '0');
+ } else
+ stream_id = 100;
+ fport->pad = (pj_uint8_t)ch.len & 1;
+
+ TRACE_((THIS_FILE, "Reading movi data at pos %u (%x), id: %.*s, "
+ "length: %u", (unsigned long)pos,
+ (unsigned long)pos, 4, cid, ch.len));
+
+ /* We are only interested in data with our stream id */
+ if (stream_id != fport->stream_id) {
+ if (COMPARE_TAG(ch.id, PJMEDIA_AVI_LIST_TAG))
+ PJ_LOG(5, (THIS_FILE, "Unsupported LIST tag found in "
+ "the movi data."));
+ else if (COMPARE_TAG(ch.id, PJMEDIA_AVI_RIFF_TAG)) {
+ PJ_LOG(3, (THIS_FILE, "Unsupported format: multiple "
+ "AVIs in a single file."));
+ status = AVI_EOF;
+ goto on_error2;
+ }
+
+ status = pj_file_setpos(fport->fd, ch.len,
+ PJ_SEEK_CUR);
+ continue;
+ }
+ fport->size_left = ch.len;
+ }
+
+ frame->type = (fport->base.info.fmt.type == PJMEDIA_TYPE_VIDEO ?
+ PJMEDIA_FRAME_TYPE_VIDEO : PJMEDIA_FRAME_TYPE_AUDIO);
+ frame->timestamp.u64 = 0;
+ if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+ if (size_to_read > fport->size_left)
+ size_to_read = fport->size_left;
+ status = file_read3(fport->fd, (char *)frame->buf + frame->size -
+ size_to_read, size_to_read,
+ fport->bits_per_sample, &size_read);
+ if (status != PJ_SUCCESS)
+ goto on_error2;
+ fport->size_left -= size_to_read;
+ } else {
+ pj_assert(frame->size >= ch.len);
+ status = file_read3(fport->fd, frame->buf, ch.len,
+ 0, &size_read);
+ if (status != PJ_SUCCESS)
+ goto on_error2;
+ frame->size = ch.len;
+ fport->size_left = 0;
+ }
+
+ break;
+
+ } while(1);
+
+ return PJ_SUCCESS;
+
+on_error2:
+ if (status == AVI_EOF) {
+ size_to_read -= size_read;
+ pj_bzero((char *)frame->buf + frame->size - size_to_read,
+ size_to_read);
+ fport->eof = PJ_TRUE;
+
+ return PJ_SUCCESS;
+ }
+
+ return status;
+}
+
+/*
+ * Destroy port.
+ */
+static pj_status_t avi_on_destroy(pjmedia_port *this_port)
+{
+ struct avi_reader_port *fport = (struct avi_reader_port*) this_port;
+
+ pj_assert(this_port->info.signature == SIGNATURE);
+
+ if (fport->fd != (pj_oshandle_t) -1)
+ pj_file_close(fport->fd);
+ return PJ_SUCCESS;
+}
diff --git a/pjmedia/src/pjmedia/bidirectional.c b/pjmedia/src/pjmedia/bidirectional.c
index eb33a061..a106d2dd 100644
--- a/pjmedia/src/pjmedia/bidirectional.c
+++ b/pjmedia/src/pjmedia/bidirectional.c
@@ -22,7 +22,7 @@
#define THIS_FILE "bidirectional.c"
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('B', 'D', 'I', 'R')
+#define SIGNATURE PJMEDIA_SIG_PORT_BIDIR
struct bidir_port
{
@@ -33,7 +33,7 @@ struct bidir_port
static pj_status_t put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
struct bidir_port *p = (struct bidir_port*)this_port;
return pjmedia_port_put_frame(p->put_port, frame);
@@ -54,14 +54,16 @@ PJ_DEF(pj_status_t) pjmedia_bidirectional_port_create( pj_pool_t *pool,
pjmedia_port **p_port )
{
struct bidir_port *port;
+ const pjmedia_audio_format_detail *gafd;
port = PJ_POOL_ZALLOC_T(pool, struct bidir_port);
+ gafd = pjmedia_format_get_audio_format_detail(&get_port->info.fmt, 1);
pjmedia_port_info_init(&port->base.info, &get_port->info.name, SIGNATURE,
- get_port->info.clock_rate,
- get_port->info.channel_count,
- get_port->info.bits_per_sample,
- get_port->info.samples_per_frame);
+ gafd->clock_rate,
+ gafd->channel_count,
+ gafd->bits_per_sample,
+ PJMEDIA_AFD_SPF(gafd));
port->get_port = get_port;
port->put_port = put_port;
diff --git a/pjmedia/src/pjmedia/clock_thread.c b/pjmedia/src/pjmedia/clock_thread.c
index 245db00c..5875450f 100644
--- a/pjmedia/src/pjmedia/clock_thread.c
+++ b/pjmedia/src/pjmedia/clock_thread.c
@@ -23,6 +23,84 @@
#include <pj/lock.h>
#include <pj/os.h>
#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/compat/high_precision.h>
+
+/* API: Init clock source */
+PJ_DEF(pj_status_t) pjmedia_clock_src_init( pjmedia_clock_src *clocksrc,
+ pjmedia_type media_type,
+ unsigned clock_rate,
+ unsigned ptime_usec )
+{
+ PJ_ASSERT_RETURN(clocksrc, PJ_EINVAL);
+
+ clocksrc->media_type = media_type;
+ clocksrc->clock_rate = clock_rate;
+ clocksrc->ptime_usec = ptime_usec;
+ pj_set_timestamp32(&clocksrc->timestamp, 0, 0);
+ pj_get_timestamp(&clocksrc->last_update);
+
+ return PJ_SUCCESS;
+}
+
+/* API: Update clock source */
+PJ_DECL(pj_status_t) pjmedia_clock_src_update( pjmedia_clock_src *clocksrc,
+ const pj_timestamp *timestamp )
+{
+ PJ_ASSERT_RETURN(clocksrc, PJ_EINVAL);
+
+ if (timestamp)
+ pj_memcpy(&clocksrc->timestamp, timestamp, sizeof(pj_timestamp));
+ pj_get_timestamp(&clocksrc->last_update);
+
+ return PJ_SUCCESS;
+}
+
+/* API: Get clock source's current timestamp */
+PJ_DEF(pj_status_t)
+pjmedia_clock_src_get_current_timestamp( const pjmedia_clock_src *clocksrc,
+ pj_timestamp *timestamp)
+{
+ pj_timestamp now;
+ unsigned elapsed_ms;
+
+ PJ_ASSERT_RETURN(clocksrc && timestamp, PJ_EINVAL);
+
+ pj_get_timestamp(&now);
+ elapsed_ms = pj_elapsed_msec(&clocksrc->last_update, &now);
+ pj_memcpy(timestamp, &clocksrc->timestamp, sizeof(pj_timestamp));
+ pj_add_timestamp32(timestamp, elapsed_ms * clocksrc->clock_rate / 1000);
+
+ return PJ_SUCCESS;
+}
+
+/* API: Get clock source's time (in ms) */
+PJ_DEF(pj_uint32_t)
+pjmedia_clock_src_get_time_msec( const pjmedia_clock_src *clocksrc )
+{
+ pj_timestamp ts;
+
+ pjmedia_clock_src_get_current_timestamp(clocksrc, &ts);
+
+#if PJ_HAS_INT64
+ if (ts.u64 > 0x3FFFFFFFFFFFFFUL)
+ return (pj_uint32_t)(ts.u64 / clocksrc->clock_rate * 1000);
+ else
+ return (pj_uint32_t)(ts.u64 * 1000 / clocksrc->clock_rate);
+#elif PJ_HAS_FLOATING_POINT
+ return (pj_uint32_t)((1.0 * ts.u32.hi * 0xFFFFFFFFUL + ts.u32.lo)
+ * 1000.0 / clocksrc->clock_rate);
+#else
+ if (ts.u32.lo > 0x3FFFFFUL)
+ return (pj_uint32_t)(0xFFFFFFFFUL / clocksrc->clock_rate * ts.u32.hi
+ * 1000UL + ts.u32.lo / clocksrc->clock_rate *
+ 1000UL);
+ else
+ return (pj_uint32_t)(0xFFFFFFFFUL / clocksrc->clock_rate * ts.u32.hi
+ * 1000UL + ts.u32.lo * 1000UL /
+ clocksrc->clock_rate);
+#endif
+}
/*
@@ -50,6 +128,7 @@ struct pjmedia_clock
static int clock_thread(void *arg);
#define MAX_JUMP_MSEC 500
+#define USEC_IN_SEC (pj_uint64_t)1000000
/*
* Create media clock.
@@ -63,25 +142,42 @@ PJ_DEF(pj_status_t) pjmedia_clock_create( pj_pool_t *pool,
void *user_data,
pjmedia_clock **p_clock)
{
+ pjmedia_clock_param param;
+
+ param.usec_interval = (unsigned)(samples_per_frame * USEC_IN_SEC /
+ channel_count / clock_rate);
+ param.clock_rate = clock_rate;
+ return pjmedia_clock_create2(pool, &param, options, cb,
+ user_data, p_clock);
+}
+
+PJ_DEF(pj_status_t) pjmedia_clock_create2(pj_pool_t *pool,
+ const pjmedia_clock_param *param,
+ unsigned options,
+ pjmedia_clock_callback *cb,
+ void *user_data,
+ pjmedia_clock **p_clock)
+{
pjmedia_clock *clock;
pj_status_t status;
- PJ_ASSERT_RETURN(pool && clock_rate && samples_per_frame && p_clock,
- PJ_EINVAL);
+ PJ_ASSERT_RETURN(pool && param->usec_interval && param->clock_rate &&
+ p_clock, PJ_EINVAL);
clock = PJ_POOL_ALLOC_T(pool, pjmedia_clock);
-
status = pj_get_timestamp_freq(&clock->freq);
if (status != PJ_SUCCESS)
return status;
- clock->interval.u64 = samples_per_frame * clock->freq.u64 /
- channel_count / clock_rate;
+ clock->interval.u64 = param->usec_interval * clock->freq.u64 /
+ USEC_IN_SEC;
clock->next_tick.u64 = 0;
clock->timestamp.u64 = 0;
clock->max_jump = MAX_JUMP_MSEC * clock->freq.u64 / 1000;
- clock->timestamp_inc = samples_per_frame / channel_count;
+ clock->timestamp_inc = (unsigned)(param->usec_interval *
+ param->clock_rate /
+ USEC_IN_SEC);
clock->options = options;
clock->cb = cb;
clock->user_data = user_data;
@@ -149,6 +245,22 @@ PJ_DEF(pj_status_t) pjmedia_clock_stop(pjmedia_clock *clock)
}
+/*
+ * Update the clock.
+ */
+PJ_DEF(pj_status_t) pjmedia_clock_modify(pjmedia_clock *clock,
+ const pjmedia_clock_param *param)
+{
+ clock->interval.u64 = param->usec_interval * clock->freq.u64 /
+ USEC_IN_SEC;
+ clock->timestamp_inc = (unsigned)(param->usec_interval *
+ param->clock_rate /
+ USEC_IN_SEC);
+
+ return PJ_SUCCESS;
+}
+
+
/* Calculate next tick */
PJ_INLINE(void) clock_calc_next_tick(pjmedia_clock *clock,
pj_timestamp *now)
diff --git a/pjmedia/src/pjmedia/codec.c b/pjmedia/src/pjmedia/codec.c
index 647b0b5d..111ad0c7 100644
--- a/pjmedia/src/pjmedia/codec.c
+++ b/pjmedia/src/pjmedia/codec.c
@@ -72,10 +72,19 @@ PJ_DEF(pj_status_t) pjmedia_codec_mgr_init (pjmedia_codec_mgr *mgr,
*/
PJ_DEF(pj_status_t) pjmedia_codec_mgr_destroy (pjmedia_codec_mgr *mgr)
{
+ pjmedia_codec_factory *factory;
unsigned i;
PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+ /* Destroy all factories in the list */
+ factory = mgr->factory_list.next;
+ while (factory != &mgr->factory_list) {
+ pjmedia_codec_factory *next = factory->next;
+ (*factory->op->destroy)();
+ factory = next;
+ }
+
/* Cleanup all pools of all codec default params */
for (i=0; i<mgr->codec_cnt; ++i) {
if (mgr->codec_desc[i].param) {
@@ -111,6 +120,13 @@ PJ_DEF(pj_status_t) pjmedia_codec_mgr_register_factory( pjmedia_codec_mgr *mgr,
PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL);
+ /* Since 2.0 we require codec factory to implement "destroy" op. Please
+ * see: https://trac.pjsip.org/repos/ticket/1294
+ *
+ * Really! Please do see it.
+ */
+ PJ_ASSERT_RETURN(factory->op->destroy != NULL, PJ_ENOTSUP);
+
/* Enum codecs */
count = PJ_ARRAY_SIZE(info);
status = factory->op->enum_info(factory, &count, info);
diff --git a/pjmedia/src/pjmedia/conf_switch.c b/pjmedia/src/pjmedia/conf_switch.c
index d7df076e..4299a872 100644
--- a/pjmedia/src/pjmedia/conf_switch.c
+++ b/pjmedia/src/pjmedia/conf_switch.c
@@ -23,10 +23,10 @@
#include <pjmedia/port.h>
#include <pjmedia/silencedet.h>
#include <pjmedia/sound_port.h>
-#include <pjmedia/stream.h>
#include <pj/array.h>
#include <pj/assert.h>
#include <pj/log.h>
+#include <pj/math.h>
#include <pj/pool.h>
#include <pj/string.h>
@@ -80,6 +80,7 @@ struct conf_port
/* Shortcut for port info. */
pjmedia_port_info *info;
+ unsigned samples_per_frame;
/* Calculated signal levels: */
unsigned tx_level; /**< Last tx level to this port. */
@@ -123,7 +124,7 @@ struct pjmedia_conf
/* Prototypes */
static pj_status_t put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame);
+ pjmedia_frame *frame);
static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_frame *frame);
static pj_status_t destroy_port(pjmedia_port *this_port);
@@ -166,6 +167,7 @@ static pj_status_t create_conf_port( pj_pool_t *pool,
/* Save some port's infos, for convenience. */
conf_port->port = port;
conf_port->info = &port->info;
+ conf_port->samples_per_frame= PJMEDIA_PINFO_SAMPLES_PER_FRAME(&port->info);
/* Init pjmedia_frame structure in the TX buffer. */
f = (pjmedia_frame*)conf_port->tx_buf;
@@ -504,6 +506,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
{
struct conf_port *src_port, *dst_port;
pj_bool_t start_sound = PJ_FALSE;
+ pjmedia_audio_format_detail *src_afd, *dst_afd;
unsigned i;
/* Check arguments */
@@ -523,31 +526,32 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
return PJ_EINVAL;
}
+ src_afd = pjmedia_format_get_audio_format_detail(&src_port->info->fmt, 1);
+ dst_afd = pjmedia_format_get_audio_format_detail(&dst_port->info->fmt, 1);
+
/* Format must match. */
- if (src_port->info->format.id != dst_port->info->format.id ||
- src_port->info->format.bitrate != dst_port->info->format.bitrate)
+ if (src_port->info->fmt.id != dst_port->info->fmt.id ||
+ src_afd->avg_bps != dst_afd->avg_bps)
{
pj_mutex_unlock(conf->mutex);
return PJMEDIA_ENOTCOMPATIBLE;
}
/* Clock rate must match. */
- if (src_port->info->clock_rate != dst_port->info->clock_rate) {
+ if (src_afd->clock_rate != dst_afd->clock_rate) {
pj_mutex_unlock(conf->mutex);
return PJMEDIA_ENCCLOCKRATE;
}
/* Channel count must match. */
- if (src_port->info->channel_count != dst_port->info->channel_count) {
+ if (src_afd->channel_count != dst_afd->channel_count) {
pj_mutex_unlock(conf->mutex);
return PJMEDIA_ENCCLOCKRATE;
}
/* Source and sink ptime must be equal or a multiplication factor. */
- if ((src_port->info->samples_per_frame %
- dst_port->info->samples_per_frame != 0) &&
- (dst_port->info->samples_per_frame %
- src_port->info->samples_per_frame != 0))
+ if ((src_afd->frame_time_usec % dst_afd->frame_time_usec != 0) &&
+ (dst_afd->frame_time_usec % src_afd->frame_time_usec != 0))
{
pj_mutex_unlock(conf->mutex);
return PJMEDIA_ENCSAMPLESPFRAME;
@@ -829,6 +833,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
pjmedia_conf_port_info *info)
{
struct conf_port *conf_port;
+ const pjmedia_audio_format_detail *afd;
/* Check arguments */
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
@@ -843,6 +848,8 @@ PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
return PJ_EINVAL;
}
+ afd = pjmedia_format_get_audio_format_detail(&conf_port->info->fmt, 1);
+
pj_bzero(info, sizeof(pjmedia_conf_port_info));
info->slot = slot;
@@ -852,11 +859,11 @@ PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
info->listener_cnt = conf_port->listener_cnt;
info->listener_slots = conf_port->listener_slots;
info->transmitter_cnt = conf_port->transmitter_cnt;
- info->clock_rate = conf_port->info->clock_rate;
- info->channel_count = conf_port->info->channel_count;
- info->samples_per_frame = conf_port->info->samples_per_frame;
- info->bits_per_sample = conf_port->info->bits_per_sample;
- info->format = conf_port->port->info.format;
+ info->clock_rate = afd->clock_rate;
+ info->channel_count = afd->channel_count;
+ info->samples_per_frame = conf_port->samples_per_frame;
+ info->bits_per_sample = afd->bits_per_sample;
+ info->format = conf_port->port->info.fmt;
info->tx_adj_level = conf_port->tx_adj_level - NORMAL_LEVEL;
info->rx_adj_level = conf_port->rx_adj_level - NORMAL_LEVEL;
@@ -960,7 +967,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf,
}
/* Level adjustment is applicable only for ports that work with raw PCM. */
- PJ_ASSERT_RETURN(conf_port->info->format.id == PJMEDIA_FORMAT_L16,
+ PJ_ASSERT_RETURN(conf_port->info->fmt.id == PJMEDIA_FORMAT_L16,
PJ_EIGNORED);
/* Set normalized adjustment level. */
@@ -1002,7 +1009,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf,
}
/* Level adjustment is applicable only for ports that work with raw PCM. */
- PJ_ASSERT_RETURN(conf_port->info->format.id == PJMEDIA_FORMAT_L16,
+ PJ_ASSERT_RETURN(conf_port->info->fmt.id == PJMEDIA_FORMAT_L16,
PJ_EIGNORED);
/* Set normalized adjustment level. */
@@ -1046,7 +1053,7 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
* i.e: samples count in TX buffer equal to listener's
* samples per frame.
*/
- if (f_dst->samples_cnt >= cport_dst->info->samples_per_frame)
+ if (f_dst->samples_cnt >= cport_dst->samples_per_frame)
{
if (cport_dst->slot) {
pjmedia_port_put_frame(cport_dst->port,
@@ -1058,8 +1065,8 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
}
/* Update TX timestamp. */
- pj_add_timestamp32(&cport_dst->ts_tx,
- cport_dst->info->samples_per_frame);
+ pj_add_timestamp32(&cport_dst->ts_tx,
+ cport_dst->samples_per_frame);
}
}
@@ -1075,7 +1082,7 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
/* Copy frame to listener's TX buffer. */
nsamples_to_copy = f_end - f_start;
- nsamples_req = cport_dst->info->samples_per_frame -
+ nsamples_req = cport_dst->samples_per_frame -
(frm_dst->size>>1);
if (nsamples_to_copy > nsamples_req)
nsamples_to_copy = nsamples_req;
@@ -1112,7 +1119,7 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
* i.e: samples count in TX buffer equal to listener's
* samples per frame.
*/
- if ((frm_dst->size >> 1) == cport_dst->info->samples_per_frame)
+ if ((frm_dst->size >> 1) == cport_dst->samples_per_frame)
{
if (cport_dst->slot) {
pjmedia_port_put_frame(cport_dst->port, frm_dst);
@@ -1123,7 +1130,7 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
- cport_dst->info->samples_per_frame);
+ cport_dst->samples_per_frame);
}
}
@@ -1131,18 +1138,18 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
/* Check port format. */
if (cport_dst->port &&
- cport_dst->port->info.format.id == PJMEDIA_FORMAT_L16)
+ cport_dst->port->info.fmt.id == PJMEDIA_FORMAT_L16)
{
/* When there is already some samples in listener's TX buffer,
* pad the buffer with "zero samples".
*/
if (frm_dst->size != 0) {
pjmedia_zero_samples((pj_int16_t*)frm_dst->buf,
- cport_dst->info->samples_per_frame -
+ cport_dst->samples_per_frame -
(frm_dst->size>>1));
frm_dst->type = PJMEDIA_FRAME_TYPE_AUDIO;
- frm_dst->size = cport_dst->info->samples_per_frame << 1;
+ frm_dst->size = cport_dst->samples_per_frame << 1;
if (cport_dst->slot) {
pjmedia_port_put_frame(cport_dst->port, frm_dst);
@@ -1152,7 +1159,7 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
- cport_dst->info->samples_per_frame);
+ cport_dst->samples_per_frame);
}
} else {
pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
@@ -1160,7 +1167,7 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
if (f_dst->samples_cnt != 0) {
frm_dst->type = PJMEDIA_FRAME_TYPE_EXTENDED;
pjmedia_frame_ext_append_subframe(f_dst, NULL, 0, (pj_uint16_t)
- (cport_dst->info->samples_per_frame - f_dst->samples_cnt));
+ (cport_dst->samples_per_frame - f_dst->samples_cnt));
if (cport_dst->slot) {
pjmedia_port_put_frame(cport_dst->port, frm_dst);
@@ -1171,7 +1178,7 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
- cport_dst->info->samples_per_frame);
+ cport_dst->samples_per_frame);
}
}
@@ -1185,7 +1192,7 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
pjmedia_port_put_frame(cport_dst->port, frm_dst);
/* Update TX timestamp. */
- pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->info->samples_per_frame);
+ pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame);
}
}
@@ -1211,6 +1218,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
*/
for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
struct conf_port *cport = conf->ports[i];
+ unsigned master_samples_per_frame;
/* Skip empty port. */
if (!cport)
@@ -1219,9 +1227,11 @@ static pj_status_t get_frame(pjmedia_port *this_port,
/* Var "ci" is to count how many ports have been visited so far. */
++ci;
+ master_samples_per_frame = PJMEDIA_PINFO_SAMPLES_PER_FRAME(
+ &conf->master_port->info);
+
/* Update clock of the port. */
- pj_add_timestamp32(&cport->ts_clock,
- conf->master_port->info.samples_per_frame);
+ pj_add_timestamp32(&cport->ts_clock, master_samples_per_frame);
/* Skip if we're not allowed to receive from this port or
* the port doesn't have listeners.
@@ -1230,8 +1240,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
cport->listener_cnt == 0)
{
cport->rx_level = 0;
- pj_add_timestamp32(&cport->ts_rx,
- conf->master_port->info.samples_per_frame);
+ pj_add_timestamp32(&cport->ts_rx, master_samples_per_frame);
continue;
}
@@ -1245,10 +1254,10 @@ static pj_status_t get_frame(pjmedia_port *this_port,
unsigned j;
pj_int32_t level = 0;
- pj_add_timestamp32(&cport->ts_rx, cport->info->samples_per_frame);
+ pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
f->buf = &conf->buf[sizeof(pjmedia_frame)];
- f->size = cport->info->samples_per_frame<<1;
+ f->size = cport->samples_per_frame<<1;
/* Get frame from port. */
status = pjmedia_port_get_frame(cport->port, f);
@@ -1302,7 +1311,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
/* Skip if this listener doesn't want to receive audio */
if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
pj_add_timestamp32(&listener->ts_tx,
- listener->info->samples_per_frame);
+ listener->samples_per_frame);
listener->tx_level = 0;
continue;
}
@@ -1361,7 +1370,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
tmp_f.size = 0;
pjmedia_port_put_frame(cport->port, &tmp_f);
- pj_add_timestamp32(&cport->ts_tx, cport->info->samples_per_frame);
+ pj_add_timestamp32(&cport->ts_tx, cport->samples_per_frame);
}
}
}
@@ -1380,7 +1389,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_frame_ext_subframe *sf;
unsigned samples_per_subframe;
- if (f_src_->samples_cnt < this_cport->info->samples_per_frame) {
+ if (f_src_->samples_cnt < this_cport->samples_per_frame) {
f_dst->base.type = PJMEDIA_FRAME_TYPE_NONE;
f_dst->samples_cnt = 0;
f_dst->subframe_cnt = 0;
@@ -1393,7 +1402,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
samples_per_subframe = f_src_->samples_cnt / f_src_->subframe_cnt;
- while (f_dst->samples_cnt < this_cport->info->samples_per_frame) {
+ while (f_dst->samples_cnt < this_cport->samples_per_frame) {
sf = pjmedia_frame_ext_get_subframe(f_src_, i++);
pj_assert(sf);
pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
@@ -1404,7 +1413,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_frame_ext_pop_subframes(f_src_, i);
} else if (f_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
- if ((f_src->size>>1) < this_cport->info->samples_per_frame) {
+ if ((f_src->size>>1) < this_cport->samples_per_frame) {
frame->type = PJMEDIA_FRAME_TYPE_NONE;
frame->size = 0;
break;
@@ -1412,15 +1421,15 @@ static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_copy_samples((pj_int16_t*)frame->buf,
(pj_int16_t*)f_src->buf,
- this_cport->info->samples_per_frame);
- frame->size = this_cport->info->samples_per_frame << 1;
+ this_cport->samples_per_frame);
+ frame->size = this_cport->samples_per_frame << 1;
/* Shift left TX buffer. */
f_src->size -= frame->size;
if (f_src->size)
pjmedia_move_samples((pj_int16_t*)f_src->buf,
(pj_int16_t*)f_src->buf +
- this_cport->info->samples_per_frame,
+ this_cport->samples_per_frame,
f_src->size >> 1);
} else { /* PJMEDIA_FRAME_TYPE_NONE */
pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src;
@@ -1442,7 +1451,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
* Recorder callback.
*/
static pj_status_t put_frame(pjmedia_port *this_port,
- const pjmedia_frame *f)
+ pjmedia_frame *f)
{
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
struct conf_port *cport;
@@ -1460,7 +1469,7 @@ static pj_status_t put_frame(pjmedia_port *this_port,
return PJ_SUCCESS;
}
- pj_add_timestamp32(&cport->ts_rx, cport->info->samples_per_frame);
+ pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
/* Skip if this port is muted/disabled. */
if (cport->rx_setting == PJMEDIA_PORT_DISABLE) {
@@ -1526,7 +1535,7 @@ static pj_status_t put_frame(pjmedia_port *this_port,
/* Skip if this listener doesn't want to receive audio */
if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
pj_add_timestamp32(&listener->ts_tx,
- listener->info->samples_per_frame);
+ listener->samples_per_frame);
listener->tx_level = 0;
continue;
}
@@ -1534,7 +1543,7 @@ static pj_status_t put_frame(pjmedia_port *this_port,
/* Skip loopback for now. */
if (listener == cport) {
pj_add_timestamp32(&listener->ts_tx,
- listener->info->samples_per_frame);
+ listener->samples_per_frame);
listener->tx_level = 0;
continue;
}
diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c
index 46dd4597..905ae588 100644
--- a/pjmedia/src/pjmedia/conference.c
+++ b/pjmedia/src/pjmedia/conference.c
@@ -26,7 +26,6 @@
#include <pjmedia/silencedet.h>
#include <pjmedia/sound_port.h>
#include <pjmedia/stereo.h>
-#include <pjmedia/stream.h>
#include <pj/array.h>
#include <pj/assert.h>
#include <pj/log.h>
@@ -65,7 +64,7 @@ static FILE *fhnd_rec;
#define BYTES_PER_SAMPLE 2
#define SIGNATURE PJMEDIA_CONF_BRIDGE_SIGNATURE
-#define SIGNATURE_PORT PJMEDIA_PORT_SIGNATURE('C', 'O', 'N', 'P')
+#define SIGNATURE_PORT PJMEDIA_SIG_PORT_CONF_PASV
/* Normal level is hardcodec to 128 in all over places */
#define NORMAL_LEVEL 128
#define SLOT_TYPE unsigned
@@ -241,7 +240,7 @@ struct pjmedia_conf
/* Prototypes */
static pj_status_t put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame);
+ pjmedia_frame *frame);
static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_frame *frame);
static pj_status_t get_frame_pasv(pjmedia_port *this_port,
@@ -285,10 +284,13 @@ static pj_status_t create_conf_port( pj_pool_t *pool,
/* Save some port's infos, for convenience. */
if (port) {
+ pjmedia_audio_format_detail *afd;
+
+ afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, 1);
conf_port->port = port;
- conf_port->clock_rate = port->info.clock_rate;
- conf_port->samples_per_frame = port->info.samples_per_frame;
- conf_port->channel_count = port->info.channel_count;
+ conf_port->clock_rate = afd->clock_rate;
+ conf_port->samples_per_frame = PJMEDIA_AFD_SPF(afd);
+ conf_port->channel_count = afd->channel_count;
} else {
conf_port->port = NULL;
conf_port->clock_rate = conf->clock_rate;
@@ -750,8 +752,9 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
* - same between port & conference bridge.
* - monochannel on port or conference bridge.
*/
- if (strm_port->info.channel_count != conf->channel_count &&
- (strm_port->info.channel_count != 1 && conf->channel_count != 1))
+ if (PJMEDIA_PIA_CCNT(&strm_port->info) != conf->channel_count &&
+ (PJMEDIA_PIA_CCNT(&strm_port->info) != 1 &&
+ conf->channel_count != 1))
{
pj_assert(!"Number of channels mismatch");
return PJMEDIA_ENCCHANNEL;
@@ -2050,7 +2053,7 @@ static pj_status_t get_frame_pasv(pjmedia_port *this_port,
* Recorder (or passive port) callback.
*/
static pj_status_t put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
struct conf_port *port = conf->ports[this_port->port_data.ldata];
diff --git a/pjmedia/src/pjmedia/converter.c b/pjmedia/src/pjmedia/converter.c
new file mode 100644
index 00000000..14496fa0
--- /dev/null
+++ b/pjmedia/src/pjmedia/converter.c
@@ -0,0 +1,178 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/converter.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+#define THIS_FILE "converter.c"
+
+struct pjmedia_converter_mgr
+{
+ pjmedia_converter_factory factory_list;
+};
+
+static pjmedia_converter_mgr *converter_manager_instance;
+
+#if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL
+PJ_DECL(pj_status_t)
+pjmedia_libswscale_converter_init(pjmedia_converter_mgr *mgr);
+#endif
+
+
+PJ_DEF(pj_status_t) pjmedia_converter_mgr_create(pj_pool_t *pool,
+ pjmedia_converter_mgr **p_mgr)
+{
+ pjmedia_converter_mgr *mgr;
+ pj_status_t status;
+
+ mgr = PJ_POOL_ALLOC_T(pool, pjmedia_converter_mgr);
+ pj_list_init(&mgr->factory_list);
+
+ if (!converter_manager_instance)
+ converter_manager_instance = mgr;
+
+#if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL
+ status = pjmedia_libswscale_converter_init(mgr);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(4,(THIS_FILE, status,
+ "Error initializing libswscale converter"));
+ }
+#endif
+
+ if (p_mgr)
+ *p_mgr = mgr;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pjmedia_converter_mgr*) pjmedia_converter_mgr_instance(void)
+{
+ pj_assert(converter_manager_instance != NULL);
+ return converter_manager_instance;
+}
+
+PJ_DEF(void) pjmedia_converter_mgr_set_instance(pjmedia_converter_mgr *mgr)
+{
+ converter_manager_instance = mgr;
+}
+
+PJ_DEF(void) pjmedia_converter_mgr_destroy(pjmedia_converter_mgr *mgr)
+{
+ pjmedia_converter_factory *f;
+
+ if (!mgr) mgr = pjmedia_converter_mgr_instance();
+
+ PJ_ASSERT_ON_FAIL(mgr != NULL, return);
+
+ f = mgr->factory_list.next;
+ while (f != &mgr->factory_list) {
+ pjmedia_converter_factory *next = f->next;
+ pj_list_erase(f);
+ (*f->op->destroy_factory)(f);
+ f = next;
+ }
+
+ if (converter_manager_instance == mgr)
+ converter_manager_instance = NULL;
+}
+
+PJ_DEF(pj_status_t)
+pjmedia_converter_mgr_register_factory(pjmedia_converter_mgr *mgr,
+ pjmedia_converter_factory *factory)
+{
+ pjmedia_converter_factory *pf;
+
+ if (!mgr) mgr = pjmedia_converter_mgr_instance();
+
+ PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVAL);
+
+ PJ_ASSERT_RETURN(!pj_list_find_node(&mgr->factory_list, factory),
+ PJ_EEXISTS);
+
+ pf = mgr->factory_list.next;
+ while (pf != &mgr->factory_list) {
+ if (pf->priority > factory->priority)
+ break;
+ pf = pf->next;
+ }
+ pj_list_insert_before(pf, factory);
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t)
+pjmedia_converter_mgr_unregister_factory(pjmedia_converter_mgr *mgr,
+ pjmedia_converter_factory *f,
+ pj_bool_t destroy)
+{
+ if (!mgr) mgr = pjmedia_converter_mgr_instance();
+
+ PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVAL);
+
+ PJ_ASSERT_RETURN(pj_list_find_node(&mgr->factory_list, f), PJ_ENOTFOUND);
+ pj_list_erase(f);
+ if (destroy)
+ (*f->op->destroy_factory)(f);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_converter_create(pjmedia_converter_mgr *mgr,
+ pj_pool_t *pool,
+ pjmedia_conversion_param *param,
+ pjmedia_converter **p_cv)
+{
+ pjmedia_converter_factory *f;
+ pjmedia_converter *cv = NULL;
+ pj_status_t status = PJ_ENOTFOUND;
+
+ if (!mgr) mgr = pjmedia_converter_mgr_instance();
+
+ PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVAL);
+
+ *p_cv = NULL;
+
+ f = mgr->factory_list.next;
+ while (f != &mgr->factory_list) {
+ status = (*f->op->create_converter)(f, pool, param, &cv);
+ if (status == PJ_SUCCESS)
+ break;
+ f = f->next;
+ }
+
+ if (status != PJ_SUCCESS)
+ return status;
+
+ *p_cv = cv;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_converter_convert(pjmedia_converter *cv,
+ pjmedia_frame *src_frame,
+ pjmedia_frame *dst_frame)
+{
+ return (*cv->op->convert)(cv, src_frame, dst_frame);
+}
+
+PJ_DEF(void) pjmedia_converter_destroy(pjmedia_converter *cv)
+{
+ (*cv->op->destroy)(cv);
+}
+
+
diff --git a/pjmedia/src/pjmedia/converter_libswscale.c b/pjmedia/src/pjmedia/converter_libswscale.c
new file mode 100644
index 00000000..b9e74a22
--- /dev/null
+++ b/pjmedia/src/pjmedia/converter_libswscale.c
@@ -0,0 +1,200 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/converter.h>
+#include <pj/errno.h>
+
+#if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL
+
+#include "ffmpeg_util.h"
+#include <libswscale/swscale.h>
+
+static pj_status_t factory_create_converter(pjmedia_converter_factory *cf,
+ pj_pool_t *pool,
+ const pjmedia_conversion_param*prm,
+ pjmedia_converter **p_cv);
+static void factory_destroy_factory(pjmedia_converter_factory *cf);
+static pj_status_t libswscale_conv_convert(pjmedia_converter *converter,
+ pjmedia_frame *src_frame,
+ pjmedia_frame *dst_frame);
+static void libswscale_conv_destroy(pjmedia_converter *converter);
+
+
+struct fmt_info
+{
+ const pjmedia_video_format_info *fmt_info;
+ pjmedia_video_apply_fmt_param apply_param;
+};
+
+struct ffmpeg_converter
+{
+ pjmedia_converter base;
+ struct SwsContext *sws_ctx;
+ struct fmt_info src,
+ dst;
+};
+
+static pjmedia_converter_factory_op libswscale_factory_op =
+{
+ &factory_create_converter,
+ &factory_destroy_factory
+};
+
+static pjmedia_converter_op liswscale_converter_op =
+{
+ &libswscale_conv_convert,
+ &libswscale_conv_destroy
+};
+
+static pj_status_t factory_create_converter(pjmedia_converter_factory *cf,
+ pj_pool_t *pool,
+ const pjmedia_conversion_param *prm,
+ pjmedia_converter **p_cv)
+{
+ enum PixelFormat srcFormat, dstFormat;
+ const pjmedia_video_format_detail *src_detail, *dst_detail;
+ const pjmedia_video_format_info *src_fmt_info, *dst_fmt_info;
+ struct SwsContext *sws_ctx;
+ struct ffmpeg_converter *fcv;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(cf);
+
+ /* Only supports video */
+ if (prm->src.type != PJMEDIA_TYPE_VIDEO ||
+ prm->dst.type != prm->src.type ||
+ prm->src.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO ||
+ prm->dst.detail_type != prm->src.detail_type)
+ {
+ return PJ_ENOTSUP;
+ }
+
+ /* lookup source format info */
+ src_fmt_info = pjmedia_get_video_format_info(
+ pjmedia_video_format_mgr_instance(),
+ prm->src.id);
+ if (!src_fmt_info)
+ return PJ_ENOTSUP;
+
+ /* lookup destination format info */
+ dst_fmt_info = pjmedia_get_video_format_info(
+ pjmedia_video_format_mgr_instance(),
+ prm->dst.id);
+ if (!dst_fmt_info)
+ return PJ_ENOTSUP;
+
+ src_detail = pjmedia_format_get_video_format_detail(&prm->src, PJ_TRUE);
+ dst_detail = pjmedia_format_get_video_format_detail(&prm->dst, PJ_TRUE);
+
+ status = pjmedia_format_id_to_PixelFormat(prm->src.id, &srcFormat);
+ if (status != PJ_SUCCESS)
+ return PJ_ENOTSUP;
+
+ status = pjmedia_format_id_to_PixelFormat(prm->dst.id, &dstFormat);
+ if (status != PJ_SUCCESS)
+ return PJ_ENOTSUP;
+
+ sws_ctx = sws_getContext(src_detail->size.w, src_detail->size.h, srcFormat,
+ dst_detail->size.w, dst_detail->size.h, dstFormat,
+ SWS_BICUBIC,
+ NULL, NULL, NULL);
+ if (sws_ctx == NULL)
+ return PJ_ENOTSUP;
+
+ fcv = PJ_POOL_ZALLOC_T(pool, struct ffmpeg_converter);
+ fcv->base.op = &liswscale_converter_op;
+ fcv->sws_ctx = sws_ctx;
+ fcv->src.apply_param.size = src_detail->size;
+ fcv->src.fmt_info = src_fmt_info;
+ fcv->dst.apply_param.size = dst_detail->size;
+ fcv->dst.fmt_info = dst_fmt_info;
+
+ *p_cv = &fcv->base;
+
+ return PJ_SUCCESS;
+}
+
+static void factory_destroy_factory(pjmedia_converter_factory *cf)
+{
+ PJ_UNUSED_ARG(cf);
+}
+
+static pj_status_t libswscale_conv_convert(pjmedia_converter *converter,
+ pjmedia_frame *src_frame,
+ pjmedia_frame *dst_frame)
+{
+ struct ffmpeg_converter *fcv = (struct ffmpeg_converter*)converter;
+ struct fmt_info *src = &fcv->src,
+ *dst = &fcv->dst;
+ int h;
+
+ src->apply_param.buffer = src_frame->buf;
+ (*src->fmt_info->apply_fmt)(src->fmt_info, &src->apply_param);
+
+ dst->apply_param.buffer = dst_frame->buf;
+ (*dst->fmt_info->apply_fmt)(dst->fmt_info, &dst->apply_param);
+
+ h = sws_scale(fcv->sws_ctx,
+ src->apply_param.planes, src->apply_param.strides,
+ 0, src->apply_param.size.h,
+ dst->apply_param.planes, dst->apply_param.strides);
+
+ return h==(int)dst->apply_param.size.h ? PJ_SUCCESS : PJ_EUNKNOWN;
+}
+
+static void libswscale_conv_destroy(pjmedia_converter *converter)
+{
+ struct ffmpeg_converter *fcv = (struct ffmpeg_converter*)converter;
+ if (fcv->sws_ctx) {
+ struct SwsContext *tmp = fcv->sws_ctx;
+ fcv->sws_ctx = NULL;
+ sws_freeContext(tmp);
+ }
+}
+
+static pjmedia_converter_factory libswscale_factory =
+{
+ NULL, NULL, /* list */
+ "libswscale", /* name */
+ PJMEDIA_CONVERTER_PRIORITY_NORMAL+1, /* priority */
+ NULL /* op will be init-ed later */
+};
+
+PJ_DEF(pj_status_t)
+pjmedia_libswscale_converter_init(pjmedia_converter_mgr *mgr)
+{
+ libswscale_factory.op = &libswscale_factory_op;
+ return pjmedia_converter_mgr_register_factory(mgr, &libswscale_factory);
+}
+
+
+PJ_DEF(pj_status_t)
+pjmedia_libswscale_converter_shutdown(pjmedia_converter_mgr *mgr,
+ pj_pool_t *pool)
+{
+ PJ_UNUSED_ARG(pool);
+ return pjmedia_converter_mgr_unregister_factory(mgr, &libswscale_factory,
+ PJ_TRUE);
+}
+
+#ifdef _MSC_VER
+# pragma comment( lib, "avutil.lib")
+# pragma comment( lib, "swscale.lib")
+#endif
+
+#endif /* #if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL */
diff --git a/pjmedia/src/pjmedia/delaybuf.c b/pjmedia/src/pjmedia/delaybuf.c
index 39f0e618..a54ec5de 100644
--- a/pjmedia/src/pjmedia/delaybuf.c
+++ b/pjmedia/src/pjmedia/delaybuf.c
@@ -21,6 +21,7 @@
#include <pjmedia/delaybuf.h>
#include <pjmedia/circbuf.h>
#include <pjmedia/errno.h>
+#include <pjmedia/frame.h>
#include <pjmedia/wsola.h>
#include <pj/assert.h>
#include <pj/lock.h>
diff --git a/pjmedia/src/pjmedia/dummy.c b/pjmedia/src/pjmedia/dummy.c
new file mode 100644
index 00000000..805de1cf
--- /dev/null
+++ b/pjmedia/src/pjmedia/dummy.c
@@ -0,0 +1,24 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjmedia/frame.h>
+#include <pjmedia/port.h>
+#include <pjmedia/types.h>
+
diff --git a/pjmedia/src/pjmedia/echo_common.c b/pjmedia/src/pjmedia/echo_common.c
index 1cb2e311..7316dfe8 100644
--- a/pjmedia/src/pjmedia/echo_common.c
+++ b/pjmedia/src/pjmedia/echo_common.c
@@ -20,6 +20,7 @@
#include <pjmedia/echo.h>
#include <pjmedia/delaybuf.h>
+#include <pjmedia/frame.h>
#include <pjmedia/errno.h>
#include <pj/assert.h>
#include <pj/list.h>
diff --git a/pjmedia/src/pjmedia/echo_port.c b/pjmedia/src/pjmedia/echo_port.c
index c3f689ed..4c098cc4 100644
--- a/pjmedia/src/pjmedia/echo_port.c
+++ b/pjmedia/src/pjmedia/echo_port.c
@@ -26,7 +26,7 @@
#define THIS_FILE "ec_port.c"
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('E', 'C', 'H', 'O')
+#define SIGNATURE PJMEDIA_SIG_PORT_ECHO
#define BUF_COUNT 32
struct ec
@@ -38,7 +38,7 @@ struct ec
static pj_status_t ec_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame);
+ pjmedia_frame *frame);
static pj_status_t ec_get_frame(pjmedia_port *this_port,
pjmedia_frame *frame);
static pj_status_t ec_on_destroy(pjmedia_port *this_port);
@@ -52,25 +52,29 @@ PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool,
pjmedia_port **p_port )
{
const pj_str_t AEC = { "EC", 2 };
+ pjmedia_audio_format_detail *afd;
struct ec *ec;
pj_status_t status;
PJ_ASSERT_RETURN(pool && dn_port && p_port, PJ_EINVAL);
- PJ_ASSERT_RETURN(dn_port->info.bits_per_sample==16 && tail_ms,
+
+ afd = pjmedia_format_get_audio_format_detail(&dn_port->info.fmt, PJ_TRUE);
+
+ PJ_ASSERT_RETURN(afd->bits_per_sample==16 && tail_ms,
PJ_EINVAL);
/* Create the port and the AEC itself */
ec = PJ_POOL_ZALLOC_T(pool, struct ec);
pjmedia_port_info_init(&ec->base.info, &AEC, SIGNATURE,
- dn_port->info.clock_rate,
- dn_port->info.channel_count,
- dn_port->info.bits_per_sample,
- dn_port->info.samples_per_frame);
-
- status = pjmedia_echo_create2(pool, dn_port->info.clock_rate,
- dn_port->info.channel_count,
- dn_port->info.samples_per_frame,
+ afd->clock_rate,
+ afd->channel_count,
+ afd->bits_per_sample,
+ PJMEDIA_AFD_SPF(afd));
+
+ status = pjmedia_echo_create2(pool, afd->clock_rate,
+ afd->channel_count,
+ PJMEDIA_AFD_SPF(afd),
tail_ms, latency_ms, options, &ec->ec);
if (status != PJ_SUCCESS)
return status;
@@ -89,7 +93,7 @@ PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool,
static pj_status_t ec_put_frame( pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
struct ec *ec = (struct ec*)this_port;
@@ -99,7 +103,7 @@ static pj_status_t ec_put_frame( pjmedia_port *this_port,
return pjmedia_port_put_frame(ec->dn_port, frame);
}
- PJ_ASSERT_RETURN(frame->size == this_port->info.samples_per_frame * 2,
+ PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
PJ_EINVAL);
pjmedia_echo_capture(ec->ec, (pj_int16_t*)frame->buf, 0);
@@ -119,7 +123,7 @@ static pj_status_t ec_get_frame( pjmedia_port *this_port,
status = pjmedia_port_get_frame(ec->dn_port, frame);
if (status!=PJ_SUCCESS || frame->type!=PJMEDIA_FRAME_TYPE_AUDIO) {
pjmedia_zero_samples((pj_int16_t*)frame->buf,
- this_port->info.samples_per_frame);
+ PJMEDIA_PIA_SPF(&this_port->info));
}
pjmedia_echo_playback(ec->ec, (pj_int16_t*)frame->buf);
diff --git a/pjmedia/src/pjmedia/echo_speex.c b/pjmedia/src/pjmedia/echo_speex.c
index e666e2db..19428597 100644
--- a/pjmedia/src/pjmedia/echo_speex.c
+++ b/pjmedia/src/pjmedia/echo_speex.c
@@ -20,6 +20,7 @@
#include <pjmedia/echo.h>
#include <pjmedia/errno.h>
+#include <pjmedia/frame.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/pool.h>
diff --git a/pjmedia/src/pjmedia/echo_suppress.c b/pjmedia/src/pjmedia/echo_suppress.c
index a0333cb3..85a6efb4 100644
--- a/pjmedia/src/pjmedia/echo_suppress.c
+++ b/pjmedia/src/pjmedia/echo_suppress.c
@@ -20,6 +20,7 @@
#include <pjmedia/types.h>
#include <pjmedia/alaw_ulaw.h>
#include <pjmedia/errno.h>
+#include <pjmedia/frame.h>
#include <pjmedia/silencedet.h>
#include <pj/array.h>
#include <pj/assert.h>
diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c
index 112a0981..75831f1f 100644
--- a/pjmedia/src/pjmedia/endpoint.c
+++ b/pjmedia/src/pjmedia/endpoint.c
@@ -20,6 +20,7 @@
#include <pjmedia/endpoint.h>
#include <pjmedia/errno.h>
#include <pjmedia/sdp.h>
+#include <pjmedia/vid_codec.h>
#include <pjmedia-audiodev/audiodev.h>
#include <pj/assert.h>
#include <pj/ioqueue.h>
@@ -312,87 +313,37 @@ PJ_DEF(pj_pool_t*) pjmedia_endpt_create_pool( pjmedia_endpt *endpt,
return pj_pool_create(endpt->pf, name, initial, increment, NULL);
}
-/**
- * Create a SDP session description that describes the endpoint
- * capability.
- */
-PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
- pj_pool_t *pool,
- unsigned stream_cnt,
- const pjmedia_sock_info sock_info[],
- pjmedia_sdp_session **p_sdp )
+/* Common initialization for both audio and video SDP media line */
+static pj_status_t init_sdp_media(pjmedia_sdp_media *m,
+ pj_pool_t *pool,
+ const pj_str_t *media_type,
+ const pjmedia_sock_info *sock_info)
{
- pj_time_val tv;
- unsigned i;
- const pj_sockaddr *addr0;
- pjmedia_sdp_session *sdp;
- pjmedia_sdp_media *m;
+ char tmp_addr[PJ_INET6_ADDRSTRLEN];
pjmedia_sdp_attr *attr;
+ const pj_sockaddr *addr;
- /* Sanity check arguments */
- PJ_ASSERT_RETURN(endpt && pool && p_sdp && stream_cnt, PJ_EINVAL);
-
- /* Check that there are not too many codecs */
- PJ_ASSERT_RETURN(endpt->codec_mgr.codec_cnt <= PJMEDIA_MAX_SDP_FMT,
- PJ_ETOOMANY);
-
- /* Create and initialize basic SDP session */
- sdp = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
-
- addr0 = &sock_info[0].rtp_addr_name;
-
- pj_gettimeofday(&tv);
- sdp->origin.user = pj_str("-");
- sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
- sdp->origin.net_type = STR_IN;
-
- if (addr0->addr.sa_family == pj_AF_INET()) {
- sdp->origin.addr_type = STR_IP4;
- pj_strdup2(pool, &sdp->origin.addr,
- pj_inet_ntoa(addr0->ipv4.sin_addr));
- } else if (addr0->addr.sa_family == pj_AF_INET6()) {
- char tmp_addr[PJ_INET6_ADDRSTRLEN];
-
- sdp->origin.addr_type = STR_IP6;
- pj_strdup2(pool, &sdp->origin.addr,
- pj_sockaddr_print(addr0, tmp_addr, sizeof(tmp_addr), 0));
-
- } else {
- pj_assert(!"Invalid address family");
- return PJ_EAFNOTSUP;
- }
-
- sdp->name = STR_SDP_NAME;
-
- /* Since we only support one media stream at present, put the
- * SDP connection line in the session level.
- */
- sdp->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
- sdp->conn->net_type = sdp->origin.net_type;
- sdp->conn->addr_type = sdp->origin.addr_type;
- sdp->conn->addr = sdp->origin.addr;
+ pj_strdup(pool, &m->desc.media, media_type);
+ addr = &sock_info->rtp_addr_name;
- /* SDP time and attributes. */
- sdp->time.start = sdp->time.stop = 0;
- sdp->attr_count = 0;
+ /* Validate address family */
+ PJ_ASSERT_RETURN(addr->addr.sa_family == pj_AF_INET() ||
+ addr->addr.sa_family == pj_AF_INET6(),
+ PJ_EAFNOTSUP);
- /* Create media stream 0: */
+ /* SDP connection line */
+ m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
+ m->conn->net_type = STR_IN;
+ m->conn->addr_type = (addr->addr.sa_family==pj_AF_INET())? STR_IP4:STR_IP6;
+ pj_sockaddr_print(addr, tmp_addr, sizeof(tmp_addr), 0);
+ pj_strdup2(pool, &m->conn->addr, tmp_addr);
- sdp->media_count = 1;
- m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
- sdp->media[0] = m;
-
- /* Standard media info: */
- pj_strdup(pool, &m->desc.media, &STR_AUDIO);
- m->desc.port = pj_sockaddr_get_port(addr0);
+ /* Port and transport in media description */
+ m->desc.port = pj_sockaddr_get_port(addr);
m->desc.port_count = 1;
pj_strdup (pool, &m->desc.transport, &STR_RTP_AVP);
- /* Init media line and attribute list. */
- m->desc.fmt_count = 0;
- m->attr_count = 0;
-
/* Add "rtcp" attribute */
#if defined(PJMEDIA_HAS_RTCP_IN_SDP) && PJMEDIA_HAS_RTCP_IN_SDP!=0
if (sock_info->rtcp_addr_name.addr.sa_family != 0) {
@@ -402,13 +353,45 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
}
#endif
+ /* Add sendrecv attribute. */
+ attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
+ attr->name = STR_SENDRECV;
+ m->attr[m->attr_count++] = attr;
+
+ return PJ_SUCCESS;
+}
+
+/* Create m=audio SDP media line */
+PJ_DEF(pj_status_t) pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ const pjmedia_sock_info *si,
+ unsigned options,
+ pjmedia_sdp_media **p_m)
+{
+ const pj_str_t STR_AUDIO = { "audio", 5 };
+ pjmedia_sdp_media *m;
+ pjmedia_sdp_attr *attr;
+ unsigned i;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(options);
+
+ /* Check that there are not too many codecs */
+ PJ_ASSERT_RETURN(endpt->codec_mgr.codec_cnt <= PJMEDIA_MAX_SDP_FMT,
+ PJ_ETOOMANY);
+
+ /* Create and init basic SDP media */
+ m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
+ status = init_sdp_media(m, pool, &STR_AUDIO, si);
+ if (status != PJ_SUCCESS)
+ return status;
+
/* Add format, rtpmap, and fmtp (when applicable) for each codec */
for (i=0; i<endpt->codec_mgr.codec_cnt; ++i) {
pjmedia_codec_info *codec_info;
pjmedia_sdp_rtpmap rtpmap;
char tmp_param[3];
- pjmedia_sdp_attr *attr;
pjmedia_codec_param codec_param;
pj_str_t *fmt;
@@ -512,11 +495,6 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
}
}
- /* Add sendrecv attribute. */
- attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
- attr->name = STR_SENDRECV;
- m->attr[m->attr_count++] = attr;
-
#if defined(PJMEDIA_RTP_PT_TELEPHONE_EVENTS) && \
PJMEDIA_RTP_PT_TELEPHONE_EVENTS != 0
/*
@@ -541,11 +519,250 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
}
#endif
+ *p_m = m;
+ return PJ_SUCCESS;
+}
+
+/* Create m=video SDP media line */
+PJ_DEF(pj_status_t) pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ const pjmedia_sock_info *si,
+ unsigned options,
+ pjmedia_sdp_media **p_m)
+{
+ const pj_str_t STR_VIDEO = { "video", 5 };
+ pjmedia_sdp_media *m;
+ pjmedia_vid_codec_info codec_info[PJMEDIA_VID_CODEC_MGR_MAX_CODECS];
+ unsigned codec_prio[PJMEDIA_VID_CODEC_MGR_MAX_CODECS];
+ pjmedia_sdp_attr *attr;
+ unsigned cnt, i;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(options);
+
+ /* Make sure video codec manager is instantiated */
+ if (!pjmedia_vid_codec_mgr_instance())
+ pjmedia_vid_codec_mgr_create(endpt->pool, NULL);
+
+ /* Create and init basic SDP media */
+ m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
+ status = init_sdp_media(m, pool, &STR_VIDEO, si);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ cnt = PJ_ARRAY_SIZE(codec_info);
+ status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &cnt,
+ codec_info, codec_prio);
+
+ /* Check that there are not too many codecs */
+ PJ_ASSERT_RETURN(0 <= PJMEDIA_MAX_SDP_FMT,
+ PJ_ETOOMANY);
+
+ /* Add format, rtpmap, and fmtp (when applicable) for each codec */
+ for (i=0; i<cnt; ++i) {
+ pjmedia_sdp_rtpmap rtpmap;
+ pjmedia_vid_codec_param codec_param;
+ pj_str_t *fmt;
+
+ pj_bzero(&rtpmap, sizeof(rtpmap));
+
+ if (codec_prio[i] == PJMEDIA_CODEC_PRIO_DISABLED)
+ break;
+
+ if (i > PJMEDIA_MAX_SDP_FMT) {
+ /* Too many codecs, perhaps it is better to tell application by
+ * returning appropriate status code.
+ */
+ PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY,
+ "Skipping some video codecs"));
+ break;
+ }
+
+ /* Must support RTP packetization and bidirectional */
+ if (!codec_info[i].has_rtp_pack ||
+ codec_info[i].dir != PJMEDIA_DIR_ENCODING_DECODING)
+ {
+ continue;
+ }
+
+ pjmedia_vid_codec_mgr_get_default_param(NULL, &codec_info[i],
+ &codec_param);
+
+ fmt = &m->desc.fmt[m->desc.fmt_count++];
+ fmt->ptr = (char*) pj_pool_alloc(pool, 8);
+ fmt->slen = pj_utoa(codec_info[i].pt, fmt->ptr);
+ rtpmap.pt = *fmt;
+
+ /* Encoding name */
+ rtpmap.enc_name = codec_info[i].encoding_name;
+
+ /* Clock rate */
+ rtpmap.clock_rate = codec_info[i].clock_rate;
+
+ if (codec_info[i].pt >= 96 || pjmedia_add_rtpmap_for_static_pt) {
+ pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
+ m->attr[m->attr_count++] = attr;
+ }
+
+ /* Add fmtp params */
+ if (codec_param.dec_fmtp.cnt > 0) {
+ enum { MAX_FMTP_STR_LEN = 160 };
+ char buf[MAX_FMTP_STR_LEN];
+ unsigned buf_len = 0, j;
+ pjmedia_codec_fmtp *dec_fmtp = &codec_param.dec_fmtp;
+
+ /* Print codec PT */
+ buf_len += pj_ansi_snprintf(buf,
+ MAX_FMTP_STR_LEN - buf_len,
+ "%d",
+ codec_info[i].pt);
+
+ for (j = 0; j < dec_fmtp->cnt; ++j) {
+ unsigned test_len = 2;
+
+ /* Check if buf still available */
+ test_len = dec_fmtp->param[j].val.slen +
+ dec_fmtp->param[j].name.slen;
+ if (test_len + buf_len >= MAX_FMTP_STR_LEN)
+ return PJ_ETOOBIG;
+
+ /* Print delimiter */
+ buf_len += pj_ansi_snprintf(&buf[buf_len],
+ MAX_FMTP_STR_LEN - buf_len,
+ (j == 0?" ":";"));
+
+ /* Print an fmtp param */
+ if (dec_fmtp->param[j].name.slen)
+ buf_len += pj_ansi_snprintf(
+ &buf[buf_len],
+ MAX_FMTP_STR_LEN - buf_len,
+ "%.*s=%.*s",
+ (int)dec_fmtp->param[j].name.slen,
+ dec_fmtp->param[j].name.ptr,
+ (int)dec_fmtp->param[j].val.slen,
+ dec_fmtp->param[j].val.ptr);
+ else
+ buf_len += pj_ansi_snprintf(&buf[buf_len],
+ MAX_FMTP_STR_LEN - buf_len,
+ "%.*s",
+ (int)dec_fmtp->param[j].val.slen,
+ dec_fmtp->param[j].val.ptr);
+ }
+
+ attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
+
+ attr->name = pj_str("fmtp");
+ attr->value = pj_strdup3(pool, buf);
+ m->attr[m->attr_count++] = attr;
+ }
+ }
+
+ *p_m = m;
+ return PJ_SUCCESS;
+}
+
+
+/**
+ * Create a "blank" SDP session description. The SDP will contain basic SDP
+ * fields such as origin, time, and name, but without any media lines.
+ */
+PJ_DEF(pj_status_t) pjmedia_endpt_create_base_sdp( pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ const pj_str_t *sess_name,
+ const pj_sockaddr *origin,
+ pjmedia_sdp_session **p_sdp)
+{
+ pj_time_val tv;
+ pjmedia_sdp_session *sdp;
+
+ /* Sanity check arguments */
+ PJ_ASSERT_RETURN(endpt && pool && p_sdp, PJ_EINVAL);
+
+ sdp = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
+
+ pj_gettimeofday(&tv);
+ sdp->origin.user = pj_str("-");
+ sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
+ sdp->origin.net_type = STR_IN;
+
+ if (origin->addr.sa_family == pj_AF_INET()) {
+ sdp->origin.addr_type = STR_IP4;
+ pj_strdup2(pool, &sdp->origin.addr,
+ pj_inet_ntoa(origin->ipv4.sin_addr));
+ } else if (origin->addr.sa_family == pj_AF_INET6()) {
+ char tmp_addr[PJ_INET6_ADDRSTRLEN];
+
+ sdp->origin.addr_type = STR_IP6;
+ pj_strdup2(pool, &sdp->origin.addr,
+ pj_sockaddr_print(origin, tmp_addr, sizeof(tmp_addr), 0));
+
+ } else {
+ pj_assert(!"Invalid address family");
+ return PJ_EAFNOTSUP;
+ }
+
+ if (sess_name)
+ pj_strdup(pool, &sdp->name, sess_name);
+ else
+ sdp->name = STR_SDP_NAME;
+
+ /* SDP time and attributes. */
+ sdp->time.start = sdp->time.stop = 0;
+ sdp->attr_count = 0;
+
/* Done */
*p_sdp = sdp;
return PJ_SUCCESS;
+}
+/**
+ * Create a SDP session description that describes the endpoint
+ * capability.
+ */
+PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ unsigned stream_cnt,
+ const pjmedia_sock_info sock_info[],
+ pjmedia_sdp_session **p_sdp )
+{
+ const pj_sockaddr *addr0;
+ pjmedia_sdp_session *sdp;
+ pjmedia_sdp_media *m;
+ unsigned i;
+ pj_status_t status;
+
+ /* Sanity check arguments */
+ PJ_ASSERT_RETURN(endpt && pool && p_sdp && stream_cnt, PJ_EINVAL);
+ PJ_ASSERT_RETURN(stream_cnt < PJMEDIA_MAX_SDP_MEDIA, PJ_ETOOMANY);
+
+ addr0 = &sock_info[0].rtp_addr_name;
+
+ /* Create and initialize basic SDP session */
+ status = pjmedia_endpt_create_base_sdp(endpt, pool, NULL, addr0, &sdp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Audio is first, by convention */
+ status = pjmedia_endpt_create_audio_sdp(endpt, pool,
+ &sock_info[0], 0, &m);
+ if (status != PJ_SUCCESS)
+ return status;
+ sdp->media[sdp->media_count++] = m;
+
+ /* The remaining stream, if any, are videos (by convention as well) */
+ for (i=1; i<stream_cnt; ++i) {
+ status = pjmedia_endpt_create_video_sdp(endpt, pool,
+ &sock_info[i], 0, &m);
+ if (status != PJ_SUCCESS)
+ return status;
+ sdp->media[sdp->media_count++] = m;
+ }
+
+ /* Done */
+ *p_sdp = sdp;
+
+ return PJ_SUCCESS;
}
diff --git a/pjmedia/src/pjmedia/errno.c b/pjmedia/src/pjmedia/errno.c
index 5c9b2c8b..db66a398 100644
--- a/pjmedia/src/pjmedia/errno.c
+++ b/pjmedia/src/pjmedia/errno.c
@@ -114,6 +114,7 @@ static const struct
PJ_BUILD_ERR( PJMEDIA_EREMOTENODTMF, "Remote does not support DTMF" ),
PJ_BUILD_ERR( PJMEDIA_RTP_EINDTMF, "Invalid DTMF digit" ),
PJ_BUILD_ERR( PJMEDIA_RTP_EREMNORFC2833,"Remote does not support RFC 2833" ),
+ PJ_BUILD_ERR( PJMEDIA_EBADFMT, "Bad format"),
/* RTP session errors. */
PJ_BUILD_ERR( PJMEDIA_RTP_EINPKT, "Invalid RTP packet" ),
@@ -142,6 +143,7 @@ static const struct
PJ_BUILD_ERR( PJMEDIA_EWAVEUNSUPP, "Unsupported WAVE file format" ),
PJ_BUILD_ERR( PJMEDIA_EWAVETOOSHORT, "WAVE file too short" ),
PJ_BUILD_ERR( PJMEDIA_EFRMFILETOOBIG, "Sound frame too large for file buffer"),
+ PJ_BUILD_ERR( PJMEDIA_EAVIUNSUPP, "Unsupported AVI file"),
/* Sound device errors: */
PJ_BUILD_ERR( PJMEDIA_ENOSNDREC, "No suitable sound capture device" ),
diff --git a/pjmedia/src/pjmedia/event.c b/pjmedia/src/pjmedia/event.c
new file mode 100644
index 00000000..fee0917e
--- /dev/null
+++ b/pjmedia/src/pjmedia/event.c
@@ -0,0 +1,148 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/event.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/string.h>
+
+#define THIS_FILE "event.c"
+
+#if 1
+# define TRACE_(x) PJ_LOG(6,x)
+#else
+# define TRACE_(x)
+#endif
+
+PJ_DEF(void) pjmedia_event_init( pjmedia_event *event,
+ pjmedia_event_type type,
+ const pj_timestamp *ts,
+ const pjmedia_event_publisher *epub)
+{
+ pj_bzero(event, sizeof(*event));
+ event->type = type;
+ if (ts)
+ event->timestamp.u64 = ts->u64;
+ event->epub = epub;
+ if (epub)
+ event->epub_sig = epub->sig;
+}
+
+PJ_DEF(void) pjmedia_event_publisher_init(pjmedia_event_publisher *epub,
+ pjmedia_obj_sig sig)
+{
+ pj_bzero(epub, sizeof(*epub));
+ pj_list_init(&epub->subscription_list);
+ epub->sig = sig;
+}
+
+PJ_DEF(void) pjmedia_event_subscription_init( pjmedia_event_subscription *esub,
+ pjmedia_event_cb *cb,
+ void *user_data)
+{
+ pj_bzero(esub, sizeof(*esub));
+ esub->cb = cb;
+ esub->user_data = user_data;
+}
+
+PJ_DEF(pj_bool_t)
+pjmedia_event_publisher_has_sub(pjmedia_event_publisher *epub)
+{
+ PJ_ASSERT_RETURN(epub, PJ_FALSE);
+ return epub->subscription_list.next &&
+ (!pj_list_empty(&epub->subscription_list));
+}
+
+PJ_DEF(pj_status_t) pjmedia_event_subscribe( pjmedia_event_publisher *epub,
+ pjmedia_event_subscription *esub)
+{
+ PJ_ASSERT_RETURN(epub && esub && esub->cb, PJ_EINVAL);
+ /* Must not currently subscribe to anything */
+ PJ_ASSERT_RETURN(esub->subscribe_to == NULL, PJ_EINVALIDOP);
+
+ pj_list_push_back(&epub->subscription_list, esub);
+ esub->subscribe_to = epub;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_event_unsubscribe(pjmedia_event_subscription *esub)
+{
+ PJ_ASSERT_RETURN(esub, PJ_EINVAL);
+ if (esub->subscribe_to) {
+ PJ_ASSERT_RETURN(
+ pj_list_find_node(&esub->subscribe_to->subscription_list,
+ esub)==esub, PJ_ENOTFOUND);
+ pj_list_erase(esub);
+ esub->subscribe_to = NULL;
+ }
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_event_publish( pjmedia_event_publisher *epub,
+ pjmedia_event *event)
+{
+ pjmedia_event_subscription *esub;
+ char event_name[5];
+ char epub_name[5];
+ pj_status_t err = PJ_SUCCESS;
+
+ PJ_ASSERT_RETURN(epub && event, PJ_EINVAL);
+
+ TRACE_((THIS_FILE, "Event %s is published by publisher %s",
+ pjmedia_fourcc_name(event->type, event_name),
+ pjmedia_fourcc_name(epub->sig, epub_name)));
+
+ esub = epub->subscription_list.next;
+ if (!esub)
+ return err;
+
+ while (esub != &epub->subscription_list) {
+ pjmedia_event_subscription *next;
+ pj_status_t status;
+
+ /* just in case esub is destroyed in the callback */
+ next = esub->next;
+
+ status = (*esub->cb)(esub, event);
+ if (status != PJ_SUCCESS && err == PJ_SUCCESS)
+ err = status;
+
+ esub = next;
+ }
+
+ return err;
+}
+
+static pj_status_t republisher_cb(pjmedia_event_subscription *esub,
+ pjmedia_event *event)
+{
+ return pjmedia_event_publish((pjmedia_event_publisher*)esub->user_data,
+ event);
+}
+
+PJ_DEF(pj_status_t) pjmedia_event_republish(pjmedia_event_publisher *esrc,
+ pjmedia_event_publisher *epub,
+ pjmedia_event_subscription *esub)
+{
+ PJ_ASSERT_RETURN(esrc && epub && esub, PJ_EINVAL);
+
+ pjmedia_event_subscription_init(esub, &republisher_cb, epub);
+ return pjmedia_event_subscribe(esrc, esub);
+}
+
diff --git a/pjmedia/src/pjmedia/ffmpeg_util.c b/pjmedia/src/pjmedia/ffmpeg_util.c
new file mode 100644
index 00000000..55dc48ab
--- /dev/null
+++ b/pjmedia/src/pjmedia/ffmpeg_util.c
@@ -0,0 +1,153 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/types.h>
+#include <pj/errno.h>
+
+#if PJMEDIA_HAS_LIBAVFORMAT && PJMEDIA_HAS_LIBAVUTIL
+
+#include "ffmpeg_util.h"
+#include <libavformat/avformat.h>
+
+/* Conversion table between pjmedia_format_id and PixelFormat */
+static const struct ffmpeg_fmt_table_t
+{
+ pjmedia_format_id id;
+ enum PixelFormat pf;
+} ffmpeg_fmt_table[] =
+{
+ { PJMEDIA_FORMAT_RGBA, PIX_FMT_RGBA},
+ { PJMEDIA_FORMAT_RGB24,PIX_FMT_RGB24},
+ { PJMEDIA_FORMAT_BGRA, PIX_FMT_BGRA},
+
+ { PJMEDIA_FORMAT_AYUV, PIX_FMT_NONE},
+ { PJMEDIA_FORMAT_YUY2, PIX_FMT_YUYV422},
+ { PJMEDIA_FORMAT_UYVY, PIX_FMT_UYVY422},
+ { PJMEDIA_FORMAT_I420, PIX_FMT_YUV420P},
+ { PJMEDIA_FORMAT_YV12, PIX_FMT_YUV422P},
+ { PJMEDIA_FORMAT_I420JPEG, PIX_FMT_YUVJ420P},
+ { PJMEDIA_FORMAT_I422JPEG, PIX_FMT_YUVJ422P},
+};
+
+/* Conversion table between pjmedia_format_id and CodecID */
+static const struct ffmpeg_codec_table_t
+{
+ pjmedia_format_id id;
+ enum CodecID codec_id;
+} ffmpeg_codec_table[] =
+{
+ {PJMEDIA_FORMAT_H261, CODEC_ID_H261},
+ {PJMEDIA_FORMAT_H263, CODEC_ID_H263},
+ {PJMEDIA_FORMAT_H263P, CODEC_ID_H263P},
+ {PJMEDIA_FORMAT_H264, CODEC_ID_H264},
+ {PJMEDIA_FORMAT_MPEG1VIDEO, CODEC_ID_MPEG1VIDEO},
+ {PJMEDIA_FORMAT_MPEG2VIDEO, CODEC_ID_MPEG2VIDEO},
+ {PJMEDIA_FORMAT_MPEG4, CODEC_ID_MPEG4},
+ {PJMEDIA_FORMAT_MJPEG, CODEC_ID_MJPEG},
+#if LIBAVCODEC_VERSION_MAJOR < 53
+ {PJMEDIA_FORMAT_XVID, CODEC_ID_XVID},
+#endif
+};
+
+static int pjmedia_ffmpeg_ref_cnt;
+
+void pjmedia_ffmpeg_add_ref()
+{
+ if (pjmedia_ffmpeg_ref_cnt++ == 0) {
+ av_register_all();
+ }
+}
+
+void pjmedia_ffmpeg_dec_ref()
+{
+ if (pjmedia_ffmpeg_ref_cnt-- == 1) {
+ /* How to shutdown ffmpeg? */
+ }
+
+ if (pjmedia_ffmpeg_ref_cnt < 0) pjmedia_ffmpeg_ref_cnt = 0;
+}
+
+pj_status_t pjmedia_format_id_to_PixelFormat(pjmedia_format_id fmt_id,
+ enum PixelFormat *pixel_format)
+{
+ unsigned i;
+ for (i=0; i<PJ_ARRAY_SIZE(ffmpeg_fmt_table); ++i) {
+ const struct ffmpeg_fmt_table_t *t = &ffmpeg_fmt_table[i];
+ if (t->id==fmt_id && t->pf != PIX_FMT_NONE) {
+ *pixel_format = t->pf;
+ return PJ_SUCCESS;
+ }
+ }
+
+ *pixel_format = PIX_FMT_NONE;
+ return PJ_ENOTFOUND;
+}
+
+pj_status_t PixelFormat_to_pjmedia_format_id(enum PixelFormat pf,
+ pjmedia_format_id *fmt_id)
+{
+ unsigned i;
+ for (i=0; i<PJ_ARRAY_SIZE(ffmpeg_fmt_table); ++i) {
+ const struct ffmpeg_fmt_table_t *t = &ffmpeg_fmt_table[i];
+ if (t->pf == pf) {
+ if (fmt_id) *fmt_id = t->id;
+ return PJ_SUCCESS;
+ }
+ }
+
+ return PJ_ENOTFOUND;
+}
+
+pj_status_t pjmedia_format_id_to_CodecID(pjmedia_format_id fmt_id,
+ enum CodecID *codec_id)
+{
+ unsigned i;
+ for (i=0; i<PJ_ARRAY_SIZE(ffmpeg_codec_table); ++i) {
+ const struct ffmpeg_codec_table_t *t = &ffmpeg_codec_table[i];
+ if (t->id==fmt_id && t->codec_id != PIX_FMT_NONE) {
+ *codec_id = t->codec_id;
+ return PJ_SUCCESS;
+ }
+ }
+
+ *codec_id = PIX_FMT_NONE;
+ return PJ_ENOTFOUND;
+}
+
+pj_status_t CodecID_to_pjmedia_format_id(enum CodecID codec_id,
+ pjmedia_format_id *fmt_id)
+{
+ unsigned i;
+ for (i=0; i<PJ_ARRAY_SIZE(ffmpeg_codec_table); ++i) {
+ const struct ffmpeg_codec_table_t *t = &ffmpeg_codec_table[i];
+ if (t->codec_id == codec_id) {
+ if (fmt_id) *fmt_id = t->id;
+ return PJ_SUCCESS;
+ }
+ }
+
+ return PJ_ENOTFOUND;
+}
+
+
+#ifdef _MSC_VER
+# pragma comment( lib, "avformat.lib")
+# pragma comment( lib, "avutil.lib")
+#endif
+
+#endif /* #if PJMEDIA_HAS_LIBAVFORMAT && PJMEDIA_HAS_LIBAVUTIL */
diff --git a/pjmedia/src/pjmedia/ffmpeg_util.h b/pjmedia/src/pjmedia/ffmpeg_util.h
new file mode 100644
index 00000000..70e9aaac
--- /dev/null
+++ b/pjmedia/src/pjmedia/ffmpeg_util.h
@@ -0,0 +1,55 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file contains common utilities that are useful for pjmedia components
+ * that use ffmpeg. This is not a public API.
+ */
+
+#ifndef __PJMEDIA_FFMPEG_UTIL_H__
+#define __PJMEDIA_FFMPEG_UTIL_H__
+
+#include <pjmedia/format.h>
+
+#ifdef _MSC_VER
+# ifndef __cplusplus
+# define inline _inline
+# endif
+# pragma warning(disable:4244) /* possible loss of data */
+#endif
+
+#include <libavutil/avutil.h>
+#include <libavcodec/avcodec.h>
+
+void pjmedia_ffmpeg_add_ref();
+void pjmedia_ffmpeg_dec_ref();
+
+pj_status_t pjmedia_format_id_to_PixelFormat(pjmedia_format_id fmt_id,
+ enum PixelFormat *pixel_format);
+
+pj_status_t PixelFormat_to_pjmedia_format_id(enum PixelFormat pf,
+ pjmedia_format_id *fmt_id);
+
+pj_status_t pjmedia_format_id_to_CodecID(pjmedia_format_id fmt_id,
+ enum CodecID *codec_id);
+
+pj_status_t CodecID_to_pjmedia_format_id(enum CodecID codec_id,
+ pjmedia_format_id *fmt_id);
+
+#endif /* __PJMEDIA_FFMPEG_UTIL_H__ */
diff --git a/pjmedia/src/pjmedia/format.c b/pjmedia/src/pjmedia/format.c
new file mode 100644
index 00000000..a5612dae
--- /dev/null
+++ b/pjmedia/src/pjmedia/format.c
@@ -0,0 +1,366 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/format.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+static pj_status_t apply_packed_fmt(const pjmedia_video_format_info *fi,
+ pjmedia_video_apply_fmt_param *aparam);
+
+static pj_status_t apply_planar_420(const pjmedia_video_format_info *fi,
+ pjmedia_video_apply_fmt_param *aparam);
+
+static pj_status_t apply_planar_422(const pjmedia_video_format_info *fi,
+ pjmedia_video_apply_fmt_param *aparam);
+
+struct pjmedia_video_format_mgr
+{
+ unsigned max_info;
+ unsigned info_cnt;
+ pjmedia_video_format_info **infos;
+};
+
+static pjmedia_video_format_mgr *video_format_mgr_instance;
+static pjmedia_video_format_info built_in_vid_fmt_info[] =
+{
+ {PJMEDIA_FORMAT_RGB24, "RGB24", PJMEDIA_COLOR_MODEL_RGB, 24, 1, &apply_packed_fmt},
+ {PJMEDIA_FORMAT_RGBA, "RGBA", PJMEDIA_COLOR_MODEL_RGB, 32, 1, &apply_packed_fmt},
+ {PJMEDIA_FORMAT_BGRA, "BGRA", PJMEDIA_COLOR_MODEL_RGB, 32, 1, &apply_packed_fmt},
+ {PJMEDIA_FORMAT_DIB , "DIB ", PJMEDIA_COLOR_MODEL_RGB, 24, 1, &apply_packed_fmt},
+ {PJMEDIA_FORMAT_AYUV, "AYUV", PJMEDIA_COLOR_MODEL_YUV, 32, 1, &apply_packed_fmt},
+ {PJMEDIA_FORMAT_YUY2, "YUY2", PJMEDIA_COLOR_MODEL_YUV, 16, 1, &apply_packed_fmt},
+ {PJMEDIA_FORMAT_UYVY, "UYVY", PJMEDIA_COLOR_MODEL_YUV, 16, 1, &apply_packed_fmt},
+ {PJMEDIA_FORMAT_YVYU, "YVYU", PJMEDIA_COLOR_MODEL_YUV, 16, 1, &apply_packed_fmt},
+ {PJMEDIA_FORMAT_I420, "I420", PJMEDIA_COLOR_MODEL_YUV, 12, 3, &apply_planar_420},
+ {PJMEDIA_FORMAT_YV12, "YV12", PJMEDIA_COLOR_MODEL_YUV, 16, 3, &apply_planar_422},
+ {PJMEDIA_FORMAT_I420JPEG, "I420JPG", PJMEDIA_COLOR_MODEL_YUV, 12, 3, &apply_planar_420},
+ {PJMEDIA_FORMAT_I422JPEG, "I422JPG", PJMEDIA_COLOR_MODEL_YUV, 16, 3, &apply_planar_422},
+};
+
+
+PJ_DEF(void) pjmedia_format_init_audio( pjmedia_format *fmt,
+ pj_uint32_t fmt_id,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned bits_per_sample,
+ unsigned frame_time_usec,
+ pj_uint32_t avg_bps,
+ pj_uint32_t max_bps)
+{
+ fmt->id = fmt_id;
+ fmt->type = PJMEDIA_TYPE_AUDIO;
+ fmt->detail_type = PJMEDIA_FORMAT_DETAIL_AUDIO;
+
+ fmt->det.aud.clock_rate = clock_rate;
+ fmt->det.aud.channel_count = channel_count;
+ fmt->det.aud.bits_per_sample = bits_per_sample;
+ fmt->det.aud.frame_time_usec = frame_time_usec;
+ fmt->det.aud.avg_bps = avg_bps;
+ fmt->det.aud.max_bps = max_bps;
+}
+
+PJ_DEF(void) pjmedia_format_init_video( pjmedia_format *fmt,
+ pj_uint32_t fmt_id,
+ unsigned width,
+ unsigned height,
+ unsigned fps_num,
+ unsigned fps_denum)
+{
+ fmt->id = fmt_id;
+ fmt->type = PJMEDIA_TYPE_VIDEO;
+ fmt->detail_type = PJMEDIA_FORMAT_DETAIL_VIDEO;
+
+ fmt->det.vid.size.w = width;
+ fmt->det.vid.size.h = height;
+ fmt->det.vid.fps.num = fps_num;
+ fmt->det.vid.fps.denum = fps_denum;
+ fmt->det.vid.avg_bps = fmt->det.vid.max_bps = 0;
+
+ if (pjmedia_video_format_mgr_instance()) {
+ const pjmedia_video_format_info *vfi;
+ pjmedia_video_apply_fmt_param vafp;
+ pj_uint32_t bps;
+
+ vfi = pjmedia_get_video_format_info(NULL, fmt->id);
+ if (vfi) {
+ pj_bzero(&vafp, sizeof(vafp));
+ vafp.size = fmt->det.vid.size;
+ vfi->apply_fmt(vfi, &vafp);
+
+ bps = vafp.framebytes * fps_num * (pj_size_t)8 / fps_denum;
+ fmt->det.vid.avg_bps = fmt->det.vid.max_bps = bps;
+ }
+ }
+}
+
+PJ_DEF(pjmedia_audio_format_detail*)
+pjmedia_format_get_audio_format_detail(const pjmedia_format *fmt,
+ pj_bool_t assert_valid)
+{
+ if (fmt->detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO) {
+ return (pjmedia_audio_format_detail*) &fmt->det.aud;
+ } else {
+ pj_assert(!assert_valid || !"Invalid audio format detail");
+ return NULL;
+ }
+}
+
+PJ_DEF(pjmedia_video_format_detail*)
+pjmedia_format_get_video_format_detail(const pjmedia_format *fmt,
+ pj_bool_t assert_valid)
+{
+ if (fmt->detail_type==PJMEDIA_FORMAT_DETAIL_VIDEO) {
+ return (pjmedia_video_format_detail*)&fmt->det.vid;
+ } else {
+ pj_assert(!assert_valid || !"Invalid video format detail");
+ return NULL;
+ }
+}
+
+PJ_DEF(pjmedia_format*) pjmedia_format_copy(pjmedia_format *dst,
+ const pjmedia_format *src)
+{
+ return (pjmedia_format*)pj_memcpy(dst, src, sizeof(*src));
+}
+
+
+static pj_status_t apply_packed_fmt(const pjmedia_video_format_info *fi,
+ pjmedia_video_apply_fmt_param *aparam)
+{
+ unsigned i;
+ pj_size_t stride;
+
+ stride = (pj_size_t)((aparam->size.w*fi->bpp) >> 3);
+
+ /* Calculate memsize */
+ aparam->framebytes = stride * aparam->size.h;
+
+ /* Packed formats only use 1 plane */
+ aparam->planes[0] = aparam->buffer;
+ aparam->strides[0] = stride;
+ aparam->plane_bytes[0] = aparam->framebytes;
+
+ /* Zero unused planes */
+ for (i=1; i<PJMEDIA_MAX_VIDEO_PLANES; ++i) {
+ aparam->strides[i] = 0;
+ aparam->planes[i] = NULL;
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t apply_planar_420(const pjmedia_video_format_info *fi,
+ pjmedia_video_apply_fmt_param *aparam)
+{
+ unsigned i;
+ pj_size_t Y_bytes;
+
+ PJ_UNUSED_ARG(fi);
+
+ /* Calculate memsize */
+ Y_bytes = (pj_size_t)(aparam->size.w * aparam->size.h);
+ aparam->framebytes = Y_bytes + (Y_bytes>>1);
+
+ /* Planar formats use 3 plane */
+ aparam->strides[0] = aparam->size.w;
+ aparam->strides[1] = aparam->strides[2] = (aparam->size.w>>1);
+
+ aparam->planes[0] = aparam->buffer;
+ aparam->planes[1] = aparam->planes[0] + Y_bytes;
+ aparam->planes[2] = aparam->planes[1] + (Y_bytes>>2);
+
+ aparam->plane_bytes[0] = Y_bytes;
+ aparam->plane_bytes[1] = aparam->plane_bytes[2] = (Y_bytes>>2);
+
+ /* Zero unused planes */
+ for (i=3; i<PJMEDIA_MAX_VIDEO_PLANES; ++i) {
+ aparam->strides[i] = 0;
+ aparam->planes[i] = NULL;
+ aparam->plane_bytes[i] = 0;
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t apply_planar_422(const pjmedia_video_format_info *fi,
+ pjmedia_video_apply_fmt_param *aparam)
+{
+ unsigned i;
+ pj_size_t Y_bytes;
+
+ PJ_UNUSED_ARG(fi);
+
+ /* Calculate memsize */
+ Y_bytes = (pj_size_t)(aparam->size.w * aparam->size.h);
+ aparam->framebytes = (Y_bytes << 1);
+
+ /* Planar formats use 3 plane */
+ aparam->strides[0] = aparam->size.w;
+ aparam->strides[1] = aparam->strides[2] = (aparam->size.w>>1);
+
+ aparam->planes[0] = aparam->buffer;
+ aparam->planes[1] = aparam->planes[0] + Y_bytes;
+ aparam->planes[2] = aparam->planes[1] + (Y_bytes>>1);
+
+ aparam->plane_bytes[0] = Y_bytes;
+ aparam->plane_bytes[1] = aparam->plane_bytes[2] = (Y_bytes>>1);
+
+ /* Zero unused planes */
+ for (i=3; i<PJMEDIA_MAX_VIDEO_PLANES; ++i) {
+ aparam->strides[i] = 0;
+ aparam->planes[i] = NULL;
+ aparam->plane_bytes[i] = 0;
+ }
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t)
+pjmedia_video_format_mgr_create(pj_pool_t *pool,
+ unsigned max_fmt,
+ unsigned options,
+ pjmedia_video_format_mgr **p_mgr)
+{
+ pjmedia_video_format_mgr *mgr;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(pool && options==0, PJ_EINVAL);
+
+ PJ_UNUSED_ARG(options);
+
+ mgr = PJ_POOL_ALLOC_T(pool, pjmedia_video_format_mgr);
+ mgr->max_info = max_fmt;
+ mgr->info_cnt = 0;
+ mgr->infos = pj_pool_calloc(pool, max_fmt, sizeof(pjmedia_video_format_info *));
+
+ if (video_format_mgr_instance == NULL)
+ video_format_mgr_instance = mgr;
+
+ for (i=0; i<PJ_ARRAY_SIZE(built_in_vid_fmt_info); ++i) {
+ pjmedia_register_video_format_info(mgr,
+ &built_in_vid_fmt_info[i]);
+ }
+
+ if (p_mgr)
+ *p_mgr = mgr;
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(const pjmedia_video_format_info*)
+pjmedia_get_video_format_info(pjmedia_video_format_mgr *mgr,
+ pj_uint32_t id)
+{
+ pjmedia_video_format_info **first;
+ int comp;
+ unsigned n;
+
+ if (!mgr)
+ mgr = pjmedia_video_format_mgr_instance();
+
+ PJ_ASSERT_RETURN(mgr != NULL, NULL);
+
+ /* Binary search for the appropriate format id */
+ comp = -1;
+ first = &mgr->infos[0];
+ n = mgr->info_cnt;
+ for (; n > 0; ) {
+ unsigned half = n / 2;
+ pjmedia_video_format_info **mid = first + half;
+
+ if ((*mid)->id < id) {
+ first = ++mid;
+ n -= half + 1;
+ } else if ((*mid)->id==id) {
+ return *mid;
+ } else {
+ n = half;
+ }
+ }
+
+ return NULL;
+}
+
+
+PJ_DEF(pj_status_t)
+pjmedia_register_video_format_info(pjmedia_video_format_mgr *mgr,
+ pjmedia_video_format_info *info)
+{
+ unsigned i;
+
+ if (!mgr)
+ mgr = pjmedia_video_format_mgr_instance();
+
+ PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVALIDOP);
+
+ if (mgr->info_cnt >= mgr->max_info)
+ return PJ_ETOOMANY;
+
+ /* Insert to the array, sorted */
+ for (i=0; i<mgr->info_cnt; ++i) {
+ if (mgr->infos[i]->id >= info->id)
+ break;
+ }
+
+ if (i < mgr->info_cnt) {
+ if (mgr->infos[i]->id == info->id) {
+ /* just overwrite */
+ mgr->infos[i] = info;
+ return PJ_SUCCESS;
+ }
+
+ pj_memmove(&mgr->infos[i+1], &mgr->infos[i],
+ (mgr->info_cnt - i) * sizeof(pjmedia_video_format_info*));
+ }
+
+ mgr->infos[i] = info;
+ mgr->info_cnt++;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pjmedia_video_format_mgr*) pjmedia_video_format_mgr_instance(void)
+{
+ pj_assert(video_format_mgr_instance != NULL);
+ return video_format_mgr_instance;
+}
+
+PJ_DEF(void)
+pjmedia_video_format_mgr_set_instance(pjmedia_video_format_mgr *mgr)
+{
+ video_format_mgr_instance = mgr;
+}
+
+
+PJ_DEF(void) pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr *mgr)
+{
+ if (!mgr)
+ mgr = pjmedia_video_format_mgr_instance();
+
+ PJ_ASSERT_ON_FAIL(mgr != NULL, return);
+
+ mgr->info_cnt = 0;
+ if (video_format_mgr_instance == mgr)
+ video_format_mgr_instance = NULL;
+}
+
diff --git a/pjmedia/src/pjmedia/g711.c b/pjmedia/src/pjmedia/g711.c
index 33f6eac6..1a47c53d 100644
--- a/pjmedia/src/pjmedia/g711.c
+++ b/pjmedia/src/pjmedia/g711.c
@@ -111,7 +111,8 @@ static pjmedia_codec_factory_op g711_factory_op =
&g711_default_attr,
&g711_enum_codecs,
&g711_alloc_codec,
- &g711_dealloc_codec
+ &g711_dealloc_codec,
+ &pjmedia_codec_g711_deinit
};
/* G711 factory private data */
diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c
index b85eea61..9ae84b64 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -68,6 +68,7 @@ typedef struct jb_framelist_t
int *frame_type; /**< frame type array */
pj_size_t *content_len; /**< frame length array */
pj_uint32_t *bit_info; /**< frame bit info array */
+ pj_uint32_t *ts; /**< timestamp array */
/* States */
unsigned head; /**< index of head, pointed frame
@@ -197,7 +198,10 @@ static pj_status_t jb_framelist_init( pj_pool_t *pool,
pj_pool_alloc(pool,
sizeof(framelist->bit_info[0])*
framelist->max_count);
-
+ framelist->ts = (pj_uint32_t*)
+ pj_pool_alloc(pool,
+ sizeof(framelist->ts[0])*
+ framelist->max_count);
return jb_framelist_reset(framelist);
@@ -258,7 +262,9 @@ static int jb_framelist_origin(const jb_framelist_t *framelist)
static pj_bool_t jb_framelist_get(jb_framelist_t *framelist,
void *frame, pj_size_t *size,
pjmedia_jb_frame_type *p_type,
- pj_uint32_t *bit_info)
+ pj_uint32_t *bit_info,
+ pj_uint32_t *ts,
+ int *seq)
{
if (framelist->size) {
pj_bool_t prev_discarded = PJ_FALSE;
@@ -294,6 +300,10 @@ static pj_bool_t jb_framelist_get(jb_framelist_t *framelist,
if (bit_info)
*bit_info = framelist->bit_info[framelist->head];
}
+ if (ts)
+ *ts = framelist->ts[framelist->head];
+ if (seq)
+ *seq = framelist->origin;
//pj_bzero(framelist->content +
// framelist->head * framelist->frame_size,
@@ -301,6 +311,7 @@ static pj_bool_t jb_framelist_get(jb_framelist_t *framelist,
framelist->frame_type[framelist->head] = PJMEDIA_JB_MISSING_FRAME;
framelist->content_len[framelist->head] = 0;
framelist->bit_info[framelist->head] = 0;
+ framelist->ts[framelist->head] = 0;
framelist->origin++;
framelist->head = (framelist->head + 1) % framelist->max_count;
@@ -317,6 +328,53 @@ static pj_bool_t jb_framelist_get(jb_framelist_t *framelist,
}
+static pj_bool_t jb_framelist_peek(jb_framelist_t *framelist,
+ unsigned offset,
+ const void **frame,
+ pj_size_t *size,
+ pjmedia_jb_frame_type *type,
+ pj_uint32_t *bit_info,
+ pj_uint32_t *ts,
+ int *seq)
+{
+ unsigned pos, idx;
+
+ if (offset >= jb_framelist_eff_size(framelist))
+ return PJ_FALSE;
+
+ pos = framelist->head;
+ idx = offset;
+
+ /* Find actual peek position, note there may be discarded frames */
+ while (1) {
+ if (framelist->frame_type[pos] != PJMEDIA_JB_DISCARDED_FRAME) {
+ if (idx == 0)
+ break;
+ else
+ --idx;
+ }
+ pos = (pos + 1) % framelist->max_count;
+ }
+
+ /* Return the frame pointer */
+ if (frame)
+ *frame = framelist->content + pos*framelist->frame_size;
+ if (type)
+ *type = (pjmedia_jb_frame_type)
+ framelist->frame_type[pos];
+ if (size)
+ *size = framelist->content_len[pos];
+ if (bit_info)
+ *bit_info = framelist->bit_info[pos];
+ if (ts)
+ *ts = framelist->ts[pos];
+ if (seq)
+ *seq = framelist->origin + offset;
+
+ return PJ_TRUE;
+}
+
+
/* Remove oldest frames as many as param 'count' */
static unsigned jb_framelist_remove_head(jb_framelist_t *framelist,
unsigned count)
@@ -385,6 +443,7 @@ static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist,
const void *frame,
unsigned frame_size,
pj_uint32_t bit_info,
+ pj_uint32_t ts,
unsigned frame_type)
{
int distance;
@@ -438,6 +497,7 @@ static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist,
framelist->frame_type[pos] = frame_type;
framelist->content_len[pos] = frame_size;
framelist->bit_info[pos] = bit_info;
+ framelist->ts[pos] = ts;
/* update framelist size */
if (framelist->origin + (int)framelist->size <= index)
@@ -747,7 +807,7 @@ PJ_DEF(void) pjmedia_jbuf_put_frame( pjmedia_jbuf *jb,
pj_size_t frame_size,
int frame_seq)
{
- pjmedia_jbuf_put_frame2(jb, frame, frame_size, 0, frame_seq, NULL);
+ pjmedia_jbuf_put_frame3(jb, frame, frame_size, 0, frame_seq, 0, NULL);
}
PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb,
@@ -757,6 +817,18 @@ PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb,
int frame_seq,
pj_bool_t *discarded)
{
+ pjmedia_jbuf_put_frame3(jb, frame, frame_size, bit_info, frame_seq, 0,
+ discarded);
+}
+
+PJ_DEF(void) pjmedia_jbuf_put_frame3(pjmedia_jbuf *jb,
+ const void *frame,
+ pj_size_t frame_size,
+ pj_uint32_t bit_info,
+ int frame_seq,
+ pj_uint32_t ts,
+ pj_bool_t *discarded)
+{
pj_size_t min_frame_size;
int new_size, cur_size, frame_type = PJMEDIA_JB_NORMAL_FRAME;
pj_status_t status;
@@ -824,7 +896,7 @@ PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb,
/* Attempt to store the frame */
min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size);
status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
- min_frame_size, bit_info, frame_type);
+ min_frame_size, bit_info, ts, frame_type);
/* Jitter buffer is full, remove some older frames */
while (status == PJ_ETOOMANY) {
@@ -847,7 +919,7 @@ PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb,
#endif
removed = jb_framelist_remove_head(&jb->jb_framelist, distance);
status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
- min_frame_size, bit_info, frame_type);
+ min_frame_size, bit_info, ts, frame_type);
jb->jb_discard += removed;
}
@@ -879,7 +951,8 @@ PJ_DEF(void) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb,
void *frame,
char *p_frame_type)
{
- pjmedia_jbuf_get_frame2(jb, frame, NULL, p_frame_type, NULL);
+ pjmedia_jbuf_get_frame3(jb, frame, NULL, p_frame_type, NULL,
+ NULL, NULL);
}
/*
@@ -891,6 +964,21 @@ PJ_DEF(void) pjmedia_jbuf_get_frame2(pjmedia_jbuf *jb,
char *p_frame_type,
pj_uint32_t *bit_info)
{
+ pjmedia_jbuf_get_frame3(jb, frame, size, p_frame_type, bit_info,
+ NULL, NULL);
+}
+
+/*
+ * Get frame from jitter buffer.
+ */
+PJ_DEF(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb,
+ void *frame,
+ pj_size_t *size,
+ char *p_frame_type,
+ pj_uint32_t *bit_info,
+ pj_uint32_t *ts,
+ int *seq)
+{
if (jb->jb_status == JB_STATUS_PREFETCHING) {
/* Can't return frame because jitter buffer is filling up
@@ -914,7 +1002,7 @@ PJ_DEF(void) pjmedia_jbuf_get_frame2(pjmedia_jbuf *jb,
/* Try to retrieve a frame from frame list */
res = jb_framelist_get(&jb->jb_framelist, frame, size, &ftype,
- bit_info);
+ bit_info, ts, seq);
if (res) {
/* We've successfully retrieved a frame from the frame list, but
* the frame could be a blank frame!
@@ -982,3 +1070,50 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_get_state( const pjmedia_jbuf *jb,
return PJ_SUCCESS;
}
+
+PJ_DEF(void) pjmedia_jbuf_peek_frame( pjmedia_jbuf *jb,
+ unsigned offset,
+ const void **frame,
+ pj_size_t *size,
+ char *p_frm_type,
+ pj_uint32_t *bit_info,
+ pj_uint32_t *ts,
+ int *seq)
+{
+ pjmedia_jb_frame_type ftype;
+ pj_bool_t res;
+
+ res = jb_framelist_peek(&jb->jb_framelist, offset, frame, size, &ftype,
+ bit_info, ts, seq);
+ if (!res)
+ *p_frm_type = PJMEDIA_JB_ZERO_EMPTY_FRAME;
+ else if (ftype == PJMEDIA_JB_NORMAL_FRAME)
+ *p_frm_type = PJMEDIA_JB_NORMAL_FRAME;
+ else
+ *p_frm_type = PJMEDIA_JB_MISSING_FRAME;
+}
+
+
+PJ_DEF(unsigned) pjmedia_jbuf_remove_frame(pjmedia_jbuf *jb,
+ unsigned frame_cnt)
+{
+ unsigned count, last_discard_num;
+
+ last_discard_num = jb->jb_framelist.discarded_num;
+ count = jb_framelist_remove_head(&jb->jb_framelist, frame_cnt);
+
+ /* Remove some more when there were discarded frames included */
+ while (jb->jb_framelist.discarded_num < last_discard_num) {
+ /* Calculate frames count to be removed next */
+ frame_cnt = last_discard_num - jb->jb_framelist.discarded_num;
+
+ /* Normalize non-discarded frames count just been removed */
+ count -= frame_cnt;
+
+ /* Remove more frames */
+ last_discard_num = jb->jb_framelist.discarded_num;
+ count += jb_framelist_remove_head(&jb->jb_framelist, frame_cnt);
+ }
+
+ return count;
+}
diff --git a/pjmedia/src/pjmedia/master_port.c b/pjmedia/src/pjmedia/master_port.c
index a59796b7..25a9e995 100644
--- a/pjmedia/src/pjmedia/master_port.c
+++ b/pjmedia/src/pjmedia/master_port.c
@@ -56,38 +56,41 @@ PJ_DEF(pj_status_t) pjmedia_master_port_create( pj_pool_t *pool,
unsigned channel_count;
unsigned samples_per_frame;
unsigned bytes_per_frame;
+ pjmedia_audio_format_detail *u_afd, *d_afd;
pj_status_t status;
/* Sanity check */
PJ_ASSERT_RETURN(pool && u_port && d_port && p_m, PJ_EINVAL);
+ u_afd = pjmedia_format_get_audio_format_detail(&u_port->info.fmt, PJ_TRUE);
+ d_afd = pjmedia_format_get_audio_format_detail(&d_port->info.fmt, PJ_TRUE);
/* Both ports MUST have equal clock rate */
- PJ_ASSERT_RETURN(u_port->info.clock_rate == d_port->info.clock_rate,
+ PJ_ASSERT_RETURN(u_afd->clock_rate == d_afd->clock_rate,
PJMEDIA_ENCCLOCKRATE);
/* Both ports MUST have equal samples per frame */
- PJ_ASSERT_RETURN(u_port->info.samples_per_frame==
- d_port->info.samples_per_frame,
+ PJ_ASSERT_RETURN(PJMEDIA_PIA_SPF(&u_port->info)==
+ PJMEDIA_PIA_SPF(&d_port->info),
PJMEDIA_ENCSAMPLESPFRAME);
/* Both ports MUST have equal channel count */
- PJ_ASSERT_RETURN(u_port->info.channel_count == d_port->info.channel_count,
+ PJ_ASSERT_RETURN(u_afd->channel_count == d_afd->channel_count,
PJMEDIA_ENCCHANNEL);
/* Get clock_rate and samples_per_frame from one of the port. */
- clock_rate = u_port->info.clock_rate;
- samples_per_frame = u_port->info.samples_per_frame;
- channel_count = u_port->info.channel_count;
+ clock_rate = u_afd->clock_rate;
+ samples_per_frame = PJMEDIA_PIA_SPF(&u_port->info);
+ channel_count = u_afd->channel_count;
/* Get the bytes_per_frame value, to determine the size of the
* buffer. We take the larger size of the two ports.
*/
- bytes_per_frame = u_port->info.bytes_per_frame;
- if (d_port->info.bytes_per_frame > bytes_per_frame)
- bytes_per_frame = d_port->info.bytes_per_frame;
+ bytes_per_frame = PJMEDIA_AFD_AVG_FSZ(u_afd);
+ if (PJMEDIA_AFD_AVG_FSZ(d_afd) > bytes_per_frame)
+ bytes_per_frame = PJMEDIA_AFD_AVG_FSZ(d_afd);
/* Create the master port instance */
@@ -207,13 +210,16 @@ PJ_DEF(pj_status_t) pjmedia_master_port_set_uport(pjmedia_master_port *m,
{
PJ_ASSERT_RETURN(m && port, PJ_EINVAL);
+ /* Only supports audio for now */
+ PJ_ASSERT_RETURN(port->info.fmt.type==PJMEDIA_TYPE_AUDIO, PJ_ENOTSUP);
+
/* If we have downstream port, make sure they have matching samples per
* frame.
*/
if (m->d_port) {
PJ_ASSERT_RETURN(
- port->info.clock_rate/port->info.samples_per_frame==
- m->d_port->info.clock_rate/m->d_port->info.samples_per_frame,
+ PJMEDIA_PIA_PTIME(&port->info) ==
+ PJMEDIA_PIA_PTIME(&m->d_port->info),
PJMEDIA_ENCSAMPLESPFRAME
);
}
@@ -246,13 +252,16 @@ PJ_DEF(pj_status_t) pjmedia_master_port_set_dport(pjmedia_master_port *m,
{
PJ_ASSERT_RETURN(m && port, PJ_EINVAL);
+ /* Only supports audio for now */
+ PJ_ASSERT_RETURN(port->info.fmt.type==PJMEDIA_TYPE_AUDIO, PJ_ENOTSUP);
+
/* If we have upstream port, make sure they have matching samples per
* frame.
*/
if (m->u_port) {
PJ_ASSERT_RETURN(
- port->info.clock_rate/port->info.samples_per_frame==
- m->u_port->info.clock_rate/m->u_port->info.samples_per_frame,
+ PJMEDIA_PIA_PTIME(&port->info) ==
+ PJMEDIA_PIA_PTIME(&m->u_port->info),
PJMEDIA_ENCSAMPLESPFRAME
);
}
diff --git a/pjmedia/src/pjmedia/mem_capture.c b/pjmedia/src/pjmedia/mem_capture.c
index fe01cd42..a6f9a99e 100644
--- a/pjmedia/src/pjmedia/mem_capture.c
+++ b/pjmedia/src/pjmedia/mem_capture.c
@@ -25,7 +25,7 @@
#define THIS_FILE "mem_capture.c"
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('M', 'R', 'e', 'c')
+#define SIGNATURE PJMEDIA_SIG_PORT_MEM_CAPTURE
#define BYTES_PER_SAMPLE 2
struct mem_rec
@@ -46,7 +46,7 @@ struct mem_rec
static pj_status_t rec_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame);
+ pjmedia_frame *frame);
static pj_status_t rec_get_frame(pjmedia_port *this_port,
pjmedia_frame *frame);
static pj_status_t rec_on_destroy(pjmedia_port *this_port);
@@ -140,7 +140,7 @@ PJ_DEF(pj_size_t) pjmedia_mem_capture_get_size(pjmedia_port *port)
static pj_status_t rec_put_frame( pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
struct mem_rec *rec;
char *endpos;
diff --git a/pjmedia/src/pjmedia/mem_player.c b/pjmedia/src/pjmedia/mem_player.c
index c32bcb09..b0b6fa9c 100644
--- a/pjmedia/src/pjmedia/mem_player.c
+++ b/pjmedia/src/pjmedia/mem_player.c
@@ -25,7 +25,7 @@
#define THIS_FILE "mem_player.c"
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('M', 'P', 'l', 'y')
+#define SIGNATURE PJMEDIA_SIG_PORT_MEM_PLAYER
#define BYTES_PER_SAMPLE 2
struct mem_player
@@ -48,7 +48,7 @@ struct mem_player
static pj_status_t mem_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame);
+ pjmedia_frame *frame);
static pj_status_t mem_get_frame(pjmedia_port *this_port,
pjmedia_frame *frame);
static pj_status_t mem_on_destroy(pjmedia_port *this_port);
@@ -125,7 +125,7 @@ PJ_DEF(pj_status_t) pjmedia_mem_player_set_eof_cb( pjmedia_port *port,
static pj_status_t mem_put_frame( pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
PJ_UNUSED_ARG(this_port);
PJ_UNUSED_ARG(frame);
@@ -165,7 +165,7 @@ static pj_status_t mem_get_frame( pjmedia_port *this_port,
player->eof = PJ_FALSE;
}
- size_needed = this_port->info.bytes_per_frame;
+ size_needed = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
size_written = 0;
endpos = player->buffer + player->buf_size;
@@ -200,11 +200,11 @@ static pj_status_t mem_get_frame( pjmedia_port *this_port,
}
}
- frame->size = this_port->info.bytes_per_frame;
+ frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
frame->timestamp.u64 = player->timestamp.u64;
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
- player->timestamp.u64 += this_port->info.samples_per_frame;
+ player->timestamp.u64 += PJMEDIA_PIA_SPF(&this_port->info);
return PJ_SUCCESS;
}
diff --git a/pjmedia/src/pjmedia/null_port.c b/pjmedia/src/pjmedia/null_port.c
index be9ec97a..97b8a8b0 100644
--- a/pjmedia/src/pjmedia/null_port.c
+++ b/pjmedia/src/pjmedia/null_port.c
@@ -24,12 +24,12 @@
#include <pj/string.h>
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('N', 'U', 'L', 'L')
+#define SIGNATURE PJMEDIA_SIG_PORT_NULL
static pj_status_t null_get_frame(pjmedia_port *this_port,
pjmedia_frame *frame);
static pj_status_t null_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame);
+ pjmedia_frame *frame);
static pj_status_t null_on_destroy(pjmedia_port *this_port);
@@ -67,7 +67,7 @@ PJ_DEF(pj_status_t) pjmedia_null_port_create( pj_pool_t *pool,
* Put frame to file.
*/
static pj_status_t null_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
PJ_UNUSED_ARG(this_port);
PJ_UNUSED_ARG(frame);
@@ -82,10 +82,10 @@ static pj_status_t null_get_frame(pjmedia_port *this_port,
pjmedia_frame *frame)
{
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
- frame->size = this_port->info.samples_per_frame * 2;
- frame->timestamp.u32.lo += this_port->info.samples_per_frame;
+ frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
+ frame->timestamp.u32.lo += PJMEDIA_PIA_SPF(&this_port->info);
pjmedia_zero_samples((pj_int16_t*)frame->buf,
- this_port->info.samples_per_frame);
+ PJMEDIA_PIA_SPF(&this_port->info));
return PJ_SUCCESS;
}
diff --git a/pjmedia/src/pjmedia/port.c b/pjmedia/src/pjmedia/port.c
index 1db5c742..e838263a 100644
--- a/pjmedia/src/pjmedia/port.c
+++ b/pjmedia/src/pjmedia/port.c
@@ -21,6 +21,7 @@
#include <pjmedia/errno.h>
#include <pj/assert.h>
#include <pj/log.h>
+#include <pj/pool.h>
#define THIS_FILE "port.c"
@@ -37,24 +38,53 @@ PJ_DEF(pj_status_t) pjmedia_port_info_init( pjmedia_port_info *info,
unsigned bits_per_sample,
unsigned samples_per_frame)
{
+#define USEC_IN_SEC (pj_uint64_t)1000000
+ unsigned frame_time_usec, avg_bps;
+
pj_bzero(info, sizeof(*info));
+ info->signature = signature;
+ info->dir = PJMEDIA_DIR_ENCODING_DECODING;
info->name = *name;
+
+ frame_time_usec = (unsigned)(samples_per_frame * USEC_IN_SEC /
+ channel_count / clock_rate);
+ avg_bps = clock_rate * channel_count * bits_per_sample;
+
+ pjmedia_format_init_audio(&info->fmt, PJMEDIA_FORMAT_L16, clock_rate,
+ channel_count, bits_per_sample, frame_time_usec,
+ avg_bps, avg_bps);
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_port_info_init2( pjmedia_port_info *info,
+ const pj_str_t *name,
+ unsigned signature,
+ pjmedia_dir dir,
+ const pjmedia_format *fmt)
+{
+ pj_bzero(info, sizeof(*info));
info->signature = signature;
- info->type = PJMEDIA_TYPE_AUDIO;
- info->has_info = PJ_TRUE;
- info->need_info = PJ_FALSE;
- info->pt = 0xFF;
- info->encoding_name = pj_str("pcm");
- info->clock_rate = clock_rate;
- info->channel_count = channel_count;
- info->bits_per_sample = bits_per_sample;
- info->samples_per_frame = samples_per_frame;
- info->bytes_per_frame = samples_per_frame * bits_per_sample / 8;
+ info->dir = dir;
+ info->name = *name;
+
+ pjmedia_format_copy(&info->fmt, fmt);
return PJ_SUCCESS;
}
+/**
+ * Get a clock source from the port.
+ */
+PJ_DEF(pjmedia_clock_src *) pjmedia_port_get_clock_src( pjmedia_port *port,
+ pjmedia_dir dir )
+{
+ if (port && port->get_clock_src)
+ return port->get_clock_src(port, dir);
+ else
+ return NULL;
+}
/**
* Get a frame from the port (and subsequent downstream ports).
@@ -77,7 +107,7 @@ PJ_DEF(pj_status_t) pjmedia_port_get_frame( pjmedia_port *port,
* Put a frame to the port (and subsequent downstream ports).
*/
PJ_DEF(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port,
- const pjmedia_frame *frame )
+ pjmedia_frame *frame )
{
PJ_ASSERT_RETURN(port && frame, PJ_EINVAL);
@@ -87,6 +117,19 @@ PJ_DEF(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port,
return PJ_EINVALIDOP;
}
+/*
+ * Get event publisher
+ */
+PJ_DEF(pjmedia_event_publisher*)
+pjmedia_port_get_event_publisher(pjmedia_port *port)
+{
+ PJ_ASSERT_RETURN(port, NULL);
+
+ if (port->get_event_pub)
+ return (*port->get_event_pub)(port);
+
+ return NULL;
+}
/**
* Destroy port (and subsequent downstream ports)
diff --git a/pjmedia/src/pjmedia/resample_port.c b/pjmedia/src/pjmedia/resample_port.c
index 2f907129..5991877e 100644
--- a/pjmedia/src/pjmedia/resample_port.c
+++ b/pjmedia/src/pjmedia/resample_port.c
@@ -25,7 +25,7 @@
#define BYTES_PER_SAMPLE 2
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('R','S','M','P')
+#define SIGNATURE PJMEDIA_SIG_PORT_RESAMPLE
struct resample_port
@@ -42,7 +42,7 @@ struct resample_port
static pj_status_t resample_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame);
+ pjmedia_frame *frame);
static pj_status_t resample_get_frame(pjmedia_port *this_port,
pjmedia_frame *frame);
static pj_status_t resample_destroy(pjmedia_port *this_port);
@@ -57,40 +57,40 @@ PJ_DEF(pj_status_t) pjmedia_resample_port_create( pj_pool_t *pool,
{
const pj_str_t name = pj_str("resample");
struct resample_port *rport;
- unsigned ptime;
+ pjmedia_audio_format_detail *d_afd, *r_afd;
pj_status_t status;
/* Validate arguments. */
PJ_ASSERT_RETURN(pool && dn_port && clock_rate && p_port, PJ_EINVAL);
/* Only supports 16bit samples per frame */
- PJ_ASSERT_RETURN(dn_port->info.bits_per_sample == 16, PJMEDIA_ENCBITS);
+ PJ_ASSERT_RETURN(PJMEDIA_PIA_BITS(&dn_port->info) == 16, PJMEDIA_ENCBITS);
+
+ d_afd = pjmedia_format_get_audio_format_detail(&dn_port->info.fmt, 1);
- ptime = dn_port->info.samples_per_frame * 1000 /
- dn_port->info.clock_rate;
-
/* Create and initialize port. */
rport = PJ_POOL_ZALLOC_T(pool, struct resample_port);
PJ_ASSERT_RETURN(rport != NULL, PJ_ENOMEM);
pjmedia_port_info_init(&rport->base.info, &name, SIGNATURE, clock_rate,
- dn_port->info.channel_count, BYTES_PER_SAMPLE * 8,
- clock_rate * ptime / 1000);
+ d_afd->channel_count, BYTES_PER_SAMPLE * 8,
+ clock_rate * d_afd->frame_time_usec / 1000000);
rport->dn_port = dn_port;
rport->options = opt;
+ r_afd = pjmedia_format_get_audio_format_detail(&rport->base.info.fmt, 1);
/* Create buffers.
* We need separate buffer for get_frame() and put_frame() since
* both functions may run simultaneously.
*/
rport->get_buf = (pj_int16_t*)
- pj_pool_alloc(pool, dn_port->info.bytes_per_frame);
+ pj_pool_alloc(pool, PJMEDIA_PIA_AVG_FSZ(&dn_port->info));
PJ_ASSERT_RETURN(rport->get_buf != NULL, PJ_ENOMEM);
rport->put_buf = (pj_int16_t*)
- pj_pool_alloc(pool, dn_port->info.bytes_per_frame);
+ pj_pool_alloc(pool, PJMEDIA_PIA_AVG_FSZ(&dn_port->info));
PJ_ASSERT_RETURN(rport->put_buf != NULL, PJ_ENOMEM);
@@ -98,10 +98,10 @@ PJ_DEF(pj_status_t) pjmedia_resample_port_create( pj_pool_t *pool,
status = pjmedia_resample_create(pool,
(opt&PJMEDIA_RESAMPLE_USE_LINEAR)==0,
(opt&PJMEDIA_RESAMPLE_USE_SMALL_FILTER)==0,
- dn_port->info.channel_count,
- dn_port->info.clock_rate,
- rport->base.info.clock_rate,
- dn_port->info.samples_per_frame,
+ d_afd->channel_count,
+ d_afd->clock_rate,
+ r_afd->clock_rate,
+ PJMEDIA_PIA_SPF(&dn_port->info),
&rport->resample_get);
if (status != PJ_SUCCESS)
return status;
@@ -110,10 +110,10 @@ PJ_DEF(pj_status_t) pjmedia_resample_port_create( pj_pool_t *pool,
status = pjmedia_resample_create(pool,
(opt&PJMEDIA_RESAMPLE_USE_LINEAR)==0,
(opt&PJMEDIA_RESAMPLE_USE_SMALL_FILTER)==0,
- dn_port->info.channel_count,
- rport->base.info.clock_rate,
- dn_port->info.clock_rate,
- rport->base.info.samples_per_frame,
+ d_afd->channel_count,
+ r_afd->clock_rate,
+ d_afd->clock_rate,
+ PJMEDIA_PIA_SPF(&rport->base.info),
&rport->resample_put);
/* Media port interface */
@@ -131,7 +131,7 @@ PJ_DEF(pj_status_t) pjmedia_resample_port_create( pj_pool_t *pool,
static pj_status_t resample_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
struct resample_port *rport = (struct resample_port*) this_port;
pjmedia_frame downstream_frame;
@@ -147,7 +147,7 @@ static pj_status_t resample_put_frame(pjmedia_port *this_port,
rport->put_buf);
downstream_frame.buf = rport->put_buf;
- downstream_frame.size = rport->dn_port->info.bytes_per_frame;
+ downstream_frame.size = PJMEDIA_PIA_AVG_FSZ(&rport->dn_port->info);
} else {
downstream_frame.buf = frame->buf;
downstream_frame.size = frame->size;
@@ -175,7 +175,7 @@ static pj_status_t resample_get_frame(pjmedia_port *this_port,
}
tmp_frame.buf = rport->get_buf;
- tmp_frame.size = rport->dn_port->info.bytes_per_frame;
+ tmp_frame.size = PJMEDIA_PIA_AVG_FSZ(&rport->dn_port->info);
tmp_frame.timestamp.u64 = frame->timestamp.u64;
tmp_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
@@ -187,8 +187,8 @@ static pj_status_t resample_get_frame(pjmedia_port *this_port,
frame->type = tmp_frame.type;
frame->timestamp = tmp_frame.timestamp;
/* Copy whatever returned as long as the buffer size is enough */
- frame->size = tmp_frame.size < rport->base.info.bytes_per_frame ?
- tmp_frame.size : rport->base.info.bytes_per_frame;
+ frame->size = tmp_frame.size < PJMEDIA_PIA_AVG_FSZ(&rport->base.info) ?
+ tmp_frame.size : PJMEDIA_PIA_AVG_FSZ(&rport->base.info);
if (tmp_frame.size) {
pjmedia_copy_samples((pj_int16_t*)frame->buf,
(const pj_int16_t*)tmp_frame.buf,
@@ -201,7 +201,7 @@ static pj_status_t resample_get_frame(pjmedia_port *this_port,
(const pj_int16_t*) tmp_frame.buf,
(pj_int16_t*) frame->buf);
- frame->size = rport->base.info.bytes_per_frame;
+ frame->size = PJMEDIA_PIA_AVG_FSZ(&rport->base.info);
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
return PJ_SUCCESS;
diff --git a/pjmedia/src/pjmedia/sdp_cmp.c b/pjmedia/src/pjmedia/sdp_cmp.c
index ce6c9403..b4a23c74 100644
--- a/pjmedia/src/pjmedia/sdp_cmp.c
+++ b/pjmedia/src/pjmedia/sdp_cmp.c
@@ -295,3 +295,10 @@ PJ_DEF(pj_status_t) pjmedia_sdp_session_cmp( const pjmedia_sdp_session *sd1,
}
+PJ_DEF(pj_status_t) pjmedia_sdp_conn_cmp(const pjmedia_sdp_conn *conn1,
+ const pjmedia_sdp_conn *conn2,
+ unsigned option)
+{
+ PJ_UNUSED_ARG(option);
+ return compare_conn(conn1, conn2);
+}
diff --git a/pjmedia/src/pjmedia/sdp_neg.c b/pjmedia/src/pjmedia/sdp_neg.c
index 2e3dde6d..dbba836d 100644
--- a/pjmedia/src/pjmedia/sdp_neg.c
+++ b/pjmedia/src/pjmedia/sdp_neg.c
@@ -52,7 +52,7 @@ static const char *state_str[] =
"STATE_DONE",
};
-#define GET_FMTP_IVAL(ival, fmtp, param, default_val) \
+#define GET_FMTP_IVAL_BASE(ival, base, fmtp, param, default_val) \
do { \
pj_str_t s; \
char *p; \
@@ -63,9 +63,39 @@ static const char *state_str[] =
} \
pj_strset(&s, p + param.slen, fmtp.fmt_param.slen - \
(p - fmtp.fmt_param.ptr) - param.slen); \
- ival = pj_strtoul(&s); \
+ ival = pj_strtoul2(&s, NULL, base); \
} while (0)
+#define GET_FMTP_IVAL(ival, fmtp, param, default_val) \
+ GET_FMTP_IVAL_BASE(ival, 10, fmtp, param, default_val)
+
+
+/* Definition of customized SDP format negotiation callback */
+struct fmt_match_cb_t
+{
+ pj_str_t fmt_name;
+ pjmedia_sdp_neg_fmt_match_cb cb;
+};
+
+/* Number of registered customized SDP format negotiation callbacks */
+static unsigned fmt_match_cb_cnt;
+
+/* The registered customized SDP format negotiation callbacks */
+static struct fmt_match_cb_t
+ fmt_match_cb[PJMEDIA_SDP_NEG_MAX_CUSTOM_FMT_NEG_CB];
+
+/* Redefining a very long identifier name, just for convenience */
+#define ALLOW_MODIFY_ANSWER PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER
+
+static pj_status_t custom_fmt_match( pj_pool_t *pool,
+ const pj_str_t *fmt_name,
+ pjmedia_sdp_media *offer,
+ unsigned o_fmt_idx,
+ pjmedia_sdp_media *answer,
+ unsigned a_fmt_idx,
+ unsigned option);
+
+
/*
* Get string representation of negotiator state.
*/
@@ -231,6 +261,31 @@ PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_neg_local( pjmedia_sdp_neg *neg,
return PJ_SUCCESS;
}
+static pjmedia_sdp_media *sdp_media_clone_deactivate(
+ pj_pool_t *pool,
+ const pjmedia_sdp_media *rem_med,
+ const pjmedia_sdp_media *local_med,
+ const pjmedia_sdp_session *local_sess)
+{
+ pjmedia_sdp_media *res;
+
+ res = pjmedia_sdp_media_clone_deactivate(pool, rem_med);
+ if (!res)
+ return NULL;
+
+ if (!res->conn && (!local_sess || !local_sess->conn)) {
+ if (local_med && local_med->conn)
+ res->conn = pjmedia_sdp_conn_clone(pool, local_med->conn);
+ else {
+ res->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
+ res->conn->net_type = pj_str("IN");
+ res->conn->addr_type = pj_str("IP4");
+ res->conn->addr = pj_str("127.0.0.1");
+ }
+ }
+
+ return res;
+}
/*
* Modify local SDP and wait for remote answer.
@@ -311,7 +366,7 @@ PJ_DEF(pj_status_t) pjmedia_sdp_neg_modify_local_offer( pj_pool_t *pool,
if (!found) {
pjmedia_sdp_media *m;
- m = pjmedia_sdp_media_clone_deactivate(pool, om);
+ m = sdp_media_clone_deactivate(pool, om, om, local);
pj_array_insert(new_offer->media, sizeof(new_offer->media[0]),
new_offer->media_count++, oi, &m);
@@ -876,10 +931,12 @@ static pj_status_t process_m_answer( pj_pool_t *pool,
(ar.param.slen==1 && *ar.param.ptr=='1')))
{
/* Further check for G7221, negotiate bitrate. */
- if (pj_stricmp2(&or_.enc_name, "G7221") == 0) {
+ if (pj_stricmp2(&or_.enc_name, "G7221") == 0)
+ {
if (match_g7221(offer, i, answer, j))
break;
} else
+
/* Further check for AMR, negotiate fmtp. */
if (pj_stricmp2(&or_.enc_name, "AMR") == 0 ||
pj_stricmp2(&or_.enc_name, "AMR-WB") == 0)
@@ -887,7 +944,13 @@ static pj_status_t process_m_answer( pj_pool_t *pool,
if (match_amr(offer, i, answer, j, PJ_FALSE,
NULL))
break;
- } else {
+ } else
+
+ /* Call custom format matching callbacks */
+ if (custom_fmt_match(pool, &or_.enc_name,
+ offer, i, answer, j, 0) ==
+ PJ_SUCCESS)
+ {
/* Match! */
break;
}
@@ -1016,7 +1079,8 @@ static pj_status_t process_answer(pj_pool_t *pool,
pjmedia_sdp_media *am;
/* Generate matching-but-disabled-media for the answer */
- am = pjmedia_sdp_media_clone_deactivate(pool, offer->media[omi]);
+ am = sdp_media_clone_deactivate(pool, offer->media[omi],
+ offer->media[omi], offer);
answer->media[answer->media_count++] = am;
++ami;
@@ -1078,7 +1142,7 @@ static pj_status_t match_offer(pj_pool_t *pool,
/* If offer has zero port, just clone the offer */
if (offer->desc.port == 0) {
- answer = pjmedia_sdp_media_clone_deactivate(pool, offer);
+ answer = sdp_media_clone_deactivate(pool, offer, preanswer, NULL);
*p_answer = answer;
return PJ_SUCCESS;
}
@@ -1181,12 +1245,31 @@ static pj_status_t match_offer(pj_pool_t *pool,
{
/* Match! */
if (is_codec) {
+ pjmedia_sdp_media *o, *a;
+ unsigned o_fmt_idx, a_fmt_idx;
+
+ o = (pjmedia_sdp_media*)offer;
+ a = (pjmedia_sdp_media*)preanswer;
+ o_fmt_idx = prefer_remote_codec_order? i:j;
+ a_fmt_idx = prefer_remote_codec_order? j:i;
+
+ /* Call custom format matching callbacks */
+ if (custom_fmt_match(pool, &or_.enc_name,
+ o, o_fmt_idx,
+ a, a_fmt_idx,
+ ALLOW_MODIFY_ANSWER) !=
+ PJ_SUCCESS)
+ {
+ continue;
+ } else
+
/* Further check for G7221, negotiate bitrate */
if (pj_stricmp2(&or_.enc_name, "G7221") == 0 &&
!match_g7221(master, i, slave, j))
{
continue;
- } else
+ } else
+
/* Further check for AMR, negotiate fmtp */
if (pj_stricmp2(&or_.enc_name, "AMR")==0 ||
pj_stricmp2(&or_.enc_name, "AMR-WB")==0)
@@ -1200,6 +1283,7 @@ static pj_status_t match_offer(pj_pool_t *pool,
PJ_TRUE, &pt_amr_need_adapt))
continue;
}
+
found_matching_codec = 1;
} else {
found_matching_telephone_event = 1;
@@ -1371,7 +1455,7 @@ static pj_status_t create_answer( pj_pool_t *pool,
* ignore anything in the media once it sees that the port
* number is zero.
*/
- am = pjmedia_sdp_media_clone_deactivate(pool, om);
+ am = sdp_media_clone_deactivate(pool, om, om, answer);
} else {
/* The answer is in am */
pj_assert(am != NULL);
@@ -1473,3 +1557,134 @@ PJ_DEF(pj_status_t) pjmedia_sdp_neg_negotiate( pj_pool_t *pool,
return status;
}
+
+static pj_status_t custom_fmt_match(pj_pool_t *pool,
+ const pj_str_t *fmt_name,
+ pjmedia_sdp_media *offer,
+ unsigned o_fmt_idx,
+ pjmedia_sdp_media *answer,
+ unsigned a_fmt_idx,
+ unsigned option)
+{
+ unsigned i;
+
+ for (i = 0; i < fmt_match_cb_cnt; ++i) {
+ if (pj_stricmp(fmt_name, &fmt_match_cb[i].fmt_name) == 0) {
+ pj_assert(fmt_match_cb[i].cb);
+ return (*fmt_match_cb[i].cb)(pool, offer, o_fmt_idx,
+ answer, a_fmt_idx,
+ option);
+ }
+ }
+
+ /* Not customized format matching found, should be matched */
+ return PJ_SUCCESS;
+}
+
+/* Register customized SDP format negotiation callback function. */
+PJ_DECL(pj_status_t) pjmedia_sdp_neg_register_fmt_match_cb(
+ const pj_str_t *fmt_name,
+ pjmedia_sdp_neg_fmt_match_cb cb)
+{
+ struct fmt_match_cb_t *f = NULL;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(fmt_name, PJ_EINVAL);
+
+ /* Check if the callback for the format name has been registered */
+ for (i = 0; i < fmt_match_cb_cnt; ++i) {
+ if (pj_stricmp(fmt_name, &fmt_match_cb[i].fmt_name) == 0)
+ break;
+ }
+
+ /* Unregistration */
+
+ if (cb == NULL) {
+ if (i == fmt_match_cb_cnt)
+ return PJ_ENOTFOUND;
+
+ pj_array_erase(fmt_match_cb, sizeof(fmt_match_cb[0]),
+ fmt_match_cb_cnt, i);
+ fmt_match_cb_cnt--;
+
+ return PJ_SUCCESS;
+ }
+
+ /* Registration */
+
+ if (i < fmt_match_cb_cnt) {
+ /* The same format name has been registered before */
+ if (cb != fmt_match_cb[i].cb)
+ return PJ_EEXISTS;
+ else
+ return PJ_SUCCESS;
+ }
+
+ if (fmt_match_cb_cnt >= PJ_ARRAY_SIZE(fmt_match_cb))
+ return PJ_ETOOMANY;
+
+ f = &fmt_match_cb[fmt_match_cb_cnt++];
+ f->fmt_name = *fmt_name;
+ f->cb = cb;
+
+ return PJ_SUCCESS;
+}
+
+
+/* Match format in the SDP media offer and answer. */
+PJ_DEF(pj_bool_t) pjmedia_sdp_neg_fmt_match( pj_pool_t *pool,
+ pjmedia_sdp_media *offer,
+ unsigned o_fmt_idx,
+ pjmedia_sdp_media *answer,
+ unsigned a_fmt_idx,
+ unsigned option)
+{
+ const pjmedia_sdp_attr *attr;
+ pjmedia_sdp_rtpmap o_rtpmap, a_rtpmap;
+
+ /* Get the format rtpmap from the offer. */
+ attr = pjmedia_sdp_media_find_attr2(offer, "rtpmap",
+ &offer->desc.fmt[o_fmt_idx]);
+ if (!attr) {
+ pj_assert(!"Bug! Offer haven't been validated");
+ return PJ_EBUG;
+ }
+ pjmedia_sdp_attr_get_rtpmap(attr, &o_rtpmap);
+
+ /* Get the format rtpmap from the answer. */
+ attr = pjmedia_sdp_media_find_attr2(answer, "rtpmap",
+ &answer->desc.fmt[a_fmt_idx]);
+ if (!attr) {
+ pj_assert(!"Bug! Answer haven't been validated");
+ return PJ_EBUG;
+ }
+ pjmedia_sdp_attr_get_rtpmap(attr, &a_rtpmap);
+
+ if (pj_stricmp(&o_rtpmap.enc_name, &a_rtpmap.enc_name) != 0 ||
+ o_rtpmap.clock_rate != a_rtpmap.clock_rate)
+ {
+ return PJMEDIA_SDP_EFORMATNOTEQUAL;
+ }
+
+ /* Further check for G7221, negotiate bitrate. */
+ if (pj_stricmp2(&o_rtpmap.enc_name, "G7221") == 0) {
+ if (match_g7221(offer, o_fmt_idx, answer, a_fmt_idx))
+ return PJ_SUCCESS;
+ else
+ return PJMEDIA_SDP_EFORMATNOTEQUAL;
+ } else
+ /* Further check for AMR, negotiate fmtp. */
+ if (pj_stricmp2(&o_rtpmap.enc_name, "AMR") == 0 ||
+ pj_stricmp2(&o_rtpmap.enc_name, "AMR-WB") == 0)
+ {
+ if (match_amr(offer, o_fmt_idx, answer, a_fmt_idx, PJ_FALSE, NULL))
+ return PJ_SUCCESS;
+ else
+ return PJMEDIA_SDP_EFORMATNOTEQUAL;
+ }
+ PJ_TODO(replace_hardcoded_fmt_match_in_sdp_neg_with_custom_fmt_match_cb);
+
+ return custom_fmt_match(pool, &o_rtpmap.enc_name,
+ offer, o_fmt_idx, answer, a_fmt_idx, option);
+}
+
diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c
index 34dc19fc..48b42af9 100644
--- a/pjmedia/src/pjmedia/session.c
+++ b/pjmedia/src/pjmedia/session.c
@@ -140,7 +140,7 @@ static void parse_fmtp( pj_pool_t *pool,
}
}
-
+#if 0 // Moved to stream.c
/*
* Create stream info from SDP media line.
*/
@@ -604,7 +604,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
return PJ_SUCCESS;
}
-
+#endif
/*
* Initialize session info from SDP session descriptors.
diff --git a/pjmedia/src/pjmedia/silencedet.c b/pjmedia/src/pjmedia/silencedet.c
index b351c3eb..e0342241 100644
--- a/pjmedia/src/pjmedia/silencedet.c
+++ b/pjmedia/src/pjmedia/silencedet.c
@@ -23,6 +23,7 @@
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/pool.h>
+#include <pj/string.h>
#define THIS_FILE "silencedet.c"
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index 5924720d..e377a1ed 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -44,6 +44,9 @@ struct pjmedia_snd_port
pjmedia_dir dir;
pjmedia_port *port;
+ pjmedia_clock_src cap_clocksrc,
+ play_clocksrc;
+
unsigned clock_rate;
unsigned channel_count;
unsigned samples_per_frame;
@@ -70,6 +73,8 @@ static pj_status_t play_cb(void *user_data, pjmedia_frame *frame)
const unsigned required_size = frame->size;
pj_status_t status;
+ pjmedia_clock_src_update(&snd_port->play_clocksrc, &frame->timestamp);
+
port = snd_port->port;
if (port == NULL)
goto no_frame;
@@ -127,6 +132,8 @@ static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame)
pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
pjmedia_port *port;
+ pjmedia_clock_src_update(&snd_port->cap_clocksrc, &frame->timestamp);
+
port = snd_port->port;
if (port == NULL)
return PJ_SUCCESS;
@@ -138,6 +145,7 @@ static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame)
pjmedia_port_put_frame(port, frame);
+
return PJ_SUCCESS;
}
@@ -423,6 +431,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
{
pjmedia_snd_port *snd_port;
pj_status_t status;
+ unsigned ptime_usec;
PJ_ASSERT_RETURN(pool && prm && p_port, PJ_EINVAL);
@@ -439,6 +448,13 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
snd_port->bits_per_sample = prm->base.bits_per_sample;
pj_memcpy(&snd_port->aud_param, prm, sizeof(snd_port->aud_param));
snd_port->options = prm->options;
+
+ ptime_usec = prm->base.samples_per_frame * 1000 / prm->base.channel_count /
+ prm->base.clock_rate * 1000;
+ pjmedia_clock_src_init(&snd_port->cap_clocksrc, PJMEDIA_TYPE_AUDIO,
+ snd_port->clock_rate, ptime_usec);
+ pjmedia_clock_src_init(&snd_port->play_clocksrc, PJMEDIA_TYPE_AUDIO,
+ snd_port->clock_rate, ptime_usec);
/* Start sound device immediately.
* If there's no port connected, the sound callback will return
@@ -639,29 +655,42 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_get_ec_tail( pjmedia_snd_port *snd_port,
/*
+ * Get clock source.
+ */
+PJ_DEF(pjmedia_clock_src *)
+pjmedia_snd_port_get_clock_src( pjmedia_snd_port *snd_port,
+ pjmedia_dir dir )
+{
+ return (dir == PJMEDIA_DIR_CAPTURE? &snd_port->cap_clocksrc:
+ &snd_port->play_clocksrc);
+}
+
+
+/*
* Connect a port.
*/
PJ_DEF(pj_status_t) pjmedia_snd_port_connect( pjmedia_snd_port *snd_port,
pjmedia_port *port)
{
- pjmedia_port_info *pinfo;
+ pjmedia_audio_format_detail *afd;
PJ_ASSERT_RETURN(snd_port && port, PJ_EINVAL);
+ afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, PJ_TRUE);
+
/* Check that port has the same configuration as the sound device
* port.
*/
- pinfo = &port->info;
- if (pinfo->clock_rate != snd_port->clock_rate)
+ if (afd->clock_rate != snd_port->clock_rate)
return PJMEDIA_ENCCLOCKRATE;
- if (pinfo->samples_per_frame != snd_port->samples_per_frame)
+ if (PJMEDIA_AFD_SPF(afd) != snd_port->samples_per_frame)
return PJMEDIA_ENCSAMPLESPFRAME;
- if (pinfo->channel_count != snd_port->channel_count)
+ if (afd->channel_count != snd_port->channel_count)
return PJMEDIA_ENCCHANNEL;
- if (pinfo->bits_per_sample != snd_port->bits_per_sample)
+ if (afd->bits_per_sample != snd_port->bits_per_sample)
return PJMEDIA_ENCBITS;
/* Port is okay. */
diff --git a/pjmedia/src/pjmedia/splitcomb.c b/pjmedia/src/pjmedia/splitcomb.c
index 570cda3a..2f4baf11 100644
--- a/pjmedia/src/pjmedia/splitcomb.c
+++ b/pjmedia/src/pjmedia/splitcomb.c
@@ -25,8 +25,8 @@
#include <pj/pool.h>
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('S', 'p', 'C', 'b')
-#define SIGNATURE_PORT PJMEDIA_PORT_SIGNATURE('S', 'p', 'C', 'P')
+#define SIGNATURE PJMEDIA_SIG_PORT_SPLIT_COMB
+#define SIGNATURE_PORT PJMEDIA_SIG_PORT_SPLIT_COMB_P
#define THIS_FILE "splitcomb.c"
#define TMP_SAMP_TYPE pj_int16_t
@@ -189,13 +189,13 @@ struct reverse_port
* Prototypes.
*/
static pj_status_t put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame);
+ pjmedia_frame *frame);
static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_frame *frame);
static pj_status_t on_destroy(pjmedia_port *this_port);
static pj_status_t rport_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame);
+ pjmedia_frame *frame);
static pj_status_t rport_get_frame(pjmedia_port *this_port,
pjmedia_frame *frame);
static pj_status_t rport_on_destroy(pjmedia_port *this_port);
@@ -284,7 +284,7 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_set_channel( pjmedia_port *splitcomb,
PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL);
/* Check the channel number */
- PJ_ASSERT_RETURN(ch_num < sc->base.info.channel_count, PJ_EINVAL);
+ PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL);
/* options is unused for now */
PJ_UNUSED_ARG(options);
@@ -308,7 +308,8 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool,
const pj_str_t name = pj_str("scomb-rev");
struct splitcomb *sc = (struct splitcomb*) splitcomb;
struct reverse_port *rport;
- unsigned buf_cnt, ptime;
+ unsigned buf_cnt;
+ const pjmedia_audio_format_detail *sc_afd, *p_afd;
pjmedia_port *port;
pj_status_t status;
@@ -319,11 +320,13 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool,
PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL);
/* Check the channel number */
- PJ_ASSERT_RETURN(ch_num < sc->base.info.channel_count, PJ_EINVAL);
+ PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL);
/* options is unused for now */
PJ_UNUSED_ARG(options);
+ sc_afd = pjmedia_format_get_audio_format_detail(&splitcomb->info.fmt, 1);
+
/* Create the port */
rport = PJ_POOL_ZALLOC_T(pool, struct reverse_port);
rport->parent = sc;
@@ -332,10 +335,12 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool,
/* Initialize port info... */
port = &rport->base;
pjmedia_port_info_init(&port->info, &name, SIGNATURE_PORT,
- splitcomb->info.clock_rate, 1,
- splitcomb->info.bits_per_sample,
- splitcomb->info.samples_per_frame /
- splitcomb->info.channel_count);
+ sc_afd->clock_rate, 1,
+ sc_afd->bits_per_sample,
+ PJMEDIA_PIA_SPF(&splitcomb->info) /
+ sc_afd->channel_count);
+
+ p_afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, 1);
/* ... and the callbacks */
port->put_frame = &rport_put_frame;
@@ -347,30 +352,27 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool,
if (buf_cnt == 0)
buf_cnt = MAX_BUF_CNT;
- ptime = port->info.samples_per_frame * 1000 / port->info.clock_rate /
- port->info.channel_count;
-
rport->max_burst = MAX_BURST;
rport->max_null_frames = MAX_NULL_FRAMES;
/* Create downstream/put buffers */
status = pjmedia_delay_buf_create(pool, "scombdb-dn",
- port->info.clock_rate,
- port->info.samples_per_frame,
- port->info.channel_count,
- buf_cnt * ptime, 0,
- &rport->buf[DIR_DOWNSTREAM].dbuf);
+ p_afd->clock_rate,
+ PJMEDIA_PIA_SPF(&port->info),
+ p_afd->channel_count,
+ buf_cnt * p_afd->frame_time_usec / 1000,
+ 0, &rport->buf[DIR_DOWNSTREAM].dbuf);
if (status != PJ_SUCCESS) {
return status;
}
/* Create upstream/get buffers */
status = pjmedia_delay_buf_create(pool, "scombdb-up",
- port->info.clock_rate,
- port->info.samples_per_frame,
- port->info.channel_count,
- buf_cnt * ptime, 0,
- &rport->buf[DIR_UPSTREAM].dbuf);
+ p_afd->clock_rate,
+ PJMEDIA_PIA_SPF(&port->info),
+ p_afd->channel_count,
+ buf_cnt * p_afd->frame_time_usec / 1000,
+ 0, &rport->buf[DIR_UPSTREAM].dbuf);
if (status != PJ_SUCCESS) {
pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf);
return status;
@@ -378,7 +380,8 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool,
/* And temporary upstream/get buffer */
rport->tmp_up_buf = (pj_int16_t*)
- pj_pool_alloc(pool, port->info.bytes_per_frame);
+ pj_pool_alloc(pool,
+ PJMEDIA_PIA_AVG_FSZ(&port->info));
/* Save port in the splitcomb */
sc->port_desc[ch_num].port = &rport->base;
@@ -436,7 +439,7 @@ static void op_update(struct reverse_port *rport, int dir, int op)
rport->buf[dir].level += op;
if (op == OP_PUT) {
- rport->buf[dir].ts.u64 += rport->base.info.samples_per_frame;
+ rport->buf[dir].ts.u64 += PJMEDIA_PIA_SPF(&rport->base.info);
}
if (rport->buf[dir].paused) {
@@ -484,14 +487,14 @@ static void op_update(struct reverse_port *rport, int dir, int op)
* it to the appropriate port.
*/
static pj_status_t put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
struct splitcomb *sc = (struct splitcomb*) this_port;
unsigned ch;
/* Handle null frame */
if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
- for (ch=0; ch < this_port->info.channel_count; ++ch) {
+ for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
pjmedia_port *port = sc->port_desc[ch].port;
if (!port) continue;
@@ -533,7 +536,7 @@ static pj_status_t put_frame(pjmedia_port *this_port,
/* Generate zero frame. */
pjmedia_zero_samples(sc->put_buf,
- port->info.samples_per_frame);
+ PJMEDIA_PIA_SPF(&this_port->info));
/* Put frame to delay buffer */
pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf,
@@ -547,13 +550,13 @@ static pj_status_t put_frame(pjmedia_port *this_port,
/* Not sure how we would handle partial frame, so better reject
* it for now.
*/
- PJ_ASSERT_RETURN(frame->size == this_port->info.bytes_per_frame,
+ PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
PJ_EINVAL);
/*
* Write mono frame into each channels
*/
- for (ch=0; ch < this_port->info.channel_count; ++ch) {
+ for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
pjmedia_port *port = sc->port_desc[ch].port;
if (!port)
@@ -561,17 +564,17 @@ static pj_status_t put_frame(pjmedia_port *this_port,
/* Extract the mono frame to temporary buffer */
extract_mono_frame((const pj_int16_t*)frame->buf, sc->put_buf, ch,
- this_port->info.channel_count,
+ PJMEDIA_PIA_CCNT(&this_port->info),
frame->size * 8 /
- this_port->info.bits_per_sample /
- this_port->info.channel_count);
+ PJMEDIA_PIA_BITS(&this_port->info) /
+ PJMEDIA_PIA_CCNT(&this_port->info));
if (!sc->port_desc[ch].reversed) {
/* Write to normal port */
pjmedia_frame mono_frame;
mono_frame.buf = sc->put_buf;
- mono_frame.size = frame->size / this_port->info.channel_count;
+ mono_frame.size = frame->size / PJMEDIA_PIA_CCNT(&this_port->info);
mono_frame.type = frame->type;
mono_frame.timestamp.u64 = frame->timestamp.u64;
@@ -612,20 +615,20 @@ static pj_status_t get_frame(pjmedia_port *this_port,
pj_bool_t has_frame = PJ_FALSE;
/* Read frame from each port */
- for (ch=0; ch < this_port->info.channel_count; ++ch) {
+ for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
pjmedia_port *port = sc->port_desc[ch].port;
pjmedia_frame mono_frame;
pj_status_t status;
if (!port) {
pjmedia_zero_samples(sc->get_buf,
- this_port->info.samples_per_frame /
- this_port->info.channel_count);
+ PJMEDIA_PIA_SPF(&this_port->info) /
+ PJMEDIA_PIA_CCNT(&this_port->info));
} else if (sc->port_desc[ch].reversed == PJ_FALSE) {
/* Read from normal port */
mono_frame.buf = sc->get_buf;
- mono_frame.size = port->info.bytes_per_frame;
+ mono_frame.size = PJMEDIA_PIA_AVG_FSZ(&port->info);
mono_frame.timestamp.u64 = frame->timestamp.u64;
status = pjmedia_port_get_frame(port, &mono_frame);
@@ -633,7 +636,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
mono_frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
{
pjmedia_zero_samples(sc->get_buf,
- port->info.samples_per_frame);
+ PJMEDIA_PIA_SPF(&port->info));
}
frame->timestamp.u64 = mono_frame.timestamp.u64;
@@ -651,7 +654,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
} else {
pjmedia_zero_samples(sc->get_buf,
- port->info.samples_per_frame);
+ PJMEDIA_PIA_SPF(&port->info));
}
frame->timestamp.u64 = rport->buf[DIR_UPSTREAM].ts.u64;
@@ -660,9 +663,9 @@ static pj_status_t get_frame(pjmedia_port *this_port,
/* Combine the mono frame into multichannel frame */
store_mono_frame(sc->get_buf,
(pj_int16_t*)frame->buf, ch,
- this_port->info.channel_count,
- this_port->info.samples_per_frame /
- this_port->info.channel_count);
+ PJMEDIA_PIA_CCNT(&this_port->info),
+ PJMEDIA_PIA_SPF(&this_port->info) /
+ PJMEDIA_PIA_CCNT(&this_port->info));
has_frame = PJ_TRUE;
}
@@ -670,7 +673,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
/* Return NO_FRAME is we don't get any frames from downstream ports */
if (has_frame) {
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
- frame->size = this_port->info.bytes_per_frame;
+ frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
} else
frame->type = PJMEDIA_FRAME_TYPE_NONE;
@@ -694,11 +697,11 @@ static pj_status_t on_destroy(pjmedia_port *this_port)
* will be picked up by get_frame() above.
*/
static pj_status_t rport_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
struct reverse_port *rport = (struct reverse_port*) this_port;
- pj_assert(frame->size <= rport->base.info.bytes_per_frame);
+ pj_assert(frame->size <= PJMEDIA_PIA_AVG_FSZ(&rport->base.info));
/* Handle NULL frame */
if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) {
@@ -730,7 +733,7 @@ static pj_status_t rport_put_frame(pjmedia_port *this_port,
/* Generate zero frame. */
pjmedia_zero_samples(rport->tmp_up_buf,
- this_port->info.samples_per_frame);
+ PJMEDIA_PIA_SPF(&this_port->info));
/* Put frame to delay buffer */
return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf,
@@ -738,7 +741,7 @@ static pj_status_t rport_put_frame(pjmedia_port *this_port,
}
/* Not sure how to handle partial frame, so better reject for now */
- PJ_ASSERT_RETURN(frame->size == this_port->info.bytes_per_frame,
+ PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
PJ_EINVAL);
/* Reset NULL frame counter */
@@ -755,7 +758,7 @@ static pj_status_t rport_put_frame(pjmedia_port *this_port,
* modifies the frame content.
*/
pjmedia_copy_samples(rport->tmp_up_buf, (const pj_int16_t*)frame->buf,
- this_port->info.samples_per_frame);
+ PJMEDIA_PIA_SPF(&this_port->info));
/* Put frame to delay buffer */
return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf,
@@ -783,7 +786,7 @@ static pj_status_t rport_get_frame(pjmedia_port *this_port,
}
/* Get frame from delay buffer */
- frame->size = this_port->info.bytes_per_frame;
+ frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
frame->timestamp.u64 = rport->buf[DIR_DOWNSTREAM].ts.u64;
diff --git a/pjmedia/src/pjmedia/stereo_port.c b/pjmedia/src/pjmedia/stereo_port.c
index 2aad1690..1b4a49df 100644
--- a/pjmedia/src/pjmedia/stereo_port.c
+++ b/pjmedia/src/pjmedia/stereo_port.c
@@ -24,7 +24,7 @@
#include <pj/string.h>
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('S','T','R','O')
+#define SIGNATURE PJMEDIA_SIG_PORT_STEREO
struct stereo_port
@@ -39,7 +39,7 @@ struct stereo_port
static pj_status_t stereo_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame);
+ pjmedia_frame *frame);
static pj_status_t stereo_get_frame(pjmedia_port *this_port,
pjmedia_frame *frame);
static pj_status_t stereo_destroy(pjmedia_port *this_port);
@@ -60,24 +60,27 @@ PJ_DEF(pj_status_t) pjmedia_stereo_port_create( pj_pool_t *pool,
PJ_ASSERT_RETURN(pool && dn_port && channel_count && p_port, PJ_EINVAL);
/* Only supports 16bit samples per frame */
- PJ_ASSERT_RETURN(dn_port->info.bits_per_sample == 16, PJMEDIA_ENCBITS);
+ PJ_ASSERT_RETURN(PJMEDIA_PIA_BITS(&dn_port->info) == 16,
+ PJMEDIA_ENCBITS);
/* Validate channel counts */
- PJ_ASSERT_RETURN(((dn_port->info.channel_count>1 && channel_count==1) ||
- (dn_port->info.channel_count==1 && channel_count>1)),
+ PJ_ASSERT_RETURN(((PJMEDIA_PIA_CCNT(&dn_port->info)>1 &&
+ channel_count==1) ||
+ (PJMEDIA_PIA_CCNT(&dn_port->info)==1 &&
+ channel_count>1)),
PJ_EINVAL);
/* Create and initialize port. */
sport = PJ_POOL_ZALLOC_T(pool, struct stereo_port);
PJ_ASSERT_RETURN(sport != NULL, PJ_ENOMEM);
- samples_per_frame = dn_port->info.samples_per_frame * channel_count /
- dn_port->info.channel_count;
+ samples_per_frame = PJMEDIA_PIA_SPF(&dn_port->info) * channel_count /
+ PJMEDIA_PIA_CCNT(&dn_port->info);
pjmedia_port_info_init(&sport->base.info, &name, SIGNATURE,
- dn_port->info.clock_rate,
+ PJMEDIA_PIA_SRATE(&dn_port->info),
channel_count,
- dn_port->info.bits_per_sample,
+ PJMEDIA_PIA_BITS(&dn_port->info),
samples_per_frame);
sport->dn_port = dn_port;
@@ -85,12 +88,14 @@ PJ_DEF(pj_status_t) pjmedia_stereo_port_create( pj_pool_t *pool,
/* We always need buffer for put_frame */
sport->put_buf = (pj_int16_t*)
- pj_pool_alloc(pool, dn_port->info.bytes_per_frame);
+ pj_pool_alloc(pool,
+ PJMEDIA_PIA_AVG_FSZ(&dn_port->info));
/* See if we need buffer for get_frame */
- if (dn_port->info.channel_count > channel_count) {
+ if (PJMEDIA_PIA_CCNT(&dn_port->info) > channel_count) {
sport->get_buf = (pj_int16_t*)
- pj_pool_alloc(pool, dn_port->info.bytes_per_frame);
+ pj_pool_alloc(pool,
+ PJMEDIA_PIA_AVG_FSZ(&dn_port->info));
}
/* Media port interface */
@@ -106,9 +111,10 @@ PJ_DEF(pj_status_t) pjmedia_stereo_port_create( pj_pool_t *pool,
}
static pj_status_t stereo_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
struct stereo_port *sport = (struct stereo_port*) this_port;
+ const pjmedia_audio_format_detail *s_afd, *dn_afd;
pjmedia_frame tmp_frame;
/* Return if we don't have downstream port. */
@@ -116,23 +122,27 @@ static pj_status_t stereo_put_frame(pjmedia_port *this_port,
return PJ_SUCCESS;
}
+ s_afd = pjmedia_format_get_audio_format_detail(&this_port->info.fmt, 1);
+ dn_afd = pjmedia_format_get_audio_format_detail(&sport->dn_port->info.fmt,
+ 1);
+
if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
tmp_frame.buf = sport->put_buf;
- if (sport->dn_port->info.channel_count == 1) {
+ if (dn_afd->channel_count == 1) {
pjmedia_convert_channel_nto1((pj_int16_t*)tmp_frame.buf,
(const pj_int16_t*)frame->buf,
- sport->base.info.channel_count,
- sport->base.info.samples_per_frame,
+ s_afd->channel_count,
+ PJMEDIA_AFD_SPF(s_afd),
(sport->options & PJMEDIA_STEREO_MIX),
0);
} else {
pjmedia_convert_channel_1ton((pj_int16_t*)tmp_frame.buf,
(const pj_int16_t*)frame->buf,
- sport->dn_port->info.channel_count,
- sport->base.info.samples_per_frame,
+ dn_afd->channel_count,
+ PJMEDIA_AFD_SPF(s_afd),
sport->options);
}
- tmp_frame.size = sport->dn_port->info.bytes_per_frame;
+ tmp_frame.size = PJMEDIA_AFD_AVG_FSZ(dn_afd);
} else {
tmp_frame.buf = frame->buf;
tmp_frame.size = frame->size;
@@ -150,6 +160,7 @@ static pj_status_t stereo_get_frame(pjmedia_port *this_port,
pjmedia_frame *frame)
{
struct stereo_port *sport = (struct stereo_port*) this_port;
+ const pjmedia_audio_format_detail *s_afd, *dn_afd;
pjmedia_frame tmp_frame;
pj_status_t status;
@@ -159,8 +170,12 @@ static pj_status_t stereo_get_frame(pjmedia_port *this_port,
return PJ_SUCCESS;
}
+ s_afd = pjmedia_format_get_audio_format_detail(&this_port->info.fmt, 1);
+ dn_afd = pjmedia_format_get_audio_format_detail(&sport->dn_port->info.fmt,
+ 1);
+
tmp_frame.buf = sport->get_buf? sport->get_buf : frame->buf;
- tmp_frame.size = sport->dn_port->info.bytes_per_frame;
+ tmp_frame.size = PJMEDIA_PIA_AVG_FSZ(&sport->dn_port->info);
tmp_frame.timestamp.u64 = frame->timestamp.u64;
tmp_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
@@ -177,21 +192,21 @@ static pj_status_t stereo_get_frame(pjmedia_port *this_port,
return PJ_SUCCESS;
}
- if (sport->base.info.channel_count == 1) {
+ if (s_afd->channel_count == 1) {
pjmedia_convert_channel_nto1((pj_int16_t*)frame->buf,
(const pj_int16_t*)tmp_frame.buf,
- sport->dn_port->info.channel_count,
- sport->dn_port->info.samples_per_frame,
+ dn_afd->channel_count,
+ PJMEDIA_AFD_SPF(s_afd),
(sport->options & PJMEDIA_STEREO_MIX), 0);
} else {
pjmedia_convert_channel_1ton((pj_int16_t*)frame->buf,
(const pj_int16_t*)tmp_frame.buf,
- sport->base.info.channel_count,
- sport->dn_port->info.samples_per_frame,
+ s_afd->channel_count,
+ PJMEDIA_AFD_SPF(dn_afd),
sport->options);
}
- frame->size = sport->base.info.bytes_per_frame;
+ frame->size = PJMEDIA_AFD_AVG_FSZ(s_afd);
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
return PJ_SUCCESS;
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 42761c33..1fe2863d 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -22,6 +22,7 @@
#include <pjmedia/rtp.h>
#include <pjmedia/rtcp.h>
#include <pjmedia/jbuf.h>
+#include <pjmedia/stream_common.h>
#include <pj/array.h>
#include <pj/assert.h>
#include <pj/ctype.h>
@@ -64,6 +65,13 @@
# define TRACE_JB_OPENED(s) (s->trace_jb_fd != TRACE_JB_INVALID_FD)
#endif
+#ifndef PJMEDIA_STREAM_SIZE
+# define PJMEDIA_STREAM_SIZE 1000
+#endif
+
+#ifndef PJMEDIA_STREAM_INC
+# define PJMEDIA_STREAM_INC 1000
+#endif
/**
@@ -77,6 +85,7 @@ struct pjmedia_channel
pj_bool_t paused; /**< Paused?. */
unsigned out_pkt_size; /**< Size of output buffer. */
void *out_pkt; /**< Output buffer. */
+ unsigned out_pkt_len; /**< Length of data in buffer. */
pjmedia_rtp_session rtp; /**< RTP session. */
};
@@ -98,11 +107,13 @@ struct pjmedia_stream
{
pjmedia_endpt *endpt; /**< Media endpoint. */
pjmedia_codec_mgr *codec_mgr; /**< Codec manager instance. */
-
+ pjmedia_stream_info si; /**< Creation parameter. */
pjmedia_port port; /**< Port interface. */
pjmedia_channel *enc; /**< Encoding channel. */
pjmedia_channel *dec; /**< Decoding channel. */
+ pj_pool_t *own_pool; /**< Only created if not given */
+
pjmedia_dir dir; /**< Stream direction. */
void *user_data; /**< User data. */
pj_str_t cname; /**< SDES CNAME */
@@ -177,8 +188,6 @@ struct pjmedia_stream
/**< Normalized ts length per frame
received according to
'erroneous' definition */
- pj_uint32_t rtp_rx_last_ts;/**< Last received RTP timestamp
- for timestamp checking */
unsigned rtp_rx_last_cnt;/**< Nb of frames in last pkt */
unsigned rtp_rx_check_cnt;
/**< Counter of remote timestamp
@@ -208,6 +217,8 @@ struct pjmedia_stream
pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/
char *trace_jb_buf; /**< Jitter tracing buffer. */
#endif
+
+ pj_uint32_t rtp_rx_last_ts; /**< Last received RTP timestamp*/
};
@@ -471,7 +482,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame)
/* Lock jitter buffer mutex first */
pj_mutex_lock( stream->jb_mutex );
- samples_required = stream->port.info.samples_per_frame;
+ samples_required = PJMEDIA_PIA_SPF(&stream->port.info);
samples_per_frame = stream->codec_param.info.frm_ptime *
stream->codec_param.info.clock_rate *
stream->codec_param.info.channel_cnt /
@@ -504,9 +515,9 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame)
frame_out.buf = p_out_samp + samples_count;
frame_out.size = frame->size - samples_count*2;
- status = (*stream->codec->op->recover)(stream->codec,
- frame_out.size,
- &frame_out);
+ status = pjmedia_codec_recover(stream->codec,
+ frame_out.size,
+ &frame_out);
++stream->plc_cnt;
@@ -553,9 +564,9 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame)
do {
frame_out.buf = p_out_samp + samples_count;
frame_out.size = frame->size - samples_count*2;
- status = (*stream->codec->op->recover)(stream->codec,
- frame_out.size,
- &frame_out);
+ status = pjmedia_codec_recover(stream->codec,
+ frame_out.size,
+ &frame_out);
if (status != PJ_SUCCESS)
break;
@@ -608,9 +619,9 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame)
do {
frame_out.buf = p_out_samp + samples_count;
frame_out.size = frame->size - samples_count*2;
- status = (*stream->codec->op->recover)(stream->codec,
- frame_out.size,
- &frame_out);
+ status = pjmedia_codec_recover(stream->codec,
+ frame_out.size,
+ &frame_out);
if (status != PJ_SUCCESS)
break;
samples_count += samples_per_frame;
@@ -659,8 +670,8 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame)
frame_out.buf = p_out_samp + samples_count;
frame_out.size = frame->size - samples_count*BYTES_PER_SAMPLE;
- status = stream->codec->op->decode( stream->codec, &frame_in,
- frame_out.size, &frame_out);
+ status = pjmedia_codec_decode( stream->codec, &frame_in,
+ frame_out.size, &frame_out);
if (status != 0) {
LOGERR_((port->info.name.ptr, "codec decode() error",
status));
@@ -725,7 +736,7 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame)
* until we have enough frames according to codec's ptime.
*/
- samples_required = stream->port.info.samples_per_frame;
+ samples_required = PJMEDIA_PIA_SPF(&stream->port.info);
samples_per_frame = stream->codec_param.info.frm_ptime *
stream->codec_param.info.clock_rate *
stream->codec_param.info.channel_cnt /
@@ -763,8 +774,8 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame)
frame_in.bit_info = bit_info;
frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO;
- status = stream->codec->op->decode( stream->codec, &frame_in,
- 0, frame);
+ status = pjmedia_codec_decode( stream->codec, &frame_in,
+ 0, frame);
if (status != PJ_SUCCESS) {
LOGERR_((port->info.name.ptr, "codec decode() error",
status));
@@ -790,8 +801,7 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame)
/* Try to generate frame by invoking PLC (when any) */
status = PJ_SUCCESS;
if (stream->codec->op->recover) {
- status = (*stream->codec->op->recover)(stream->codec,
- 0, frame);
+ status = pjmedia_codec_recover(stream->codec, 0, frame);
}
/* No PLC or PLC failed */
@@ -876,7 +886,7 @@ static void create_dtmf_payload(pjmedia_stream *stream,
*first = 1;
}
- digit->duration += stream->port.info.samples_per_frame;
+ digit->duration += PJMEDIA_PIA_SPF(&stream->port.info);
event->event = (pj_uint8_t)digit->event;
event->e_vol = 10;
@@ -1087,7 +1097,7 @@ static void rebuffer(pjmedia_stream *stream,
/* How many samples are needed */
count = stream->codec_param.info.enc_ptime *
- stream->port.info.clock_rate / 1000;
+ PJMEDIA_PIA_SRATE(&stream->port.info) / 1000;
/* See if we have enough samples */
if (stream->enc_buf_count >= count) {
@@ -1110,7 +1120,7 @@ static void rebuffer(pjmedia_stream *stream,
* put_frame_imp()
*/
static pj_status_t put_frame_imp( pjmedia_port *port,
- const pjmedia_frame *frame )
+ pjmedia_frame *frame )
{
pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
pjmedia_channel *channel = stream->enc;
@@ -1151,8 +1161,8 @@ static pj_status_t put_frame_imp( pjmedia_port *port,
if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO)
ts_len = (frame->size >> 1) / stream->codec_param.info.channel_cnt;
else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED)
- ts_len = stream->port.info.samples_per_frame /
- stream->port.info.channel_count;
+ ts_len = PJMEDIA_PIA_SPF(&stream->port.info) /
+ PJMEDIA_PIA_CCNT(&stream->port.info);
else
ts_len = 0;
@@ -1219,7 +1229,7 @@ static pj_status_t put_frame_imp( pjmedia_port *port,
*/
} else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO &&
frame->buf == NULL &&
- stream->port.info.format.id == PJMEDIA_FORMAT_L16 &&
+ stream->port.info.fmt.id == PJMEDIA_FORMAT_L16 &&
(stream->dir & PJMEDIA_DIR_ENCODING) &&
stream->codec_param.info.frm_ptime *
stream->codec_param.info.channel_cnt *
@@ -1237,10 +1247,10 @@ static pj_status_t put_frame_imp( pjmedia_port *port,
silence_frame.timestamp.u32.lo = pj_ntohl(stream->enc->rtp.out_hdr.ts);
/* Encode! */
- status = stream->codec->op->encode( stream->codec, &silence_frame,
- channel->out_pkt_size -
- sizeof(pjmedia_rtp_hdr),
- &frame_out);
+ status = pjmedia_codec_encode( stream->codec, &silence_frame,
+ channel->out_pkt_size -
+ sizeof(pjmedia_rtp_hdr),
+ &frame_out);
if (status != PJ_SUCCESS) {
LOGERR_((stream->port.info.name.ptr,
"Codec encode() error", status));
@@ -1261,10 +1271,10 @@ static pj_status_t put_frame_imp( pjmedia_port *port,
(frame->type == PJMEDIA_FRAME_TYPE_EXTENDED))
{
/* Encode! */
- status = stream->codec->op->encode( stream->codec, frame,
- channel->out_pkt_size -
- sizeof(pjmedia_rtp_hdr),
- &frame_out);
+ status = pjmedia_codec_encode( stream->codec, frame,
+ channel->out_pkt_size -
+ sizeof(pjmedia_rtp_hdr),
+ &frame_out);
if (status != PJ_SUCCESS) {
LOGERR_((stream->port.info.name.ptr,
"Codec encode() error", status));
@@ -1362,7 +1372,7 @@ static pj_status_t put_frame_imp( pjmedia_port *port,
* RTP packet, and transmit to peer.
*/
static pj_status_t put_frame( pjmedia_port *port,
- const pjmedia_frame *frame )
+ pjmedia_frame *frame )
{
pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
pjmedia_frame tmp_zero_frame;
@@ -1410,10 +1420,11 @@ static pj_status_t put_frame( pjmedia_port *port,
*/
if (stream->vad_enabled != stream->codec_param.setting.vad &&
(stream->tx_duration - stream->ts_vad_disabled) >
- stream->port.info.clock_rate * PJMEDIA_STREAM_VAD_SUSPEND_MSEC / 1000)
+ PJMEDIA_PIA_SRATE(&stream->port.info) *
+ PJMEDIA_STREAM_VAD_SUSPEND_MSEC / 1000)
{
stream->codec_param.setting.vad = stream->vad_enabled;
- stream->codec->op->modify(stream->codec, &stream->codec_param);
+ pjmedia_codec_modify(stream->codec, &stream->codec_param);
PJ_LOG(4,(stream->port.info.name.ptr,"VAD re-enabled"));
}
@@ -1573,7 +1584,6 @@ static void on_rx_rtp( void *data,
const void *payload;
unsigned payloadlen;
pjmedia_rtp_status seq_st;
- pj_bool_t check_pt;
pj_status_t status;
pj_bool_t pkt_discarded = PJ_FALSE;
@@ -1603,15 +1613,8 @@ static void on_rx_rtp( void *data,
/* Update RTP session (also checks if RTP session can accept
* the incoming packet.
*/
- check_pt = (hdr->pt != stream->rx_event_pt) && PJMEDIA_STREAM_CHECK_RTP_PT;
- pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, check_pt);
-#if !PJMEDIA_STREAM_CHECK_RTP_PT
- if (!check_pt && hdr->pt != channel->rtp.out_pt &&
- hdr->pt != stream->rx_event_pt)
- {
- seq_st.status.flag.badpt = 1;
- }
-#endif
+ pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st,
+ hdr->pt != stream->rx_event_pt);
if (seq_st.status.value) {
TRC_ ((stream->port.info.name.ptr,
"RTP status: badpt=%d, badssrc=%d, dup=%d, "
@@ -1671,7 +1674,6 @@ static void on_rx_rtp( void *data,
if (seq_st.status.flag.restart) {
status = pjmedia_jbuf_reset(stream->jb);
PJ_LOG(4,(stream->port.info.name.ptr, "Jitter buffer reset"));
-
} else {
/*
* Packets may contain more than one frames, while the jitter
@@ -1688,12 +1690,8 @@ static void on_rx_rtp( void *data,
ts.u64 = pj_ntohl(hdr->ts);
/* Parse the payload. */
- status = (*stream->codec->op->parse)(stream->codec,
- (void*)payload,
- payloadlen,
- &ts,
- &count,
- frames);
+ status = pjmedia_codec_parse(stream->codec, (void*)payload,
+ payloadlen, &ts, &count, frames);
if (status != PJ_SUCCESS) {
LOGERR_((stream->port.info.name.ptr,
"Codec parse() error",
@@ -1721,9 +1719,9 @@ static void on_rx_rtp( void *data,
unsigned frm_ts_span;
/* Calculate actual frame timestamp span */
- frm_ts_span = stream->port.info.samples_per_frame /
+ frm_ts_span = PJMEDIA_PIA_SPF(&stream->port.info) /
stream->codec_param.setting.frm_per_pkt/
- stream->port.info.channel_count;
+ PJMEDIA_PIA_CCNT(&stream->port.info);
/* Get remote frame timestamp span */
peer_frm_ts_diff =
@@ -1895,13 +1893,20 @@ static pj_status_t create_channel( pj_pool_t *pool,
/* Allocate buffer for outgoing packet. */
- channel->out_pkt_size = sizeof(pjmedia_rtp_hdr) +
- stream->codec_param.info.max_bps *
- PJMEDIA_MAX_FRAME_DURATION_MS /
- 8 / 1000;
-
- if (channel->out_pkt_size > PJMEDIA_MAX_MTU)
- channel->out_pkt_size = PJMEDIA_MAX_MTU;
+ if (param->type == PJMEDIA_TYPE_AUDIO) {
+ channel->out_pkt_size = sizeof(pjmedia_rtp_hdr) +
+ stream->codec_param.info.max_bps *
+ PJMEDIA_MAX_FRAME_DURATION_MS /
+ 8 / 1000;
+ if (channel->out_pkt_size > PJMEDIA_MAX_MTU -
+ PJMEDIA_STREAM_RESV_PAYLOAD_LEN)
+ {
+ channel->out_pkt_size = PJMEDIA_MAX_MTU -
+ PJMEDIA_STREAM_RESV_PAYLOAD_LEN;
+ }
+ } else {
+ return PJ_ENOTSUP;
+ }
/* It should big enough to hold (minimally) RTCP SR with an SDES. */
min_out_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) +
@@ -1955,16 +1960,27 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
pjmedia_stream *stream;
pj_str_t name;
unsigned jb_init, jb_max, jb_min_pre, jb_max_pre, len;
+ pjmedia_audio_format_detail *afd;
+ pj_pool_t *own_pool = NULL;
char *p;
pj_status_t status;
- PJ_ASSERT_RETURN(pool && info && p_stream, PJ_EINVAL);
+ PJ_ASSERT_RETURN(endpt && info && p_stream, PJ_EINVAL);
+ if (pool == NULL) {
+ own_pool = pjmedia_endpt_create_pool( endpt, "strm%p",
+ PJMEDIA_STREAM_SIZE,
+ PJMEDIA_STREAM_INC);
+ PJ_ASSERT_RETURN(own_pool != NULL, PJ_ENOMEM);
+ pool = own_pool;
+ }
/* Allocate the media stream: */
stream = PJ_POOL_ZALLOC_T(pool, pjmedia_stream);
PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM);
+ stream->own_pool = own_pool;
+ pj_memcpy(&stream->si, info, sizeof(*info));
/* Init stream/port name */
name.ptr = (char*) pj_pool_alloc(pool, M);
@@ -1974,15 +1990,17 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
* once we have more info about the codec.
*/
pjmedia_port_info_init(&stream->port.info, &name,
- PJMEDIA_PORT_SIGNATURE('S', 'T', 'R', 'M'),
+ PJMEDIA_SIG_PORT_STREAM,
info->fmt.clock_rate, info->fmt.channel_cnt,
16, 80);
+ afd = pjmedia_format_get_audio_format_detail(&stream->port.info.fmt, 1);
/* Init port. */
- pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name);
- stream->port.info.clock_rate = info->fmt.clock_rate;
- stream->port.info.channel_count = info->fmt.channel_cnt;
+ //No longer there in 2.0
+ //pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name);
+ afd->clock_rate = info->fmt.clock_rate;
+ afd->channel_count = info->fmt.channel_cnt;
stream->port.port_data.pdata = stream;
/* Init stream: */
@@ -2048,38 +2066,38 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
stream->codec_param.setting.frm_per_pkt = 1;
/* Open the codec. */
- status = stream->codec->op->open(stream->codec, &stream->codec_param);
+ status = pjmedia_codec_open(stream->codec, &stream->codec_param);
if (status != PJ_SUCCESS)
goto err_cleanup;
/* Set additional info and callbacks. */
- stream->port.info.bits_per_sample = 16;
- stream->port.info.samples_per_frame = info->fmt.clock_rate *
- stream->codec_param.info.channel_cnt *
- stream->codec_param.info.frm_ptime *
- stream->codec_param.setting.frm_per_pkt /
- 1000;
- stream->port.info.format.id = stream->codec_param.info.fmt_id;
+ afd->bits_per_sample = 16;
+ afd->frame_time_usec = stream->codec_param.info.frm_ptime *
+ stream->codec_param.setting.frm_per_pkt * 1000;
+ stream->port.info.fmt.id = stream->codec_param.info.fmt_id;
if (stream->codec_param.info.fmt_id == PJMEDIA_FORMAT_L16) {
/* Raw format */
- stream->port.info.bytes_per_frame = stream->port.info.samples_per_frame *
- stream->port.info.bits_per_sample / 8;
+ afd->avg_bps = afd->max_bps = afd->clock_rate *
+ afd->bits_per_sample / 8;
+
stream->port.put_frame = &put_frame;
stream->port.get_frame = &get_frame;
} else {
/* Encoded format */
- stream->port.info.bytes_per_frame = stream->codec_param.info.max_bps *
- stream->codec_param.info.frm_ptime *
- stream->codec_param.setting.frm_per_pkt /
- 8 / 1000;
- if ((stream->codec_param.info.max_bps * stream->codec_param.info.frm_ptime *
- stream->codec_param.setting.frm_per_pkt) % 8000 != 0)
+ afd->avg_bps = stream->codec_param.info.avg_bps;
+ afd->max_bps = stream->codec_param.info.max_bps;
+
+ /* Not applicable for 2.0
+ if ((stream->codec_param.info.max_bps *
+ stream->codec_param.info.frm_ptime *
+ stream->codec_param.setting.frm_per_pkt) % 8000 != 0)
{
++stream->port.info.bytes_per_frame;
}
stream->port.info.format.bitrate = stream->codec_param.info.avg_bps;
stream->port.info.format.vad = (stream->codec_param.setting.vad != 0);
+ */
stream->port.put_frame = &put_frame;
stream->port.get_frame = &get_frame_ext;
@@ -2096,14 +2114,13 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
stream->enc_samples_per_pkt = stream->codec_param.info.enc_ptime *
stream->codec_param.info.channel_cnt *
- stream->port.info.clock_rate / 1000;
+ afd->clock_rate / 1000;
/* Set buffer size as twice the largest ptime value between
* stream's ptime, encoder ptime, or decoder ptime.
*/
- ptime = stream->port.info.samples_per_frame * 1000 /
- stream->port.info.clock_rate;
+ ptime = afd->frame_time_usec / 1000;
if (stream->codec_param.info.enc_ptime > ptime)
ptime = stream->codec_param.info.enc_ptime;
@@ -2114,12 +2131,12 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
ptime <<= 1;
/* Allocate buffer */
- stream->enc_buf_size = stream->port.info.clock_rate * ptime / 1000;
+ stream->enc_buf_size = afd->clock_rate * ptime / 1000;
stream->enc_buf = (pj_int16_t*)
pj_pool_alloc(pool, stream->enc_buf_size * 2);
} else {
- stream->enc_samples_per_pkt = stream->port.info.samples_per_frame;
+ stream->enc_samples_per_pkt = PJMEDIA_AFD_SPF(afd);
}
@@ -2128,7 +2145,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
if (PJMEDIA_STREAM_VAD_SUSPEND_MSEC > 0 && stream->vad_enabled) {
stream->codec_param.setting.vad = 0;
stream->ts_vad_disabled = 0;
- stream->codec->op->modify(stream->codec, &stream->codec_param);
+ pjmedia_codec_modify(stream->codec, &stream->codec_param);
PJ_LOG(4,(stream->port.info.name.ptr,"VAD temporarily disabled"));
}
@@ -2152,7 +2169,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
stream->rtp_rx_last_cnt = 0;
stream->rtp_tx_ts_len_per_pkt = stream->enc_samples_per_pkt /
stream->codec_param.info.channel_cnt;
- stream->rtp_rx_ts_len_per_frame = stream->port.info.samples_per_frame /
+ stream->rtp_rx_ts_len_per_frame = PJMEDIA_AFD_SPF(afd) /
stream->codec_param.setting.frm_per_pkt /
stream->codec_param.info.channel_cnt;
@@ -2226,7 +2243,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
rtcp_setting.ssrc = info->ssrc;
rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts);
rtcp_setting.clock_rate = info->fmt.clock_rate;
- rtcp_setting.samples_per_frame = stream->port.info.samples_per_frame;
+ rtcp_setting.samples_per_frame = PJMEDIA_AFD_SPF(afd);
#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0)
/* Special case for G.722 */
@@ -2432,7 +2449,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream )
/* Free codec. */
if (stream->codec) {
- stream->codec->op->close(stream->codec);
+ pjmedia_codec_close(stream->codec);
pjmedia_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec);
stream->codec = NULL;
}
@@ -2455,6 +2472,11 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream )
}
#endif
+ if (stream->own_pool) {
+ pj_pool_t *pool = stream->own_pool;
+ stream->own_pool = NULL;
+ pj_pool_release(pool);
+ }
return PJ_SUCCESS;
}
@@ -2516,6 +2538,15 @@ PJ_DEF(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream)
}
+PJ_DEF(pj_status_t) pjmedia_stream_get_info( const pjmedia_stream *stream,
+ pjmedia_stream_info *info)
+{
+ PJ_ASSERT_RETURN(stream && info, PJ_EINVAL);
+
+ pj_memcpy(info, &stream->si, sizeof(pjmedia_stream_info));
+ return PJ_SUCCESS;
+}
+
/*
* Get stream statistics.
*/
@@ -2760,3 +2791,487 @@ PJ_DEF(pj_status_t) pjmedia_stream_set_dtmf_callback(pjmedia_stream *stream,
return PJ_SUCCESS;
}
+
+static const pj_str_t ID_AUDIO = { "audio", 5};
+static const pj_str_t ID_IN = { "IN", 2 };
+static const pj_str_t ID_IP4 = { "IP4", 3};
+static const pj_str_t ID_IP6 = { "IP6", 3};
+static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
+static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
+//static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
+static const pj_str_t ID_RTPMAP = { "rtpmap", 6 };
+static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 };
+
+static const pj_str_t STR_INACTIVE = { "inactive", 8 };
+static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
+static const pj_str_t STR_SENDONLY = { "sendonly", 8 };
+static const pj_str_t STR_RECVONLY = { "recvonly", 8 };
+
+
+/*
+ * Internal function for collecting codec info and param from the SDP media.
+ */
+static pj_status_t get_audio_codec_info_param(pjmedia_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_codec_mgr *mgr,
+ const pjmedia_sdp_media *local_m,
+ const pjmedia_sdp_media *rem_m)
+{
+ const pjmedia_sdp_attr *attr;
+ pjmedia_sdp_rtpmap *rtpmap;
+ unsigned i, fmti, pt = 0;
+ pj_status_t status;
+
+ /* Find the first codec which is not telephone-event */
+ for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) {
+ if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) )
+ return PJMEDIA_EINVALIDPT;
+ pt = pj_strtoul(&local_m->desc.fmt[fmti]);
+ if ( PJMEDIA_RTP_PT_TELEPHONE_EVENTS == 0 ||
+ pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS )
+ break;
+ }
+ if ( fmti >= local_m->desc.fmt_count )
+ return PJMEDIA_EINVALIDPT;
+
+ /* Get codec info.
+ * For static payload types, get the info from codec manager.
+ * For dynamic payload types, MUST get the rtpmap.
+ */
+ if (pt < 96) {
+ pj_bool_t has_rtpmap;
+
+ rtpmap = NULL;
+ has_rtpmap = PJ_TRUE;
+
+ attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,
+ &local_m->desc.fmt[fmti]);
+ if (attr == NULL) {
+ has_rtpmap = PJ_FALSE;
+ }
+ if (attr != NULL) {
+ status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
+ if (status != PJ_SUCCESS)
+ has_rtpmap = PJ_FALSE;
+ }
+
+ /* Build codec format info: */
+ if (has_rtpmap) {
+ si->fmt.type = si->type;
+ si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
+ pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
+ si->fmt.clock_rate = rtpmap->clock_rate;
+
+#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0)
+ /* The session info should have the actual clock rate, because
+ * this info is used for calculationg buffer size, etc in stream
+ */
+ if (si->fmt.pt == PJMEDIA_RTP_PT_G722)
+ si->fmt.clock_rate = 16000;
+#endif
+
+ /* For audio codecs, rtpmap parameters denotes the number of
+ * channels.
+ */
+ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
+ si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param);
+ } else {
+ si->fmt.channel_cnt = 1;
+ }
+
+ } else {
+ const pjmedia_codec_info *p_info;
+
+ status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info));
+ }
+
+ /* For static payload type, pt's are symetric */
+ si->tx_pt = pt;
+
+ } else {
+
+ attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,
+ &local_m->desc.fmt[fmti]);
+ if (attr == NULL)
+ return PJMEDIA_EMISSINGRTPMAP;
+
+ status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Build codec format info: */
+
+ si->fmt.type = si->type;
+ si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
+ pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
+ si->fmt.clock_rate = rtpmap->clock_rate;
+
+ /* For audio codecs, rtpmap parameters denotes the number of
+ * channels.
+ */
+ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
+ si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param);
+ } else {
+ si->fmt.channel_cnt = 1;
+ }
+
+ /* Determine payload type for outgoing channel, by finding
+ * dynamic payload type in remote SDP that matches the answer.
+ */
+ si->tx_pt = 0xFFFF;
+ for (i=0; i<rem_m->desc.fmt_count; ++i) {
+ unsigned rpt;
+ pjmedia_sdp_attr *r_attr;
+ pjmedia_sdp_rtpmap r_rtpmap;
+
+ rpt = pj_strtoul(&rem_m->desc.fmt[i]);
+ if (rpt < 96)
+ continue;
+
+ r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP,
+ &rem_m->desc.fmt[i]);
+ if (!r_attr)
+ continue;
+
+ if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS)
+ continue;
+
+ if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) &&
+ rtpmap->clock_rate == r_rtpmap.clock_rate)
+ {
+ /* Found matched codec. */
+ si->tx_pt = rpt;
+
+ break;
+ }
+ }
+
+ if (si->tx_pt == 0xFFFF)
+ return PJMEDIA_EMISSINGRTPMAP;
+ }
+
+
+ /* Now that we have codec info, get the codec param. */
+ si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param);
+ status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt,
+ si->param);
+
+ /* Get remote fmtp for our encoder. */
+ pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt,
+ &si->param->setting.enc_fmtp);
+
+ /* Get local fmtp for our decoder. */
+ pjmedia_stream_info_parse_fmtp(pool, local_m, si->fmt.pt,
+ &si->param->setting.dec_fmtp);
+
+ /* Get the remote ptime for our encoder. */
+ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+ "ptime", NULL);
+ if (attr) {
+ pj_str_t tmp_val = attr->value;
+ unsigned frm_per_pkt;
+
+ pj_strltrim(&tmp_val);
+
+ /* Round up ptime when the specified is not multiple of frm_ptime */
+ frm_per_pkt = (pj_strtoul(&tmp_val) +
+ si->param->info.frm_ptime/2) /
+ si->param->info.frm_ptime;
+ if (frm_per_pkt != 0) {
+ si->param->setting.frm_per_pkt = (pj_uint8_t)frm_per_pkt;
+ }
+ }
+
+ /* Get remote maxptime for our encoder. */
+ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+ "maxptime", NULL);
+ if (attr) {
+ pj_str_t tmp_val = attr->value;
+
+ pj_strltrim(&tmp_val);
+ si->tx_maxptime = pj_strtoul(&tmp_val);
+ }
+
+ /* When direction is NONE (it means SDP negotiation has failed) we don't
+ * need to return a failure here, as returning failure will cause
+ * the whole SDP to be rejected. See ticket #:
+ * http://
+ *
+ * Thanks Alain Totouom
+ */
+ if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE)
+ return status;
+
+
+ /* Get incomming payload type for telephone-events */
+ si->rx_event_pt = -1;
+ for (i=0; i<local_m->attr_count; ++i) {
+ pjmedia_sdp_rtpmap r;
+
+ attr = local_m->attr[i];
+ if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
+ continue;
+ if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
+ continue;
+ if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
+ si->rx_event_pt = pj_strtoul(&r.pt);
+ break;
+ }
+ }
+
+ /* Get outgoing payload type for telephone-events */
+ si->tx_event_pt = -1;
+ for (i=0; i<rem_m->attr_count; ++i) {
+ pjmedia_sdp_rtpmap r;
+
+ attr = rem_m->attr[i];
+ if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
+ continue;
+ if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
+ continue;
+ if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
+ si->tx_event_pt = pj_strtoul(&r.pt);
+ break;
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Create stream info from SDP media line.
+ */
+PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
+ pjmedia_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_endpt *endpt,
+ const pjmedia_sdp_session *local,
+ const pjmedia_sdp_session *remote,
+ unsigned stream_idx)
+{
+ pjmedia_codec_mgr *mgr;
+ const pjmedia_sdp_attr *attr;
+ const pjmedia_sdp_media *local_m;
+ const pjmedia_sdp_media *rem_m;
+ const pjmedia_sdp_conn *local_conn;
+ const pjmedia_sdp_conn *rem_conn;
+ int rem_af, local_af;
+ pj_sockaddr local_addr;
+ pj_status_t status;
+
+
+ /* Validate arguments: */
+ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL);
+ PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL);
+ PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL);
+
+ /* Keep SDP shortcuts */
+ local_m = local->media[stream_idx];
+ rem_m = remote->media[stream_idx];
+
+ local_conn = local_m->conn ? local_m->conn : local->conn;
+ if (local_conn == NULL)
+ return PJMEDIA_SDP_EMISSINGCONN;
+
+ rem_conn = rem_m->conn ? rem_m->conn : remote->conn;
+ if (rem_conn == NULL)
+ return PJMEDIA_SDP_EMISSINGCONN;
+
+ /* Media type must be audio */
+ if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) != 0)
+ return PJMEDIA_EINVALIMEDIATYPE;
+
+ /* Get codec manager. */
+ mgr = pjmedia_endpt_get_codec_mgr(endpt);
+
+ /* Reset: */
+
+ pj_bzero(si, sizeof(*si));
+
+#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
+ /* Set default RTCP XR enabled/disabled */
+ si->rtcp_xr_enabled = PJ_TRUE;
+#endif
+
+ /* Media type: */
+ si->type = PJMEDIA_TYPE_AUDIO;
+
+ /* Transport protocol */
+
+ /* At this point, transport type must be compatible,
+ * the transport instance will do more validation later.
+ */
+ status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport,
+ &local_m->desc.transport);
+ if (status != PJ_SUCCESS)
+ return PJMEDIA_SDPNEG_EINVANSTP;
+
+ if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) {
+
+ si->proto = PJMEDIA_TP_PROTO_RTP_AVP;
+
+ } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) {
+
+ si->proto = PJMEDIA_TP_PROTO_RTP_SAVP;
+
+ } else {
+
+ si->proto = PJMEDIA_TP_PROTO_UNKNOWN;
+ return PJ_SUCCESS;
+ }
+
+
+ /* Check address family in remote SDP */
+ rem_af = pj_AF_UNSPEC();
+ if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) {
+ if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) {
+ rem_af = pj_AF_INET();
+ } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) {
+ rem_af = pj_AF_INET6();
+ }
+ }
+
+ if (rem_af==pj_AF_UNSPEC()) {
+ /* Unsupported address family */
+ return PJ_EAFNOTSUP;
+ }
+
+ /* Set remote address: */
+ status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr,
+ rem_m->desc.port);
+ if (status != PJ_SUCCESS) {
+ /* Invalid IP address. */
+ return PJMEDIA_EINVALIDIP;
+ }
+
+ /* Check address family of local info */
+ local_af = pj_AF_UNSPEC();
+ if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) {
+ if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) {
+ local_af = pj_AF_INET();
+ } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) {
+ local_af = pj_AF_INET6();
+ }
+ }
+
+ if (local_af==pj_AF_UNSPEC()) {
+ /* Unsupported address family */
+ return PJ_SUCCESS;
+ }
+
+ /* Set remote address: */
+ status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr,
+ local_m->desc.port);
+ if (status != PJ_SUCCESS) {
+ /* Invalid IP address. */
+ return PJMEDIA_EINVALIDIP;
+ }
+
+ /* Local and remote address family must match */
+ if (local_af != rem_af)
+ return PJ_EAFNOTSUP;
+
+ /* Media direction: */
+
+ if (local_m->desc.port == 0 ||
+ pj_sockaddr_has_addr(&local_addr)==PJ_FALSE ||
+ pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE ||
+ pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL)
+ {
+ /* Inactive stream. */
+
+ si->dir = PJMEDIA_DIR_NONE;
+
+ } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) {
+
+ /* Send only stream. */
+
+ si->dir = PJMEDIA_DIR_ENCODING;
+
+ } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) {
+
+ /* Recv only stream. */
+
+ si->dir = PJMEDIA_DIR_DECODING;
+
+ } else {
+
+ /* Send and receive stream. */
+
+ si->dir = PJMEDIA_DIR_ENCODING_DECODING;
+
+ }
+
+ /* No need to do anything else if stream is rejected */
+ if (local_m->desc.port == 0) {
+ return PJ_SUCCESS;
+ }
+
+ /* If "rtcp" attribute is present in the SDP, set the RTCP address
+ * from that attribute. Otherwise, calculate from RTP address.
+ */
+ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+ "rtcp", NULL);
+ if (attr) {
+ pjmedia_sdp_rtcp_attr rtcp;
+ status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
+ if (status == PJ_SUCCESS) {
+ if (rtcp.addr.slen) {
+ status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr,
+ (pj_uint16_t)rtcp.port);
+ } else {
+ pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL,
+ (pj_uint16_t)rtcp.port);
+ pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp),
+ pj_sockaddr_get_addr(&si->rem_addr),
+ pj_sockaddr_get_addr_len(&si->rem_addr));
+ }
+ }
+ }
+
+ if (!pj_sockaddr_has_addr(&si->rem_rtcp)) {
+ int rtcp_port;
+
+ pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr));
+ rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1;
+ pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port);
+ }
+
+
+ /* Get the payload number for receive channel. */
+ /*
+ Previously we used to rely on fmt[0] being the selected codec,
+ but some UA sends telephone-event as fmt[0] and this would
+ cause assert failure below.
+
+ Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch.
+
+ // And codec must be numeric!
+ if (!pj_isdigit(*local_m->desc.fmt[0].ptr) ||
+ !pj_isdigit(*rem_m->desc.fmt[0].ptr))
+ {
+ return PJMEDIA_EINVALIDPT;
+ }
+
+ pt = pj_strtoul(&local_m->desc.fmt[0]);
+ pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 ||
+ pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS);
+ */
+
+ /* Get codec info and param */
+ status = get_audio_codec_info_param(si, pool, mgr, local_m, rem_m);
+
+ /* Leave SSRC to random. */
+ si->ssrc = pj_rand();
+
+ /* Set default jitter buffer parameter. */
+ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;
+
+ return status;
+}
diff --git a/pjmedia/src/pjmedia/stream_common.c b/pjmedia/src/pjmedia/stream_common.c
new file mode 100644
index 00000000..9970cc78
--- /dev/null
+++ b/pjmedia/src/pjmedia/stream_common.c
@@ -0,0 +1,111 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/stream_common.h>
+#include <pj/log.h>
+
+#define THIS_FILE "stream_common.c"
+
+/*
+ * Parse fmtp for specified format/payload type.
+ */
+PJ_DEF(pj_status_t) pjmedia_stream_info_parse_fmtp( pj_pool_t *pool,
+ const pjmedia_sdp_media *m,
+ unsigned pt,
+ pjmedia_codec_fmtp *fmtp)
+{
+ const pjmedia_sdp_attr *attr;
+ pjmedia_sdp_fmtp sdp_fmtp;
+ char *p, *p_end, fmt_buf[8];
+ pj_str_t fmt;
+ pj_status_t status;
+
+ pj_assert(m && fmtp);
+
+ pj_bzero(fmtp, sizeof(pjmedia_codec_fmtp));
+
+ /* Get "fmtp" attribute for the format */
+ pj_ansi_sprintf(fmt_buf, "%d", pt);
+ fmt = pj_str(fmt_buf);
+ attr = pjmedia_sdp_media_find_attr2(m, "fmtp", &fmt);
+ if (attr == NULL)
+ return PJ_SUCCESS;
+
+ /* Parse "fmtp" attribute */
+ status = pjmedia_sdp_attr_get_fmtp(attr, &sdp_fmtp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Prepare parsing */
+ p = sdp_fmtp.fmt_param.ptr;
+ p_end = p + sdp_fmtp.fmt_param.slen;
+
+ /* Parse */
+ while (p < p_end) {
+ char *token, *start, *end;
+
+ if (fmtp->cnt >= PJMEDIA_CODEC_MAX_FMTP_CNT) {
+ PJ_LOG(4,(THIS_FILE,
+ "Warning: fmtp parameter count exceeds "
+ "PJMEDIA_CODEC_MAX_FMTP_CNT"));
+ return PJ_SUCCESS;
+ }
+
+ /* Skip whitespaces */
+ while (p < p_end && (*p == ' ' || *p == '\t')) ++p;
+ if (p == p_end)
+ break;
+
+ /* Get token */
+ start = p;
+ while (p < p_end && *p != ';' && *p != '=') ++p;
+ end = p - 1;
+
+ /* Right trim */
+ while (end >= start && (*end == ' ' || *end == '\t' ||
+ *end == '\r' || *end == '\n' ))
+ --end;
+
+ /* Forward a char after trimming */
+ ++end;
+
+ /* Store token */
+ if (end > start) {
+ if (pool) {
+ token = (char*)pj_pool_alloc(pool, end - start);
+ pj_ansi_strncpy(token, start, end - start);
+ } else {
+ token = start;
+ }
+ if (*p == '=')
+ /* Got param name */
+ pj_strset(&fmtp->param[fmtp->cnt].name, token, end - start);
+ else
+ /* Got param value */
+ pj_strset(&fmtp->param[fmtp->cnt++].val, token, end - start);
+ } else if (*p != '=') {
+ ++fmtp->cnt;
+ }
+
+ /* Next */
+ ++p;
+ }
+
+ return PJ_SUCCESS;
+}
+
diff --git a/pjmedia/src/pjmedia/tonegen.c b/pjmedia/src/pjmedia/tonegen.c
index a59c7fd3..954494bf 100644
--- a/pjmedia/src/pjmedia/tonegen.c
+++ b/pjmedia/src/pjmedia/tonegen.c
@@ -334,7 +334,7 @@ static void generate_tone(struct gen_state *state,
/****************************************************************************/
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('t', 'n', 'g', 'n')
+#define SIGNATURE PJMEDIA_SIG_PORT_TONEGEN
#define THIS_FILE "tonegen.c"
#if 0
@@ -558,7 +558,7 @@ static pj_status_t tonegen_get_frame(pjmedia_port *port,
{
struct tonegen *tonegen = (struct tonegen*) port;
short *dst, *end;
- unsigned clock_rate = tonegen->base.info.clock_rate;
+ unsigned clock_rate = PJMEDIA_PIA_SRATE(&tonegen->base.info);
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL);
@@ -622,7 +622,7 @@ static pj_status_t tonegen_get_frame(pjmedia_port *port,
}
dst = (short*) frame->buf;
- end = dst + port->info.samples_per_frame;
+ end = dst + PJMEDIA_PIA_SPF(&port->info);
while (dst < end) {
pjmedia_tone_desc *dig = &tonegen->digits[tonegen->cur_digit];
@@ -636,7 +636,8 @@ static pj_status_t tonegen_get_frame(pjmedia_port *port,
if (tonegen->dig_samples == 0 &&
(tonegen->count!=1 || !(dig->flags & PJMEDIA_TONE_INITIALIZED)))
{
- init_generate_tone(&tonegen->state, port->info.clock_rate,
+ init_generate_tone(&tonegen->state,
+ PJMEDIA_PIA_SRATE(&port->info),
dig->freq1, dig->freq2, dig->volume);
dig->flags |= PJMEDIA_TONE_INITIALIZED;
if (tonegen->cur_digit > 0) {
@@ -651,7 +652,8 @@ static pj_status_t tonegen_get_frame(pjmedia_port *port,
cnt = on_samp - tonegen->dig_samples;
if (cnt > required)
cnt = required;
- generate_tone(&tonegen->state, port->info.channel_count,
+ generate_tone(&tonegen->state,
+ PJMEDIA_PIA_CCNT(&port->info),
cnt, dst);
dst += cnt;
@@ -729,7 +731,7 @@ static pj_status_t tonegen_get_frame(pjmedia_port *port,
pjmedia_zero_samples(dst, end-dst);
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
- frame->size = port->info.bytes_per_frame;
+ frame->size = PJMEDIA_PIA_AVG_FSZ(&port->info);
TRACE_((THIS_FILE, "tonegen_get_frame(): frame created, level=%u",
pjmedia_calc_avg_signal((pj_int16_t*)frame->buf, frame->size/2)));
diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c
index df2a68ed..54ae0324 100644
--- a/pjmedia/src/pjmedia/transport_ice.c
+++ b/pjmedia/src/pjmedia/transport_ice.c
@@ -206,6 +206,22 @@ PJ_DEF(pj_status_t) pjmedia_ice_create2(pjmedia_endpt *endpt,
unsigned options,
pjmedia_transport **p_tp)
{
+ return pjmedia_ice_create3(endpt, name, comp_cnt, cfg, cb,
+ options, NULL, p_tp);
+}
+
+/*
+ * Create ICE media transport.
+ */
+PJ_DEF(pj_status_t) pjmedia_ice_create3(pjmedia_endpt *endpt,
+ const char *name,
+ unsigned comp_cnt,
+ const pj_ice_strans_cfg *cfg,
+ const pjmedia_ice_cb *cb,
+ unsigned options,
+ void *user_data,
+ pjmedia_transport **p_tp)
+{
pj_pool_t *pool;
pj_ice_strans_cb ice_st_cb;
struct transport_ice *tp_ice;
@@ -223,6 +239,7 @@ PJ_DEF(pj_status_t) pjmedia_ice_create2(pjmedia_endpt *endpt,
pj_ansi_strcpy(tp_ice->base.name, pool->obj_name);
tp_ice->base.op = &transport_ice_op;
tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE;
+ tp_ice->base.user_data = user_data;
tp_ice->initial_sdp = PJ_TRUE;
tp_ice->oa_role = ROLE_NONE;
tp_ice->use_ice = PJ_FALSE;
diff --git a/pjmedia/src/pjmedia/types.c b/pjmedia/src/pjmedia/types.c
new file mode 100644
index 00000000..2f178f24
--- /dev/null
+++ b/pjmedia/src/pjmedia/types.c
@@ -0,0 +1,47 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/types.h>
+#include <pj/assert.h>
+
+/**
+ * Utility function to return the string name for a pjmedia_type.
+ *
+ * @param t The media type.
+ *
+ * @return String.
+ */
+PJ_DEF(const char*) pjmedia_type_name(pjmedia_type t)
+{
+ const char *type_names[] = {
+ "none",
+ "audio",
+ "video",
+ "application",
+ "unknown"
+ };
+
+ pj_assert(t < PJ_ARRAY_SIZE(type_names));
+ pj_assert(PJMEDIA_TYPE_UNKNOWN == 4);
+
+ if (t < PJ_ARRAY_SIZE(type_names))
+ return type_names[t];
+ else
+ return "??";
+}
diff --git a/pjmedia/src/pjmedia/vid_codec.c b/pjmedia/src/pjmedia/vid_codec.c
new file mode 100644
index 00000000..8eded7df
--- /dev/null
+++ b/pjmedia/src/pjmedia/vid_codec.c
@@ -0,0 +1,731 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/vid_codec.h>
+#include <pjmedia/errno.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/string.h>
+
+#define THIS_FILE "vid_codec.c"
+
+static pjmedia_vid_codec_mgr *def_vid_codec_mgr;
+
+
+/*
+ * Codec manager maintains array of these structs for each supported
+ * codec.
+ */
+typedef struct pjmedia_vid_codec_desc
+{
+ pjmedia_vid_codec_info info; /**< Codec info. */
+ pjmedia_codec_id id; /**< Fully qualified name */
+ pjmedia_codec_priority prio; /**< Priority. */
+ pjmedia_vid_codec_factory *factory; /**< The factory. */
+ pjmedia_vid_codec_param *def_param; /**< Default codecs
+ parameters. */
+} pjmedia_vid_codec_desc;
+
+
+/* The declaration of video codec manager */
+struct pjmedia_vid_codec_mgr
+{
+ /** Codec manager mutex. */
+ pj_mutex_t *mutex;
+
+ /** List of codec factories registered to codec manager. */
+ pjmedia_vid_codec_factory factory_list;
+
+ /** Number of supported codecs. */
+ unsigned codec_cnt;
+
+ /** Array of codec descriptor. */
+ pjmedia_vid_codec_desc codec_desc[PJMEDIA_CODEC_MGR_MAX_CODECS];
+
+};
+
+
+
+/* Sort codecs in codec manager based on priorities */
+static void sort_codecs(pjmedia_vid_codec_mgr *mgr);
+
+/*
+ * Initialize pjmedia_vid_codec structure with default values.
+ */
+PJ_DEF(void) pjmedia_vid_codec_reset(pjmedia_vid_codec *codec,
+ pjmedia_obj_sig sig)
+{
+ pj_bzero(codec, sizeof(*codec));
+ pjmedia_event_publisher_init(&codec->epub, sig);
+}
+
+/*
+ * Duplicate video codec parameter.
+ */
+PJ_DEF(pjmedia_vid_codec_param*) pjmedia_vid_codec_param_clone(
+ pj_pool_t *pool,
+ const pjmedia_vid_codec_param *src)
+{
+ pjmedia_vid_codec_param *p;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(pool && src, NULL);
+
+ p = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_param);
+
+ /* Update codec param */
+ pj_memcpy(p, src, sizeof(pjmedia_vid_codec_param));
+ for (i = 0; i < src->dec_fmtp.cnt; ++i) {
+ pj_strdup(pool, &p->dec_fmtp.param[i].name,
+ &src->dec_fmtp.param[i].name);
+ pj_strdup(pool, &p->dec_fmtp.param[i].val,
+ &src->dec_fmtp.param[i].val);
+ }
+ for (i = 0; i < src->enc_fmtp.cnt; ++i) {
+ pj_strdup(pool, &p->enc_fmtp.param[i].name,
+ &src->enc_fmtp.param[i].name);
+ pj_strdup(pool, &p->enc_fmtp.param[i].val,
+ &src->enc_fmtp.param[i].val);
+ }
+
+ return p;
+}
+
+/*
+ * Initialize codec manager.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_create(
+ pj_pool_t *pool,
+ pjmedia_vid_codec_mgr **p_mgr)
+{
+ pjmedia_vid_codec_mgr *mgr;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(pool, PJ_EINVAL);
+
+ mgr = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_mgr);
+ pj_list_init (&mgr->factory_list);
+ mgr->codec_cnt = 0;
+
+ /* Create mutex */
+ status = pj_mutex_create_recursive(pool, "vid-codec-mgr", &mgr->mutex);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if (!def_vid_codec_mgr)
+ def_vid_codec_mgr = mgr;
+
+ if (p_mgr)
+ *p_mgr = mgr;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Initialize codec manager.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_destroy (pjmedia_vid_codec_mgr *mgr)
+{
+ if (!mgr) mgr = def_vid_codec_mgr;
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ /* Destroy mutex */
+ if (mgr->mutex)
+ pj_mutex_destroy(mgr->mutex);
+
+ /* Just for safety, set codec manager states to zero */
+ pj_bzero(mgr, sizeof(pjmedia_vid_codec_mgr));
+
+ if (mgr == def_vid_codec_mgr)
+ def_vid_codec_mgr = NULL;
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pjmedia_vid_codec_mgr*) pjmedia_vid_codec_mgr_instance(void)
+{
+ //pj_assert(def_vid_codec_mgr);
+ return def_vid_codec_mgr;
+}
+
+PJ_DEF(void) pjmedia_vid_codec_mgr_set_instance(pjmedia_vid_codec_mgr* mgr)
+{
+ def_vid_codec_mgr = mgr;
+}
+
+
+/*
+ * Register a codec factory.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_register_factory(
+ pjmedia_vid_codec_mgr *mgr,
+ pjmedia_vid_codec_factory *factory)
+{
+ pjmedia_vid_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS];
+ unsigned i, count;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(factory, PJ_EINVAL);
+
+ if (!mgr) mgr = def_vid_codec_mgr;
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ /* Enum codecs */
+ count = PJ_ARRAY_SIZE(info);
+ status = factory->op->enum_info(factory, &count, info);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_mutex_lock(mgr->mutex);
+
+ /* Check codec count */
+ if (count + mgr->codec_cnt > PJ_ARRAY_SIZE(mgr->codec_desc)) {
+ pj_mutex_unlock(mgr->mutex);
+ return PJ_ETOOMANY;
+ }
+
+
+ /* Save the codecs */
+ for (i=0; i<count; ++i) {
+ pj_memcpy( &mgr->codec_desc[mgr->codec_cnt+i],
+ &info[i], sizeof(pjmedia_vid_codec_info));
+ mgr->codec_desc[mgr->codec_cnt+i].prio = PJMEDIA_CODEC_PRIO_NORMAL;
+ mgr->codec_desc[mgr->codec_cnt+i].factory = factory;
+ pjmedia_vid_codec_info_to_id( &info[i],
+ mgr->codec_desc[mgr->codec_cnt+i].id,
+ sizeof(pjmedia_codec_id));
+ }
+
+ /* Update count */
+ mgr->codec_cnt += count;
+
+ /* Re-sort codec based on priorities */
+ sort_codecs(mgr);
+
+ /* Add factory to the list */
+ pj_list_push_back(&mgr->factory_list, factory);
+
+ pj_mutex_unlock(mgr->mutex);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Unregister a codec factory.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_unregister_factory(
+ pjmedia_vid_codec_mgr *mgr,
+ pjmedia_vid_codec_factory *factory)
+{
+ unsigned i;
+ PJ_ASSERT_RETURN(factory, PJ_EINVAL);
+
+ if (!mgr) mgr = def_vid_codec_mgr;
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ pj_mutex_lock(mgr->mutex);
+
+ /* Factory must be registered. */
+ if (pj_list_find_node(&mgr->factory_list, factory) != factory) {
+ pj_mutex_unlock(mgr->mutex);
+ return PJ_ENOTFOUND;
+ }
+
+ /* Erase factory from the factory list */
+ pj_list_erase(factory);
+
+
+ /* Remove all supported codecs from the codec manager that were created
+ * by the specified factory.
+ */
+ for (i=0; i<mgr->codec_cnt; ) {
+
+ if (mgr->codec_desc[i].factory == factory) {
+ /* Remove the codec from array of codec descriptions */
+ pj_array_erase(mgr->codec_desc, sizeof(mgr->codec_desc[0]),
+ mgr->codec_cnt, i);
+ --mgr->codec_cnt;
+
+ } else {
+ ++i;
+ }
+ }
+
+ pj_mutex_unlock(mgr->mutex);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Enum all codecs.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_enum_codecs(
+ pjmedia_vid_codec_mgr *mgr,
+ unsigned *count,
+ pjmedia_vid_codec_info codecs[],
+ unsigned *prio)
+{
+ unsigned i;
+
+ PJ_ASSERT_RETURN(count && codecs, PJ_EINVAL);
+
+ if (!mgr) mgr = def_vid_codec_mgr;
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ pj_mutex_lock(mgr->mutex);
+
+ if (*count > mgr->codec_cnt)
+ *count = mgr->codec_cnt;
+
+ for (i=0; i<*count; ++i) {
+ pj_memcpy(&codecs[i],
+ &mgr->codec_desc[i].info,
+ sizeof(pjmedia_vid_codec_info));
+ }
+
+ if (prio) {
+ for (i=0; i < *count; ++i)
+ prio[i] = mgr->codec_desc[i].prio;
+ }
+
+ pj_mutex_unlock(mgr->mutex);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get codec info for the specified payload type.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info(
+ pjmedia_vid_codec_mgr *mgr,
+ unsigned pt,
+ const pjmedia_vid_codec_info **p_info)
+{
+ unsigned i;
+
+ PJ_ASSERT_RETURN(p_info, PJ_EINVAL);
+
+ if (!mgr) mgr = def_vid_codec_mgr;
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ pj_mutex_lock(mgr->mutex);
+
+ for (i=0; i<mgr->codec_cnt; ++i) {
+ if (mgr->codec_desc[i].info.pt == pt) {
+ *p_info = &mgr->codec_desc[i].info;
+
+ pj_mutex_unlock(mgr->mutex);
+ return PJ_SUCCESS;
+ }
+ }
+
+ pj_mutex_unlock(mgr->mutex);
+
+ return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info2(
+ pjmedia_vid_codec_mgr *mgr,
+ pjmedia_format_id fmt_id,
+ const pjmedia_vid_codec_info **p_info)
+{
+ unsigned i;
+
+ PJ_ASSERT_RETURN(p_info, PJ_EINVAL);
+
+ if (!mgr) mgr = def_vid_codec_mgr;
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ pj_mutex_lock(mgr->mutex);
+
+ for (i=0; i<mgr->codec_cnt; ++i) {
+ if (mgr->codec_desc[i].info.fmt_id == fmt_id) {
+ *p_info = &mgr->codec_desc[i].info;
+
+ pj_mutex_unlock(mgr->mutex);
+ return PJ_SUCCESS;
+ }
+ }
+
+ pj_mutex_unlock(mgr->mutex);
+
+ return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+/*
+ * Convert codec info struct into a unique codec identifier.
+ * A codec identifier looks something like "H263/34".
+ */
+PJ_DEF(char*) pjmedia_vid_codec_info_to_id(
+ const pjmedia_vid_codec_info *info,
+ char *id, unsigned max_len )
+{
+ int len;
+
+ PJ_ASSERT_RETURN(info && id && max_len, NULL);
+
+ len = pj_ansi_snprintf(id, max_len, "%.*s/%u",
+ (int)info->encoding_name.slen,
+ info->encoding_name.ptr,
+ info->pt);
+
+ if (len < 1 || len >= (int)max_len) {
+ id[0] = '\0';
+ return NULL;
+ }
+
+ return id;
+}
+
+
+/*
+ * Find codecs by the unique codec identifier. This function will find
+ * all codecs that match the codec identifier prefix. For example, if
+ * "L16" is specified, then it will find "L16/8000/1", "L16/16000/1",
+ * and so on, up to the maximum count specified in the argument.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_find_codecs_by_id(
+ pjmedia_vid_codec_mgr *mgr,
+ const pj_str_t *codec_id,
+ unsigned *count,
+ const pjmedia_vid_codec_info *p_info[],
+ unsigned prio[])
+{
+ unsigned i, found = 0;
+
+ PJ_ASSERT_RETURN(codec_id && count && *count, PJ_EINVAL);
+
+ if (!mgr) mgr = def_vid_codec_mgr;
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ pj_mutex_lock(mgr->mutex);
+
+ for (i=0; i<mgr->codec_cnt; ++i) {
+
+ if (codec_id->slen == 0 ||
+ pj_strnicmp2(codec_id, mgr->codec_desc[i].id,
+ codec_id->slen) == 0)
+ {
+
+ if (p_info)
+ p_info[found] = &mgr->codec_desc[i].info;
+ if (prio)
+ prio[found] = mgr->codec_desc[i].prio;
+
+ ++found;
+
+ if (found >= *count)
+ break;
+ }
+
+ }
+
+ pj_mutex_unlock(mgr->mutex);
+
+ *count = found;
+
+ return found ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+
+/* Swap two codecs positions in codec manager */
+static void swap_codec(pjmedia_vid_codec_mgr *mgr, unsigned i, unsigned j)
+{
+ pjmedia_vid_codec_desc tmp;
+
+ pj_memcpy(&tmp, &mgr->codec_desc[i], sizeof(pjmedia_vid_codec_desc));
+
+ pj_memcpy(&mgr->codec_desc[i], &mgr->codec_desc[j],
+ sizeof(pjmedia_vid_codec_desc));
+
+ pj_memcpy(&mgr->codec_desc[j], &tmp, sizeof(pjmedia_vid_codec_desc));
+}
+
+
+/* Sort codecs in codec manager based on priorities */
+static void sort_codecs(pjmedia_vid_codec_mgr *mgr)
+{
+ unsigned i;
+
+ /* Re-sort */
+ for (i=0; i<mgr->codec_cnt; ++i) {
+ unsigned j, max;
+
+ for (max=i, j=i+1; j<mgr->codec_cnt; ++j) {
+ if (mgr->codec_desc[j].prio > mgr->codec_desc[max].prio)
+ max = j;
+ }
+
+ if (max != i)
+ swap_codec(mgr, i, max);
+ }
+
+ /* Change PJMEDIA_CODEC_PRIO_HIGHEST codecs to NEXT_HIGHER */
+ for (i=0; i<mgr->codec_cnt; ++i) {
+ if (mgr->codec_desc[i].prio == PJMEDIA_CODEC_PRIO_HIGHEST)
+ mgr->codec_desc[i].prio = PJMEDIA_CODEC_PRIO_NEXT_HIGHER;
+ else
+ break;
+ }
+}
+
+
+/**
+ * Set codec priority. The codec priority determines the order of
+ * the codec in the SDP created by the endpoint. If more than one codecs
+ * are found with the same codec_id prefix, then the function sets the
+ * priorities of all those codecs.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_set_codec_priority(
+ pjmedia_vid_codec_mgr *mgr,
+ const pj_str_t *codec_id,
+ pj_uint8_t prio)
+{
+ unsigned i, found = 0;
+
+ PJ_ASSERT_RETURN(codec_id, PJ_EINVAL);
+
+ if (!mgr) mgr = def_vid_codec_mgr;
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ pj_mutex_lock(mgr->mutex);
+
+ /* Update the priorities of affected codecs */
+ for (i=0; i<mgr->codec_cnt; ++i)
+ {
+ if (codec_id->slen == 0 ||
+ pj_strnicmp2(codec_id, mgr->codec_desc[i].id,
+ codec_id->slen) == 0)
+ {
+ mgr->codec_desc[i].prio = (pjmedia_codec_priority) prio;
+ ++found;
+ }
+ }
+
+ if (!found) {
+ pj_mutex_unlock(mgr->mutex);
+ return PJ_ENOTFOUND;
+ }
+
+ /* Re-sort codecs */
+ sort_codecs(mgr);
+
+ pj_mutex_unlock(mgr->mutex);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Allocate one codec.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_alloc_codec(
+ pjmedia_vid_codec_mgr *mgr,
+ const pjmedia_vid_codec_info *info,
+ pjmedia_vid_codec **p_codec)
+{
+ pjmedia_vid_codec_factory *factory;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(info && p_codec, PJ_EINVAL);
+
+ if (!mgr) mgr = def_vid_codec_mgr;
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ *p_codec = NULL;
+
+ pj_mutex_lock(mgr->mutex);
+
+ factory = mgr->factory_list.next;
+ while (factory != &mgr->factory_list) {
+
+ if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) {
+
+ status = (*factory->op->alloc_codec)(factory, info, p_codec);
+ if (status == PJ_SUCCESS) {
+ pj_mutex_unlock(mgr->mutex);
+ return PJ_SUCCESS;
+ }
+
+ }
+
+ factory = factory->next;
+ }
+
+ pj_mutex_unlock(mgr->mutex);
+
+ return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+/*
+ * Get default codec parameter.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_default_param(
+ pjmedia_vid_codec_mgr *mgr,
+ const pjmedia_vid_codec_info *info,
+ pjmedia_vid_codec_param *param )
+{
+ pjmedia_vid_codec_factory *factory;
+ pj_status_t status;
+ pjmedia_codec_id codec_id;
+ pjmedia_vid_codec_desc *codec_desc = NULL;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(info && param, PJ_EINVAL);
+
+ if (!mgr) mgr = def_vid_codec_mgr;
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ if (!pjmedia_vid_codec_info_to_id(info, (char*)&codec_id,
+ sizeof(codec_id)))
+ return PJ_EINVAL;
+
+ pj_mutex_lock(mgr->mutex);
+
+ /* First, lookup default param in codec desc */
+ for (i=0; i < mgr->codec_cnt; ++i) {
+ if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) {
+ codec_desc = &mgr->codec_desc[i];
+ break;
+ }
+ }
+
+ /* If we found the codec and its default param is set, return it */
+ if (codec_desc && codec_desc->def_param) {
+ pj_memcpy(param, codec_desc->def_param,
+ sizeof(pjmedia_vid_codec_param));
+
+ pj_mutex_unlock(mgr->mutex);
+ return PJ_SUCCESS;
+ }
+
+ /* Otherwise query the default param from codec factory */
+ factory = mgr->factory_list.next;
+ while (factory != &mgr->factory_list) {
+
+ if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) {
+
+ status = (*factory->op->default_attr)(factory, info, param);
+ if (status == PJ_SUCCESS) {
+ /* Check for invalid max_bps. */
+ //if (param->info.max_bps < param->info.avg_bps)
+ // param->info.max_bps = param->info.avg_bps;
+
+ pj_mutex_unlock(mgr->mutex);
+ return PJ_SUCCESS;
+ }
+
+ }
+
+ factory = factory->next;
+ }
+
+ pj_mutex_unlock(mgr->mutex);
+
+
+ return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+/*
+ * Set default codec parameter.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_set_default_param(
+ pjmedia_vid_codec_mgr *mgr,
+ pj_pool_t *pool,
+ const pjmedia_vid_codec_info *info,
+ const pjmedia_vid_codec_param *param )
+{
+ unsigned i;
+ pjmedia_codec_id codec_id;
+ pjmedia_vid_codec_desc *codec_desc = NULL;
+ pjmedia_vid_codec_param *p;
+
+ PJ_ASSERT_RETURN(info, PJ_EINVAL);
+
+ if (!mgr) mgr = def_vid_codec_mgr;
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ if (!pjmedia_vid_codec_info_to_id(info, (char*)&codec_id, sizeof(codec_id)))
+ return PJ_EINVAL;
+
+ pj_mutex_lock(mgr->mutex);
+
+ /* Lookup codec desc */
+ for (i=0; i < mgr->codec_cnt; ++i) {
+ if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) {
+ codec_desc = &mgr->codec_desc[i];
+ break;
+ }
+ }
+
+ /* Codec not found */
+ if (!codec_desc) {
+ pj_mutex_unlock(mgr->mutex);
+ return PJMEDIA_CODEC_EUNSUP;
+ }
+
+ /* If codec param is previously set */
+ if (codec_desc->def_param) {
+ codec_desc->def_param = NULL;
+ }
+
+ /* When param is set to NULL, i.e: setting default codec param to library
+ * default setting, just return PJ_SUCCESS.
+ */
+ if (NULL == param) {
+ pj_mutex_unlock(mgr->mutex);
+ return PJ_SUCCESS;
+ }
+
+ /* Update codec default param */
+ p = pjmedia_vid_codec_param_clone(pool, param);
+ if (!p)
+ return PJ_EINVAL;
+ codec_desc->def_param = p;
+
+ pj_mutex_unlock(mgr->mutex);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Dealloc codec.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_vid_codec_mgr_dealloc_codec(pjmedia_vid_codec_mgr *mgr,
+ pjmedia_vid_codec *codec)
+{
+ PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+
+ if (!mgr) mgr = def_vid_codec_mgr;
+ PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+ return (*codec->factory->op->dealloc_codec)(codec->factory, codec);
+}
+
diff --git a/pjmedia/src/pjmedia/vid_codec_util.c b/pjmedia/src/pjmedia/vid_codec_util.c
new file mode 100644
index 00000000..17e713a3
--- /dev/null
+++ b/pjmedia/src/pjmedia/vid_codec_util.c
@@ -0,0 +1,619 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/vid_codec_util.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/stream_common.h>
+#include <pjlib-util/base64.h>
+#include <pj/ctype.h>
+#include <pj/math.h>
+
+#define THIS_FILE "vid_codec_util.c"
+
+/* If this is set to non-zero, H.264 custom negotiation will require
+ * "profile-level-id" and "packetization-mode" to be exact match to
+ * get a successful negotiation. Note that flexible answer (updating
+ * SDP answer to match remote offer) is always active regardless the
+ * value of this macro.
+ */
+#define H264_STRICT_SDP_NEGO 0
+
+
+/* ITU resolution definition */
+struct mpi_resolution_t
+{
+ pj_str_t name;
+ pjmedia_rect_size size;
+}
+mpi_resolutions [] =
+{
+ {{"CIF",3}, {352,288}},
+ {{"QCIF",4}, {176,144}},
+ {{"SQCIF",5}, {88,72}},
+ {{"CIF4",4}, {704,576}},
+ {{"CIF16",5}, {1408,1142}},
+};
+
+
+/* Parse fmtp value for custom resolution, e.g: "CUSTOM=800,600,2" */
+static pj_status_t parse_custom_res_fmtp(const pj_str_t *fmtp_val,
+ pjmedia_rect_size *size,
+ unsigned *mpi)
+{
+ const char *p, *p_end;
+ pj_str_t token;
+ unsigned long val[3] = {0};
+ unsigned i = 0;
+
+ p = token.ptr = fmtp_val->ptr;
+ p_end = p + fmtp_val->slen;
+
+ while (p<=p_end && i<PJ_ARRAY_SIZE(val)) {
+ if (*p==',' || p==p_end) {
+ token.slen = (char*)p - token.ptr;
+ val[i++] = pj_strtoul(&token);
+ token.ptr = (char*)p+1;
+ }
+ ++p;
+ }
+
+ if (!val[0] || !val[1])
+ return PJ_ETOOSMALL;
+
+ if (val[2]<1 || val[2]>32)
+ return PJ_EINVAL;
+
+ size->w = val[0];
+ size->h = val[1];
+ *mpi = val[2];
+ return PJ_SUCCESS;
+}
+
+
+/* H263 fmtp parser */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_parse_h263_fmtp(
+ const pjmedia_codec_fmtp *fmtp,
+ pjmedia_vid_codec_h263_fmtp *h263_fmtp)
+{
+ const pj_str_t CUSTOM = {"CUSTOM", 6};
+ unsigned i;
+
+ pj_bzero(h263_fmtp, sizeof(*h263_fmtp));
+
+ for (i=0; i<fmtp->cnt; ++i) {
+ unsigned j;
+ pj_bool_t parsed = PJ_FALSE;
+
+ if (h263_fmtp->mpi_cnt >= PJ_ARRAY_SIZE(h263_fmtp->mpi)) {
+ pj_assert(!"Too small MPI array in H263 fmtp");
+ continue;
+ }
+
+ /* Standard size MPIs */
+ for (j=0; j<PJ_ARRAY_SIZE(mpi_resolutions) && !parsed; ++j) {
+ if (pj_stricmp(&fmtp->param[i].name, &mpi_resolutions[j].name)==0)
+ {
+ unsigned mpi;
+
+ mpi = pj_strtoul(&fmtp->param[i].val);
+ if (mpi<1 || mpi>32)
+ return PJMEDIA_SDP_EINFMTP;
+
+ h263_fmtp->mpi[h263_fmtp->mpi_cnt].size =
+ mpi_resolutions[j].size;
+ h263_fmtp->mpi[h263_fmtp->mpi_cnt].val = mpi;
+ ++h263_fmtp->mpi_cnt;
+ parsed = PJ_TRUE;
+ }
+ }
+ if (parsed)
+ continue;
+
+ /* Custom size MPIs */
+ if (pj_stricmp(&fmtp->param[i].name, &CUSTOM)==0) {
+ pjmedia_rect_size size;
+ unsigned mpi;
+ pj_status_t status;
+
+ status = parse_custom_res_fmtp(&fmtp->param[i].val, &size, &mpi);
+ if (status != PJ_SUCCESS)
+ return PJMEDIA_SDP_EINFMTP;
+
+ h263_fmtp->mpi[h263_fmtp->mpi_cnt].size = size;
+ h263_fmtp->mpi[h263_fmtp->mpi_cnt].val = mpi;
+ ++h263_fmtp->mpi_cnt;
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_vid_codec_h263_apply_fmtp(
+ pjmedia_vid_codec_param *param)
+{
+ if (param->dir & PJMEDIA_DIR_ENCODING) {
+ pjmedia_vid_codec_h263_fmtp fmtp_loc, fmtp_rem;
+ pjmedia_rect_size size = {0};
+ unsigned mpi = 0;
+ pjmedia_video_format_detail *vfd;
+ pj_status_t status;
+
+ /* Get local param */
+ status = pjmedia_vid_codec_parse_h263_fmtp(&param->dec_fmtp,
+ &fmtp_loc);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Get remote param */
+ status = pjmedia_vid_codec_parse_h263_fmtp(&param->enc_fmtp,
+ &fmtp_rem);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Negotiate size & MPI setting */
+ if (fmtp_rem.mpi_cnt == 0) {
+ /* Remote doesn't specify MPI setting, send QCIF=1 */
+ size.w = 176;
+ size.h = 144;
+ mpi = 1;
+ } else if (fmtp_loc.mpi_cnt == 0) {
+ /* Local MPI setting not set, just use remote preference. */
+ size = fmtp_rem.mpi[0].size;
+ mpi = fmtp_rem.mpi[0].val;
+ } else {
+ /* Both have preferences, let's try to match them */
+ unsigned i, j;
+ pj_bool_t matched = PJ_FALSE;
+ pj_uint32_t min_diff = 0xFFFFFFFF;
+ pj_uint32_t loc_sq, rem_sq, diff;
+
+ /* Find the exact size match or the closest size, then choose
+ * the highest MPI among the match/closest pair.
+ */
+ for (i = 0; i < fmtp_rem.mpi_cnt && !matched; ++i) {
+ rem_sq = fmtp_rem.mpi[i].size.w * fmtp_rem.mpi[i].size.h;
+ for (j = 0; j < fmtp_loc.mpi_cnt; ++j) {
+ /* See if we got exact match */
+ if (fmtp_rem.mpi[i].size.w == fmtp_loc.mpi[j].size.w &&
+ fmtp_rem.mpi[i].size.h == fmtp_loc.mpi[j].size.h)
+ {
+ size = fmtp_rem.mpi[i].size;
+ mpi = PJ_MAX(fmtp_rem.mpi[i].val,
+ fmtp_loc.mpi[j].val);
+ matched = PJ_TRUE;
+ break;
+ }
+
+ /* Otherwise keep looking for the closest match */
+ loc_sq = fmtp_loc.mpi[j].size.w * fmtp_loc.mpi[j].size.h;
+ diff = loc_sq>rem_sq? (loc_sq-rem_sq):(rem_sq-loc_sq);
+ if (diff < min_diff) {
+ size = rem_sq<loc_sq? fmtp_rem.mpi[i].size :
+ fmtp_loc.mpi[j].size;
+ mpi = PJ_MAX(fmtp_rem.mpi[i].val,
+ fmtp_loc.mpi[j].val);
+ }
+ }
+ }
+ }
+
+ /* Apply the negotiation result */
+ vfd = pjmedia_format_get_video_format_detail(&param->enc_fmt,
+ PJ_TRUE);
+ vfd->size = size;
+ vfd->fps.num = 30000;
+ vfd->fps.denum = 1001 * mpi;
+ }
+
+ if (param->dir & PJMEDIA_DIR_DECODING) {
+ /* Here we just want to find the highest resolution and the lowest MPI
+ * we support and set it as the decoder param.
+ */
+ pjmedia_vid_codec_h263_fmtp fmtp;
+ pjmedia_video_format_detail *vfd;
+ pj_status_t status;
+
+ status = pjmedia_vid_codec_parse_h263_fmtp(&param->dec_fmtp,
+ &fmtp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ vfd = pjmedia_format_get_video_format_detail(&param->dec_fmt,
+ PJ_TRUE);
+
+ if (fmtp.mpi_cnt == 0) {
+ /* No resolution specified, lets just assume 4CIF=1! */
+ vfd->size.w = 704;
+ vfd->size.h = 576;
+ vfd->fps.num = 30000;
+ vfd->fps.denum = 1001;
+ } else {
+ unsigned i, max_size = 0, max_size_idx = 0, min_mpi = 32;
+
+ /* Get the largest size and the lowest MPI */
+ for (i = 0; i < fmtp.mpi_cnt; ++i) {
+ if (fmtp.mpi[i].size.w * fmtp.mpi[i].size.h > max_size) {
+ max_size = fmtp.mpi[i].size.w * fmtp.mpi[i].size.h;
+ max_size_idx = i;
+ }
+ if (fmtp.mpi[i].val < min_mpi)
+ min_mpi = fmtp.mpi[i].val;
+ }
+
+ vfd->size = fmtp.mpi[max_size_idx].size;
+ vfd->fps.num = 30000;
+ vfd->fps.denum = 1001 * min_mpi;
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* H264 fmtp parser */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp(
+ const pjmedia_codec_fmtp *fmtp,
+ pjmedia_vid_codec_h264_fmtp *h264_fmtp)
+{
+ const pj_str_t PROFILE_LEVEL_ID = {"profile-level-id", 16};
+ const pj_str_t MAX_MBPS = {"max-mbps", 8};
+ const pj_str_t MAX_FS = {"max-fs", 6};
+ const pj_str_t MAX_CPB = {"max-cpb", 7};
+ const pj_str_t MAX_DPB = {"max-dpb", 7};
+ const pj_str_t MAX_BR = {"max-br", 6};
+ const pj_str_t PACKETIZATION_MODE = {"packetization-mode", 18};
+ const pj_str_t SPROP_PARAMETER_SETS = {"sprop-parameter-sets", 20};
+ unsigned i;
+
+ pj_bzero(h264_fmtp, sizeof(*h264_fmtp));
+
+ for (i=0; i<fmtp->cnt; ++i) {
+ unsigned tmp;
+ if (pj_stricmp(&fmtp->param[i].name, &PROFILE_LEVEL_ID)==0) {
+ pj_str_t endst;
+
+ if (fmtp->param[i].val.slen != 6)
+ return PJMEDIA_SDP_EINFMTP;
+
+ tmp = pj_strtoul2(&fmtp->param[i].val, &endst, 16);
+ if (endst.slen)
+ return PJMEDIA_SDP_EINFMTP;
+
+ h264_fmtp->profile_idc = (pj_uint8_t)((tmp >> 16) & 0xFF);
+ h264_fmtp->profile_iop = (pj_uint8_t)((tmp >> 8) & 0xFF);
+ h264_fmtp->level = (pj_uint8_t)(tmp & 0xFF);
+ } else if (pj_stricmp(&fmtp->param[i].name, &PACKETIZATION_MODE)==0) {
+ tmp = pj_strtoul(&fmtp->param[i].val);
+ if (tmp >= 0 && tmp <= 2)
+ h264_fmtp->packetization_mode = (pj_uint8_t)tmp;
+ else
+ return PJMEDIA_SDP_EINFMTP;
+ } else if (pj_stricmp(&fmtp->param[i].name, &MAX_MBPS)==0) {
+ tmp = pj_strtoul(&fmtp->param[i].val);
+ h264_fmtp->max_mbps = tmp;
+ } else if (pj_stricmp(&fmtp->param[i].name, &MAX_FS)==0) {
+ tmp = pj_strtoul(&fmtp->param[i].val);
+ h264_fmtp->max_fs = tmp;
+ } else if (pj_stricmp(&fmtp->param[i].name, &MAX_CPB)==0) {
+ tmp = pj_strtoul(&fmtp->param[i].val);
+ h264_fmtp->max_cpb = tmp;
+ } else if (pj_stricmp(&fmtp->param[i].name, &MAX_DPB)==0) {
+ tmp = pj_strtoul(&fmtp->param[i].val);
+ h264_fmtp->max_dpb = tmp;
+ } else if (pj_stricmp(&fmtp->param[i].name, &MAX_BR)==0) {
+ tmp = pj_strtoul(&fmtp->param[i].val);
+ h264_fmtp->max_br = tmp;
+ } else if (pj_stricmp(&fmtp->param[i].name, &SPROP_PARAMETER_SETS)==0)
+ {
+ pj_str_t sps_st;
+
+ sps_st = fmtp->param[i].val;
+ while (sps_st.slen) {
+ pj_str_t tmp_st;
+ int tmp_len;
+ const pj_uint8_t start_code[3] = {0, 0, 1};
+ char *p;
+ pj_uint8_t *nal;
+ pj_status_t status;
+
+ /* Find field separator ',' */
+ tmp_st = sps_st;
+ p = pj_strchr(&sps_st, ',');
+ if (p) {
+ tmp_st.slen = p - sps_st.ptr;
+ sps_st.ptr = p+1;
+ sps_st.slen -= (tmp_st.slen+1);
+ } else {
+ sps_st.slen = 0;
+ }
+
+ /* Decode field and build NAL unit for this param */
+ nal = &h264_fmtp->sprop_param_sets[
+ h264_fmtp->sprop_param_sets_len];
+ tmp_len = PJ_ARRAY_SIZE(h264_fmtp->sprop_param_sets) -
+ h264_fmtp->sprop_param_sets_len -
+ PJ_ARRAY_SIZE(start_code);
+ status = pj_base64_decode(&tmp_st,
+ nal + PJ_ARRAY_SIZE(start_code),
+ &tmp_len);
+ if (status != PJ_SUCCESS)
+ return PJMEDIA_SDP_EINFMTP;
+
+ tmp_len += PJ_ARRAY_SIZE(start_code);
+ pj_memcpy(nal, start_code, PJ_ARRAY_SIZE(start_code));
+ h264_fmtp->sprop_param_sets_len += tmp_len;
+ }
+ }
+ }
+
+ /* When profile-level-id is not specified, use default value "42000A" */
+ if (h264_fmtp->profile_idc == 0) {
+ h264_fmtp->profile_idc = 0x42;
+ h264_fmtp->profile_iop = 0x00;
+ h264_fmtp->level = 0x0A;
+ }
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_match_sdp(pj_pool_t *pool,
+ pjmedia_sdp_media *offer,
+ unsigned o_fmt_idx,
+ pjmedia_sdp_media *answer,
+ unsigned a_fmt_idx,
+ unsigned option)
+{
+ const pj_str_t PROFILE_LEVEL_ID = {"profile-level-id", 16};
+ const pj_str_t PACKETIZATION_MODE = {"packetization-mode", 18};
+ pjmedia_codec_fmtp o_fmtp_raw, a_fmtp_raw;
+ pjmedia_vid_codec_h264_fmtp o_fmtp, a_fmtp;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(pool);
+
+ /* Parse offer */
+ status = pjmedia_stream_info_parse_fmtp(
+ NULL, offer,
+ pj_strtoul(&offer->desc.fmt[o_fmt_idx]),
+ &o_fmtp_raw);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = pjmedia_vid_codec_h264_parse_fmtp(&o_fmtp_raw, &o_fmtp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Parse answer */
+ status = pjmedia_stream_info_parse_fmtp(
+ NULL, answer,
+ pj_strtoul(&answer->desc.fmt[a_fmt_idx]),
+ &a_fmtp_raw);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = pjmedia_vid_codec_h264_parse_fmtp(&a_fmtp_raw, &a_fmtp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if (option & PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER) {
+ unsigned i;
+
+ /* Flexible negotiation, if the answer has higher capability than
+ * the offer, adjust the answer capability to be match to the offer.
+ */
+ if (a_fmtp.profile_idc >= o_fmtp.profile_idc)
+ a_fmtp.profile_idc = o_fmtp.profile_idc;
+ if (a_fmtp.profile_iop != o_fmtp.profile_iop)
+ a_fmtp.profile_iop = o_fmtp.profile_iop;
+ if (a_fmtp.level >= o_fmtp.level)
+ a_fmtp.level = o_fmtp.level;
+ if (a_fmtp.packetization_mode >= o_fmtp.packetization_mode)
+ a_fmtp.packetization_mode = o_fmtp.packetization_mode;
+
+ /* Match them now */
+#if H264_STRICT_SDP_NEGO
+ if (a_fmtp.profile_idc != o_fmtp.profile_idc ||
+ a_fmtp.profile_iop != o_fmtp.profile_iop ||
+ a_fmtp.level != o_fmtp.level ||
+ a_fmtp.packetization_mode != o_fmtp.packetization_mode)
+ {
+ return PJMEDIA_SDP_EFORMATNOTEQUAL;
+ }
+#else
+ if (a_fmtp.profile_idc != o_fmtp.profile_idc)
+ {
+ return PJMEDIA_SDP_EFORMATNOTEQUAL;
+ }
+#endif
+
+ /* Update the answer */
+ for (i = 0; i < a_fmtp_raw.cnt; ++i) {
+ if (pj_stricmp(&a_fmtp_raw.param[i].name, &PROFILE_LEVEL_ID) == 0)
+ {
+ char *p = a_fmtp_raw.param[i].val.ptr;
+ pj_val_to_hex_digit(a_fmtp.profile_idc, p);
+ p += 2;
+ pj_val_to_hex_digit(a_fmtp.profile_iop, p);
+ p += 2;
+ pj_val_to_hex_digit(a_fmtp.level, p);
+ }
+ else if (pj_stricmp(&a_fmtp_raw.param[i].name, &PACKETIZATION_MODE) == 0)
+ {
+ char *p = a_fmtp_raw.param[i].val.ptr;
+ *p = '0' + a_fmtp.packetization_mode;
+ }
+ }
+ } else {
+#if H264_STRICT_SDP_NEGO
+ /* Strict negotiation */
+ if (a_fmtp.profile_idc != o_fmtp.profile_idc ||
+ a_fmtp.profile_iop != o_fmtp.profile_iop ||
+ a_fmtp.level != o_fmtp.level ||
+ a_fmtp.packetization_mode != o_fmtp.packetization_mode)
+ {
+ return PJMEDIA_SDP_EFORMATNOTEQUAL;
+ }
+#else
+ /* Permissive negotiation */
+ if (a_fmtp.profile_idc != o_fmtp.profile_idc)
+ {
+ return PJMEDIA_SDP_EFORMATNOTEQUAL;
+ }
+#endif
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* Declaration of H.264 level info */
+typedef struct h264_level_info_t
+{
+ unsigned id; /* Level id. */
+ unsigned max_mbps; /* Max macroblocks per second. */
+ unsigned max_mb; /* Max macroblocks. */
+ unsigned bitrate; /* Max bitrate (kbps). */
+ unsigned def_w; /* Default width. */
+ unsigned def_h; /* Default height. */
+ unsigned def_fps; /* Default fps. */
+} h264_level_info_t;
+
+
+/* Get H.264 level info from specified level ID */
+static pj_status_t get_h264_level_info(unsigned id, h264_level_info_t *level)
+{
+ unsigned i;
+ const h264_level_info_t level_info[] =
+ {
+ { 10, 1485, 99, 64, 176, 144, 15 },
+ { 9, 1485, 99, 128, 176, 144, 15 }, /*< level 1b */
+ { 11, 3000, 396, 192, 320, 240, 10 },
+ { 12, 6000, 396, 384, 352, 288, 15 },
+ { 13, 11880, 396, 768, 352, 288, 15 },
+ { 20, 11880, 396, 2000, 352, 288, 30 },
+ { 21, 19800, 792, 4000, 352, 288, 30 },
+ { 22, 20250, 1620, 4000, 352, 288, 30 },
+ { 30, 40500, 1620, 10000, 720, 480, 30 },
+ { 31, 108000, 3600, 14000, 1280, 720, 30 },
+ { 32, 216000, 5120, 20000, 1280, 720, 30 },
+ { 40, 245760, 8192, 20000, 1920, 1080, 30 },
+ { 41, 245760, 8192, 50000, 1920, 1080, 30 },
+ { 42, 522240, 8704, 50000, 1920, 1080, 30 },
+ { 50, 589824, 22080, 135000, 1920, 1080, 30 },
+ { 51, 983040, 36864, 240000, 1920, 1080, 30 },
+ };
+
+ for (i = 0; i < PJ_ARRAY_SIZE(level_info); ++i) {
+ if (level_info[i].id == id) {
+ *level = level_info[i];
+ return PJ_SUCCESS;
+ }
+ }
+ return PJ_ENOTFOUND;
+}
+
+
+#define CALC_H264_MB_NUM(size) (((size.w+15)/16)*((size.h+15)/16))
+#define CALC_H264_MBPS(size,fps) CALC_H264_MB_NUM(size)*fps.num/fps.denum
+
+
+PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp(
+ pjmedia_vid_codec_param *param)
+{
+ const unsigned default_fps = 30;
+
+ if (param->dir & PJMEDIA_DIR_ENCODING) {
+ pjmedia_vid_codec_h264_fmtp fmtp;
+ pjmedia_video_format_detail *vfd;
+ h264_level_info_t level_info;
+ pj_status_t status;
+
+ /* Get remote param */
+ status = pjmedia_vid_codec_h264_parse_fmtp(&param->enc_fmtp,
+ &fmtp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = get_h264_level_info(fmtp.level, &level_info);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Size and fps for encoding direction must conform to H.264 level
+ * specified by remote SDP fmtp.
+ */
+ vfd = pjmedia_format_get_video_format_detail(&param->enc_fmt,
+ PJ_TRUE);
+ if (vfd->size.w && vfd->size.h) {
+ unsigned mb, mbps;
+
+ if (vfd->fps.num == 0 || vfd->fps.denum == 0) {
+ vfd->fps.num = default_fps;
+ vfd->fps.denum = 1;
+ }
+ mb = CALC_H264_MB_NUM(vfd->size);
+ mbps = CALC_H264_MBPS(vfd->size, vfd->fps);
+ if (mb > level_info.max_mb || mbps > level_info.max_mbps) {
+ vfd->size.w = level_info.def_w;
+ vfd->size.h = level_info.def_h;
+ vfd->fps.num = level_info.def_fps;
+ vfd->fps.denum = 1;
+ }
+ } else {
+ vfd->size.w = level_info.def_w;
+ vfd->size.h = level_info.def_h;
+ vfd->fps.num = level_info.def_fps;
+ vfd->fps.denum = 1;
+ }
+ }
+
+ if (param->dir & PJMEDIA_DIR_DECODING) {
+ /* Here we just want to find the highest resolution possible from the
+ * fmtp and set it as the decoder param.
+ */
+ pjmedia_vid_codec_h264_fmtp fmtp;
+ pjmedia_video_format_detail *vfd;
+ h264_level_info_t level_info;
+ pj_status_t status;
+
+ status = pjmedia_vid_codec_h264_parse_fmtp(&param->dec_fmtp,
+ &fmtp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = get_h264_level_info(fmtp.level, &level_info);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ vfd = pjmedia_format_get_video_format_detail(&param->dec_fmt,
+ PJ_TRUE);
+
+ if (vfd->size.w * vfd->size.h < level_info.def_w * level_info.def_h) {
+ vfd->size.w = level_info.def_w;
+ vfd->size.h = level_info.def_h;
+ }
+
+ if (vfd->fps.num == 0 || vfd->fps.denum == 0) {
+ vfd->fps.num = default_fps;
+ vfd->fps.denum = 1;
+ }
+ }
+
+ return PJ_SUCCESS;
+}
diff --git a/pjmedia/src/pjmedia/vid_port.c b/pjmedia/src/pjmedia/vid_port.c
new file mode 100644
index 00000000..e5db8b10
--- /dev/null
+++ b/pjmedia/src/pjmedia/vid_port.c
@@ -0,0 +1,948 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/vid_port.h>
+#include <pjmedia/clock.h>
+#include <pjmedia/converter.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/event.h>
+#include <pjmedia/vid_codec.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+
+#define SIGNATURE PJMEDIA_SIG_VID_PORT
+#define THIS_FILE "vid_port.c"
+
+typedef struct vid_pasv_port vid_pasv_port;
+
+enum role
+{
+ ROLE_NONE,
+ ROLE_ACTIVE,
+ ROLE_PASSIVE
+};
+
+struct pjmedia_vid_port
+{
+ pj_pool_t *pool;
+ pj_str_t dev_name;
+ pjmedia_dir dir;
+// pjmedia_rect_size cap_size;
+ pjmedia_vid_dev_stream *strm;
+ pjmedia_vid_dev_cb strm_cb;
+ void *strm_cb_data;
+ enum role role,
+ stream_role;
+ vid_pasv_port *pasv_port;
+ pjmedia_port *client_port;
+ pj_bool_t destroy_client_port;
+
+ pjmedia_converter *conv;
+ void *conv_buf;
+ pj_size_t conv_buf_size;
+ pjmedia_conversion_param conv_param;
+
+ pjmedia_event_publisher epub;
+ pjmedia_event_subscription esub_dev;
+ pjmedia_event_subscription esub_client_port;
+
+ pjmedia_clock *clock;
+ pjmedia_clock_src clocksrc;
+
+ struct sync_clock_src_t
+ {
+ pjmedia_clock_src *sync_clocksrc;
+ pj_int32_t sync_delta;
+ unsigned max_sync_ticks;
+ unsigned nsync_frame;
+ unsigned nsync_progress;
+ } sync_clocksrc;
+
+ pjmedia_frame *frm_buf;
+ pj_size_t frm_buf_size;
+ pj_mutex_t *frm_mutex;
+};
+
+struct vid_pasv_port
+{
+ pjmedia_port base;
+ pjmedia_vid_port *vp;
+};
+
+static pj_status_t vidstream_cap_cb(pjmedia_vid_dev_stream *stream,
+ void *user_data,
+ pjmedia_frame *frame);
+static pj_status_t vidstream_render_cb(pjmedia_vid_dev_stream *stream,
+ void *user_data,
+ pjmedia_frame *frame);
+static pj_status_t vidstream_event_cb(pjmedia_event_subscription *esub,
+ pjmedia_event *event);
+static pj_status_t client_port_event_cb(pjmedia_event_subscription *esub,
+ pjmedia_event *event);
+
+static void enc_clock_cb(const pj_timestamp *ts, void *user_data);
+static void dec_clock_cb(const pj_timestamp *ts, void *user_data);
+
+static pj_status_t vid_pasv_port_put_frame(struct pjmedia_port *this_port,
+ pjmedia_frame *frame);
+
+static pj_status_t vid_pasv_port_get_frame(struct pjmedia_port *this_port,
+ pjmedia_frame *frame);
+
+
+PJ_DEF(void) pjmedia_vid_port_param_default(pjmedia_vid_port_param *prm)
+{
+ pj_bzero(prm, sizeof(*prm));
+ prm->active = PJ_TRUE;
+}
+
+static const char *vid_dir_name(pjmedia_dir dir)
+{
+ switch (dir) {
+ case PJMEDIA_DIR_CAPTURE:
+ return "capture";
+ case PJMEDIA_DIR_RENDER:
+ return "render";
+ default:
+ return "??";
+ }
+}
+
+static pj_status_t create_converter(pjmedia_vid_port *vp)
+{
+ /* Instantiate converter if necessary */
+ if (vp->conv_param.src.id != vp->conv_param.dst.id ||
+ vp->conv_param.src.det.vid.size.w != vp->conv_param.dst.det.vid.size.w ||
+ vp->conv_param.src.det.vid.size.h != vp->conv_param.dst.det.vid.size.h)
+ {
+ pj_status_t status;
+
+ /* Yes, we need converter */
+ const pjmedia_video_format_info *vfi;
+ pjmedia_video_apply_fmt_param vafp;
+
+ if (vp->conv) {
+ pjmedia_converter_destroy(vp->conv);
+ vp->conv = NULL;
+ }
+
+ status = pjmedia_converter_create(NULL, vp->pool, &vp->conv_param,
+ &vp->conv);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(4,(THIS_FILE, status, "Error creating converter"));
+ return status;
+ }
+
+ /* Allocate buffer for conversion */
+ vfi = pjmedia_get_video_format_info(NULL, vp->conv_param.dst.id);
+ if (!vfi)
+ return PJMEDIA_EBADFMT;
+
+ pj_bzero(&vafp, sizeof(vafp));
+ vafp.size = vp->conv_param.dst.det.vid.size;
+ status = vfi->apply_fmt(vfi, &vafp);
+ if (status != PJ_SUCCESS)
+ return PJMEDIA_EBADFMT;
+
+ if (vafp.framebytes > vp->conv_buf_size) {
+ vp->conv_buf = pj_pool_alloc(vp->pool, vafp.framebytes);
+ vp->conv_buf_size = vafp.framebytes;
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_vid_port_create( pj_pool_t *pool,
+ const pjmedia_vid_port_param *prm,
+ pjmedia_vid_port **p_vid_port)
+{
+ pjmedia_vid_port *vp;
+ const pjmedia_video_format_detail *vfd;
+ char dev_name[64];
+ pjmedia_vid_dev_cb vid_cb;
+ pj_bool_t need_frame_buf = PJ_FALSE;
+ pj_status_t status;
+ unsigned ptime_usec;
+ pjmedia_vid_dev_param vparam;
+ pjmedia_vid_dev_info di;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(pool && prm && p_vid_port, PJ_EINVAL);
+ PJ_ASSERT_RETURN(prm->vidparam.fmt.type == PJMEDIA_TYPE_VIDEO &&
+ prm->vidparam.dir != PJMEDIA_DIR_NONE &&
+ prm->vidparam.dir != PJMEDIA_DIR_CAPTURE_RENDER,
+ PJ_EINVAL);
+
+ /* Retrieve the video format detail */
+ vfd = pjmedia_format_get_video_format_detail(&prm->vidparam.fmt, PJ_TRUE);
+ if (!vfd)
+ return PJ_EINVAL;
+
+ PJ_ASSERT_RETURN(vfd->fps.num, PJ_EINVAL);
+
+ /* Allocate videoport */
+ vp = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_port);
+ vp->pool = pj_pool_create(pool->factory, "video port", 500, 500, NULL);
+ vp->role = prm->active ? ROLE_ACTIVE : ROLE_PASSIVE;
+ vp->dir = prm->vidparam.dir;
+// vp->cap_size = vfd->size;
+ pjmedia_event_publisher_init(&vp->epub, SIGNATURE);
+
+ vparam = prm->vidparam;
+ dev_name[0] = '\0';
+
+ /* Get device info */
+ if (vp->dir & PJMEDIA_DIR_CAPTURE)
+ status = pjmedia_vid_dev_get_info(prm->vidparam.cap_id, &di);
+ else
+ status = pjmedia_vid_dev_get_info(prm->vidparam.rend_id, &di);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_ansi_snprintf(dev_name, sizeof(dev_name), "%s [%s]",
+ di.name, di.driver);
+
+ for (i = 0; i < di.fmt_cnt; ++i) {
+ if (prm->vidparam.fmt.id == di.fmt[i].id)
+ break;
+ }
+
+ if (i == di.fmt_cnt) {
+ /* The device has no no matching format. Pick one from
+ * the supported formats, and later use converter to
+ * convert it to the required format.
+ */
+ pj_assert(di.fmt_cnt != 0);
+ vparam.fmt.id = di.fmt[0].id;
+ }
+
+ pj_strdup2_with_null(pool, &vp->dev_name, di.name);
+ vp->stream_role = di.has_callback ? ROLE_ACTIVE : ROLE_PASSIVE;
+
+ PJ_LOG(4,(THIS_FILE,
+ "Opening device %s for %s: format=%s, size=%dx%d @%d:%d fps",
+ dev_name,
+ vid_dir_name(prm->vidparam.dir),
+ pjmedia_get_video_format_info(NULL, vparam.fmt.id)->name,
+ vfd->size.w, vfd->size.h,
+ vfd->fps.num, vfd->fps.denum));
+
+ ptime_usec = PJMEDIA_PTIME(&vfd->fps);
+ pjmedia_clock_src_init(&vp->clocksrc, PJMEDIA_TYPE_VIDEO,
+ prm->vidparam.clock_rate, ptime_usec);
+ vp->sync_clocksrc.max_sync_ticks =
+ PJMEDIA_CLOCK_SYNC_MAX_RESYNC_DURATION *
+ 1000 / vp->clocksrc.ptime_usec;
+
+ /* Create the video stream */
+ pj_bzero(&vid_cb, sizeof(vid_cb));
+ vid_cb.capture_cb = &vidstream_cap_cb;
+ vid_cb.render_cb = &vidstream_render_cb;
+
+ status = pjmedia_vid_dev_stream_create(&vparam, &vid_cb, vp,
+ &vp->strm);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ PJ_LOG(4,(THIS_FILE,
+ "Device %s opened: format=%s, size=%dx%d @%d:%d fps",
+ dev_name,
+ pjmedia_get_video_format_info(NULL, vparam.fmt.id)->name,
+ vparam.fmt.det.vid.size.w, vparam.fmt.det.vid.size.h,
+ vparam.fmt.det.vid.fps.num, vparam.fmt.det.vid.fps.denum));
+
+ /* Subscribe to device's events */
+ pjmedia_event_subscription_init(&vp->esub_dev, vidstream_event_cb, vp);
+ pjmedia_event_subscribe(
+ pjmedia_vid_dev_stream_get_event_publisher(vp->strm),
+ &vp->esub_dev);
+
+ if (vp->dir & PJMEDIA_DIR_CAPTURE) {
+ pjmedia_format_copy(&vp->conv_param.src, &vparam.fmt);
+ pjmedia_format_copy(&vp->conv_param.dst, &prm->vidparam.fmt);
+ } else {
+ pjmedia_format_copy(&vp->conv_param.src, &prm->vidparam.fmt);
+ pjmedia_format_copy(&vp->conv_param.dst, &vparam.fmt);
+ }
+
+ status = create_converter(vp);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ if (vp->role==ROLE_ACTIVE && vp->stream_role==ROLE_PASSIVE) {
+ pjmedia_clock_param param;
+
+ /* Active role is wanted, but our device is passive, so create
+ * master clocks to run the media flow.
+ */
+ need_frame_buf = PJ_TRUE;
+
+ param.usec_interval = PJMEDIA_PTIME(&vfd->fps);
+ param.clock_rate = prm->vidparam.clock_rate;
+ status = pjmedia_clock_create2(pool, &param,
+ PJMEDIA_CLOCK_NO_HIGHEST_PRIO,
+ (vp->dir & PJMEDIA_DIR_ENCODING) ?
+ &enc_clock_cb: &dec_clock_cb,
+ vp, &vp->clock);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ } else if (vp->role==ROLE_PASSIVE) {
+ vid_pasv_port *pp;
+
+ /* Always need to create media port for passive role */
+ vp->pasv_port = pp = PJ_POOL_ZALLOC_T(pool, vid_pasv_port);
+ pp->vp = vp;
+ pp->base.get_frame = &vid_pasv_port_get_frame;
+ pp->base.put_frame = &vid_pasv_port_put_frame;
+ pjmedia_port_info_init2(&pp->base.info, &vp->dev_name,
+ PJMEDIA_SIG_VID_PORT,
+ prm->vidparam.dir, &prm->vidparam.fmt);
+
+ if (vp->stream_role == ROLE_ACTIVE) {
+ need_frame_buf = PJ_TRUE;
+ }
+ }
+
+ if (need_frame_buf) {
+ const pjmedia_video_format_info *vfi;
+ pjmedia_video_apply_fmt_param vafp;
+
+ vfi = pjmedia_get_video_format_info(NULL, vparam.fmt.id);
+ if (!vfi) {
+ status = PJ_ENOTFOUND;
+ goto on_error;
+ }
+
+ pj_bzero(&vafp, sizeof(vafp));
+ vafp.size = vparam.fmt.det.vid.size;
+ status = vfi->apply_fmt(vfi, &vafp);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ vp->frm_buf = PJ_POOL_ZALLOC_T(pool, pjmedia_frame);
+ vp->frm_buf_size = vafp.framebytes;
+ vp->frm_buf->buf = pj_pool_alloc(pool, vafp.framebytes);
+ vp->frm_buf->size = vp->frm_buf_size;
+ vp->frm_buf->type = PJMEDIA_FRAME_TYPE_NONE;
+
+ status = pj_mutex_create_simple(pool, vp->dev_name.ptr,
+ &vp->frm_mutex);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+
+ *p_vid_port = vp;
+
+ return PJ_SUCCESS;
+
+on_error:
+ pjmedia_vid_port_destroy(vp);
+ return status;
+}
+
+PJ_DEF(void) pjmedia_vid_port_set_cb(pjmedia_vid_port *vid_port,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data)
+{
+ pj_assert(vid_port && cb);
+ pj_memcpy(&vid_port->strm_cb, cb, sizeof(*cb));
+ vid_port->strm_cb_data = user_data;
+}
+
+PJ_DEF(pjmedia_event_publisher*)
+pjmedia_vid_port_get_event_publisher(pjmedia_vid_port *vid_port)
+{
+ PJ_ASSERT_RETURN(vid_port, NULL);
+ return &vid_port->epub;
+}
+
+PJ_DEF(pjmedia_vid_dev_stream*)
+pjmedia_vid_port_get_stream(pjmedia_vid_port *vp)
+{
+ PJ_ASSERT_RETURN(vp, NULL);
+ return vp->strm;
+}
+
+
+PJ_DEF(pjmedia_port*)
+pjmedia_vid_port_get_passive_port(pjmedia_vid_port *vp)
+{
+ PJ_ASSERT_RETURN(vp && vp->role==ROLE_PASSIVE, NULL);
+ return &vp->pasv_port->base;
+}
+
+
+PJ_DEF(pjmedia_clock_src *)
+pjmedia_vid_port_get_clock_src( pjmedia_vid_port *vid_port )
+{
+ PJ_ASSERT_RETURN(vid_port, NULL);
+ return &vid_port->clocksrc;
+}
+
+PJ_DECL(pj_status_t)
+pjmedia_vid_port_set_clock_src( pjmedia_vid_port *vid_port,
+ pjmedia_clock_src *clocksrc)
+{
+ PJ_ASSERT_RETURN(vid_port && clocksrc, PJ_EINVAL);
+
+ vid_port->sync_clocksrc.sync_clocksrc = clocksrc;
+ vid_port->sync_clocksrc.sync_delta =
+ pjmedia_clock_src_get_time_msec(&vid_port->clocksrc) -
+ pjmedia_clock_src_get_time_msec(clocksrc);
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_vid_port_connect(pjmedia_vid_port *vp,
+ pjmedia_port *port,
+ pj_bool_t destroy)
+{
+ pjmedia_event_publisher *epub;
+
+ PJ_ASSERT_RETURN(vp && vp->role==ROLE_ACTIVE, PJ_EINVAL);
+ vp->destroy_client_port = destroy;
+ vp->client_port = port;
+
+ /* Subscribe to client port's events */
+ epub = pjmedia_port_get_event_publisher(port);
+ if (epub) {
+ pjmedia_event_subscription_init(&vp->esub_client_port,
+ &client_port_event_cb,
+ vp);
+ pjmedia_event_subscribe(epub, &vp->esub_client_port);
+ }
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_vid_port_disconnect(pjmedia_vid_port *vp)
+{
+ PJ_ASSERT_RETURN(vp && vp->role==ROLE_ACTIVE, PJ_EINVAL);
+
+ if (vp->client_port) {
+ pjmedia_event_unsubscribe(&vp->esub_client_port);
+ vp->client_port = NULL;
+ }
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pjmedia_port*)
+pjmedia_vid_port_get_connected_port(pjmedia_vid_port *vp)
+{
+ PJ_ASSERT_RETURN(vp && vp->role==ROLE_ACTIVE, NULL);
+ return vp->client_port;
+}
+
+PJ_DEF(pj_status_t) pjmedia_vid_port_start(pjmedia_vid_port *vp)
+{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(vp, PJ_EINVAL);
+
+ status = pjmedia_vid_dev_stream_start(vp->strm);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ if (vp->clock) {
+ status = pjmedia_clock_start(vp->clock);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+
+ return PJ_SUCCESS;
+
+on_error:
+ pjmedia_vid_port_stop(vp);
+ return status;
+}
+
+PJ_DEF(pj_status_t) pjmedia_vid_port_stop(pjmedia_vid_port *vp)
+{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(vp, PJ_EINVAL);
+
+ status = pjmedia_vid_dev_stream_stop(vp->strm);
+
+ if (vp->clock) {
+ status = pjmedia_clock_stop(vp->clock);
+ }
+
+ return status;
+}
+
+PJ_DEF(void) pjmedia_vid_port_destroy(pjmedia_vid_port *vp)
+{
+ PJ_ASSERT_ON_FAIL(vp, return);
+
+ PJ_LOG(4,(THIS_FILE, "Closing %s..", vp->dev_name.ptr));
+
+ if (vp->clock) {
+ pjmedia_clock_destroy(vp->clock);
+ vp->clock = NULL;
+ }
+ if (vp->strm) {
+ pjmedia_vid_dev_stream_destroy(vp->strm);
+ vp->strm = NULL;
+ }
+ if (vp->client_port) {
+ if (vp->destroy_client_port)
+ pjmedia_port_destroy(vp->client_port);
+ vp->client_port = NULL;
+ }
+ if (vp->frm_mutex) {
+ pj_mutex_destroy(vp->frm_mutex);
+ vp->frm_mutex = NULL;
+ }
+ if (vp->conv) {
+ pjmedia_converter_destroy(vp->conv);
+ vp->conv = NULL;
+ }
+ pj_pool_release(vp->pool);
+}
+
+/*
+static void save_rgb_frame(int width, int height, const pjmedia_frame *frm)
+{
+ static int counter;
+ FILE *pFile;
+ char szFilename[32];
+ const pj_uint8_t *pFrame = (const pj_uint8_t*)frm->buf;
+ int y;
+
+ if (counter > 10)
+ return;
+
+ // Open file
+ sprintf(szFilename, "frame%02d.ppm", counter++);
+ pFile=fopen(szFilename, "wb");
+ if(pFile==NULL)
+ return;
+
+ // Write header
+ fprintf(pFile, "P6\n%d %d\n255\n", width, height);
+
+ // Write pixel data
+ for(y=0; y<height; y++)
+ fwrite(pFrame+y*width*3, 1, width*3, pFile);
+
+ // Close file
+ fclose(pFile);
+}
+*/
+
+/* Handle event from vidstream */
+static pj_status_t vidstream_event_cb(pjmedia_event_subscription *esub,
+ pjmedia_event *event)
+{
+ pjmedia_vid_port *vp = (pjmedia_vid_port*)esub->user_data;
+
+ /* Just republish the event to our client */
+ return pjmedia_event_publish(&vp->epub, event);
+}
+
+static pj_status_t client_port_event_cb(pjmedia_event_subscription *esub,
+ pjmedia_event *event)
+{
+ pjmedia_vid_port *vp = (pjmedia_vid_port*)esub->user_data;
+
+ if (event->type == PJMEDIA_EVENT_FMT_CHANGED) {
+ const pjmedia_video_format_detail *vfd;
+ pj_status_t status;
+
+ ++event->proc_cnt;
+
+ pjmedia_vid_port_stop(vp);
+
+ /* Retrieve the video format detail */
+ vfd = pjmedia_format_get_video_format_detail(&vp->client_port->info.fmt,
+ PJ_TRUE);
+ if (!vfd)
+ return PJMEDIA_EVID_BADFORMAT;
+ pj_assert(vfd->fps.num);
+
+ /* Change the destination format to the new format */
+ pjmedia_format_copy(&vp->conv_param.src,
+ &vp->client_port->info.fmt);
+ /* Only copy the size here */
+ vp->conv_param.dst.det.vid.size =
+ vp->client_port->info.fmt.det.vid.size,
+
+ status = create_converter(vp);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(4,(THIS_FILE, status, "Error recreating converter"));
+ return status;
+ }
+
+ status = pjmedia_vid_dev_stream_set_cap(vp->strm,
+ PJMEDIA_VID_DEV_CAP_FORMAT,
+ &vp->conv_param.dst);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "failure in changing the format of the "
+ "video device"));
+ PJ_LOG(3, (THIS_FILE, "reverting to its original format: %s",
+ status != PJMEDIA_EVID_ERR ? "success" :
+ "failure"));
+ return status;
+ }
+
+ if (vp->stream_role == ROLE_PASSIVE) {
+ pjmedia_vid_dev_param vid_param;
+ pjmedia_clock_param clock_param;
+
+ /**
+ * Initially, frm_buf was allocated the biggest
+ * supported size, so we do not need to re-allocate
+ * the buffer here.
+ */
+ /* Adjust the clock */
+ pjmedia_vid_dev_stream_get_param(vp->strm, &vid_param);
+ clock_param.usec_interval = PJMEDIA_PTIME(&vfd->fps);
+ clock_param.clock_rate = vid_param.clock_rate;
+ pjmedia_clock_modify(vp->clock, &clock_param);
+ }
+
+ pjmedia_vid_port_start(vp);
+ }
+
+ /* Republish the event */
+ return pjmedia_event_publish(&vp->epub, event);
+}
+
+static pj_status_t convert_frame(pjmedia_vid_port *vp,
+ pjmedia_frame *src_frame,
+ pjmedia_frame *dst_frame)
+{
+ pj_status_t status = PJ_SUCCESS;
+
+ if (vp->conv) {
+ dst_frame->buf = vp->conv_buf;
+ dst_frame->size = vp->conv_buf_size;
+ status = pjmedia_converter_convert(vp->conv,
+ src_frame, dst_frame);
+ }
+
+ return status;
+}
+
+/* Copy frame to buffer. */
+static void copy_frame_to_buffer(pjmedia_vid_port *vp,
+ pjmedia_frame *frame)
+{
+ pj_mutex_lock(vp->frm_mutex);
+ pjmedia_frame_copy(vp->frm_buf, frame);
+ pj_mutex_unlock(vp->frm_mutex);
+}
+
+/* Get frame from buffer and convert it if necessary. */
+static pj_status_t get_frame_from_buffer(pjmedia_vid_port *vp,
+ pjmedia_frame *frame)
+{
+ pj_status_t status = PJ_SUCCESS;
+
+ pj_mutex_lock(vp->frm_mutex);
+ if (vp->conv)
+ status = convert_frame(vp, vp->frm_buf, frame);
+ else
+ pjmedia_frame_copy(frame, vp->frm_buf);
+ pj_mutex_unlock(vp->frm_mutex);
+
+ return status;
+}
+
+static void enc_clock_cb(const pj_timestamp *ts, void *user_data)
+{
+ /* We are here because user wants us to be active but the stream is
+ * passive. So get a frame from the stream and push it to user.
+ */
+ pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data;
+ pj_status_t status;
+
+ pj_assert(vp->role==ROLE_ACTIVE && vp->stream_role==ROLE_PASSIVE);
+
+ PJ_UNUSED_ARG(ts);
+
+ if (!vp->client_port)
+ return;
+
+ vp->frm_buf->size = vp->frm_buf_size;
+ status = pjmedia_vid_dev_stream_get_frame(vp->strm, vp->frm_buf);
+ if (status != PJ_SUCCESS)
+ return;
+
+ //save_rgb_frame(vp->cap_size.w, vp->cap_size.h, vp->frm_buf);
+
+ vidstream_cap_cb(vp->strm, vp, vp->frm_buf);
+}
+
+static void dec_clock_cb(const pj_timestamp *ts, void *user_data)
+{
+ /* We are here because user wants us to be active but the stream is
+ * passive. So get a frame from the stream and push it to user.
+ */
+ pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data;
+ pj_status_t status;
+ pjmedia_frame frame;
+
+ pj_assert(vp->role==ROLE_ACTIVE && vp->stream_role==ROLE_PASSIVE);
+
+ PJ_UNUSED_ARG(ts);
+
+ if (!vp->client_port)
+ return;
+
+ status = vidstream_render_cb(vp->strm, vp, &frame);
+ if (status != PJ_SUCCESS)
+ return;
+
+ if (frame.size > 0)
+ status = pjmedia_vid_dev_stream_put_frame(vp->strm, &frame);
+}
+
+static pj_status_t vidstream_cap_cb(pjmedia_vid_dev_stream *stream,
+ void *user_data,
+ pjmedia_frame *frame)
+{
+ pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data;
+
+ if (vp->role==ROLE_ACTIVE) {
+ pj_status_t status;
+ pjmedia_frame frame_;
+
+ status = convert_frame(vp, frame, &frame_);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if (vp->client_port)
+ status = pjmedia_port_put_frame(vp->client_port,
+ (vp->conv? &frame_: frame));
+ if (status != PJ_SUCCESS)
+ return status;
+ } else {
+ /* We are passive while the stream is active so we just store the
+ * frame in the buffer.
+ * The decoding counterpart is located in vid_pasv_port_put_frame()
+ */
+ copy_frame_to_buffer(vp, frame);
+ }
+ /* This is tricky since the frame is still in its original unconverted
+ * format, which may not be what the application expects.
+ */
+ if (vp->strm_cb.capture_cb)
+ return (*vp->strm_cb.capture_cb)(stream, vp->strm_cb_data, frame);
+ return PJ_SUCCESS;
+}
+
+static pj_status_t vidstream_render_cb(pjmedia_vid_dev_stream *stream,
+ void *user_data,
+ pjmedia_frame *frame)
+{
+ pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data;
+ pj_status_t status = PJ_SUCCESS;
+
+ pj_bzero(frame, sizeof(pjmedia_frame));
+ if (vp->role==ROLE_ACTIVE) {
+ unsigned frame_ts = vp->clocksrc.clock_rate / 1000 *
+ vp->clocksrc.ptime_usec / 1000;
+
+ if (!vp->client_port)
+ return status;
+
+ if (vp->sync_clocksrc.sync_clocksrc) {
+ pjmedia_clock_src *src = vp->sync_clocksrc.sync_clocksrc;
+ pj_int32_t diff;
+ unsigned nsync_frame;
+
+ /* Synchronization */
+ /* Calculate the time difference (in ms) with the sync source */
+ diff = pjmedia_clock_src_get_time_msec(&vp->clocksrc) -
+ pjmedia_clock_src_get_time_msec(src) -
+ vp->sync_clocksrc.sync_delta;
+
+ /* Check whether sync source made a large jump */
+ if (diff < 0 && -diff > PJMEDIA_CLOCK_SYNC_MAX_SYNC_MSEC) {
+ pjmedia_clock_src_update(&vp->clocksrc, NULL);
+ vp->sync_clocksrc.sync_delta =
+ pjmedia_clock_src_get_time_msec(src) -
+ pjmedia_clock_src_get_time_msec(&vp->clocksrc);
+ vp->sync_clocksrc.nsync_frame = 0;
+ return status;
+ }
+
+ /* Calculate the difference (in frames) with the sync source */
+ nsync_frame = abs(diff) * 1000 / vp->clocksrc.ptime_usec;
+ if (nsync_frame == 0) {
+ /* Nothing to sync */
+ vp->sync_clocksrc.nsync_frame = 0;
+ } else {
+ pj_int32_t init_sync_frame = nsync_frame;
+
+ /* Check whether it's a new sync or whether we need to reset
+ * the sync
+ */
+ if (vp->sync_clocksrc.nsync_frame == 0 ||
+ (vp->sync_clocksrc.nsync_frame > 0 &&
+ nsync_frame > vp->sync_clocksrc.nsync_frame))
+ {
+ vp->sync_clocksrc.nsync_frame = nsync_frame;
+ vp->sync_clocksrc.nsync_progress = 0;
+ } else {
+ init_sync_frame = vp->sync_clocksrc.nsync_frame;
+ }
+
+ if (diff >= 0) {
+ unsigned skip_mod;
+
+ /* We are too fast */
+ if (vp->sync_clocksrc.max_sync_ticks > 0) {
+ skip_mod = init_sync_frame /
+ vp->sync_clocksrc.max_sync_ticks + 2;
+ } else
+ skip_mod = init_sync_frame + 2;
+
+ PJ_LOG(5, (THIS_FILE, "synchronization: early by %d ms",
+ diff));
+ /* We'll play a frame every skip_mod-th tick instead of
+ * a complete pause
+ */
+ if (++vp->sync_clocksrc.nsync_progress % skip_mod > 0) {
+ pjmedia_clock_src_update(&vp->clocksrc, NULL);
+ return status;
+ }
+ } else {
+ unsigned i, ndrop = init_sync_frame;
+
+ /* We are too late, drop the frame */
+ if (vp->sync_clocksrc.max_sync_ticks > 0) {
+ ndrop /= vp->sync_clocksrc.max_sync_ticks;
+ ndrop++;
+ }
+ PJ_LOG(5, (THIS_FILE, "synchronization: late, "
+ "dropping %d frame(s)", ndrop));
+
+ if (ndrop >= nsync_frame) {
+ vp->sync_clocksrc.nsync_frame = 0;
+ ndrop = nsync_frame;
+ } else
+ vp->sync_clocksrc.nsync_progress += ndrop;
+
+ for (i = 0; i < ndrop; i++) {
+ vp->frm_buf->size = vp->frm_buf_size;
+ status = pjmedia_port_get_frame(vp->client_port,
+ vp->frm_buf);
+ if (status != PJ_SUCCESS) {
+ pjmedia_clock_src_update(&vp->clocksrc, NULL);
+ return status;
+ }
+
+ pj_add_timestamp32(&vp->clocksrc.timestamp,
+ frame_ts);
+ }
+ }
+ }
+ }
+
+ vp->frm_buf->size = vp->frm_buf_size;
+ status = pjmedia_port_get_frame(vp->client_port, vp->frm_buf);
+ if (status != PJ_SUCCESS) {
+ pjmedia_clock_src_update(&vp->clocksrc, NULL);
+ return status;
+ }
+ pj_add_timestamp32(&vp->clocksrc.timestamp, frame_ts);
+ pjmedia_clock_src_update(&vp->clocksrc, NULL);
+
+ status = convert_frame(vp, vp->frm_buf, frame);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if (!vp->conv)
+ pj_memcpy(frame, vp->frm_buf, sizeof(*frame));
+ } else {
+ /* The stream is active while we are passive so we need to get the
+ * frame from the buffer.
+ * The encoding counterpart is located in vid_pasv_port_get_frame()
+ */
+ get_frame_from_buffer(vp, frame);
+ }
+ if (vp->strm_cb.render_cb)
+ return (*vp->strm_cb.render_cb)(stream, vp->strm_cb_data, frame);
+ return PJ_SUCCESS;
+}
+
+static pj_status_t vid_pasv_port_put_frame(struct pjmedia_port *this_port,
+ pjmedia_frame *frame)
+{
+ struct vid_pasv_port *vpp = (struct vid_pasv_port*)this_port;
+ pjmedia_vid_port *vp = vpp->vp;
+
+ if (vp->stream_role==ROLE_PASSIVE) {
+ /* We are passive and the stream is passive.
+ * The encoding counterpart is in vid_pasv_port_get_frame().
+ */
+ pj_status_t status;
+ pjmedia_frame frame_;
+
+ status = convert_frame(vp, frame, &frame_);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ return pjmedia_vid_dev_stream_put_frame(vp->strm,
+ (vp->conv? &frame_: frame));
+ } else {
+ /* We are passive while the stream is active so we just store the
+ * frame in the buffer.
+ * The encoding counterpart is located in vidstream_cap_cb()
+ */
+ copy_frame_to_buffer(vp, frame);
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t vid_pasv_port_get_frame(struct pjmedia_port *this_port,
+ pjmedia_frame *frame)
+{
+ struct vid_pasv_port *vpp = (struct vid_pasv_port*)this_port;
+ pjmedia_vid_port *vp = vpp->vp;
+ pj_status_t status = PJ_SUCCESS;
+
+ if (vp->stream_role==ROLE_PASSIVE) {
+ /* We are passive and the stream is passive.
+ * The decoding counterpart is in vid_pasv_port_put_frame().
+ */
+ status = pjmedia_vid_dev_stream_get_frame(vp->strm,
+ (vp->conv? vp->frm_buf:
+ frame));
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = convert_frame(vp, vp->frm_buf, frame);
+ } else {
+ /* The stream is active while we are passive so we need to get the
+ * frame from the buffer.
+ * The decoding counterpart is located in vidstream_rend_cb()
+ */
+ get_frame_from_buffer(vp, frame);
+ }
+
+ return status;
+}
diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c
new file mode 100644
index 00000000..216674b5
--- /dev/null
+++ b/pjmedia/src/pjmedia/vid_stream.c
@@ -0,0 +1,1940 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/vid_stream.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/event.h>
+#include <pjmedia/rtp.h>
+#include <pjmedia/rtcp.h>
+#include <pjmedia/jbuf.h>
+#include <pjmedia/sdp_neg.h>
+#include <pjmedia/stream_common.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/ctype.h>
+#include <pj/compat/socket.h>
+#include <pj/errno.h>
+#include <pj/ioqueue.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/rand.h>
+#include <pj/sock_select.h>
+#include <pj/string.h> /* memcpy() */
+
+
+#define THIS_FILE "vid_stream.c"
+#define ERRLEVEL 1
+#define LOGERR_(expr) stream_perror expr
+#define TRC_(expr) PJ_LOG(5,expr)
+#define SIGNATURE PJMEDIA_SIG_PORT_VID_STREAM
+
+/* Tracing jitter buffer operations in a stream session to a CSV file.
+ * The trace will contain JB operation timestamp, frame info, RTP info, and
+ * the JB state right after the operation.
+ */
+#define TRACE_JB 0 /* Enable/disable trace. */
+#define TRACE_JB_PATH_PREFIX "" /* Optional path/prefix
+ for the CSV filename. */
+#if TRACE_JB
+# include <pj/file_io.h>
+# define TRACE_JB_INVALID_FD ((pj_oshandle_t)-1)
+# define TRACE_JB_OPENED(s) (s->trace_jb_fd != TRACE_JB_INVALID_FD)
+#endif
+
+#ifndef PJMEDIA_VSTREAM_SIZE
+# define PJMEDIA_VSTREAM_SIZE 1000
+#endif
+
+#ifndef PJMEDIA_VSTREAM_INC
+# define PJMEDIA_VSTREAM_INC 1000
+#endif
+
+
+/**
+ * Media channel.
+ */
+typedef struct pjmedia_vid_channel
+{
+ pjmedia_vid_stream *stream; /**< Parent stream. */
+ pjmedia_dir dir; /**< Channel direction. */
+ pjmedia_port port; /**< Port interface. */
+ unsigned pt; /**< Payload type. */
+ pj_bool_t paused; /**< Paused?. */
+ void *buf; /**< Output buffer. */
+ unsigned buf_size; /**< Size of output buffer. */
+ unsigned buf_len; /**< Length of data in buffer. */
+ pjmedia_rtp_session rtp; /**< RTP session. */
+} pjmedia_vid_channel;
+
+
+/**
+ * This structure describes media stream.
+ * A media stream is bidirectional media transmission between two endpoints.
+ * It consists of two channels, i.e. encoding and decoding channels.
+ * A media stream corresponds to a single "m=" line in a SDP session
+ * description.
+ */
+struct pjmedia_vid_stream
+{
+ pj_pool_t *own_pool; /**< Internal pool. */
+ pjmedia_endpt *endpt; /**< Media endpoint. */
+ pjmedia_vid_codec_mgr *codec_mgr; /**< Codec manager. */
+ pjmedia_vid_stream_info info; /**< Stream info. */
+
+ pjmedia_vid_channel *enc; /**< Encoding channel. */
+ pjmedia_vid_channel *dec; /**< Decoding channel. */
+
+ pjmedia_dir dir; /**< Stream direction. */
+ void *user_data; /**< User data. */
+ pj_str_t name; /**< Stream name */
+ pj_str_t cname; /**< SDES CNAME */
+
+ pjmedia_transport *transport; /**< Stream transport. */
+
+ pj_mutex_t *jb_mutex;
+ pjmedia_jbuf *jb; /**< Jitter buffer. */
+ char jb_last_frm; /**< Last frame type from jb */
+ unsigned jb_last_frm_cnt;/**< Last JB frame type counter*/
+
+ pjmedia_rtcp_session rtcp; /**< RTCP for incoming RTP. */
+ pj_uint32_t rtcp_last_tx; /**< RTCP tx time in timestamp */
+ pj_uint32_t rtcp_interval; /**< Interval, in timestamp. */
+ pj_bool_t initial_rr; /**< Initial RTCP RR sent */
+
+ unsigned frame_size; /**< Size of encoded base frame.*/
+ unsigned frame_ts_len; /**< Frame length in timestamp. */
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+ pj_bool_t use_ka; /**< Stream keep-alive with non-
+ codec-VAD mechanism is
+ enabled? */
+ pj_timestamp last_frm_ts_sent; /**< Timestamp of last sending
+ packet */
+#endif
+
+#if TRACE_JB
+ pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/
+ char *trace_jb_buf; /**< Jitter tracing buffer. */
+#endif
+
+ pjmedia_vid_codec *codec; /**< Codec instance being used. */
+ pj_uint32_t last_dec_ts; /**< Last decoded timestamp. */
+ int last_dec_seq; /**< Last decoded sequence. */
+
+ pjmedia_event_subscription esub_codec; /**< To subscribe codec events */
+ pjmedia_event_publisher epub; /**< To publish events */
+};
+
+
+/*
+ * Print error.
+ */
+static void stream_perror(const char *sender, const char *title,
+ pj_status_t status)
+{
+ char errmsg[PJ_ERR_MSG_SIZE];
+
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ PJ_LOG(4,(sender, "%s: %s [err:%d]", title, errmsg, status));
+}
+
+
+#if TRACE_JB
+
+PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len)
+{
+ pj_time_val now;
+ pj_parsed_time ptime;
+ char *p = *buf;
+
+ if (len < 14)
+ return -1;
+
+ pj_gettimeofday(&now);
+ pj_time_decode(&now, &ptime);
+ p += pj_utoa_pad(ptime.hour, p, 2, '0');
+ *p++ = ':';
+ p += pj_utoa_pad(ptime.min, p, 2, '0');
+ *p++ = ':';
+ p += pj_utoa_pad(ptime.sec, p, 2, '0');
+ *p++ = '.';
+ p += pj_utoa_pad(ptime.msec, p, 3, '0');
+ *p++ = ',';
+
+ *buf = p;
+
+ return 0;
+}
+
+PJ_INLINE(int) trace_jb_print_state(pjmedia_vid_stream *stream,
+ char **buf, pj_ssize_t len)
+{
+ char *p = *buf;
+ char *endp = *buf + len;
+ pjmedia_jb_state state;
+
+ pjmedia_jbuf_get_state(stream->jb, &state);
+
+ len = pj_ansi_snprintf(p, endp-p, "%d, %d, %d",
+ state.size, state.burst, state.prefetch);
+ if ((len < 0) || (len >= endp-p))
+ return -1;
+
+ p += len;
+ *buf = p;
+ return 0;
+}
+
+static void trace_jb_get(pjmedia_vid_stream *stream, pjmedia_jb_frame_type ft,
+ pj_size_t fsize)
+{
+ char *p = stream->trace_jb_buf;
+ char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE;
+ pj_ssize_t len = 0;
+ const char* ft_st;
+
+ if (!TRACE_JB_OPENED(stream))
+ return;
+
+ /* Print timestamp. */
+ if (trace_jb_print_timestamp(&p, endp-p))
+ goto on_insuff_buffer;
+
+ /* Print frame type and size */
+ switch(ft) {
+ case PJMEDIA_JB_MISSING_FRAME:
+ ft_st = "missing";
+ break;
+ case PJMEDIA_JB_NORMAL_FRAME:
+ ft_st = "normal";
+ break;
+ case PJMEDIA_JB_ZERO_PREFETCH_FRAME:
+ ft_st = "prefetch";
+ break;
+ case PJMEDIA_JB_ZERO_EMPTY_FRAME:
+ ft_st = "empty";
+ break;
+ default:
+ ft_st = "unknown";
+ break;
+ }
+
+ /* Print operation, size, frame count, frame type */
+ len = pj_ansi_snprintf(p, endp-p, "GET,%d,1,%s,,,,", fsize, ft_st);
+ if ((len < 0) || (len >= endp-p))
+ goto on_insuff_buffer;
+ p += len;
+
+ /* Print JB state */
+ if (trace_jb_print_state(stream, &p, endp-p))
+ goto on_insuff_buffer;
+
+ /* Print end of line */
+ if (endp-p < 2)
+ goto on_insuff_buffer;
+ *p++ = '\n';
+
+ /* Write and flush */
+ len = p - stream->trace_jb_buf;
+ pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len);
+ pj_file_flush(stream->trace_jb_fd);
+ return;
+
+on_insuff_buffer:
+ pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!");
+}
+
+static void trace_jb_put(pjmedia_vid_stream *stream,
+ const pjmedia_rtp_hdr *hdr,
+ unsigned payloadlen, unsigned frame_cnt)
+{
+ char *p = stream->trace_jb_buf;
+ char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE;
+ pj_ssize_t len = 0;
+
+ if (!TRACE_JB_OPENED(stream))
+ return;
+
+ /* Print timestamp. */
+ if (trace_jb_print_timestamp(&p, endp-p))
+ goto on_insuff_buffer;
+
+ /* Print operation, size, frame count, RTP info */
+ len = pj_ansi_snprintf(p, endp-p,
+ "PUT,%d,%d,,%d,%d,%d,",
+ payloadlen, frame_cnt,
+ pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), hdr->m);
+ if ((len < 0) || (len >= endp-p))
+ goto on_insuff_buffer;
+ p += len;
+
+ /* Print JB state */
+ if (trace_jb_print_state(stream, &p, endp-p))
+ goto on_insuff_buffer;
+
+ /* Print end of line */
+ if (endp-p < 2)
+ goto on_insuff_buffer;
+ *p++ = '\n';
+
+ /* Write and flush */
+ len = p - stream->trace_jb_buf;
+ pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len);
+ pj_file_flush(stream->trace_jb_fd);
+ return;
+
+on_insuff_buffer:
+ pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!");
+}
+
+#endif /* TRACE_JB */
+
+static void dump_port_info(const pjmedia_vid_channel *chan,
+ const char *event_name)
+{
+ const pjmedia_port_info *pi = &chan->port.info;
+ char fourcc_name[5];
+
+ PJ_LOG(5, (pi->name.ptr,
+ " %s format %s: %dx%d %s%s %d/%d(~%d)fps",
+ (chan->dir==PJMEDIA_DIR_DECODING? "Decoding":"Encoding"),
+ event_name,
+ pi->fmt.det.vid.size.w, pi->fmt.det.vid.size.h,
+ pjmedia_fourcc_name(pi->fmt.id, fourcc_name),
+ (chan->dir==PJMEDIA_DIR_ENCODING?"->":"<-"),
+ pi->fmt.det.vid.fps.num, pi->fmt.det.vid.fps.denum,
+ pi->fmt.det.vid.fps.num/pi->fmt.det.vid.fps.denum));
+}
+
+/*
+ * Handle events from stream components.
+ */
+static pj_status_t stream_event_cb(pjmedia_event_subscription *esub,
+ pjmedia_event *event)
+{
+ pjmedia_vid_stream *stream = (pjmedia_vid_stream*)esub->user_data;
+
+ if (esub == &stream->esub_codec) {
+ /* This is codec event */
+ switch (event->type) {
+ case PJMEDIA_EVENT_FMT_CHANGED:
+ /* Update param from codec */
+ pjmedia_vid_codec_get_param(stream->codec, stream->info.codec_param);
+
+ /* Update decoding channel port info */
+ pjmedia_format_copy(&stream->dec->port.info.fmt,
+ &stream->info.codec_param->dec_fmt);
+
+ /* we process the event */
+ ++event->proc_cnt;
+
+ dump_port_info(event->data.fmt_changed.dir==PJMEDIA_DIR_DECODING ?
+ stream->dec : stream->enc,
+ "changed");
+ break;
+ default:
+ break;
+ }
+ }
+
+ return pjmedia_event_publish(&stream->epub, event);
+}
+
+static pjmedia_event_publisher *port_get_epub(pjmedia_port *port)
+{
+ pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata;
+ return &stream->epub;
+}
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0
+/*
+ * Send keep-alive packet using non-codec frame.
+ */
+static void send_keep_alive_packet(pjmedia_vid_stream *stream)
+{
+#if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP
+
+ /* Keep-alive packet is empty RTP */
+ pj_status_t status;
+ void *pkt;
+ int pkt_len;
+
+ TRC_((channel->port.info.name.ptr,
+ "Sending keep-alive (RTCP and empty RTP)"));
+
+ /* Send RTP */
+ status = pjmedia_rtp_encode_rtp( &stream->enc->rtp,
+ stream->enc->pt, 0,
+ 1,
+ 0,
+ (const void**)&pkt,
+ &pkt_len);
+ pj_assert(status == PJ_SUCCESS);
+
+ pj_memcpy(stream->enc->buf, pkt, pkt_len);
+ pjmedia_transport_send_rtp(stream->transport, stream->enc->buf,
+ pkt_len);
+
+ /* Send RTCP */
+ pjmedia_rtcp_build_rtcp(&stream->rtcp, &pkt, &pkt_len);
+ pjmedia_transport_send_rtcp(stream->transport, pkt, pkt_len);
+
+#elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER
+
+ /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */
+ int pkt_len;
+ const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT;
+
+ TRC_((channel->port.info.name.ptr,
+ "Sending keep-alive (custom RTP/RTCP packets)"));
+
+ /* Send to RTP port */
+ pj_memcpy(stream->enc->buf, str_ka.ptr, str_ka.slen);
+ pkt_len = str_ka.slen;
+ pjmedia_transport_send_rtp(stream->transport, stream->enc->buf,
+ pkt_len);
+
+ /* Send to RTCP port */
+ pjmedia_transport_send_rtcp(stream->transport, stream->enc->buf,
+ pkt_len);
+
+#else
+
+ PJ_UNUSED_ARG(stream);
+
+#endif
+}
+#endif /* defined(PJMEDIA_STREAM_ENABLE_KA) */
+
+
+/**
+ * check_tx_rtcp()
+ *
+ * This function is can be called by either put_frame() or get_frame(),
+ * to transmit periodic RTCP SR/RR report.
+ */
+static void check_tx_rtcp(pjmedia_vid_stream *stream, pj_uint32_t timestamp)
+{
+ /* Note that timestamp may represent local or remote timestamp,
+ * depending on whether this function is called from put_frame()
+ * or get_frame().
+ */
+
+
+ if (stream->rtcp_last_tx == 0) {
+
+ stream->rtcp_last_tx = timestamp;
+
+ } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) {
+
+ void *rtcp_pkt;
+ int len;
+
+ pjmedia_rtcp_build_rtcp(&stream->rtcp, &rtcp_pkt, &len);
+
+ pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
+
+ stream->rtcp_last_tx = timestamp;
+ }
+}
+
+/* Build RTCP SDES packet */
+static unsigned create_rtcp_sdes(pjmedia_vid_stream *stream, pj_uint8_t *pkt,
+ unsigned max_len)
+{
+ pjmedia_rtcp_common hdr;
+ pj_uint8_t *p = pkt;
+
+ /* SDES header */
+ hdr.version = 2;
+ hdr.p = 0;
+ hdr.count = 1;
+ hdr.pt = 202;
+ hdr.length = 2 + (4+stream->cname.slen+3)/4 - 1;
+ if (max_len < (hdr.length << 2)) {
+ pj_assert(!"Not enough buffer for SDES packet");
+ return 0;
+ }
+ hdr.length = pj_htons((pj_uint16_t)hdr.length);
+ hdr.ssrc = stream->enc->rtp.out_hdr.ssrc;
+ pj_memcpy(p, &hdr, sizeof(hdr));
+ p += sizeof(hdr);
+
+ /* CNAME item */
+ *p++ = 1;
+ *p++ = (pj_uint8_t)stream->cname.slen;
+ pj_memcpy(p, stream->cname.ptr, stream->cname.slen);
+ p += stream->cname.slen;
+
+ /* END */
+ *p++ = '\0';
+ *p++ = '\0';
+
+ /* Pad to 32bit */
+ while ((p-pkt) % 4)
+ *p++ = '\0';
+
+ return (p - pkt);
+}
+
+/* Build RTCP BYE packet */
+static unsigned create_rtcp_bye(pjmedia_vid_stream *stream, pj_uint8_t *pkt,
+ unsigned max_len)
+{
+ pjmedia_rtcp_common hdr;
+
+ /* BYE header */
+ hdr.version = 2;
+ hdr.p = 0;
+ hdr.count = 1;
+ hdr.pt = 203;
+ hdr.length = 1;
+ if (max_len < (hdr.length << 2)) {
+ pj_assert(!"Not enough buffer for SDES packet");
+ return 0;
+ }
+ hdr.length = pj_htons((pj_uint16_t)hdr.length);
+ hdr.ssrc = stream->enc->rtp.out_hdr.ssrc;
+ pj_memcpy(pkt, &hdr, sizeof(hdr));
+
+ return sizeof(hdr);
+}
+
+
+#if 0
+static void dump_bin(const char *buf, unsigned len)
+{
+ unsigned i;
+
+ PJ_LOG(3,(THIS_FILE, "begin dump"));
+ for (i=0; i<len; ++i) {
+ int j;
+ char bits[9];
+ unsigned val = buf[i] & 0xFF;
+
+ bits[8] = '\0';
+ for (j=0; j<8; ++j) {
+ if (val & (1 << (7-j)))
+ bits[j] = '1';
+ else
+ bits[j] = '0';
+ }
+
+ PJ_LOG(3,(THIS_FILE, "%2d %s [%d]", i, bits, val));
+ }
+ PJ_LOG(3,(THIS_FILE, "end dump"));
+}
+#endif
+
+
+/*
+ * This callback is called by stream transport on receipt of packets
+ * in the RTP socket.
+ */
+static void on_rx_rtp( void *data,
+ void *pkt,
+ pj_ssize_t bytes_read)
+
+{
+ pjmedia_vid_stream *stream = (pjmedia_vid_stream*) data;
+ pjmedia_vid_channel *channel = stream->dec;
+ const pjmedia_rtp_hdr *hdr;
+ const void *payload;
+ unsigned payloadlen;
+ pjmedia_rtp_status seq_st;
+ pj_status_t status;
+ pj_bool_t pkt_discarded = PJ_FALSE;
+
+ /* Check for errors */
+ if (bytes_read < 0) {
+ LOGERR_((channel->port.info.name.ptr, "RTP recv() error", -bytes_read));
+ return;
+ }
+
+ /* Ignore keep-alive packets */
+ if (bytes_read < (pj_ssize_t) sizeof(pjmedia_rtp_hdr))
+ return;
+
+ /* Update RTP and RTCP session. */
+ status = pjmedia_rtp_decode_rtp(&channel->rtp, pkt, bytes_read,
+ &hdr, &payload, &payloadlen);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((channel->port.info.name.ptr, "RTP decode error", status));
+ stream->rtcp.stat.rx.discard++;
+ return;
+ }
+
+ /* Ignore the packet if decoder is paused */
+ if (channel->paused)
+ goto on_return;
+
+ /* Update RTP session (also checks if RTP session can accept
+ * the incoming packet.
+ */
+ pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, PJ_TRUE);
+ if (seq_st.status.value) {
+ TRC_ ((channel->port.info.name.ptr,
+ "RTP status: badpt=%d, badssrc=%d, dup=%d, "
+ "outorder=%d, probation=%d, restart=%d",
+ seq_st.status.flag.badpt,
+ seq_st.status.flag.badssrc,
+ seq_st.status.flag.dup,
+ seq_st.status.flag.outorder,
+ seq_st.status.flag.probation,
+ seq_st.status.flag.restart));
+
+ if (seq_st.status.flag.badpt) {
+ PJ_LOG(4,(channel->port.info.name.ptr,
+ "Bad RTP pt %d (expecting %d)",
+ hdr->pt, channel->rtp.out_pt));
+ }
+
+ if (seq_st.status.flag.badssrc) {
+ PJ_LOG(4,(channel->port.info.name.ptr,
+ "Changed RTP peer SSRC %d (previously %d)",
+ channel->rtp.peer_ssrc, stream->rtcp.peer_ssrc));
+ stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc;
+ }
+
+
+ }
+
+ /* Skip bad RTP packet */
+ if (seq_st.status.flag.bad) {
+ pkt_discarded = PJ_TRUE;
+ goto on_return;
+ }
+
+ /* Ignore if payloadlen is zero */
+ if (payloadlen == 0) {
+ pkt_discarded = PJ_TRUE;
+ goto on_return;
+ }
+
+
+ /* Put "good" packet to jitter buffer, or reset the jitter buffer
+ * when RTP session is restarted.
+ */
+ pj_mutex_lock( stream->jb_mutex );
+ if (seq_st.status.flag.restart) {
+ status = pjmedia_jbuf_reset(stream->jb);
+ PJ_LOG(4,(channel->port.info.name.ptr, "Jitter buffer reset"));
+ } else {
+ /* Just put the payload into jitter buffer */
+ pjmedia_jbuf_put_frame3(stream->jb, payload, payloadlen, 0,
+ pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), NULL);
+
+#if TRACE_JB
+ trace_jb_put(stream, hdr, payloadlen, count);
+#endif
+
+ }
+ pj_mutex_unlock( stream->jb_mutex );
+
+
+ /* Check if now is the time to transmit RTCP SR/RR report.
+ * We only do this when stream direction is "decoding only",
+ * because otherwise check_tx_rtcp() will be handled by put_frame()
+ */
+ if (stream->dir == PJMEDIA_DIR_DECODING) {
+ check_tx_rtcp(stream, pj_ntohl(hdr->ts));
+ }
+
+ if (status != 0) {
+ LOGERR_((channel->port.info.name.ptr, "Jitter buffer put() error",
+ status));
+ pkt_discarded = PJ_TRUE;
+ goto on_return;
+ }
+
+on_return:
+ /* Update RTCP session */
+ if (stream->rtcp.peer_ssrc == 0)
+ stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc;
+
+ pjmedia_rtcp_rx_rtp2(&stream->rtcp, pj_ntohs(hdr->seq),
+ pj_ntohl(hdr->ts), payloadlen, pkt_discarded);
+
+ /* Send RTCP RR and SDES after we receive some RTP packets */
+ if (stream->rtcp.received >= 10 && !stream->initial_rr) {
+ void *sr_rr_pkt;
+ pj_uint8_t *pkt;
+ int len;
+
+ /* Build RR or SR */
+ pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len);
+ pkt = (pj_uint8_t*) stream->enc->buf;
+ pj_memcpy(pkt, sr_rr_pkt, len);
+ pkt += len;
+
+ /* Append SDES */
+ len = create_rtcp_sdes(stream, (pj_uint8_t*)pkt,
+ stream->enc->buf_size - len);
+ if (len > 0) {
+ pkt += len;
+ len = ((pj_uint8_t*)pkt) - ((pj_uint8_t*)stream->enc->buf);
+ pjmedia_transport_send_rtcp(stream->transport,
+ stream->enc->buf, len);
+ }
+
+ stream->initial_rr = PJ_TRUE;
+ }
+}
+
+
+/*
+ * This callback is called by stream transport on receipt of packets
+ * in the RTCP socket.
+ */
+static void on_rx_rtcp( void *data,
+ void *pkt,
+ pj_ssize_t bytes_read)
+{
+ pjmedia_vid_stream *stream = (pjmedia_vid_stream*) data;
+
+ /* Check for errors */
+ if (bytes_read < 0) {
+ LOGERR_((stream->cname.ptr, "RTCP recv() error",
+ -bytes_read));
+ return;
+ }
+
+ pjmedia_rtcp_rx_rtcp(&stream->rtcp, pkt, bytes_read);
+}
+
+static pj_status_t put_frame(pjmedia_port *port,
+ pjmedia_frame *frame)
+{
+ pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata;
+ pjmedia_vid_channel *channel = stream->enc;
+ pj_status_t status = 0;
+ pjmedia_frame frame_out;
+ unsigned rtp_ts_len;
+ void *rtphdr;
+ int rtphdrlen;
+ unsigned processed = 0;
+
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0
+ /* If the interval since last sending packet is greater than
+ * PJMEDIA_STREAM_KA_INTERVAL, send keep-alive packet.
+ */
+ if (stream->use_ka)
+ {
+ pj_uint32_t dtx_duration;
+
+ dtx_duration = pj_timestamp_diff32(&stream->last_frm_ts_sent,
+ &frame->timestamp);
+ if (dtx_duration >
+ PJMEDIA_STREAM_KA_INTERVAL * channel->port.info.clock_rate)
+ {
+ send_keep_alive_packet(stream);
+ stream->last_frm_ts_sent = frame->timestamp;
+ }
+ }
+#endif
+
+ /* Don't do anything if stream is paused */
+ if (channel->paused) {
+ return PJ_SUCCESS;
+ }
+
+ /* Get frame length in timestamp unit */
+ rtp_ts_len = stream->frame_ts_len;
+
+ /* Init frame_out buffer. */
+ frame_out.buf = ((char*)channel->buf) + sizeof(pjmedia_rtp_hdr);
+ frame_out.size = 0;
+
+ /* Encode! */
+ status = pjmedia_vid_codec_encode(stream->codec, frame,
+ channel->buf_size -
+ sizeof(pjmedia_rtp_hdr),
+ &frame_out);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((channel->port.info.name.ptr,
+ "Codec encode() error", status));
+
+ /* Update RTP timestamp */
+ pjmedia_rtp_encode_rtp(&channel->rtp, channel->pt, 1, 0,
+ rtp_ts_len, (const void**)&rtphdr, &rtphdrlen);
+ return status;
+ }
+
+
+ while (processed < frame_out.size) {
+ pj_uint8_t *payload;
+ pj_uint8_t *rtp_pkt;
+ pj_size_t payload_len;
+
+ /* Generate RTP payload */
+ status = pjmedia_vid_codec_packetize(stream->codec,
+ (pj_uint8_t*)frame_out.buf,
+ frame_out.size,
+ &processed,
+ (const pj_uint8_t**)&payload,
+ &payload_len);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((channel->port.info.name.ptr,
+ "Codec pack() error", status));
+
+ /* Update RTP timestamp */
+ pjmedia_rtp_encode_rtp(&channel->rtp, channel->pt, 1, 0,
+ rtp_ts_len, (const void**)&rtphdr,
+ &rtphdrlen);
+ return status;
+ }
+
+ /* Encapsulate. */
+ status = pjmedia_rtp_encode_rtp( &channel->rtp,
+ channel->pt,
+ (processed==frame_out.size?1:0),
+ payload_len,
+ rtp_ts_len,
+ (const void**)&rtphdr,
+ &rtphdrlen);
+
+ if (status != PJ_SUCCESS) {
+ LOGERR_((channel->port.info.name.ptr,
+ "RTP encode_rtp() error", status));
+ return status;
+ }
+
+ /* Next packets use same timestamp */
+ rtp_ts_len = 0;
+
+ rtp_pkt = payload - sizeof(pjmedia_rtp_hdr);
+
+ /* Copy RTP header to the beginning of packet */
+ pj_memcpy(rtp_pkt, rtphdr, sizeof(pjmedia_rtp_hdr));
+
+ /* Send the RTP packet to the transport. */
+ pjmedia_transport_send_rtp(stream->transport, rtp_pkt,
+ payload_len + sizeof(pjmedia_rtp_hdr));
+ }
+
+ /* Check if now is the time to transmit RTCP SR/RR report.
+ * We only do this when stream direction is not "decoding only", because
+ * when it is, check_tx_rtcp() will be handled by get_frame().
+ */
+ if (stream->dir != PJMEDIA_DIR_DECODING) {
+ check_tx_rtcp(stream, pj_ntohl(channel->rtp.out_hdr.ts));
+ }
+
+ /* Do nothing if we have nothing to transmit */
+ if (frame_out.size == 0) {
+ return PJ_SUCCESS;
+ }
+
+ /* Update stat */
+ pjmedia_rtcp_tx_rtp(&stream->rtcp, frame_out.size);
+ stream->rtcp.stat.rtp_tx_last_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts);
+ stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq);
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+ /* Update timestamp of last sending packet. */
+ stream->last_frm_ts_sent = frame->timestamp;
+#endif
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t get_frame(pjmedia_port *port,
+ pjmedia_frame *frame)
+{
+ pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata;
+ pjmedia_vid_channel *channel = stream->dec;
+ pjmedia_frame frame_in;
+ pj_uint32_t last_ts = 0;
+ int frm_first_seq = 0, frm_last_seq = 0;
+ pj_status_t status;
+
+ /* Return no frame is channel is paused */
+ if (channel->paused) {
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ return PJ_SUCCESS;
+ }
+
+ /* Repeat get payload from the jitter buffer until all payloads with same
+ * timestamp are collected (a complete frame unpacketized).
+ */
+ {
+ pj_bool_t got_frame;
+ unsigned cnt;
+
+ channel->buf_len = 0;
+ got_frame = PJ_FALSE;
+
+ /* Lock jitter buffer mutex first */
+ pj_mutex_lock( stream->jb_mutex );
+
+ /* Check if we got a decodable frame */
+ for (cnt=0; ; ++cnt) {
+ char ptype;
+ pj_uint32_t ts;
+ int seq;
+
+ /* Peek frame from jitter buffer. */
+ pjmedia_jbuf_peek_frame(stream->jb, cnt, NULL, NULL,
+ &ptype, NULL, &ts, &seq);
+ if (ptype == PJMEDIA_JB_NORMAL_FRAME) {
+ if (last_ts == 0) {
+ last_ts = ts;
+ frm_first_seq = seq;
+ }
+ if (ts != last_ts) {
+ got_frame = PJ_TRUE;
+ break;
+ }
+ frm_last_seq = seq;
+ } else if (ptype == PJMEDIA_JB_ZERO_EMPTY_FRAME) {
+ /* No more packet in the jitter buffer */
+ break;
+ }
+ }
+
+ if (got_frame) {
+ unsigned i;
+
+ /* Generate frame bitstream from the payload */
+ channel->buf_len = 0;
+ for (i = 0; i < cnt; ++i) {
+ const pj_uint8_t *p;
+ pj_size_t psize;
+ char ptype;
+
+ /* We use jbuf_peek_frame() as it will returns the pointer of
+ * the payload (no buffer and memcpy needed), just as we need.
+ */
+ pjmedia_jbuf_peek_frame(stream->jb, i, (const void**)&p,
+ &psize, &ptype, NULL, NULL, NULL);
+
+ if (ptype != PJMEDIA_JB_NORMAL_FRAME) {
+ /* Packet lost, must set payload to NULL and keep going */
+ p = NULL;
+ psize = 0;
+ }
+
+ status = pjmedia_vid_codec_unpacketize(
+ stream->codec,
+ p, psize,
+ (pj_uint8_t*)channel->buf,
+ channel->buf_size,
+ &channel->buf_len);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((channel->port.info.name.ptr,
+ "Codec unpack() error", status));
+ /* Just ignore this unpack error */
+ }
+ }
+
+ pjmedia_jbuf_remove_frame(stream->jb, cnt);
+ }
+
+ /* Unlock jitter buffer mutex. */
+ pj_mutex_unlock( stream->jb_mutex );
+
+ if (!got_frame) {
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ frame->size = 0;
+ return PJ_SUCCESS;
+ }
+ }
+
+ /* Decode */
+ frame_in.buf = channel->buf;
+ frame_in.size = channel->buf_len;
+ frame_in.bit_info = 0;
+ frame_in.type = PJMEDIA_FRAME_TYPE_VIDEO;
+ frame_in.timestamp.u64 = last_ts;
+
+ status = pjmedia_vid_codec_decode(stream->codec, &frame_in,
+ frame->size, frame);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((port->info.name.ptr, "codec decode() error",
+ status));
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ frame->size = 0;
+ }
+
+ /* Learn remote frame rate after successful decoding */
+ if (0 && frame->type == PJMEDIA_FRAME_TYPE_VIDEO && frame->size)
+ {
+ /* Only check remote frame rate when timestamp is not wrapping and
+ * sequence is increased by 1.
+ */
+ if (last_ts > stream->last_dec_ts &&
+ frm_first_seq - stream->last_dec_seq == 1)
+ {
+ pj_uint32_t ts_diff;
+ pjmedia_video_format_detail *vfd;
+
+ ts_diff = last_ts - stream->last_dec_ts;
+ vfd = pjmedia_format_get_video_format_detail(
+ &channel->port.info.fmt, PJ_TRUE);
+ if ((int)(stream->info.codec_info.clock_rate / ts_diff) !=
+ vfd->fps.num / vfd->fps.denum)
+ {
+ /* Frame rate changed, update decoding port info */
+ vfd->fps.num = stream->info.codec_info.clock_rate;
+ vfd->fps.denum = ts_diff;
+
+ /* Update stream info */
+ stream->info.codec_param->dec_fmt.det.vid.fps = vfd->fps;
+
+ PJ_LOG(5, (channel->port.info.name.ptr,
+ "Frame rate changed to %d/%d(~%d)fps",
+ vfd->fps.num, vfd->fps.denum,
+ vfd->fps.num / vfd->fps.denum));
+
+ /* Publish PJMEDIA_EVENT_FMT_CHANGED event */
+ if (pjmedia_event_publisher_has_sub(&stream->epub)) {
+ pjmedia_event event;
+
+ dump_port_info(stream->dec, "changed");
+
+ pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED,
+ &frame_in.timestamp, &stream->epub);
+ event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING;
+ pj_memcpy(&event.data.fmt_changed.new_fmt,
+ &stream->info.codec_param->dec_fmt,
+ sizeof(pjmedia_format));
+ pjmedia_event_publish(&stream->epub, &event);
+ }
+ }
+ }
+
+ /* Update last frame seq and timestamp */
+ stream->last_dec_seq = frm_last_seq;
+ stream->last_dec_ts = last_ts;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Create media channel.
+ */
+static pj_status_t create_channel( pj_pool_t *pool,
+ pjmedia_vid_stream *stream,
+ pjmedia_dir dir,
+ unsigned pt,
+ const pjmedia_vid_stream_info *info,
+ pjmedia_vid_channel **p_channel)
+{
+ enum { M = 32 };
+ pjmedia_vid_channel *channel;
+ pj_status_t status;
+ unsigned min_out_pkt_size;
+ pj_str_t name;
+ const char *type_name;
+ pjmedia_format *fmt;
+ char fourcc_name[5];
+ pjmedia_port_info *pi;
+
+ pj_assert(info->type == PJMEDIA_TYPE_VIDEO);
+ pj_assert(dir == PJMEDIA_DIR_DECODING || dir == PJMEDIA_DIR_ENCODING);
+
+ /* Allocate memory for channel descriptor */
+ channel = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_channel);
+ PJ_ASSERT_RETURN(channel != NULL, PJ_ENOMEM);
+
+ /* Init vars */
+ if (dir==PJMEDIA_DIR_DECODING) {
+ type_name = "vstdec";
+ fmt = &info->codec_param->dec_fmt;
+ } else {
+ type_name = "vstenc";
+ fmt = &info->codec_param->enc_fmt;
+ }
+ name.ptr = (char*) pj_pool_alloc(pool, M);
+ name.slen = pj_ansi_snprintf(name.ptr, M, "%s%p", type_name, stream);
+ pi = &channel->port.info;
+
+ /* Init channel info. */
+ channel->stream = stream;
+ channel->dir = dir;
+ channel->paused = 1;
+ channel->pt = pt;
+
+ /* Allocate buffer for outgoing packet. */
+ channel->buf_size = sizeof(pjmedia_rtp_hdr) + stream->frame_size;
+
+ /* It should big enough to hold (minimally) RTCP SR with an SDES. */
+ min_out_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) +
+ sizeof(pjmedia_rtcp_common) +
+ (4 + stream->cname.slen) +
+ 32;
+
+ if (channel->buf_size < min_out_pkt_size)
+ channel->buf_size = min_out_pkt_size;
+
+ channel->buf = pj_pool_alloc(pool, channel->buf_size);
+ PJ_ASSERT_RETURN(channel->buf != NULL, PJ_ENOMEM);
+
+ /* Create RTP and RTCP sessions: */
+ if (info->rtp_seq_ts_set == 0) {
+ status = pjmedia_rtp_session_init(&channel->rtp, pt, info->ssrc);
+ } else {
+ pjmedia_rtp_session_setting settings;
+
+ settings.flags = (pj_uint8_t)((info->rtp_seq_ts_set << 2) | 3);
+ settings.default_pt = pt;
+ settings.sender_ssrc = info->ssrc;
+ settings.seq = info->rtp_seq;
+ settings.ts = info->rtp_ts;
+ status = pjmedia_rtp_session_init2(&channel->rtp, settings);
+ }
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Init port. */
+ pjmedia_port_info_init2(pi, &name, SIGNATURE, dir, fmt);
+ if (dir == PJMEDIA_DIR_DECODING) {
+ channel->port.get_frame = &get_frame;
+ } else {
+ pi->fmt.id = info->codec_param->dec_fmt.id;
+ channel->port.put_frame = &put_frame;
+ }
+
+ /* Init port. */
+ channel->port.port_data.pdata = stream;
+ channel->port.get_event_pub = &port_get_epub;
+
+ PJ_LOG(5, (name.ptr,
+ "%s channel created %dx%d %s%s%.*s %d/%d(~%d)fps",
+ (dir==PJMEDIA_DIR_ENCODING?"Encoding":"Decoding"),
+ pi->fmt.det.vid.size.w, pi->fmt.det.vid.size.h,
+ pjmedia_fourcc_name(pi->fmt.id, fourcc_name),
+ (dir==PJMEDIA_DIR_ENCODING?"->":"<-"),
+ info->codec_info.encoding_name.slen,
+ info->codec_info.encoding_name.ptr,
+ pi->fmt.det.vid.fps.num, pi->fmt.det.vid.fps.denum,
+ pi->fmt.det.vid.fps.num/pi->fmt.det.vid.fps.denum));
+
+ /* Done. */
+ *p_channel = channel;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Create stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
+ pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ pjmedia_vid_stream_info *info,
+ pjmedia_transport *tp,
+ void *user_data,
+ pjmedia_vid_stream **p_stream)
+{
+ enum { M = 32 };
+ pj_pool_t *own_pool = NULL;
+ pjmedia_vid_stream *stream;
+ unsigned jb_init, jb_max, jb_min_pre, jb_max_pre, len;
+ int frm_ptime, chunks_per_frm;
+ pjmedia_video_format_detail *vfd_enc;
+ char *p;
+ pj_status_t status;
+
+ if (!pool) {
+ own_pool = pjmedia_endpt_create_pool( endpt, "vstrm%p",
+ PJMEDIA_VSTREAM_SIZE,
+ PJMEDIA_VSTREAM_INC);
+ PJ_ASSERT_RETURN(own_pool != NULL, PJ_ENOMEM);
+ pool = own_pool;
+ }
+
+ /* Allocate stream */
+ stream = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_stream);
+ PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM);
+ stream->own_pool = own_pool;
+
+ /* Get codec manager */
+ stream->codec_mgr = pjmedia_vid_codec_mgr_instance();
+ PJ_ASSERT_RETURN(stream->codec_mgr, PJMEDIA_CODEC_EFAILED);
+
+ /* Init stream/port name */
+ stream->name.ptr = (char*) pj_pool_alloc(pool, M);
+ stream->name.slen = pj_ansi_snprintf(stream->name.ptr, M,
+ "vstrm%p", stream);
+
+ /* Create and initialize codec: */
+ status = pjmedia_vid_codec_mgr_alloc_codec(stream->codec_mgr,
+ &info->codec_info,
+ &stream->codec);
+ if (status != PJ_SUCCESS)
+ return status;
+
+
+ /* Get codec param: */
+ if (!info->codec_param) {
+ pjmedia_vid_codec_param def_param;
+
+ status = pjmedia_vid_codec_mgr_get_default_param(stream->codec_mgr,
+ &info->codec_info,
+ &def_param);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ info->codec_param = pjmedia_vid_codec_param_clone(pool, &def_param);
+ pj_assert(info->codec_param);
+ }
+
+ vfd_enc = pjmedia_format_get_video_format_detail(
+ &info->codec_param->enc_fmt, PJ_TRUE);
+
+ /* Init stream: */
+ stream->endpt = endpt;
+ stream->dir = info->dir;
+ stream->user_data = user_data;
+ stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) *
+ info->codec_info.clock_rate / 1000;
+
+ stream->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME;
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+ stream->use_ka = info->use_ka;
+#endif
+
+ /* Build random RTCP CNAME. CNAME has user@host format */
+ stream->cname.ptr = p = (char*) pj_pool_alloc(pool, 20);
+ pj_create_random_string(p, 5);
+ p += 5;
+ *p++ = '@'; *p++ = 'p'; *p++ = 'j';
+ pj_create_random_string(p, 6);
+ p += 6;
+ *p++ = '.'; *p++ = 'o'; *p++ = 'r'; *p++ = 'g';
+ stream->cname.slen = p - stream->cname.ptr;
+
+
+ /* Create mutex to protect jitter buffer: */
+
+ status = pj_mutex_create_simple(pool, NULL, &stream->jb_mutex);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Init codec param */
+ info->codec_param->dir = info->dir;
+ info->codec_param->enc_mtu = PJMEDIA_MAX_MTU - sizeof(pjmedia_rtp_hdr) -
+ PJMEDIA_STREAM_RESV_PAYLOAD_LEN;
+
+ /* Init and open the codec. */
+ status = pjmedia_vid_codec_init(stream->codec, pool);
+ if (status != PJ_SUCCESS)
+ return status;
+ status = pjmedia_vid_codec_open(stream->codec, info->codec_param);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Init event publisher and subscribe to codec events */
+ pjmedia_event_publisher_init(&stream->epub, SIGNATURE);
+ pjmedia_event_subscription_init(&stream->esub_codec, &stream_event_cb,
+ stream);
+ pjmedia_event_subscribe(&stream->codec->epub, &stream->esub_codec);
+
+ /* Estimate the maximum frame size */
+ stream->frame_size = vfd_enc->size.w * vfd_enc->size.h * 4;
+
+#if 0
+ stream->frame_size = vfd_enc->max_bps/8 * vfd_enc->fps.denum /
+ vfd_enc->fps.num;
+
+ /* As the maximum frame_size is not represented directly by maximum bps
+ * (which includes intra and predicted frames), let's increase the
+ * frame size value for safety.
+ */
+ stream->frame_size <<= 4;
+#endif
+
+ /* Validate the frame size */
+ if (stream->frame_size == 0 ||
+ stream->frame_size > PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE)
+ {
+ stream->frame_size = PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE;
+ }
+
+ /* Get frame length in timestamp unit */
+ stream->frame_ts_len = info->codec_info.clock_rate *
+ vfd_enc->fps.denum / vfd_enc->fps.num;
+
+ /* Create decoder channel */
+ status = create_channel( pool, stream, PJMEDIA_DIR_DECODING,
+ info->rx_pt, info, &stream->dec);
+ if (status != PJ_SUCCESS)
+ return status;
+
+
+ /* Create encoder channel */
+ status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING,
+ info->tx_pt, info, &stream->enc);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Init jitter buffer parameters: */
+ frm_ptime = 1000 * vfd_enc->fps.denum / vfd_enc->fps.num;
+ chunks_per_frm = stream->frame_size / PJMEDIA_MAX_MTU;
+
+ /* JB max count, default 500ms */
+ if (info->jb_max >= frm_ptime)
+ jb_max = info->jb_max * chunks_per_frm / frm_ptime;
+ else
+ jb_max = 500 * chunks_per_frm / frm_ptime;
+
+ /* JB min prefetch, default 1 frame */
+ if (info->jb_min_pre >= frm_ptime)
+ jb_min_pre = info->jb_min_pre * chunks_per_frm / frm_ptime;
+ else
+ jb_min_pre = 1;
+
+ /* JB max prefetch, default 4/5 JB max count */
+ if (info->jb_max_pre >= frm_ptime)
+ jb_max_pre = info->jb_max_pre * chunks_per_frm / frm_ptime;
+ else
+ jb_max_pre = jb_max * 4 / 5;
+
+ /* JB init prefetch, default 0 */
+ if (info->jb_init >= frm_ptime)
+ jb_init = info->jb_init * chunks_per_frm / frm_ptime;
+ else
+ jb_init = 0;
+
+ /* Create jitter buffer */
+ status = pjmedia_jbuf_create(pool, &stream->dec->port.info.name,
+ PJMEDIA_MAX_MTU,
+ 1000 * vfd_enc->fps.denum / vfd_enc->fps.num,
+ jb_max, &stream->jb);
+ if (status != PJ_SUCCESS)
+ return status;
+
+
+ /* Set up jitter buffer */
+ pjmedia_jbuf_set_adaptive( stream->jb, jb_init, jb_min_pre, jb_max_pre);
+ //pjmedia_jbuf_enable_discard(stream->jb, PJ_FALSE);
+
+ /* Init RTCP session: */
+ {
+ pjmedia_rtcp_session_setting rtcp_setting;
+
+ pjmedia_rtcp_session_setting_default(&rtcp_setting);
+ rtcp_setting.name = stream->name.ptr;
+ rtcp_setting.ssrc = info->ssrc;
+ rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts);
+ rtcp_setting.clock_rate = info->codec_info.clock_rate;
+ rtcp_setting.samples_per_frame = 1;
+
+ pjmedia_rtcp_init2(&stream->rtcp, &rtcp_setting);
+ }
+
+ /* Only attach transport when stream is ready. */
+ status = pjmedia_transport_attach(tp, stream, &info->rem_addr,
+ &info->rem_rtcp,
+ pj_sockaddr_get_len(&info->rem_addr),
+ &on_rx_rtp, &on_rx_rtcp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ stream->transport = tp;
+
+ /* Send RTCP SDES */
+ len = create_rtcp_sdes(stream, (pj_uint8_t*)stream->enc->buf,
+ stream->enc->buf_size);
+ if (len != 0) {
+ pjmedia_transport_send_rtcp(stream->transport,
+ stream->enc->buf, len);
+ }
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+ /* NAT hole punching by sending KA packet via RTP transport. */
+ if (stream->use_ka)
+ send_keep_alive_packet(stream);
+#endif
+
+#if TRACE_JB
+ {
+ char trace_name[PJ_MAXPATH];
+ pj_ssize_t len;
+
+ pj_ansi_snprintf(trace_name, sizeof(trace_name),
+ TRACE_JB_PATH_PREFIX "%s.csv",
+ channel->port.info.name.ptr);
+ status = pj_file_open(pool, trace_name, PJ_O_RDWR,
+ &stream->trace_jb_fd);
+ if (status != PJ_SUCCESS) {
+ stream->trace_jb_fd = TRACE_JB_INVALID_FD;
+ PJ_LOG(3,(THIS_FILE, "Failed creating RTP trace file '%s'",
+ trace_name));
+ } else {
+ stream->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE);
+
+ /* Print column header */
+ len = pj_ansi_snprintf(stream->trace_jb_buf, PJ_LOG_MAX_SIZE,
+ "Time, Operation, Size, Frame Count, "
+ "Frame type, RTP Seq, RTP TS, RTP M, "
+ "JB size, JB burst level, JB prefetch\n");
+ pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len);
+ pj_file_flush(stream->trace_jb_fd);
+ }
+ }
+#endif
+
+ /* Save the stream info */
+ pj_memcpy(&stream->info, info, sizeof(*info));
+ stream->info.codec_param = pjmedia_vid_codec_param_clone(
+ pool, info->codec_param);
+
+ /* Success! */
+ *p_stream = stream;
+
+ PJ_LOG(5,(THIS_FILE, "Video stream %s created", stream->name.ptr));
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream )
+{
+ unsigned len;
+ PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+ /* Send RTCP BYE */
+ if (stream->enc && stream->transport) {
+ len = create_rtcp_bye(stream, (pj_uint8_t*)stream->enc->buf,
+ stream->enc->buf_size);
+ if (len != 0) {
+ pjmedia_transport_send_rtcp(stream->transport,
+ stream->enc->buf, len);
+ }
+ }
+
+ /* Detach from transport
+ * MUST NOT hold stream mutex while detaching from transport, as
+ * it may cause deadlock. See ticket #460 for the details.
+ */
+ if (stream->transport) {
+ pjmedia_transport_detach(stream->transport, stream);
+ stream->transport = NULL;
+ }
+
+ /* This function may be called when stream is partly initialized. */
+ if (stream->jb_mutex)
+ pj_mutex_lock(stream->jb_mutex);
+
+
+ /* Free codec. */
+ if (stream->codec) {
+ pjmedia_vid_codec_close(stream->codec);
+ pjmedia_vid_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec);
+ stream->codec = NULL;
+ }
+
+ /* Free mutex */
+
+ if (stream->jb_mutex) {
+ pj_mutex_destroy(stream->jb_mutex);
+ stream->jb_mutex = NULL;
+ }
+
+ /* Destroy jitter buffer */
+ if (stream->jb)
+ pjmedia_jbuf_destroy(stream->jb);
+
+#if TRACE_JB
+ if (TRACE_JB_OPENED(stream)) {
+ pj_file_close(stream->trace_jb_fd);
+ stream->trace_jb_fd = TRACE_JB_INVALID_FD;
+ }
+#endif
+
+ if (stream->own_pool) {
+ pj_pool_t *pool = stream->own_pool;
+ stream->own_pool = NULL;
+ pj_pool_release(pool);
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get the port interface.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_get_port(pjmedia_vid_stream *stream,
+ pjmedia_dir dir,
+ pjmedia_port **p_port )
+{
+ PJ_ASSERT_RETURN(dir==PJMEDIA_DIR_ENCODING || dir==PJMEDIA_DIR_DECODING,
+ PJ_EINVAL);
+
+ if (dir == PJMEDIA_DIR_ENCODING)
+ *p_port = &stream->enc->port;
+ else
+ *p_port = &stream->dec->port;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get the transport object
+ */
+PJ_DEF(pjmedia_transport*) pjmedia_vid_stream_get_transport(
+ pjmedia_vid_stream *st)
+{
+ return st->transport;
+}
+
+
+/*
+ * Get stream statistics.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat(
+ const pjmedia_vid_stream *stream,
+ pjmedia_rtcp_stat *stat)
+{
+ PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL);
+
+ pj_memcpy(stat, &stream->rtcp.stat, sizeof(pjmedia_rtcp_stat));
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Reset the stream statistics in the middle of a stream session.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_reset_stat(pjmedia_vid_stream *stream)
+{
+ PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+ pjmedia_rtcp_init_stat(&stream->rtcp.stat);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get jitter buffer state.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat_jbuf(
+ const pjmedia_vid_stream *stream,
+ pjmedia_jb_state *state)
+{
+ PJ_ASSERT_RETURN(stream && state, PJ_EINVAL);
+ return pjmedia_jbuf_get_state(stream->jb, state);
+}
+
+
+/*
+ * Get the stream info.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_get_info(
+ const pjmedia_vid_stream *stream,
+ pjmedia_vid_stream_info *info)
+{
+ PJ_ASSERT_RETURN(stream && info, PJ_EINVAL);
+ pj_memcpy(info, &stream->info, sizeof(*info));
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Start stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream)
+{
+
+ PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP);
+
+ if (stream->enc && (stream->dir & PJMEDIA_DIR_ENCODING)) {
+ stream->enc->paused = 0;
+ //pjmedia_snd_stream_start(stream->enc->snd_stream);
+ PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream started"));
+ } else {
+ PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused"));
+ }
+
+ if (stream->dec && (stream->dir & PJMEDIA_DIR_DECODING)) {
+ stream->dec->paused = 0;
+ //pjmedia_snd_stream_start(stream->dec->snd_stream);
+ PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream started"));
+ } else {
+ PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused"));
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Pause stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream,
+ pjmedia_dir dir)
+{
+ PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+ if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) {
+ stream->enc->paused = 1;
+ PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused"));
+ }
+
+ if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) {
+ stream->dec->paused = 1;
+
+ /* Also reset jitter buffer */
+ pj_mutex_lock( stream->jb_mutex );
+ pjmedia_jbuf_reset(stream->jb);
+ pj_mutex_unlock( stream->jb_mutex );
+
+ PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused"));
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Resume stream
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream,
+ pjmedia_dir dir)
+{
+ PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+ if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) {
+ stream->enc->paused = 0;
+ PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream resumed"));
+ }
+
+ if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) {
+ stream->dec->paused = 0;
+ PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream resumed"));
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+static const pj_str_t ID_VIDEO = { "video", 5};
+static const pj_str_t ID_IN = { "IN", 2 };
+static const pj_str_t ID_IP4 = { "IP4", 3};
+static const pj_str_t ID_IP6 = { "IP6", 3};
+static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
+static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
+//static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
+static const pj_str_t ID_RTPMAP = { "rtpmap", 6 };
+
+static const pj_str_t STR_INACTIVE = { "inactive", 8 };
+static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
+static const pj_str_t STR_SENDONLY = { "sendonly", 8 };
+static const pj_str_t STR_RECVONLY = { "recvonly", 8 };
+
+
+/*
+ * Internal function for collecting codec info and param from the SDP media.
+ */
+static pj_status_t get_video_codec_info_param(pjmedia_vid_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_vid_codec_mgr *mgr,
+ const pjmedia_sdp_media *local_m,
+ const pjmedia_sdp_media *rem_m)
+{
+ unsigned pt = 0;
+ const pjmedia_vid_codec_info *p_info;
+ pj_status_t status;
+
+ pt = pj_strtoul(&local_m->desc.fmt[0]);
+
+ /* Get codec info. */
+ status = pjmedia_vid_codec_mgr_get_codec_info(mgr, pt, &p_info);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ si->codec_info = *p_info;
+
+ /* Get payload type for receiving direction */
+ si->rx_pt = pt;
+
+ /* Get payload type for transmitting direction */
+ if (pt < 96) {
+ /* For static payload type, pt's are symetric */
+ si->tx_pt = pt;
+
+ } else {
+ unsigned i;
+
+ /* Determine payload type for outgoing channel, by finding
+ * dynamic payload type in remote SDP that matches the answer.
+ */
+ si->tx_pt = 0xFFFF;
+ for (i=0; i<rem_m->desc.fmt_count; ++i) {
+ if (pjmedia_sdp_neg_fmt_match(NULL,
+ (pjmedia_sdp_media*)local_m, 0,
+ (pjmedia_sdp_media*)rem_m, i, 0) ==
+ PJ_SUCCESS)
+ {
+ /* Found matched codec. */
+ si->tx_pt = pj_strtoul(&rem_m->desc.fmt[i]);
+ break;
+ }
+ }
+
+ if (si->tx_pt == 0xFFFF)
+ return PJMEDIA_EMISSINGRTPMAP;
+ }
+
+
+ /* Now that we have codec info, get the codec param. */
+ si->codec_param = PJ_POOL_ALLOC_T(pool, pjmedia_vid_codec_param);
+ status = pjmedia_vid_codec_mgr_get_default_param(mgr,
+ &si->codec_info,
+ si->codec_param);
+
+ /* Get remote fmtp for our encoder. */
+ pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt,
+ &si->codec_param->enc_fmtp);
+
+ /* Get local fmtp for our decoder. */
+ pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt,
+ &si->codec_param->dec_fmtp);
+
+ /* When direction is NONE (it means SDP negotiation has failed) we don't
+ * need to return a failure here, as returning failure will cause
+ * the whole SDP to be rejected. See ticket #:
+ * http://
+ *
+ * Thanks Alain Totouom
+ */
+ if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE)
+ return status;
+
+ return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Create stream info from SDP media line.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp(
+ pjmedia_vid_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_endpt *endpt,
+ const pjmedia_sdp_session *local,
+ const pjmedia_sdp_session *remote,
+ unsigned stream_idx)
+{
+ const pjmedia_sdp_attr *attr;
+ const pjmedia_sdp_media *local_m;
+ const pjmedia_sdp_media *rem_m;
+ const pjmedia_sdp_conn *local_conn;
+ const pjmedia_sdp_conn *rem_conn;
+ int rem_af, local_af;
+ pj_sockaddr local_addr;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(endpt);
+
+ /* Validate arguments: */
+ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL);
+ PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL);
+ PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL);
+
+ /* Keep SDP shortcuts */
+ local_m = local->media[stream_idx];
+ rem_m = remote->media[stream_idx];
+
+ local_conn = local_m->conn ? local_m->conn : local->conn;
+ if (local_conn == NULL)
+ return PJMEDIA_SDP_EMISSINGCONN;
+
+ rem_conn = rem_m->conn ? rem_m->conn : remote->conn;
+ if (rem_conn == NULL)
+ return PJMEDIA_SDP_EMISSINGCONN;
+
+ /* Media type must be video */
+ if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) != 0)
+ return PJMEDIA_EINVALIMEDIATYPE;
+
+
+ /* Reset: */
+
+ pj_bzero(si, sizeof(*si));
+
+ /* Media type: */
+ si->type = PJMEDIA_TYPE_VIDEO;
+
+ /* Transport protocol */
+
+ /* At this point, transport type must be compatible,
+ * the transport instance will do more validation later.
+ */
+ status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport,
+ &local_m->desc.transport);
+ if (status != PJ_SUCCESS)
+ return PJMEDIA_SDPNEG_EINVANSTP;
+
+ if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) {
+
+ si->proto = PJMEDIA_TP_PROTO_RTP_AVP;
+
+ } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) {
+
+ si->proto = PJMEDIA_TP_PROTO_RTP_SAVP;
+
+ } else {
+
+ si->proto = PJMEDIA_TP_PROTO_UNKNOWN;
+ return PJ_SUCCESS;
+ }
+
+
+ /* Check address family in remote SDP */
+ rem_af = pj_AF_UNSPEC();
+ if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) {
+ if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) {
+ rem_af = pj_AF_INET();
+ } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) {
+ rem_af = pj_AF_INET6();
+ }
+ }
+
+ if (rem_af==pj_AF_UNSPEC()) {
+ /* Unsupported address family */
+ return PJ_EAFNOTSUP;
+ }
+
+ /* Set remote address: */
+ status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr,
+ rem_m->desc.port);
+ if (status != PJ_SUCCESS) {
+ /* Invalid IP address. */
+ return PJMEDIA_EINVALIDIP;
+ }
+
+ /* Check address family of local info */
+ local_af = pj_AF_UNSPEC();
+ if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) {
+ if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) {
+ local_af = pj_AF_INET();
+ } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) {
+ local_af = pj_AF_INET6();
+ }
+ }
+
+ if (local_af==pj_AF_UNSPEC()) {
+ /* Unsupported address family */
+ return PJ_SUCCESS;
+ }
+
+ /* Set remote address: */
+ status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr,
+ local_m->desc.port);
+ if (status != PJ_SUCCESS) {
+ /* Invalid IP address. */
+ return PJMEDIA_EINVALIDIP;
+ }
+
+ /* Local and remote address family must match */
+ if (local_af != rem_af)
+ return PJ_EAFNOTSUP;
+
+ /* Media direction: */
+
+ if (local_m->desc.port == 0 ||
+ pj_sockaddr_has_addr(&local_addr)==PJ_FALSE ||
+ pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE ||
+ pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL)
+ {
+ /* Inactive stream. */
+
+ si->dir = PJMEDIA_DIR_NONE;
+
+ } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) {
+
+ /* Send only stream. */
+
+ si->dir = PJMEDIA_DIR_ENCODING;
+
+ } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) {
+
+ /* Recv only stream. */
+
+ si->dir = PJMEDIA_DIR_DECODING;
+
+ } else {
+
+ /* Send and receive stream. */
+
+ si->dir = PJMEDIA_DIR_ENCODING_DECODING;
+
+ }
+
+ /* No need to do anything else if stream is rejected */
+ if (local_m->desc.port == 0) {
+ return PJ_SUCCESS;
+ }
+
+ /* If "rtcp" attribute is present in the SDP, set the RTCP address
+ * from that attribute. Otherwise, calculate from RTP address.
+ */
+ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+ "rtcp", NULL);
+ if (attr) {
+ pjmedia_sdp_rtcp_attr rtcp;
+ status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
+ if (status == PJ_SUCCESS) {
+ if (rtcp.addr.slen) {
+ status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr,
+ (pj_uint16_t)rtcp.port);
+ } else {
+ pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL,
+ (pj_uint16_t)rtcp.port);
+ pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp),
+ pj_sockaddr_get_addr(&si->rem_addr),
+ pj_sockaddr_get_addr_len(&si->rem_addr));
+ }
+ }
+ }
+
+ if (!pj_sockaddr_has_addr(&si->rem_rtcp)) {
+ int rtcp_port;
+
+ pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr));
+ rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1;
+ pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port);
+ }
+
+ /* Get codec info and param */
+ status = get_video_codec_info_param(si, pool, NULL, local_m, rem_m);
+
+ /* Leave SSRC to random. */
+ si->ssrc = pj_rand();
+
+ /* Set default jitter buffer parameter. */
+ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;
+
+ return status;
+}
+
diff --git a/pjmedia/src/pjmedia/vid_tee.c b/pjmedia/src/pjmedia/vid_tee.c
new file mode 100644
index 00000000..dd12ec3e
--- /dev/null
+++ b/pjmedia/src/pjmedia/vid_tee.c
@@ -0,0 +1,384 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/vid_tee.h>
+#include <pjmedia/converter.h>
+#include <pjmedia/errno.h>
+#include <pj/array.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+
+#define TEE_PORT_NAME "vid_tee"
+#define TEE_PORT_SIGN PJMEDIA_SIG_PORT_VID_TEE
+#define MAX_DST_PORT_COUNT 20
+
+
+typedef struct vid_tee_dst_port
+{
+ pjmedia_port *dst;
+ unsigned option;
+} vid_tee_dst_port;
+
+
+typedef struct vid_tee_port
+{
+ pjmedia_port base;
+ pj_pool_t *pool;
+ pj_pool_factory *pf;
+ pj_pool_t *buf_pool;
+ void *buf[2];
+ unsigned buf_cnt;
+ pj_size_t buf_size;
+ unsigned dst_port_maxcnt;
+ unsigned dst_port_cnt;
+ vid_tee_dst_port *dst_ports;
+
+ struct vid_tee_conv_t {
+ pjmedia_converter *conv;
+ pj_size_t conv_buf_size;
+ } *tee_conv;
+} vid_tee_port;
+
+
+static pj_status_t tee_put_frame(pjmedia_port *port, pjmedia_frame *frame);
+static pj_status_t tee_get_frame(pjmedia_port *port, pjmedia_frame *frame);
+static pj_status_t tee_destroy(pjmedia_port *port);
+
+/*
+ * Create a video tee port with the specified source media port.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_tee_create( pj_pool_t *pool,
+ const pjmedia_format *fmt,
+ unsigned max_dst_cnt,
+ pjmedia_port **p_vid_tee)
+{
+ vid_tee_port *tee;
+ pj_str_t name_st;
+ const pjmedia_video_format_info *vfi;
+ pjmedia_video_apply_fmt_param vafp;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(pool && fmt && p_vid_tee, PJ_EINVAL);
+ PJ_ASSERT_RETURN(fmt->type == PJMEDIA_TYPE_VIDEO, PJ_EINVAL);
+ PJ_ASSERT_RETURN(max_dst_cnt <= MAX_DST_PORT_COUNT, PJ_ETOOMANY);
+
+ /* Allocate video tee structure */
+ tee = PJ_POOL_ZALLOC_T(pool, vid_tee_port);
+ tee->pf = pool->factory;
+ tee->pool = pj_pool_create(tee->pf, "video tee", 500, 500, NULL);
+
+ /* Initialize video tee structure */
+ tee->dst_port_maxcnt = max_dst_cnt;
+ tee->dst_ports = (vid_tee_dst_port*)
+ pj_pool_calloc(pool, max_dst_cnt,
+ sizeof(vid_tee_dst_port));
+ tee->tee_conv = (struct vid_tee_conv_t *)
+ pj_pool_calloc(pool, max_dst_cnt,
+ sizeof(struct vid_tee_conv_t));
+
+ /* Initialize video tee buffer, its size is one frame */
+ vfi = pjmedia_get_video_format_info(NULL, fmt->id);
+ if (vfi == NULL)
+ return PJMEDIA_EBADFMT;
+
+ pj_bzero(&vafp, sizeof(vafp));
+ vafp.size = fmt->det.vid.size;
+ status = vfi->apply_fmt(vfi, &vafp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ tee->buf_size = vafp.framebytes;
+
+ /* Initialize video tee port */
+ status = pjmedia_port_info_init2(&tee->base.info,
+ pj_strset2(&name_st, (char*)TEE_PORT_NAME),
+ TEE_PORT_SIGN,
+ PJMEDIA_DIR_ENCODING,
+ fmt);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ tee->base.get_frame = &tee_get_frame;
+ tee->base.put_frame = &tee_put_frame;
+ tee->base.on_destroy = &tee_destroy;
+
+ /* Done */
+ *p_vid_tee = &tee->base;
+
+ return PJ_SUCCESS;
+}
+
+static void realloc_buf(vid_tee_port *vid_tee,
+ unsigned buf_cnt, pj_size_t buf_size)
+{
+ unsigned i;
+
+ if (buf_cnt > vid_tee->buf_cnt)
+ vid_tee->buf_cnt = buf_cnt;
+
+ if (buf_size > vid_tee->buf_size) {
+ /* We need a larger buffer here. */
+ vid_tee->buf_size = buf_size;
+ if (vid_tee->buf_pool) {
+ pj_pool_release(vid_tee->buf_pool);
+ vid_tee->buf_pool = NULL;
+ }
+ vid_tee->buf[0] = vid_tee->buf[1] = NULL;
+ }
+
+ if (!vid_tee->buf_pool) {
+ vid_tee->buf_pool = pj_pool_create(vid_tee->pf, "video tee buffer",
+ 1000, 1000, NULL);
+ }
+
+ for (i = 0; i < vid_tee->buf_cnt; i++) {
+ if (!vid_tee->buf[i])
+ vid_tee->buf[i] = pj_pool_alloc(vid_tee->buf_pool,
+ vid_tee->buf_size);
+ }
+}
+
+/*
+ * Add a destination media port to the video tee.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_tee_add_dst_port(pjmedia_port *vid_tee,
+ unsigned option,
+ pjmedia_port *port)
+{
+ vid_tee_port *tee = (vid_tee_port*)vid_tee;
+ pjmedia_video_format_detail *vfd;
+
+ PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN,
+ PJ_EINVAL);
+
+ if (tee->dst_port_cnt >= tee->dst_port_maxcnt)
+ return PJ_ETOOMANY;
+
+ if (vid_tee->info.fmt.id != port->info.fmt.id)
+ return PJMEDIA_EBADFMT;
+
+ vfd = pjmedia_format_get_video_format_detail(&port->info.fmt, PJ_TRUE);
+ if (vfd->size.w != vid_tee->info.fmt.det.vid.size.w ||
+ vfd->size.h != vid_tee->info.fmt.det.vid.size.h)
+ {
+ return PJMEDIA_EBADFMT;
+ }
+
+ realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)?
+ 1: 0, tee->buf_size);
+
+ pj_bzero(&tee->tee_conv[tee->dst_port_cnt], sizeof(tee->tee_conv[0]));
+ tee->dst_ports[tee->dst_port_cnt].dst = port;
+ tee->dst_ports[tee->dst_port_cnt].option = option;
+ ++tee->dst_port_cnt;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Add a destination media port to the video tee. Create a converter if
+ * necessary.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_tee_add_dst_port2(pjmedia_port *vid_tee,
+ unsigned option,
+ pjmedia_port *port)
+{
+ vid_tee_port *tee = (vid_tee_port*)vid_tee;
+ pjmedia_video_format_detail *vfd;
+
+ PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN,
+ PJ_EINVAL);
+
+ if (tee->dst_port_cnt >= tee->dst_port_maxcnt)
+ return PJ_ETOOMANY;
+
+ pj_bzero(&tee->tee_conv[tee->dst_port_cnt], sizeof(tee->tee_conv[0]));
+
+ /* Check if we need to create a converter. */
+ vfd = pjmedia_format_get_video_format_detail(&port->info.fmt, PJ_TRUE);
+ if (vid_tee->info.fmt.id != port->info.fmt.id ||
+ vfd->size.w != vid_tee->info.fmt.det.vid.size.w ||
+ vfd->size.h != vid_tee->info.fmt.det.vid.size.h)
+ {
+ const pjmedia_video_format_info *vfi;
+ pjmedia_video_apply_fmt_param vafp;
+ pjmedia_conversion_param conv_param;
+ pj_status_t status;
+
+ vfi = pjmedia_get_video_format_info(NULL, port->info.fmt.id);
+ if (vfi == NULL)
+ return PJMEDIA_EBADFMT;
+
+ pj_bzero(&vafp, sizeof(vafp));
+ vafp.size = port->info.fmt.det.vid.size;
+ status = vfi->apply_fmt(vfi, &vafp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)?
+ 2: 1, vafp.framebytes);
+
+ pjmedia_format_copy(&conv_param.src, &vid_tee->info.fmt);
+ pjmedia_format_copy(&conv_param.dst, &port->info.fmt);
+
+ status = pjmedia_converter_create(
+ NULL, tee->pool, &conv_param,
+ &tee->tee_conv[tee->dst_port_cnt].conv);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ tee->tee_conv[tee->dst_port_cnt].conv_buf_size = vafp.framebytes;
+ } else {
+ realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)?
+ 1: 0, tee->buf_size);
+ }
+
+ tee->dst_ports[tee->dst_port_cnt].dst = port;
+ tee->dst_ports[tee->dst_port_cnt].option = option;
+ ++tee->dst_port_cnt;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Remove a destination media port from the video tee.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_tee_remove_dst_port(pjmedia_port *vid_tee,
+ pjmedia_port *port)
+{
+ vid_tee_port *tee = (vid_tee_port*)vid_tee;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN,
+ PJ_EINVAL);
+
+ for (i = 0; i < tee->dst_port_cnt; ++i) {
+ if (tee->dst_ports[i].dst == port) {
+ if (tee->tee_conv[i].conv)
+ pjmedia_converter_destroy(tee->tee_conv[i].conv);
+
+ pj_array_erase(tee->dst_ports, sizeof(tee->dst_ports[0]),
+ tee->dst_port_cnt, i);
+ pj_array_erase(tee->tee_conv, sizeof(tee->tee_conv[0]),
+ tee->dst_port_cnt, i);
+ --tee->dst_port_cnt;
+ return PJ_SUCCESS;
+ }
+ }
+
+ return PJ_ENOTFOUND;
+}
+
+
+static pj_status_t tee_put_frame(pjmedia_port *port, pjmedia_frame *frame)
+{
+ vid_tee_port *tee = (vid_tee_port*)port;
+ unsigned i, j;
+ pj_bool_t done[MAX_DST_PORT_COUNT];
+
+ pj_bzero(done, sizeof(done));
+
+ for (i = 0; i < tee->dst_port_cnt; ++i) {
+ pjmedia_frame frame_ = *frame;
+
+ if (done[i])
+ continue;
+
+ if (tee->tee_conv[i].conv) {
+ pj_status_t status;
+
+ frame_.buf = tee->buf[0];
+ frame_.size = tee->tee_conv[i].conv_buf_size;
+ status = pjmedia_converter_convert(tee->tee_conv[i].conv,
+ frame, &frame_);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3, ("", "Failed to convert frame for destination"
+ "port %d (%.*s)", i,
+ tee->dst_ports[i].dst->info.name.slen,
+ tee->dst_ports[i].dst->info.name.ptr));
+ continue;
+ }
+ }
+
+ /* Find other destination ports which has the same format so
+ * we don't need to do the same conversion twice.
+ */
+ for (j = i; j < tee->dst_port_cnt; ++j) {
+ pjmedia_frame framep;
+
+ if (done[j] ||
+ (tee->dst_ports[j].dst->info.fmt.id !=
+ tee->dst_ports[i].dst->info.fmt.id) ||
+ (tee->dst_ports[j].dst->info.fmt.det.vid.size.w !=
+ tee->dst_ports[i].dst->info.fmt.det.vid.size.w) ||
+ (tee->dst_ports[j].dst->info.fmt.det.vid.size.h !=
+ tee->dst_ports[i].dst->info.fmt.det.vid.size.h))
+ {
+ continue;
+ }
+
+ framep = frame_;
+ /* For dst_ports that do in-place processing, we need to duplicate
+ * the data source first.
+ */
+ if (tee->dst_ports[j].option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)
+ {
+ PJ_ASSERT_RETURN(tee->buf_size <= frame_.size, PJ_ETOOBIG);
+ framep.buf = tee->buf[tee->buf_cnt-1];
+ framep.size = frame_.size;
+ pj_memcpy(framep.buf, frame_.buf, frame_.size);
+ }
+
+ /* Deliver the data */
+ pjmedia_port_put_frame(tee->dst_ports[j].dst, &framep);
+ done[j] = PJ_TRUE;
+
+ if (!tee->tee_conv[i].conv)
+ break;
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t tee_get_frame(pjmedia_port *port, pjmedia_frame *frame)
+{
+ PJ_UNUSED_ARG(port);
+ PJ_UNUSED_ARG(frame);
+
+ pj_assert(!"Bug! Tee port get_frame() shouldn't be called.");
+
+ return PJ_EBUG;
+}
+
+static pj_status_t tee_destroy(pjmedia_port *port)
+{
+ vid_tee_port *tee = (vid_tee_port*)port;
+
+ PJ_ASSERT_RETURN(port && port->info.signature==TEE_PORT_SIGN, PJ_EINVAL);
+
+ pj_pool_release(tee->pool);
+ if (tee->buf_pool)
+ pj_pool_release(tee->buf_pool);
+
+ pj_bzero(tee, sizeof(*tee));
+
+ return PJ_SUCCESS;
+}
diff --git a/pjmedia/src/pjmedia/wav_player.c b/pjmedia/src/pjmedia/wav_player.c
index e4ad3b48..3bccd527 100644
--- a/pjmedia/src/pjmedia/wav_player.c
+++ b/pjmedia/src/pjmedia/wav_player.c
@@ -32,7 +32,7 @@
#define THIS_FILE "wav_player.c"
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('F', 'P', 'l', 'y')
+#define SIGNATURE PJMEDIA_SIG_PORT_WAV_PLAYER
#define BITS_PER_SAMPLE 16
#if 1
@@ -187,7 +187,10 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool,
pjmedia_wave_hdr wave_hdr;
pj_ssize_t size_to_read, size_read;
struct file_reader_port *fport;
+ pjmedia_audio_format_detail *ad;
pj_off_t pos;
+ pj_str_t name;
+ unsigned samples_per_frame;
pj_status_t status = PJ_SUCCESS;
@@ -352,17 +355,15 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool,
fport->options = options;
/* Update port info. */
- fport->base.info.channel_count = wave_hdr.fmt_hdr.nchan;
- fport->base.info.clock_rate = wave_hdr.fmt_hdr.sample_rate;
- fport->base.info.bits_per_sample = BITS_PER_SAMPLE;
- fport->base.info.samples_per_frame = fport->base.info.clock_rate *
- wave_hdr.fmt_hdr.nchan *
- ptime / 1000;
- fport->base.info.bytes_per_frame =
- fport->base.info.samples_per_frame *
- fport->base.info.bits_per_sample / 8;
-
- pj_strdup2(pool, &fport->base.info.name, filename);
+ ad = pjmedia_format_get_audio_format_detail(&fport->base.info.fmt, 1);
+ pj_strdup2(pool, &name, filename);
+ samples_per_frame = ptime * wave_hdr.fmt_hdr.sample_rate *
+ wave_hdr.fmt_hdr.nchan / 1000;
+ pjmedia_port_info_init(&fport->base.info, &name, SIGNATURE,
+ wave_hdr.fmt_hdr.sample_rate,
+ wave_hdr.fmt_hdr.nchan,
+ BITS_PER_SAMPLE,
+ samples_per_frame);
/* If file is shorter than buffer size, adjust buffer size to file
* size. Otherwise EOF callback will be called multiple times when
@@ -379,9 +380,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool,
/* samples_per_frame must be smaller than bufsize (because get_frame()
* doesn't handle this case).
*/
- if (fport->base.info.samples_per_frame * fport->bytes_per_sample >=
- fport->bufsize)
- {
+ if (samples_per_frame * fport->bytes_per_sample >= fport->bufsize) {
pj_file_close(fport->fd);
return PJ_EINVAL;
}
@@ -415,8 +414,8 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool,
"filesize=%luKB",
(int)fport->base.info.name.slen,
fport->base.info.name.ptr,
- fport->base.info.clock_rate,
- fport->base.info.channel_count,
+ ad->clock_rate,
+ ad->channel_count,
fport->bufsize / 1000,
(unsigned long)(fport->fsize / 1000)));
diff --git a/pjmedia/src/pjmedia/wav_playlist.c b/pjmedia/src/pjmedia/wav_playlist.c
index 22dc08f1..ca5e4c55 100644
--- a/pjmedia/src/pjmedia/wav_playlist.c
+++ b/pjmedia/src/pjmedia/wav_playlist.c
@@ -32,7 +32,7 @@
#define THIS_FILE "wav_playlist.c"
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('P', 'l', 's', 't')
+#define SIGNATURE PJMEDIA_SIG_PORT_WAV_PLAYLIST
#define BYTES_PER_SAMPLE 2
@@ -236,6 +236,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool,
pjmedia_port **p_port)
{
struct playlist_port *fport;
+ pjmedia_audio_format_detail *afd;
pj_off_t pos;
pj_status_t status;
int index;
@@ -280,6 +281,8 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool,
return PJ_ENOMEM;
}
+ afd = pjmedia_format_get_audio_format_detail(&fport->base.info.fmt, 1);
+
/* start with the first file. */
fport->current_file = 0;
fport->max_file = file_count;
@@ -466,15 +469,13 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool,
* that the WAV file has the same attributes as previous files.
*/
if (!has_wave_info) {
- fport->base.info.channel_count = wavehdr.fmt_hdr.nchan;
- fport->base.info.clock_rate = wavehdr.fmt_hdr.sample_rate;
- fport->base.info.bits_per_sample = wavehdr.fmt_hdr.bits_per_sample;
- fport->base.info.samples_per_frame = fport->base.info.clock_rate *
- wavehdr.fmt_hdr.nchan *
- ptime / 1000;
- fport->base.info.bytes_per_frame =
- fport->base.info.samples_per_frame *
- fport->base.info.bits_per_sample / 8;
+ afd->channel_count = wavehdr.fmt_hdr.nchan;
+ afd->clock_rate = wavehdr.fmt_hdr.sample_rate;
+ afd->bits_per_sample = wavehdr.fmt_hdr.bits_per_sample;
+ afd->frame_time_usec = ptime * 1000;
+ afd->avg_bps = afd->max_bps = afd->clock_rate *
+ afd->channel_count *
+ afd->bits_per_sample / 8;
has_wave_info = PJ_TRUE;
@@ -483,9 +484,9 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool,
/* Check that this file has the same characteristics as the other
* files.
*/
- if (wavehdr.fmt_hdr.nchan != fport->base.info.channel_count ||
- wavehdr.fmt_hdr.sample_rate != fport->base.info.clock_rate ||
- wavehdr.fmt_hdr.bits_per_sample != fport->base.info.bits_per_sample)
+ if (wavehdr.fmt_hdr.nchan != afd->channel_count ||
+ wavehdr.fmt_hdr.sample_rate != afd->clock_rate ||
+ wavehdr.fmt_hdr.bits_per_sample != afd->bits_per_sample)
{
/* This file has different characteristics than the other
* files.
@@ -519,8 +520,8 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool,
"WAV playlist '%.*s' created: samp.rate=%d, ch=%d, bufsize=%uKB",
(int)port_label->slen,
port_label->ptr,
- fport->base.info.clock_rate,
- fport->base.info.channel_count,
+ afd->clock_rate,
+ afd->channel_count,
fport->bufsize / 1000));
return PJ_SUCCESS;
diff --git a/pjmedia/src/pjmedia/wav_writer.c b/pjmedia/src/pjmedia/wav_writer.c
index 8eb4a02c..d1636276 100644
--- a/pjmedia/src/pjmedia/wav_writer.c
+++ b/pjmedia/src/pjmedia/wav_writer.c
@@ -30,7 +30,7 @@
#define THIS_FILE "wav_writer.c"
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('F', 'W', 'R', 'T')
+#define SIGNATURE PJMEDIA_SIG_PORT_WAV_WRITER
struct file_port
@@ -51,7 +51,7 @@ struct file_port
};
static pj_status_t file_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame);
+ pjmedia_frame *frame);
static pj_status_t file_get_frame(pjmedia_port *this_port,
pjmedia_frame *frame);
static pj_status_t file_on_destroy(pjmedia_port *this_port);
@@ -198,7 +198,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool,
fport->bufsize = buff_size;
/* Check that buffer size is greater than bytes per frame */
- pj_assert(fport->bufsize >= fport->base.info.bytes_per_frame);
+ pj_assert(fport->bufsize >= PJMEDIA_PIA_AVG_FSZ(&fport->base.info));
/* Allocate buffer and set initial write position */
@@ -216,7 +216,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool,
"File writer '%.*s' created: samp.rate=%d, bufsize=%uKB",
(int)fport->base.info.name.slen,
fport->base.info.name.ptr,
- fport->base.info.clock_rate,
+ PJMEDIA_PIA_SRATE(&fport->base.info),
fport->bufsize / 1000));
@@ -308,7 +308,7 @@ static pj_status_t flush_buffer(struct file_port *fport)
* to the file.
*/
static pj_status_t file_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
struct file_port *fport = (struct file_port *)this_port;
unsigned frame_size;
diff --git a/pjmedia/src/test/codec_vectors.c b/pjmedia/src/test/codec_vectors.c
index b53850b6..5257b5a7 100644
--- a/pjmedia/src/test/codec_vectors.c
+++ b/pjmedia/src/test/codec_vectors.c
@@ -73,13 +73,13 @@ static int codec_test_encode(pjmedia_codec_mgr *mgr,
codec_param.info.avg_bps = bitrate;
codec_param.setting.vad = 0;
- status = codec->op->init(codec, pool);
+ status = pjmedia_codec_init(codec, pool);
if (status != PJ_SUCCESS) {
rc = -60;
goto on_return;
}
- status = codec->op->open(codec, &codec_param);
+ status = pjmedia_codec_open(codec, &codec_param);
if (status != PJ_SUCCESS) {
rc = -70;
goto on_return;
@@ -117,7 +117,7 @@ static int codec_test_encode(pjmedia_codec_mgr *mgr,
break;
out_frame.size = samples_per_frame;
- status = codec->op->encode(codec, &in_frame, samples_per_frame,
+ status = pjmedia_codec_encode(codec, &in_frame, samples_per_frame,
&out_frame);
if (status != PJ_SUCCESS) {
rc = -95;
@@ -188,7 +188,7 @@ on_return:
fclose(fref);
if (codec) {
- codec->op->close(codec);
+ pjmedia_codec_close(codec);
pjmedia_codec_mgr_dealloc_codec(mgr, codec);
}
@@ -326,13 +326,13 @@ static int codec_test_decode(pjmedia_codec_mgr *mgr,
codec_param.info.avg_bps = bitrate;
codec_param.setting.vad = 0;
- status = codec->op->init(codec, pool);
+ status = pjmedia_codec_init(codec, pool);
if (status != PJ_SUCCESS) {
rc = -60;
goto on_return;
}
- status = codec->op->open(codec, &codec_param);
+ status = pjmedia_codec_open(codec, &codec_param);
if (status != PJ_SUCCESS) {
rc = -70;
goto on_return;
@@ -387,8 +387,8 @@ static int codec_test_decode(pjmedia_codec_mgr *mgr,
if (has_frame) {
count = 2;
- if (codec->op->parse(codec, pkt, encoded_len, &ts,
- &count, in_frame) != PJ_SUCCESS)
+ if (pjmedia_codec_parse(codec, pkt, encoded_len, &ts,
+ &count, in_frame) != PJ_SUCCESS)
{
rc = -100;
goto on_return;
@@ -399,15 +399,15 @@ static int codec_test_decode(pjmedia_codec_mgr *mgr,
goto on_return;
}
- if (codec->op->decode(codec, &in_frame[0], samples_per_frame*2,
- &out_frame) != PJ_SUCCESS)
+ if (pjmedia_codec_decode(codec, &in_frame[0], samples_per_frame*2,
+ &out_frame) != PJ_SUCCESS)
{
rc = -120;
goto on_return;
}
} else {
- if (codec->op->recover(codec, samples_per_frame*2,
- &out_frame) != PJ_SUCCESS)
+ if (pjmedia_codec_recover(codec, samples_per_frame*2,
+ &out_frame) != PJ_SUCCESS)
{
rc = -125;
goto on_return;
@@ -483,7 +483,7 @@ on_return:
fclose(input);
if (codec) {
- codec->op->close(codec);
+ pjmedia_codec_close(codec);
pjmedia_codec_mgr_dealloc_codec(mgr, codec);
}
diff --git a/pjmedia/src/test/main.c b/pjmedia/src/test/main.c
index b48f7d77..6ea1595c 100644
--- a/pjmedia/src/test/main.c
+++ b/pjmedia/src/test/main.c
@@ -17,6 +17,8 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <pj/os.h>
+
#include "test.h"
@@ -29,8 +31,7 @@
# include "../../../pjlib/include/rtems-network-config.h"
#endif
-
-int main(int argc, char *argv[])
+static int main_func(int argc, char *argv[])
{
int rc;
char s[10];
@@ -46,4 +47,7 @@ int main(int argc, char *argv[])
return rc;
}
-
+int main(int argc, char *argv[])
+{
+ return pj_run_app(&main_func, argc, argv, 0);
+}
diff --git a/pjmedia/src/test/mips_test.c b/pjmedia/src/test/mips_test.c
index d5ece9c7..31a33ed1 100644
--- a/pjmedia/src/test/mips_test.c
+++ b/pjmedia/src/test/mips_test.c
@@ -684,7 +684,7 @@ struct codec_port
static pj_status_t codec_put_frame(struct pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
struct codec_port *cp = (struct codec_port*)this_port;
pjmedia_frame out_frame;
@@ -692,8 +692,8 @@ static pj_status_t codec_put_frame(struct pjmedia_port *this_port,
out_frame.buf = cp->pkt;
out_frame.size = sizeof(cp->pkt);
- status = cp->codec->op->encode(cp->codec, frame, sizeof(cp->pkt),
- &out_frame);
+ status = pjmedia_codec_encode(cp->codec, frame, sizeof(cp->pkt),
+ &out_frame);
pj_assert(status == PJ_SUCCESS);
if (out_frame.size != 0) {
@@ -701,16 +701,16 @@ static pj_status_t codec_put_frame(struct pjmedia_port *this_port,
unsigned frame_cnt = PJ_ARRAY_SIZE(parsed_frm);
unsigned i;
- status = cp->codec->op->parse(cp->codec, out_frame.buf,
- out_frame.size, &out_frame.timestamp,
- &frame_cnt, parsed_frm);
+ status = pjmedia_codec_parse(cp->codec, out_frame.buf,
+ out_frame.size, &out_frame.timestamp,
+ &frame_cnt, parsed_frm);
pj_assert(status == PJ_SUCCESS);
for (i=0; i<frame_cnt; ++i) {
pcm_frm.buf = cp->pcm;
pcm_frm.size = sizeof(cp->pkt);
- status = cp->codec->op->decode(cp->codec, &parsed_frm[i],
- sizeof(cp->pcm), &pcm_frm);
+ status = pjmedia_codec_decode(cp->codec, &parsed_frm[i],
+ sizeof(cp->pcm), &pcm_frm);
pj_assert(status == PJ_SUCCESS);
}
}
@@ -722,7 +722,7 @@ static pj_status_t codec_on_destroy(struct pjmedia_port *this_port)
{
struct codec_port *cp = (struct codec_port*)this_port;
- cp->codec->op->close(cp->codec);
+ pjmedia_codec_close(cp->codec);
pjmedia_codec_mgr_dealloc_codec(pjmedia_endpt_get_codec_mgr(cp->endpt),
cp->codec);
cp->codec_deinit();
@@ -782,11 +782,11 @@ static pjmedia_port* codec_encode_decode( pj_pool_t *pool,
if (status != PJ_SUCCESS)
return NULL;
- status = (*cp->codec->op->init)(cp->codec, pool);
+ status = pjmedia_codec_init(cp->codec, pool);
if (status != PJ_SUCCESS)
return NULL;
- status = cp->codec->op->open(cp->codec, &codec_param);
+ status = pjmedia_codec_open(cp->codec, &codec_param);
if (status != PJ_SUCCESS)
return NULL;
@@ -1131,13 +1131,13 @@ static pj_status_t wsola_discard_get_frame(struct pjmedia_port *this_port,
pj_status_t status;
while (pjmedia_circ_buf_get_len(wp->circbuf) <
- wp->base.info.samples_per_frame * (CIRC_BUF_FRAME_CNT-1))
+ PJMEDIA_PIA_SPF(&wp->base.info) * (CIRC_BUF_FRAME_CNT-1))
{
status = pjmedia_port_get_frame(wp->gen_port, frame);
pj_assert(status==PJ_SUCCESS);
status = pjmedia_circ_buf_write(wp->circbuf, (short*)frame->buf,
- wp->base.info.samples_per_frame);
+ PJMEDIA_PIA_SPF(&wp->base.info));
pj_assert(status==PJ_SUCCESS);
}
@@ -1149,7 +1149,7 @@ static pj_status_t wsola_discard_get_frame(struct pjmedia_port *this_port,
pjmedia_circ_buf_get_read_regions(wp->circbuf, &reg1, &reg1_len,
&reg2, &reg2_len);
- del_cnt = wp->base.info.samples_per_frame;
+ del_cnt = PJMEDIA_PIA_SPF(&wp->base.info);
status = pjmedia_wsola_discard(wp->wsola, reg1, reg1_len, reg2,
reg2_len, &del_cnt);
pj_assert(status==PJ_SUCCESS);
@@ -2010,7 +2010,7 @@ static pj_status_t delaybuf_get_frame(struct pjmedia_port *this_port,
}
static pj_status_t delaybuf_put_frame(struct pjmedia_port *this_port,
- const pjmedia_frame *frame)
+ pjmedia_frame *frame)
{
struct delaybuf_port *dp = (struct delaybuf_port*)this_port;
pj_status_t status;
@@ -2219,7 +2219,7 @@ static pj_timestamp run_entry(unsigned clock_rate, struct test_entry *e)
}
/* Port may decide to use different ptime (e.g. iLBC) */
- samples_per_frame = port->info.samples_per_frame;
+ samples_per_frame = PJMEDIA_PIA_SPF(&port->info);
gen_port = create_gen_port(pool, clock_rate, 1,
samples_per_frame, 100);
diff --git a/pjmedia/src/test/test.c b/pjmedia/src/test/test.c
index ca53d732..d243ed8a 100644
--- a/pjmedia/src/test/test.c
+++ b/pjmedia/src/test/test.c
@@ -47,15 +47,33 @@ int test_main(void)
{
int rc = 0;
pj_caching_pool caching_pool;
+ pj_pool_t *pool;
pj_init();
pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
+ pool = pj_pool_create(&caching_pool.factory, "test", 1000, 512, NULL);
pj_log_set_decor(PJ_LOG_HAS_NEWLINE);
pj_log_set_level(3);
mem = &caching_pool.factory;
+ pjmedia_video_format_mgr_create(pool, 64, 0, NULL);
+ pjmedia_converter_mgr_create(pool, NULL);
+ pjmedia_vid_codec_mgr_create(pool, NULL);
+
+#if HAS_VID_PORT_TEST
+ DO_TEST(vid_port_test());
+#endif
+
+#if HAS_VID_DEV_TEST
+ DO_TEST(vid_dev_test());
+#endif
+
+#if HAS_VID_CODEC_TEST
+ DO_TEST(vid_codec_test());
+#endif
+
#if HAS_SDP_NEG_TEST
DO_TEST(sdp_neg_test());
#endif
@@ -81,6 +99,11 @@ on_return:
PJ_LOG(3,(THIS_FILE,"Looks like everything is okay!"));
}
+ pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr_instance());
+ pjmedia_converter_mgr_destroy(pjmedia_converter_mgr_instance());
+ pjmedia_vid_codec_mgr_destroy(pjmedia_vid_codec_mgr_instance());
+
+ pj_pool_release(pool);
pj_caching_pool_destroy(&caching_pool);
return rc;
diff --git a/pjmedia/src/test/test.h b/pjmedia/src/test/test.h
index 74279c38..abd7d0bd 100644
--- a/pjmedia/src/test/test.h
+++ b/pjmedia/src/test/test.h
@@ -23,6 +23,9 @@
#include <pjmedia.h>
#include <pjlib.h>
+#define HAS_VID_DEV_TEST 1
+#define HAS_VID_PORT_TEST 0
+#define HAS_VID_CODEC_TEST 1
#define HAS_SDP_NEG_TEST 1
#define HAS_JBUF_TEST 1
#define HAS_MIPS_TEST 1
@@ -35,6 +38,9 @@ int jbuf_main(void);
int sdp_neg_test(void);
int mips_test(void);
int codec_test_vectors(void);
+int vid_codec_test(void);
+int vid_dev_test(void);
+int vid_port_test(void);
extern pj_pool_factory *mem;
void app_perror(pj_status_t status, const char *title);
diff --git a/pjmedia/src/test/vid_codec_test.c b/pjmedia/src/test/vid_codec_test.c
new file mode 100644
index 00000000..36ca19ac
--- /dev/null
+++ b/pjmedia/src/test/vid_codec_test.c
@@ -0,0 +1,467 @@
+#include "test.h"
+#include <pjmedia-codec/ffmpeg_codecs.h>
+#include <pjmedia-videodev/videodev.h>
+#include <pjmedia/vid_codec.h>
+#include <pjmedia/port.h>
+
+#define THIS_FILE "vid_codec.c"
+
+#define BYPASS_CODEC 0
+#define BYPASS_PACKETIZER 0
+
+/*
+ * Capture device setting:
+ * -1 = colorbar,
+ * -2 = any non-colorbar capture device (first found)
+ * x = specified capture device id
+ */
+#define CAPTURE_DEV -1
+
+
+typedef struct codec_port_data_t
+{
+ pjmedia_vid_codec *codec;
+ pjmedia_vid_port *rdr_port;
+ pj_uint8_t *enc_buf;
+ pj_size_t enc_buf_size;
+ pj_uint8_t *pack_buf;
+ pj_size_t pack_buf_size;
+} codec_port_data_t;
+
+static pj_status_t codec_on_event(pjmedia_event_subscription *esub,
+ pjmedia_event *event)
+{
+ codec_port_data_t *port_data = (codec_port_data_t*)esub->user_data;
+
+ if (event->type == PJMEDIA_EVENT_FMT_CHANGED) {
+ pjmedia_vid_codec *codec = port_data->codec;
+ pjmedia_vid_codec_param codec_param;
+ pj_status_t status;
+
+ ++event->proc_cnt;
+
+ status = pjmedia_vid_codec_get_param(codec, &codec_param);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = pjmedia_vid_dev_stream_set_cap(
+ pjmedia_vid_port_get_stream(port_data->rdr_port),
+ PJMEDIA_VID_DEV_CAP_FORMAT,
+ &codec_param.dec_fmt);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t codec_put_frame(pjmedia_port *port,
+ pjmedia_frame *frame)
+{
+ codec_port_data_t *port_data = (codec_port_data_t*)port->port_data.pdata;
+ pj_status_t status;
+
+#if !BYPASS_CODEC
+ {
+ pjmedia_vid_codec *codec = port_data->codec;
+ pjmedia_frame enc_frame;
+
+ enc_frame.buf = port_data->enc_buf;
+ enc_frame.size = port_data->enc_buf_size;
+
+ status = pjmedia_vid_codec_encode(codec, frame, enc_frame.size,
+ &enc_frame);
+ if (status != PJ_SUCCESS) goto on_error;
+
+#if !BYPASS_PACKETIZER
+ if (enc_frame.size) {
+ unsigned pos = 0;
+ pj_bool_t packetized = PJ_FALSE;
+ unsigned unpack_pos = 0;
+
+ while (pos < enc_frame.size) {
+ pj_uint8_t *payload;
+ pj_size_t payload_len;
+
+ status = pjmedia_vid_codec_packetize(
+ codec,
+ (pj_uint8_t*)enc_frame.buf,
+ enc_frame.size, &pos,
+ (const pj_uint8_t**)&payload,
+ &payload_len);
+ if (status == PJ_ENOTSUP)
+ break;
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ status = pjmedia_vid_codec_unpacketize(
+ codec, payload, payload_len,
+ port_data->pack_buf,
+ port_data->pack_buf_size,
+ &unpack_pos);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ // what happen if the bitstream is broken?
+ //if (i++ != 1) unpack_pos -= 10;
+
+ packetized = PJ_TRUE;
+ }
+
+ if (packetized) {
+ enc_frame.buf = port_data->pack_buf;
+ enc_frame.size = unpack_pos;
+ }
+ }
+#endif
+
+ status = pjmedia_vid_codec_decode(codec, &enc_frame,
+ frame->size, frame);
+ if (status != PJ_SUCCESS) goto on_error;
+ }
+#endif
+
+ status = pjmedia_port_put_frame(
+ pjmedia_vid_port_get_passive_port(port_data->rdr_port),
+ frame);
+ if (status != PJ_SUCCESS) goto on_error;
+
+ return PJ_SUCCESS;
+
+on_error:
+ pj_perror(3, THIS_FILE, status, "codec_put_frame() error");
+ return status;
+}
+
+static const char* dump_codec_info(const pjmedia_vid_codec_info *info)
+{
+ static char str[80];
+ unsigned i;
+ char *p = str;
+
+ /* Raw format ids */
+ for (i=0; (i<info->dec_fmt_id_cnt) && (p-str+5<sizeof(str)); ++i) {
+ pj_memcpy(p, &info->dec_fmt_id[i], 4);
+ p += 4;
+ *p++ = ' ';
+ }
+ *p = '\0';
+
+ return str;
+}
+
+static int enum_codecs()
+{
+ unsigned i, cnt;
+ pjmedia_vid_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS];
+ pj_status_t status;
+
+ PJ_LOG(3, (THIS_FILE, " codec enums"));
+ cnt = PJ_ARRAY_SIZE(info);
+ status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &cnt, info, NULL);
+ if (status != PJ_SUCCESS)
+ return 100;
+
+ for (i = 0; i < cnt; ++i) {
+ PJ_LOG(3, (THIS_FILE, " %-16.*s %c%c %s",
+ info[i].encoding_name.slen, info[i].encoding_name.ptr,
+ (info[i].dir & PJMEDIA_DIR_ENCODING? 'E' : ' '),
+ (info[i].dir & PJMEDIA_DIR_DECODING? 'D' : ' '),
+ dump_codec_info(&info[i])));
+ }
+
+ return PJ_SUCCESS;
+}
+
+static int encode_decode_test(pj_pool_t *pool, const char *codec_id)
+{
+ const pj_str_t port_name = {"codec", 5};
+
+ pjmedia_vid_codec *codec=NULL;
+ pjmedia_port codec_port;
+ codec_port_data_t codec_port_data;
+ pjmedia_vid_codec_param codec_param;
+ const pjmedia_vid_codec_info *codec_info;
+
+ pjmedia_vid_dev_index cap_idx, rdr_idx;
+ pjmedia_vid_port *capture=NULL, *renderer=NULL;
+ pjmedia_vid_port_param vport_param;
+ pjmedia_video_format_detail *vfd;
+ pjmedia_event_subscription esub;
+ pj_status_t status;
+ int rc = 0;
+
+ PJ_LOG(3, (THIS_FILE, " encode decode test"));
+
+ /* Lookup codec */
+ {
+ pj_str_t codec_id_st;
+ unsigned info_cnt = 1;
+
+ /* Lookup codec */
+ pj_cstr(&codec_id_st, codec_id);
+ status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, &codec_id_st,
+ &info_cnt,
+ &codec_info, NULL);
+ if (status != PJ_SUCCESS) {
+ rc = 205; goto on_return;
+ }
+ }
+
+
+#if CAPTURE_DEV == -1
+ /* Lookup colorbar source */
+ status = pjmedia_vid_dev_lookup("Colorbar", "Colorbar generator", &cap_idx);
+ if (status != PJ_SUCCESS) {
+ rc = 206; goto on_return;
+ }
+#elif CAPTURE_DEV == -2
+ /* Lookup any first non-colorbar source */
+ {
+ unsigned i, cnt;
+ pjmedia_vid_dev_info info;
+
+ cap_idx = -1;
+ cnt = pjmedia_vid_dev_count();
+ for (i = 0; i < cnt; ++i) {
+ status = pjmedia_vid_dev_get_info(i, &info);
+ if (status != PJ_SUCCESS) {
+ rc = 206; goto on_return;
+ }
+ if (info.dir & PJMEDIA_DIR_CAPTURE &&
+ pj_ansi_stricmp(info.driver, "Colorbar"))
+ {
+ cap_idx = i;
+ break;
+ }
+ }
+
+ if (cap_idx == -1) {
+ status = PJ_ENOTFOUND;
+ rc = 206; goto on_return;
+ }
+ }
+#else
+ cap_idx = CAPTURE_DEV;
+#endif
+
+ /* Lookup SDL renderer */
+ status = pjmedia_vid_dev_lookup("SDL", "SDL renderer", &rdr_idx);
+ if (status != PJ_SUCCESS) {
+ rc = 207; goto on_return;
+ }
+
+ /* Prepare codec */
+ {
+ pj_str_t codec_id_st;
+ unsigned info_cnt = 1;
+ const pjmedia_vid_codec_info *codec_info;
+
+ /* Lookup codec */
+ pj_cstr(&codec_id_st, codec_id);
+ status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, &codec_id_st,
+ &info_cnt,
+ &codec_info, NULL);
+ if (status != PJ_SUCCESS) {
+ rc = 245; goto on_return;
+ }
+ status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info,
+ &codec_param);
+ if (status != PJ_SUCCESS) {
+ rc = 246; goto on_return;
+ }
+
+#if !BYPASS_CODEC
+
+ /* Open codec */
+ status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info,
+ &codec);
+ if (status != PJ_SUCCESS) {
+ rc = 250; goto on_return;
+ }
+
+ status = pjmedia_vid_codec_init(codec, pool);
+ if (status != PJ_SUCCESS) {
+ rc = 251; goto on_return;
+ }
+
+ status = pjmedia_vid_codec_open(codec, &codec_param);
+ if (status != PJ_SUCCESS) {
+ rc = 252; goto on_return;
+ }
+
+ /* After opened, codec will update the param, let's sync encoder &
+ * decoder format detail.
+ */
+ codec_param.dec_fmt.det = codec_param.enc_fmt.det;
+
+ /* Subscribe to codec events */
+ pjmedia_event_subscription_init(&esub, &codec_on_event,
+ &codec_port_data);
+ pjmedia_event_subscribe(&codec->epub, &esub);
+#endif /* !BYPASS_CODEC */
+ }
+
+ pjmedia_vid_port_param_default(&vport_param);
+
+ /* Create capture, set it to active (master) */
+ status = pjmedia_vid_dev_default_param(pool, cap_idx,
+ &vport_param.vidparam);
+ if (status != PJ_SUCCESS) {
+ rc = 220; goto on_return;
+ }
+ pjmedia_format_copy(&vport_param.vidparam.fmt, &codec_param.dec_fmt);
+ vport_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
+ vport_param.active = PJ_TRUE;
+
+ if (vport_param.vidparam.fmt.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO) {
+ rc = 221; goto on_return;
+ }
+
+ vfd = pjmedia_format_get_video_format_detail(&vport_param.vidparam.fmt,
+ PJ_TRUE);
+ if (vfd == NULL) {
+ rc = 225; goto on_return;
+ }
+
+ status = pjmedia_vid_port_create(pool, &vport_param, &capture);
+ if (status != PJ_SUCCESS) {
+ rc = 226; goto on_return;
+ }
+
+ /* Create renderer, set it to passive (slave) */
+ vport_param.active = PJ_FALSE;
+ vport_param.vidparam.dir = PJMEDIA_DIR_RENDER;
+ vport_param.vidparam.rend_id = rdr_idx;
+ vport_param.vidparam.disp_size = vfd->size;
+
+ status = pjmedia_vid_port_create(pool, &vport_param, &renderer);
+ if (status != PJ_SUCCESS) {
+ rc = 230; goto on_return;
+ }
+
+ /* Init codec port */
+ pj_bzero(&codec_port, sizeof(codec_port));
+ status = pjmedia_port_info_init2(&codec_port.info, &port_name, 0x1234,
+ PJMEDIA_DIR_ENCODING,
+ &codec_param.dec_fmt);
+ if (status != PJ_SUCCESS) {
+ rc = 260; goto on_return;
+ }
+
+ codec_port_data.codec = codec;
+ codec_port_data.rdr_port = renderer;
+ codec_port_data.enc_buf_size = codec_param.dec_fmt.det.vid.size.w *
+ codec_param.dec_fmt.det.vid.size.h * 4;
+ codec_port_data.enc_buf = pj_pool_alloc(pool,
+ codec_port_data.enc_buf_size);
+ codec_port_data.pack_buf_size = codec_port_data.enc_buf_size;
+ codec_port_data.pack_buf = pj_pool_alloc(pool,
+ codec_port_data.pack_buf_size);
+
+ codec_port.put_frame = &codec_put_frame;
+ codec_port.port_data.pdata = &codec_port_data;
+
+ /* Connect capture to codec port */
+ status = pjmedia_vid_port_connect(capture,
+ &codec_port,
+ PJ_FALSE);
+ if (status != PJ_SUCCESS) {
+ rc = 270; goto on_return;
+ }
+
+#if BYPASS_CODEC
+ PJ_LOG(3, (THIS_FILE, " starting loopback test: %c%c%c%c %dx%d",
+ ((codec_param.dec_fmt.id & 0x000000FF) >> 0),
+ ((codec_param.dec_fmt.id & 0x0000FF00) >> 8),
+ ((codec_param.dec_fmt.id & 0x00FF0000) >> 16),
+ ((codec_param.dec_fmt.id & 0xFF000000) >> 24),
+ codec_param.dec_fmt.det.vid.size.w,
+ codec_param.dec_fmt.det.vid.size.h
+ ));
+#else
+ PJ_LOG(3, (THIS_FILE, " starting codec test: %c%c%c%c<->%.*s %dx%d",
+ ((codec_param.dec_fmt.id & 0x000000FF) >> 0),
+ ((codec_param.dec_fmt.id & 0x0000FF00) >> 8),
+ ((codec_param.dec_fmt.id & 0x00FF0000) >> 16),
+ ((codec_param.dec_fmt.id & 0xFF000000) >> 24),
+ codec_info->encoding_name.slen,
+ codec_info->encoding_name.ptr,
+ codec_param.dec_fmt.det.vid.size.w,
+ codec_param.dec_fmt.det.vid.size.h
+ ));
+#endif
+
+ /* Start streaming.. */
+ status = pjmedia_vid_port_start(renderer);
+ if (status != PJ_SUCCESS) {
+ rc = 275; goto on_return;
+ }
+ status = pjmedia_vid_port_start(capture);
+ if (status != PJ_SUCCESS) {
+ rc = 280; goto on_return;
+ }
+
+ /* Sleep while the video is being displayed... */
+ pj_thread_sleep(10000);
+
+on_return:
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(3, (THIS_FILE, status, " error"));
+ }
+ if (capture) {
+ pjmedia_vid_port_stop(capture);
+ pjmedia_vid_port_destroy(capture);
+ }
+ if (renderer) {
+ pjmedia_vid_port_stop(renderer);
+ pjmedia_vid_port_destroy(renderer);
+ }
+ if (codec) {
+ pjmedia_vid_codec_close(codec);
+ pjmedia_vid_codec_mgr_dealloc_codec(NULL, codec);
+ }
+
+ return rc;
+}
+
+int vid_codec_test(void)
+{
+ pj_pool_t *pool;
+ int rc = 0;
+ pj_status_t status;
+ int orig_log_level;
+
+ orig_log_level = pj_log_get_level();
+ pj_log_set_level(6);
+
+ PJ_LOG(3, (THIS_FILE, "Performing video codec tests.."));
+
+ pool = pj_pool_create(mem, "Vid codec test", 256, 256, 0);
+
+ status = pjmedia_vid_dev_subsys_init(mem);
+ if (status != PJ_SUCCESS)
+ return -10;
+
+ status = pjmedia_codec_ffmpeg_init(NULL, mem);
+ if (status != PJ_SUCCESS)
+ return -20;
+
+ rc = enum_codecs();
+ if (rc != 0)
+ goto on_return;
+
+ rc = encode_decode_test(pool, "h263-1998");
+ if (rc != 0)
+ goto on_return;
+
+on_return:
+ pjmedia_codec_ffmpeg_deinit();
+ pjmedia_vid_dev_subsys_shutdown();
+ pj_pool_release(pool);
+ pj_log_set_level(orig_log_level);
+
+ return rc;
+}
+
+
diff --git a/pjmedia/src/test/vid_dev_test.c b/pjmedia/src/test/vid_dev_test.c
new file mode 100644
index 00000000..36966047
--- /dev/null
+++ b/pjmedia/src/test/vid_dev_test.c
@@ -0,0 +1,292 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjmedia-audiodev/audiodev.h>
+#include <pjmedia-codec/ffmpeg_codecs.h>
+#include <pjmedia/vid_codec.h>
+#include <pjmedia_videodev.h>
+
+#define THIS_FILE "vid_dev_test.c"
+#define LOOP_DURATION 10
+
+static pj_bool_t is_quitting = PJ_FALSE;
+
+static const char *vid_dir_name(pjmedia_dir dir)
+{
+ switch (dir) {
+ case PJMEDIA_DIR_CAPTURE:
+ return "capture";
+ case PJMEDIA_DIR_RENDER:
+ return "render";
+ case PJMEDIA_DIR_CAPTURE_RENDER:
+ return "capture & render";
+ default:
+ return "unknown";
+ }
+}
+
+static int enum_devs(void)
+{
+ unsigned i, dev_cnt;
+ pj_status_t status;
+
+ PJ_LOG(3, (THIS_FILE, " Enum video devices:"));
+ dev_cnt = pjmedia_vid_dev_count();
+ for (i = 0; i < dev_cnt; ++i) {
+ pjmedia_vid_dev_info di;
+ status = pjmedia_vid_dev_get_info(i, &di);
+ if (status == PJ_SUCCESS) {
+ unsigned j;
+
+ PJ_LOG(3, (THIS_FILE, " %3d: %s (%s) - %s", i, di.name, di.driver,
+ vid_dir_name(di.dir)));
+
+ PJ_LOG(3,(THIS_FILE, " Supported formats:"));
+ for (j=0; j<di.fmt_cnt; ++j) {
+ const pjmedia_video_format_info *vfi;
+
+ vfi = pjmedia_get_video_format_info(NULL, di.fmt[j].id);
+ PJ_LOG(3,(THIS_FILE, " %s",
+ (vfi ? vfi->name : "unknown")));
+ }
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t vid_event_cb(pjmedia_event_subscription *esub,
+ pjmedia_event *event)
+{
+ if (event->type == PJMEDIA_EVENT_WND_CLOSED)
+ is_quitting = PJ_TRUE;
+
+ return PJ_SUCCESS;
+}
+
+static int capture_render_loopback(int cap_dev_id, int rend_dev_id,
+ const pjmedia_format *fmt)
+{
+ pj_pool_t *pool;
+ pjmedia_vid_port *capture=NULL, *renderer=NULL;
+ pjmedia_vid_dev_info cdi, rdi;
+ pjmedia_vid_port_param param;
+ pjmedia_video_format_detail *vfd;
+ pjmedia_event_subscription esub;
+ pj_status_t status;
+ int rc = 0, i;
+
+ pool = pj_pool_create(mem, "vidloop", 1000, 1000, NULL);
+
+ status = pjmedia_vid_dev_get_info(cap_dev_id, &cdi);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ status = pjmedia_vid_dev_get_info(rend_dev_id, &rdi);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ PJ_LOG(3,(THIS_FILE,
+ " %s (%s) ===> %s (%s)\t%s\t%dx%d\t@%d:%d fps",
+ cdi.name, cdi.driver, rdi.name, rdi.driver,
+ pjmedia_get_video_format_info(NULL, fmt->id)->name,
+ fmt->det.vid.size.w, fmt->det.vid.size.h,
+ fmt->det.vid.fps.num, fmt->det.vid.fps.denum));
+
+ pjmedia_vid_port_param_default(&param);
+
+ /* Create capture, set it to active (master) */
+ status = pjmedia_vid_dev_default_param(pool, cap_dev_id,
+ &param.vidparam);
+ if (status != PJ_SUCCESS) {
+ rc = 100; goto on_return;
+ }
+ param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
+ param.vidparam.fmt = *fmt;
+ param.active = PJ_TRUE;
+
+ if (param.vidparam.fmt.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO) {
+ rc = 103; goto on_return;
+ }
+
+ vfd = pjmedia_format_get_video_format_detail(&param.vidparam.fmt, PJ_TRUE);
+ if (vfd == NULL) {
+ rc = 105; goto on_return;
+ }
+
+ status = pjmedia_vid_port_create(pool, &param, &capture);
+ if (status != PJ_SUCCESS) {
+ rc = 110; goto on_return;
+ }
+
+ /* Create renderer, set it to passive (slave) */
+ status = pjmedia_vid_dev_default_param(pool, rend_dev_id,
+ &param.vidparam);
+ if (status != PJ_SUCCESS) {
+ rc = 120; goto on_return;
+ }
+
+ param.active = PJ_FALSE;
+ param.vidparam.dir = PJMEDIA_DIR_RENDER;
+ param.vidparam.rend_id = rend_dev_id;
+ param.vidparam.fmt = *fmt;
+ param.vidparam.disp_size = vfd->size;
+
+ status = pjmedia_vid_port_create(pool, &param, &renderer);
+ if (status != PJ_SUCCESS) {
+ rc = 130; goto on_return;
+ }
+
+ /* Set event handler */
+ pjmedia_event_subscription_init(&esub, &vid_event_cb, NULL);
+ pjmedia_event_subscribe(
+ pjmedia_vid_port_get_event_publisher(renderer),
+ &esub);
+
+ /* Connect capture to renderer */
+ status = pjmedia_vid_port_connect(
+ capture,
+ pjmedia_vid_port_get_passive_port(renderer),
+ PJ_FALSE);
+ if (status != PJ_SUCCESS) {
+ rc = 140; goto on_return;
+ }
+
+ /* Start streaming.. */
+ status = pjmedia_vid_port_start(renderer);
+ if (status != PJ_SUCCESS) {
+ rc = 150; goto on_return;
+ }
+ status = pjmedia_vid_port_start(capture);
+ if (status != PJ_SUCCESS) {
+ rc = 160; goto on_return;
+ }
+
+ /* Sleep while the webcam is being displayed... */
+ for (i = 0; i < LOOP_DURATION*10 && (!is_quitting); i++) {
+ pj_thread_sleep(100);
+ }
+
+on_return:
+ if (status != PJ_SUCCESS)
+ PJ_PERROR(3, (THIS_FILE, status, " error"));
+
+ if (capture)
+ pjmedia_vid_port_destroy(capture);
+ if (renderer)
+ pjmedia_vid_port_destroy(renderer);
+
+ pj_pool_release(pool);
+ return rc;
+}
+
+static int loopback_test(void)
+{
+ unsigned count, i;
+ pjmedia_format_id test_fmts[] = {
+ PJMEDIA_FORMAT_YUY2
+ };
+ pjmedia_rect_size test_sizes[] = {
+ {176,144}, /* QCIF */
+ {352,288}, /* CIF */
+ {704,576} /* 4CIF */
+ };
+ pjmedia_ratio test_fpses[] = {
+ {25, 1},
+ {30, 1},
+ };
+ pj_status_t status;
+
+ PJ_LOG(3, (THIS_FILE, " Loopback tests (prepare you webcams):"));
+
+ count = pjmedia_vid_dev_count();
+ for (i=0; i<count; ++i) {
+ pjmedia_vid_dev_info cdi;
+ unsigned j;
+
+ status = pjmedia_vid_dev_get_info(i, &cdi);
+ if (status != PJ_SUCCESS)
+ return -300;
+
+ /* Only interested with capture device */
+ if ((cdi.dir & PJMEDIA_DIR_CAPTURE) == 0)
+ continue;
+
+ for (j=i+1; j<count; ++j) {
+ pjmedia_vid_dev_info rdi;
+ unsigned k;
+
+ status = pjmedia_vid_dev_get_info(j, &rdi);
+ if (status != PJ_SUCCESS)
+ return -310;
+
+ /* Only interested with render device */
+ if ((rdi.dir & PJMEDIA_DIR_RENDER) == 0)
+ continue;
+
+ /* Test with the format, size, and fps combinations */
+ for (k=0; k<PJ_ARRAY_SIZE(test_fmts); ++k) {
+ unsigned l;
+
+ for (l=0; l<PJ_ARRAY_SIZE(test_sizes); ++l) {
+ unsigned m;
+
+ for (m=0; m<PJ_ARRAY_SIZE(test_fpses); ++m) {
+ pjmedia_format fmt;
+
+ pjmedia_format_init_video(&fmt, test_fmts[k],
+ test_sizes[l].w,
+ test_sizes[l].h,
+ test_fpses[m].num,
+ test_fpses[m].denum);
+
+ capture_render_loopback(i, j, &fmt);
+ }
+ }
+ } /* k */
+
+ }
+ }
+
+ return 0;
+}
+
+int vid_dev_test(void)
+{
+ int rc = 0;
+ pj_status_t status;
+
+ status = pjmedia_vid_dev_subsys_init(mem);
+ if (status != PJ_SUCCESS)
+ return -10;
+
+ rc = enum_devs();
+ if (rc != 0)
+ goto on_return;
+
+ rc = loopback_test();
+ if (rc != 0)
+ goto on_return;
+
+on_return:
+ pjmedia_vid_dev_subsys_shutdown();
+
+ return rc;
+}
diff --git a/pjmedia/src/test/vid_port_test.c b/pjmedia/src/test/vid_port_test.c
new file mode 100644
index 00000000..cd7acd8d
--- /dev/null
+++ b/pjmedia/src/test/vid_port_test.c
@@ -0,0 +1,241 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjmedia-audiodev/audiodev.h>
+#include <pjmedia-codec/ffmpeg_codecs.h>
+#include <pjmedia/vid_codec.h>
+#include <pjmedia_videodev.h>
+
+#define THIS_FILE "vid_dev_test.c"
+#define LOOP_DURATION 6
+
+static pj_bool_t is_quitting = PJ_FALSE;
+
+static pj_status_t vid_event_cb(pjmedia_event_subscription *esub,
+ pjmedia_event *event)
+{
+ if (event->type == PJMEDIA_EVENT_WND_CLOSED)
+ is_quitting = PJ_TRUE;
+
+ return PJ_SUCCESS;
+}
+
+static int capture_render_loopback(pj_bool_t active,
+ int cap_dev_id, int rend_dev_id,
+ const pjmedia_format *fmt)
+{
+ pj_pool_t *pool;
+ pjmedia_vid_port *capture=NULL, *renderer=NULL;
+ pjmedia_vid_dev_info cdi, rdi;
+ pjmedia_vid_port_param param;
+ pjmedia_video_format_detail *vfd;
+ pjmedia_vid_dev_cb cb;
+ pjmedia_event_subscription esub;
+ pj_status_t status;
+ int rc = 0, i;
+
+ pool = pj_pool_create(mem, "vidportloop", 1000, 1000, NULL);
+
+ status = pjmedia_vid_dev_get_info(cap_dev_id, &cdi);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ status = pjmedia_vid_dev_get_info(rend_dev_id, &rdi);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ PJ_LOG(3,(THIS_FILE,
+ " %s (%s) ===> %s (%s)\t%s\t%dx%d\t@%d:%d fps",
+ cdi.name, cdi.driver, rdi.name, rdi.driver,
+ pjmedia_get_video_format_info(NULL, fmt->id)->name,
+ fmt->det.vid.size.w, fmt->det.vid.size.h,
+ fmt->det.vid.fps.num, fmt->det.vid.fps.denum));
+
+ pjmedia_vid_port_param_default(&param);
+
+ /* Create capture, set it to active (master) */
+ status = pjmedia_vid_dev_default_param(pool, cap_dev_id,
+ &param.vidparam);
+ if (status != PJ_SUCCESS) {
+ rc = 100; goto on_return;
+ }
+ param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
+ param.vidparam.fmt = *fmt;
+ param.active = (active? PJ_TRUE: PJ_FALSE);
+
+ if (param.vidparam.fmt.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO) {
+ rc = 103; goto on_return;
+ }
+
+ vfd = pjmedia_format_get_video_format_detail(&param.vidparam.fmt, PJ_TRUE);
+ if (vfd == NULL) {
+ rc = 105; goto on_return;
+ }
+
+ status = pjmedia_vid_port_create(pool, &param, &capture);
+ if (status != PJ_SUCCESS) {
+ rc = 110; goto on_return;
+ }
+
+ /* Create renderer, set it to passive (slave) */
+ status = pjmedia_vid_dev_default_param(pool, rend_dev_id,
+ &param.vidparam);
+ if (status != PJ_SUCCESS) {
+ rc = 120; goto on_return;
+ }
+
+ param.active = (active? PJ_FALSE: PJ_TRUE);
+ param.vidparam.dir = PJMEDIA_DIR_RENDER;
+ param.vidparam.rend_id = rend_dev_id;
+ param.vidparam.fmt = *fmt;
+ param.vidparam.disp_size = vfd->size;
+
+ status = pjmedia_vid_port_create(pool, &param, &renderer);
+ if (status != PJ_SUCCESS) {
+ rc = 130; goto on_return;
+ }
+
+ /* Set event handler */
+ pjmedia_event_subscription_init(&esub, &vid_event_cb, NULL);
+ pjmedia_event_subscribe(
+ pjmedia_vid_port_get_event_publisher(renderer),
+ &esub);
+
+ /* Connect capture to renderer */
+ status = pjmedia_vid_port_connect(
+ (active? capture: renderer),
+ pjmedia_vid_port_get_passive_port(active? renderer: capture),
+ PJ_FALSE);
+ if (status != PJ_SUCCESS) {
+ rc = 140; goto on_return;
+ }
+
+ /* Start streaming.. */
+ status = pjmedia_vid_port_start(renderer);
+ if (status != PJ_SUCCESS) {
+ rc = 150; goto on_return;
+ }
+ status = pjmedia_vid_port_start(capture);
+ if (status != PJ_SUCCESS) {
+ rc = 160; goto on_return;
+ }
+
+ /* Sleep while the webcam is being displayed... */
+ for (i = 0; i < LOOP_DURATION*10 && (!is_quitting); i++) {
+ pj_thread_sleep(100);
+ }
+
+on_return:
+ if (status != PJ_SUCCESS)
+ PJ_PERROR(3, (THIS_FILE, status, " error"));
+
+ if (capture)
+ pjmedia_vid_port_destroy(capture);
+ if (renderer)
+ pjmedia_vid_port_destroy(renderer);
+
+ pj_pool_release(pool);
+ return rc;
+}
+
+static int find_device(pjmedia_dir dir,
+ pj_bool_t has_callback)
+{
+ unsigned i, count = pjmedia_vid_dev_count();
+
+ for (i = 0; i < count; ++i) {
+ pjmedia_vid_dev_info cdi;
+
+ if (pjmedia_vid_dev_get_info(i, &cdi) != PJ_SUCCESS)
+ continue;
+ if ((cdi.dir & dir) != 0 && cdi.has_callback == has_callback)
+ return i;
+ }
+
+ return -999;
+}
+
+static int vidport_test(void)
+{
+ int i, j, k, l;
+ int cap_id, rend_id;
+ pjmedia_format_id test_fmts[] = {
+ PJMEDIA_FORMAT_RGBA,
+ PJMEDIA_FORMAT_I420
+ };
+ pj_status_t status;
+
+ PJ_LOG(3, (THIS_FILE, " Video port tests:"));
+
+ /* Capturer's role: active/passive. */
+ for (i = 1; i >= 0; i--) {
+ /* Capturer's device has_callback: TRUE/FALSE. */
+ for (j = 1; j >= 0; j--) {
+ cap_id = find_device(PJMEDIA_DIR_CAPTURE, j);
+ if (cap_id < 0)
+ continue;
+
+ /* Renderer's device has callback: TRUE/FALSE. */
+ for (k = 1; k >= 0; k--) {
+ rend_id = find_device(PJMEDIA_DIR_RENDER, k);
+ if (rend_id < 0)
+ continue;
+
+ /* Check various formats to test format conversion. */
+ for (l = 0; l < PJ_ARRAY_SIZE(test_fmts); ++l) {
+ pjmedia_format fmt;
+
+ PJ_LOG(3,(THIS_FILE,
+ "capturer %s (stream: %s) ===> "
+ "renderer %s (stream: %s)",
+ (i? "active": "passive"),
+ (j? "active": "passive"),
+ (i? "passive": "active"),
+ (k? "active": "passive")));
+
+ pjmedia_format_init_video(&fmt, test_fmts[l],
+ 640, 480, 25, 1);
+ capture_render_loopback(i, cap_id, rend_id, &fmt);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+int vid_port_test(void)
+{
+ int rc = 0;
+ pj_status_t status;
+
+ status = pjmedia_vid_dev_subsys_init(mem);
+ if (status != PJ_SUCCESS)
+ return -10;
+
+ rc = vidport_test();
+ if (rc != 0)
+ goto on_return;
+
+on_return:
+ pjmedia_vid_dev_subsys_shutdown();
+
+ return rc;
+}