From cd283c8825c9a94400f27735acb1c9385e90ffc8 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 19 Jul 2011 03:42:28 +0000 Subject: 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 --- aconfigure | 458 +++- aconfigure.ac | 150 +- build.mak.in | 34 + build/rules.mak | 2 +- pjlib-util/build/pjlib_util.dsp | 282 -- pjlib-util/build/pjlib_util_test.dsp | 130 - pjlib/build/pjlib++-test.dsp | 102 - pjlib/build/pjlib++.dsp | 146 - pjlib/build/pjlib.dsp | 639 ----- pjlib/build/pjlib.dsw | 89 - pjlib/build/pjlib_samples.dsp | 113 - pjlib/build/pjlib_test.dsp | 244 -- pjlib/include/pj/compat/cc_gcc.h | 3 + pjlib/include/pj/compat/cc_msvc.h | 3 + pjlib/include/pj/config.h | 11 +- pjlib/include/pj/errno.h | 2 + pjlib/include/pj/os.h | 30 + pjlib/src/pj/errno.c | 4 +- pjlib/src/pj/os_core_darwin.m | 86 + pjlib/src/pj/os_core_symbian.cpp | 9 + pjlib/src/pj/os_core_unix.c | 8 + pjlib/src/pj/os_core_win32.c | 9 + pjmedia/build/Makefile | 48 +- pjmedia/build/os-auto.mak.in | 53 +- pjmedia/build/pjaut.dsp | 102 - pjmedia/build/pjmedia.dsp | 488 ---- pjmedia/build/pjmedia.dsw | 140 - pjmedia/build/pjmedia.vcproj | 647 ++--- pjmedia/build/pjmedia_audiodev.dsp | 154 -- pjmedia/build/pjmedia_codec.dsp | 223 -- pjmedia/build/pjmedia_codec.vcproj | 24 + pjmedia/build/pjmedia_test.dsp | 144 - pjmedia/build/pjmedia_test.vcproj | 724 ++--- pjmedia/build/pjmedia_videodev.vcproj | 2856 ++++++++++++++++++++ pjmedia/include/pjmedia-audiodev/audiodev.h | 2 + pjmedia/include/pjmedia-audiodev/config.h | 4 +- pjmedia/include/pjmedia-codec.h | 2 + pjmedia/include/pjmedia-codec/audio_codecs.h | 98 + pjmedia/include/pjmedia-codec/config.h | 14 +- pjmedia/include/pjmedia-codec/ffmpeg_codecs.h | 62 + pjmedia/include/pjmedia-codec/h263_packetizer.h | 146 + pjmedia/include/pjmedia-codec/h264_packetizer.h | 157 ++ pjmedia/include/pjmedia-codec/types.h | 25 +- pjmedia/include/pjmedia-videodev/config.h | 192 ++ pjmedia/include/pjmedia-videodev/errno.h | 159 ++ pjmedia/include/pjmedia-videodev/videodev.h | 618 +++++ pjmedia/include/pjmedia-videodev/videodev_imp.h | 202 ++ pjmedia/include/pjmedia.h | 16 +- pjmedia/include/pjmedia/avi.h | 202 ++ pjmedia/include/pjmedia/avi_stream.h | 170 ++ pjmedia/include/pjmedia/circbuf.h | 1 + pjmedia/include/pjmedia/clock.h | 129 +- pjmedia/include/pjmedia/codec.h | 202 +- pjmedia/include/pjmedia/conference.h | 6 +- pjmedia/include/pjmedia/config.h | 162 ++ pjmedia/include/pjmedia/converter.h | 322 +++ pjmedia/include/pjmedia/endpoint.h | 56 + pjmedia/include/pjmedia/errno.h | 11 +- pjmedia/include/pjmedia/event.h | 402 +++ pjmedia/include/pjmedia/format.h | 748 +++++ pjmedia/include/pjmedia/frame.h | 332 +++ pjmedia/include/pjmedia/jbuf.h | 113 +- pjmedia/include/pjmedia/port.h | 205 +- pjmedia/include/pjmedia/sdp.h | 27 +- pjmedia/include/pjmedia/sdp_neg.h | 89 + pjmedia/include/pjmedia/signatures.h | 217 ++ pjmedia/include/pjmedia/sound_port.h | 14 + pjmedia/include/pjmedia/stream.h | 41 +- pjmedia/include/pjmedia/stream_common.h | 57 + pjmedia/include/pjmedia/transport.h | 33 + pjmedia/include/pjmedia/transport_ice.h | 24 + pjmedia/include/pjmedia/types.h | 448 +-- pjmedia/include/pjmedia/vid_codec.h | 978 +++++++ pjmedia/include/pjmedia/vid_codec_util.h | 158 ++ pjmedia/include/pjmedia/vid_port.h | 241 ++ pjmedia/include/pjmedia/vid_stream.h | 319 +++ pjmedia/include/pjmedia/vid_tee.h | 142 + pjmedia/include/pjmedia_videodev.h | 30 + pjmedia/src/pjmedia-audiodev/audiodev.c | 4 +- pjmedia/src/pjmedia-audiodev/wmme_dev.c | 12 +- pjmedia/src/pjmedia-codec/audio_codecs.c | 112 + pjmedia/src/pjmedia-codec/ffmpeg_codecs.c | 1492 ++++++++++ pjmedia/src/pjmedia-codec/g722.c | 3 +- pjmedia/src/pjmedia-codec/g7221.c | 3 +- pjmedia/src/pjmedia-codec/gsm.c | 3 +- pjmedia/src/pjmedia-codec/h263_packetizer.c | 287 ++ pjmedia/src/pjmedia-codec/h264_packetizer.c | 530 ++++ pjmedia/src/pjmedia-codec/ilbc.c | 3 +- pjmedia/src/pjmedia-codec/ipp_codecs.c | 3 +- pjmedia/src/pjmedia-codec/l16.c | 3 +- pjmedia/src/pjmedia-codec/passthrough.c | 3 +- pjmedia/src/pjmedia-codec/speex_codec.c | 3 +- pjmedia/src/pjmedia-videodev/colorbar_dev.c | 622 +++++ pjmedia/src/pjmedia-videodev/dshow_dev.c | 1016 +++++++ pjmedia/src/pjmedia-videodev/dshowclasses.cpp | 245 ++ pjmedia/src/pjmedia-videodev/errno.c | 112 + pjmedia/src/pjmedia-videodev/ffmpeg_dev.c | 514 ++++ pjmedia/src/pjmedia-videodev/ios_dev.m | 685 +++++ pjmedia/src/pjmedia-videodev/qt_dev.m | 636 +++++ pjmedia/src/pjmedia-videodev/sdl_dev.c | 1291 +++++++++ pjmedia/src/pjmedia-videodev/sdl_dev_m.m | 20 + pjmedia/src/pjmedia-videodev/v4l2_dev.c | 820 ++++++ pjmedia/src/pjmedia-videodev/videodev.c | 806 ++++++ pjmedia/src/pjmedia/avi_player.c | 711 +++++ pjmedia/src/pjmedia/bidirectional.c | 14 +- pjmedia/src/pjmedia/clock_thread.c | 124 +- pjmedia/src/pjmedia/codec.c | 16 + pjmedia/src/pjmedia/conf_switch.c | 105 +- pjmedia/src/pjmedia/conference.c | 21 +- pjmedia/src/pjmedia/converter.c | 178 ++ pjmedia/src/pjmedia/converter_libswscale.c | 200 ++ pjmedia/src/pjmedia/delaybuf.c | 1 + pjmedia/src/pjmedia/dummy.c | 24 + pjmedia/src/pjmedia/echo_common.c | 1 + pjmedia/src/pjmedia/echo_port.c | 32 +- pjmedia/src/pjmedia/echo_speex.c | 1 + pjmedia/src/pjmedia/echo_suppress.c | 1 + pjmedia/src/pjmedia/endpoint.c | 371 ++- pjmedia/src/pjmedia/errno.c | 2 + pjmedia/src/pjmedia/event.c | 148 + pjmedia/src/pjmedia/ffmpeg_util.c | 153 ++ pjmedia/src/pjmedia/ffmpeg_util.h | 55 + pjmedia/src/pjmedia/format.c | 366 +++ pjmedia/src/pjmedia/g711.c | 3 +- pjmedia/src/pjmedia/jbuf.c | 149 +- pjmedia/src/pjmedia/master_port.c | 37 +- pjmedia/src/pjmedia/mem_capture.c | 6 +- pjmedia/src/pjmedia/mem_player.c | 12 +- pjmedia/src/pjmedia/null_port.c | 12 +- pjmedia/src/pjmedia/port.c | 65 +- pjmedia/src/pjmedia/resample_port.c | 50 +- pjmedia/src/pjmedia/sdp_cmp.c | 7 + pjmedia/src/pjmedia/sdp_neg.c | 233 +- pjmedia/src/pjmedia/session.c | 4 +- pjmedia/src/pjmedia/silencedet.c | 1 + pjmedia/src/pjmedia/sound_port.c | 41 +- pjmedia/src/pjmedia/splitcomb.c | 105 +- pjmedia/src/pjmedia/stereo_port.c | 67 +- pjmedia/src/pjmedia/stream.c | 701 ++++- pjmedia/src/pjmedia/stream_common.c | 111 + pjmedia/src/pjmedia/tonegen.c | 14 +- pjmedia/src/pjmedia/transport_ice.c | 17 + pjmedia/src/pjmedia/types.c | 47 + pjmedia/src/pjmedia/vid_codec.c | 731 +++++ pjmedia/src/pjmedia/vid_codec_util.c | 619 +++++ pjmedia/src/pjmedia/vid_port.c | 948 +++++++ pjmedia/src/pjmedia/vid_stream.c | 1940 +++++++++++++ pjmedia/src/pjmedia/vid_tee.c | 384 +++ pjmedia/src/pjmedia/wav_player.c | 33 +- pjmedia/src/pjmedia/wav_playlist.c | 31 +- pjmedia/src/pjmedia/wav_writer.c | 10 +- pjmedia/src/test/codec_vectors.c | 26 +- pjmedia/src/test/main.c | 10 +- pjmedia/src/test/mips_test.c | 32 +- pjmedia/src/test/test.c | 23 + pjmedia/src/test/test.h | 6 + pjmedia/src/test/vid_codec_test.c | 467 ++++ pjmedia/src/test/vid_dev_test.c | 292 ++ pjmedia/src/test/vid_port_test.c | 241 ++ pjnath/build/pjnath.dsp | 232 -- pjnath/build/pjnath.dsw | 116 - pjnath/build/pjnath_test.dsp | 145 - pjnath/build/pjstun_srv_test.dsp | 122 - pjnath/build/pjturn_client.dsp | 102 - pjnath/build/pjturn_srv.dsp | 130 - pjproject-vs8.sln | 153 +- pjproject.dsw | 680 ----- pjsip-apps/build/Makefile | 2 +- pjsip-apps/build/Samples-vc.mak | 6 +- pjsip-apps/build/Samples.mak | 5 +- pjsip-apps/build/pjsip_apps.dsw | 443 --- pjsip-apps/build/pjsua.dsp | 109 - pjsip-apps/build/pjsua.vcproj | 811 +++--- pjsip-apps/build/pjsystest.dsp | 122 - pjsip-apps/build/py_pjsua.dsp | 126 - pjsip-apps/build/python_pjsua.dsp | 116 - pjsip-apps/build/sample_debug.dsp | 104 - pjsip-apps/build/sample_debug.vcproj | 354 +-- pjsip-apps/build/samples.dsp | 241 -- pjsip-apps/build/samples.vcproj | 476 ++-- pjsip-apps/src/ipjsua/config.cfg | 2 + .../src/ipjsua/ipjsua.xcodeproj/project.pbxproj | 86 +- pjsip-apps/src/pjsua/main.c | 7 +- pjsip-apps/src/pjsua/pjsua_app.c | 636 ++++- pjsip-apps/src/pjsystest/systest.c | 27 +- pjsip-apps/src/samples/aectest.c | 28 +- pjsip-apps/src/samples/auddemo.c | 18 +- pjsip-apps/src/samples/aviplay.c | 531 ++++ pjsip-apps/src/samples/confbench.c | 22 +- pjsip-apps/src/samples/encdec.c | 33 +- pjsip-apps/src/samples/jbsim.c | 56 +- pjsip-apps/src/samples/latency.c | 16 +- pjsip-apps/src/samples/level.c | 8 +- pjsip-apps/src/samples/mix.c | 4 +- pjsip-apps/src/samples/pcaputil.c | 40 +- pjsip-apps/src/samples/pjsip-perf.c | 13 +- pjsip-apps/src/samples/playfile.c | 8 +- pjsip-apps/src/samples/playsine.c | 32 +- pjsip-apps/src/samples/recfile.c | 8 +- pjsip-apps/src/samples/resampleplay.c | 5 +- pjsip-apps/src/samples/simpleua.c | 443 ++- pjsip-apps/src/samples/stereotest.c | 12 +- pjsip-apps/src/samples/streamutil.c | 91 +- pjsip-apps/src/samples/vid_streamutil.c | 929 +++++++ pjsip/build/Makefile | 5 +- pjsip/build/pjsip.dsw | 152 -- pjsip/build/pjsip_core.dsp | 385 --- pjsip/build/pjsip_simple.dsp | 186 -- pjsip/build/pjsip_test.dsp | 184 -- pjsip/build/pjsip_ua.dsp | 148 - pjsip/build/pjsua_lib.dsp | 128 - pjsip/build/pjsua_lib.vcproj | 484 ++-- pjsip/include/pjsua-lib/pjsua.h | 755 +++++- pjsip/include/pjsua-lib/pjsua_internal.h | 160 +- pjsip/src/pjsip-ua/sip_100rel.c | 1 + pjsip/src/pjsip/sip_ua_layer.c | 4 + pjsip/src/pjsua-lib/pjsua_acc.c | 3 +- pjsip/src/pjsua-lib/pjsua_call.c | 1326 +++------ pjsip/src/pjsua-lib/pjsua_core.c | 60 +- pjsip/src/pjsua-lib/pjsua_dump.c | 944 +++++++ pjsip/src/pjsua-lib/pjsua_media.c | 1935 +++++++------ pjsip/src/pjsua-lib/pjsua_vid.c | 1648 +++++++++++ .../scripts-sendto/400_fmtp_g7221_with_bitrate.py | 2 +- .../401_fmtp_g7221_with_bitrate_24000.py | 2 +- .../401_fmtp_g7221_with_bitrate_32000.py | 2 +- .../410_fmtp_amrnb_offer_octet_align.py | 2 +- .../411_fmtp_amrnb_offer_band_eff.py | 2 +- .../412_fmtp_amrnb_offer_band_eff2.py | 2 +- third_party/build/g7221/libg7221codec.dsp | 186 -- third_party/build/gsm/libgsmcodec.dsp | 194 -- third_party/build/ilbc/libilbccodec.dsp | 282 -- third_party/build/milenage/libmilenage.dsp | 110 - third_party/build/portaudio/libportaudio.dsp | 198 -- third_party/build/resample/libresample.dsp | 118 - third_party/build/resample/libresample_dll.dsp | 133 - third_party/build/speex/libspeex.dsp | 398 --- third_party/build/srtp/libsrtp.dsp | 346 --- version.mak | 2 +- 238 files changed, 39103 insertions(+), 13994 deletions(-) delete mode 100644 pjlib-util/build/pjlib_util.dsp delete mode 100644 pjlib-util/build/pjlib_util_test.dsp delete mode 100644 pjlib/build/pjlib++-test.dsp delete mode 100644 pjlib/build/pjlib++.dsp delete mode 100644 pjlib/build/pjlib.dsp delete mode 100644 pjlib/build/pjlib.dsw delete mode 100644 pjlib/build/pjlib_samples.dsp delete mode 100644 pjlib/build/pjlib_test.dsp create mode 100644 pjlib/src/pj/os_core_darwin.m delete mode 100644 pjmedia/build/pjaut.dsp delete mode 100644 pjmedia/build/pjmedia.dsp delete mode 100644 pjmedia/build/pjmedia.dsw delete mode 100644 pjmedia/build/pjmedia_audiodev.dsp delete mode 100644 pjmedia/build/pjmedia_codec.dsp delete mode 100644 pjmedia/build/pjmedia_test.dsp create mode 100644 pjmedia/build/pjmedia_videodev.vcproj create mode 100644 pjmedia/include/pjmedia-codec/audio_codecs.h create mode 100644 pjmedia/include/pjmedia-codec/ffmpeg_codecs.h create mode 100644 pjmedia/include/pjmedia-codec/h263_packetizer.h create mode 100644 pjmedia/include/pjmedia-codec/h264_packetizer.h create mode 100644 pjmedia/include/pjmedia-videodev/config.h create mode 100644 pjmedia/include/pjmedia-videodev/errno.h create mode 100644 pjmedia/include/pjmedia-videodev/videodev.h create mode 100644 pjmedia/include/pjmedia-videodev/videodev_imp.h create mode 100644 pjmedia/include/pjmedia/avi.h create mode 100644 pjmedia/include/pjmedia/avi_stream.h create mode 100644 pjmedia/include/pjmedia/converter.h create mode 100644 pjmedia/include/pjmedia/event.h create mode 100644 pjmedia/include/pjmedia/format.h create mode 100644 pjmedia/include/pjmedia/frame.h create mode 100644 pjmedia/include/pjmedia/signatures.h create mode 100644 pjmedia/include/pjmedia/stream_common.h create mode 100644 pjmedia/include/pjmedia/vid_codec.h create mode 100644 pjmedia/include/pjmedia/vid_codec_util.h create mode 100644 pjmedia/include/pjmedia/vid_port.h create mode 100644 pjmedia/include/pjmedia/vid_stream.h create mode 100644 pjmedia/include/pjmedia/vid_tee.h create mode 100644 pjmedia/include/pjmedia_videodev.h create mode 100644 pjmedia/src/pjmedia-codec/audio_codecs.c create mode 100644 pjmedia/src/pjmedia-codec/ffmpeg_codecs.c create mode 100644 pjmedia/src/pjmedia-codec/h263_packetizer.c create mode 100644 pjmedia/src/pjmedia-codec/h264_packetizer.c create mode 100644 pjmedia/src/pjmedia-videodev/colorbar_dev.c create mode 100644 pjmedia/src/pjmedia-videodev/dshow_dev.c create mode 100644 pjmedia/src/pjmedia-videodev/dshowclasses.cpp create mode 100644 pjmedia/src/pjmedia-videodev/errno.c create mode 100644 pjmedia/src/pjmedia-videodev/ffmpeg_dev.c create mode 100644 pjmedia/src/pjmedia-videodev/ios_dev.m create mode 100644 pjmedia/src/pjmedia-videodev/qt_dev.m create mode 100644 pjmedia/src/pjmedia-videodev/sdl_dev.c create mode 100644 pjmedia/src/pjmedia-videodev/sdl_dev_m.m create mode 100644 pjmedia/src/pjmedia-videodev/v4l2_dev.c create mode 100644 pjmedia/src/pjmedia-videodev/videodev.c create mode 100644 pjmedia/src/pjmedia/avi_player.c create mode 100644 pjmedia/src/pjmedia/converter.c create mode 100644 pjmedia/src/pjmedia/converter_libswscale.c create mode 100644 pjmedia/src/pjmedia/dummy.c create mode 100644 pjmedia/src/pjmedia/event.c create mode 100644 pjmedia/src/pjmedia/ffmpeg_util.c create mode 100644 pjmedia/src/pjmedia/ffmpeg_util.h create mode 100644 pjmedia/src/pjmedia/format.c create mode 100644 pjmedia/src/pjmedia/stream_common.c create mode 100644 pjmedia/src/pjmedia/types.c create mode 100644 pjmedia/src/pjmedia/vid_codec.c create mode 100644 pjmedia/src/pjmedia/vid_codec_util.c create mode 100644 pjmedia/src/pjmedia/vid_port.c create mode 100644 pjmedia/src/pjmedia/vid_stream.c create mode 100644 pjmedia/src/pjmedia/vid_tee.c create mode 100644 pjmedia/src/test/vid_codec_test.c create mode 100644 pjmedia/src/test/vid_dev_test.c create mode 100644 pjmedia/src/test/vid_port_test.c delete mode 100644 pjnath/build/pjnath.dsp delete mode 100644 pjnath/build/pjnath.dsw delete mode 100644 pjnath/build/pjnath_test.dsp delete mode 100644 pjnath/build/pjstun_srv_test.dsp delete mode 100644 pjnath/build/pjturn_client.dsp delete mode 100644 pjnath/build/pjturn_srv.dsp delete mode 100644 pjproject.dsw delete mode 100644 pjsip-apps/build/pjsip_apps.dsw delete mode 100644 pjsip-apps/build/pjsua.dsp delete mode 100644 pjsip-apps/build/pjsystest.dsp delete mode 100644 pjsip-apps/build/py_pjsua.dsp delete mode 100644 pjsip-apps/build/python_pjsua.dsp delete mode 100644 pjsip-apps/build/sample_debug.dsp delete mode 100644 pjsip-apps/build/samples.dsp create mode 100644 pjsip-apps/src/samples/aviplay.c create mode 100644 pjsip-apps/src/samples/vid_streamutil.c delete mode 100644 pjsip/build/pjsip.dsw delete mode 100644 pjsip/build/pjsip_core.dsp delete mode 100644 pjsip/build/pjsip_simple.dsp delete mode 100644 pjsip/build/pjsip_test.dsp delete mode 100644 pjsip/build/pjsip_ua.dsp delete mode 100644 pjsip/build/pjsua_lib.dsp create mode 100644 pjsip/src/pjsua-lib/pjsua_dump.c create mode 100644 pjsip/src/pjsua-lib/pjsua_vid.c delete mode 100644 third_party/build/g7221/libg7221codec.dsp delete mode 100644 third_party/build/gsm/libgsmcodec.dsp delete mode 100644 third_party/build/ilbc/libilbccodec.dsp delete mode 100644 third_party/build/milenage/libmilenage.dsp delete mode 100644 third_party/build/portaudio/libportaudio.dsp delete mode 100644 third_party/build/resample/libresample.dsp delete mode 100644 third_party/build/resample/libresample_dll.dsp delete mode 100644 third_party/build/speex/libspeex.dsp delete mode 100644 third_party/build/srtp/libsrtp.dsp diff --git a/aconfigure b/aconfigure index 84e62c18..a1fb209a 100755 --- a/aconfigure +++ b/aconfigure @@ -600,6 +600,13 @@ libcrypto_present libssl_present openssl_h_present ac_no_ssl +ac_v4l2_ldflags +ac_v4l2_cflags +ac_ffmpeg_ldflags +ac_ffmpeg_cflags +ac_has_ffmpeg +ac_sdl_ldflags +ac_sdl_cflags ac_no_ilbc_codec ac_no_speex_codec ac_no_g7221_codec @@ -610,6 +617,10 @@ ac_no_g711_codec ac_no_speex_aec ac_no_large_filter ac_no_small_filter +ac_qt_cflags +ac_pjmedia_video_has_qt +ac_ios_cflags +ac_pjmedia_video ac_pa_use_oss ac_pa_use_alsa ac_pa_cflags @@ -716,6 +727,10 @@ enable_g7221_codec enable_speex_codec enable_ilbc_codec enable_libsamplerate +enable_sdl +enable_ffmpeg +with_ffmpeg +enable_v4l2 enable_ipp with_ipp with_ipp_samples @@ -1354,7 +1369,6 @@ Optional Features: --enable-epoll Use /dev/epoll ioqueue on Linux (experimental) --disable-sound Exclude sound (i.e. use null sound) --disable-oss Disable OSS audio (default: not disabled) - --enable-ext-sound PJMEDIA will not provide any sound device backend --disable-small-filter Exclude small filter in resampling --disable-large-filter Exclude large filter in resampling @@ -1368,6 +1382,9 @@ Optional Features: --disable-ilbc-codec Exclude iLBC codec in the build --enable-libsamplerate Link with libsamplerate when available. Note that PJMEDIA_RESAMPLE_IMP must also be configured + --disable-sdl Disable SDL (default: not disabled) + --disable-ffmpeg Disable ffmpeg (default: not disabled) + --disable-v4l2 Disable Video4Linux2 (default: not disabled) --enable-ipp Enable Intel IPP support. Specify the Intel IPP package and samples location using IPPROOT and IPPSAMPLES env var or with --with-ipp and @@ -1393,6 +1410,7 @@ Optional Packages: set, make sure that PortAudio is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths) + --with-ffmpeg=DIR Specify alternate FFMPEG prefix --with-ipp=DIR Specify the Intel IPP location --with-ipp-samples=DIR Specify the Intel IPP samples location --with-ipp-arch=ARCH Specify the Intel IPP ARCH suffix, e.g. "64" or @@ -5337,6 +5355,9 @@ case $target in *mingw* | *cygw* | *win32* | *w32* ) ac_os_objs="$ac_os_objs file_access_win32.o file_io_win32.o os_core_win32.o os_error_win32.o os_time_win32.o os_timestamp_win32.o guid_win32.o" ;; + *darwin*) + ac_os_objs="$ac_os_objs os_core_darwin.o" + ;; *) ac_os_objs="$ac_os_objs file_access_unistd.o file_io_ansi.o os_core_unix.o os_error_unix.o os_time_unix.o os_timestamp_posix.o" case $target in @@ -5612,6 +5633,57 @@ fi esac fi + +if test "$enable_video" = "no"; then + true; +else + case $target in + arm-apple-darwin*) + ac_pjmedia_video=iphone_os + + ac_ios_cflags="-DPJMEDIA_VIDEO_DEV_HAS_IOS=1" + LIBS="$LIBS -framework AVFoundation -framework UIKit -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking video device backend... AVFoundation" >&5 +$as_echo "Checking video device backend... AVFoundation" >&6; } + ;; + *darwin*) + ac_pjmedia_video=mac_os + + + SAVED_LIBS="$LIBS" + LIBS="-framework QTKit" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_pjmedia_video_has_qt=yes +else + ac_pjmedia_video_has_qt=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$SAVED_LIBS" + if test "$ac_pjmedia_video_has_qt" = "yes"; then + ac_qt_cflags="-DPJMEDIA_VIDEO_DEV_HAS_QT=1" + LIBS="$LIBS -framework QTKit -framework Foundation -framework AppKit -framework QuartzCore -framework OpenGL" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if QTKit framework is available... yes" >&5 +$as_echo "Checking if QTKit framework is available... yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if QTKit framework is available... no" >&5 +$as_echo "Checking if QTKit framework is available... no" >&6; } + fi + ;; + esac +fi + # Check whether --enable-ext_sound was given. if test "${enable_ext_sound+set}" = set; then : enableval=$enable_ext_sound; if test "$enable_ext_sound" = "yes"; then @@ -5833,6 +5905,390 @@ $as_echo "Skipping libsamplerate detection" >&6; } fi +# Check whether --enable-sdl was given. +if test "${enable_sdl+set}" = set; then : + enableval=$enable_sdl; + if test "$enable_sdl" = "no"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if SDL is disabled... yes" >&5 +$as_echo "Checking if SDL is disabled... yes" >&6; } + fi + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking SDL availability.." >&5 +$as_echo_n "checking SDL availability..... " >&6; } + if sdl-config --version; then + + + ac_sdl_cflags=`sdl-config --cflags` + ac_sdl_cflags="-DPJMEDIA_VIDEO_DEV_HAS_SDL=1 $ac_sdl_cflags" + ac_sdl_ldflags=`sdl-config --libs` + LIBS="$LIBS $ac_sdl_ldflags" + fi + +fi + + + +# Check whether --enable-ffmpeg was given. +if test "${enable_ffmpeg+set}" = set; then : + enableval=$enable_ffmpeg; + ac_has_ffmpeg=0 + + if test "$enable_ffmpeg" = "no"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if ffmpeg is disabled... yes" >&5 +$as_echo "Checking if ffmpeg is disabled... yes" >&6; } + fi + +else + + + + + FFMPEG_PREFIX="" + if test "x$with_ffmpeg" != "xno" -a "x$with_ffmpeg" != "x"; then + FFMPEG_PREFIX=$with_ffmpeg + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using ffmpeg prefix... $FFMPEG_PREFIX" >&5 +$as_echo "Using ffmpeg prefix... $FFMPEG_PREFIX" >&6; } + LIBS="-L$FFMPEG_PREFIX/lib $LIBS" + LDFLAGS="-L$FFMPEG_PREFIX/lib $LDFLAGS" + CFLAGS="-I$FFMPEG_PREFIX/include $CFLAGS" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for avdevice_version in -lavdevice" >&5 +$as_echo_n "checking for avdevice_version in -lavdevice... " >&6; } +if test "${ac_cv_lib_avdevice_avdevice_version+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lavdevice $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char avdevice_version (); +int +main () +{ +return avdevice_version (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_avdevice_avdevice_version=yes +else + ac_cv_lib_avdevice_avdevice_version=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avdevice_avdevice_version" >&5 +$as_echo "$ac_cv_lib_avdevice_avdevice_version" >&6; } +if test "x$ac_cv_lib_avdevice_avdevice_version" = x""yes; then : + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVDEVICE=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavdevice" + + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for av_register_all in -lavformat" >&5 +$as_echo_n "checking for av_register_all in -lavformat... " >&6; } +if test "${ac_cv_lib_avformat_av_register_all+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lavformat $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char av_register_all (); +int +main () +{ +return av_register_all (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_avformat_av_register_all=yes +else + ac_cv_lib_avformat_av_register_all=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avformat_av_register_all" >&5 +$as_echo "$ac_cv_lib_avformat_av_register_all" >&6; } +if test "x$ac_cv_lib_avformat_av_register_all" = x""yes; then : + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVFORMAT=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavformat" + + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for avcodec_init in -lavcodec" >&5 +$as_echo_n "checking for avcodec_init in -lavcodec... " >&6; } +if test "${ac_cv_lib_avcodec_avcodec_init+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lavcodec $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char avcodec_init (); +int +main () +{ +return avcodec_init (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_avcodec_avcodec_init=yes +else + ac_cv_lib_avcodec_avcodec_init=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avcodec_avcodec_init" >&5 +$as_echo "$ac_cv_lib_avcodec_avcodec_init" >&6; } +if test "x$ac_cv_lib_avcodec_avcodec_init" = x""yes; then : + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCODEC=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavcodec" + + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sws_scale in -lswscale" >&5 +$as_echo_n "checking for sws_scale in -lswscale... " >&6; } +if test "${ac_cv_lib_swscale_sws_scale+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lswscale $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char sws_scale (); +int +main () +{ +return sws_scale (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_swscale_sws_scale=yes +else + ac_cv_lib_swscale_sws_scale=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_swscale_sws_scale" >&5 +$as_echo "$ac_cv_lib_swscale_sws_scale" >&6; } +if test "x$ac_cv_lib_swscale_sws_scale" = x""yes; then : + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBSWSCALE=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lswscale" + + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for av_malloc in -lavutil" >&5 +$as_echo_n "checking for av_malloc in -lavutil... " >&6; } +if test "${ac_cv_lib_avutil_av_malloc+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lavutil $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char av_malloc (); +int +main () +{ +return av_malloc (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_avutil_av_malloc=yes +else + ac_cv_lib_avutil_av_malloc=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avutil_av_malloc" >&5 +$as_echo "$ac_cv_lib_avutil_av_malloc" >&6; } +if test "x$ac_cv_lib_avutil_av_malloc" = x""yes; then : + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVUTIL=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavutil" + + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for avcore_version in -lavcore" >&5 +$as_echo_n "checking for avcore_version in -lavcore... " >&6; } +if test "${ac_cv_lib_avcore_avcore_version+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lavcore $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char avcore_version (); +int +main () +{ +return avcore_version (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_avcore_avcore_version=yes +else + ac_cv_lib_avcore_avcore_version=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_avcore_avcore_version" >&5 +$as_echo "$ac_cv_lib_avcore_avcore_version" >&6; } +if test "x$ac_cv_lib_avcore_avcore_version" = x""yes; then : + ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCORE=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavcore" + + +fi + + LIBS="$LIBS $ac_ffmpeg_ldflags" + + +fi + + + +# Check whether --with-ffmpeg was given. +if test "${with_ffmpeg+set}" = set; then : + withval=$with_ffmpeg; +else + with_ffmpeg=no + +fi + + + +# Check whether --enable-v4l2 was given. +if test "${enable_v4l2+set}" = set; then : + enableval=$enable_v4l2; + if test "$enable_v4l2" = "no"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if V4L2 is disabled... yes" >&5 +$as_echo "Checking if V4L2 is disabled... yes" >&6; } + fi + +else + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for v4l2_open in -lv4l2" >&5 +$as_echo_n "checking for v4l2_open in -lv4l2... " >&6; } +if test "${ac_cv_lib_v4l2_v4l2_open+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lv4l2 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char v4l2_open (); +int +main () +{ +return v4l2_open (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_v4l2_v4l2_open=yes +else + ac_cv_lib_v4l2_v4l2_open=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_v4l2_v4l2_open" >&5 +$as_echo "$ac_cv_lib_v4l2_v4l2_open" >&6; } +if test "x$ac_cv_lib_v4l2_v4l2_open" = x""yes; then : + ac_v4l2_cflags="-DPJMEDIA_VIDEO_DEV_HAS_V4L2=1" + ac_v4l2_ldflags="-lv4l2" + LIBS="$LIBS -lv4l2" + + +fi + + +fi + # Check whether --enable-ipp was given. if test "${enable_ipp+set}" = set; then : diff --git a/aconfigure.ac b/aconfigure.ac index 7c7db55f..b084304f 100644 --- a/aconfigure.ac +++ b/aconfigure.ac @@ -408,6 +408,9 @@ case $target in *mingw* | *cygw* | *win32* | *w32* ) ac_os_objs="$ac_os_objs file_access_win32.o file_io_win32.o os_core_win32.o os_error_win32.o os_time_win32.o os_timestamp_win32.o guid_win32.o" ;; + *darwin*) + ac_os_objs="$ac_os_objs os_core_darwin.o" + ;; *) ac_os_objs="$ac_os_objs file_access_unistd.o file_io_ansi.o os_core_unix.o os_error_unix.o os_time_unix.o os_timestamp_posix.o" case $target in @@ -577,8 +580,7 @@ else AC_SUBST(ac_pa_use_oss,1) AC_ARG_ENABLE(oss, AC_HELP_STRING([--disable-oss], - [Disable OSS audio (default: not disabled)]) - , + [Disable OSS audio (default: not disabled)]), [ if test "$enable_oss" = "no"; then [ac_pa_use_oss=0] @@ -589,6 +591,37 @@ else esac fi +AC_SUBST(ac_pjmedia_video) +if test "$enable_video" = "no"; then + true; +else + case $target in + arm-apple-darwin*) + ac_pjmedia_video=iphone_os + AC_SUBST(ac_ios_cflags) + ac_ios_cflags="-DPJMEDIA_VIDEO_DEV_HAS_IOS=1" + LIBS="$LIBS -framework AVFoundation -framework UIKit -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" + AC_MSG_RESULT([Checking video device backend... AVFoundation]) + ;; + *darwin*) + ac_pjmedia_video=mac_os + AC_SUBST(ac_pjmedia_video_has_qt) + AC_SUBST(ac_qt_cflags) + SAVED_LIBS="$LIBS" + LIBS="-framework QTKit" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])],[ac_pjmedia_video_has_qt=yes],[ac_pjmedia_video_has_qt=no]) + LIBS="$SAVED_LIBS" + if test "$ac_pjmedia_video_has_qt" = "yes"; then + ac_qt_cflags="-DPJMEDIA_VIDEO_DEV_HAS_QT=1" + LIBS="$LIBS -framework QTKit -framework Foundation -framework AppKit -framework QuartzCore -framework OpenGL" + AC_MSG_RESULT([Checking if QTKit framework is available... yes]) + else + AC_MSG_RESULT([Checking if QTKit framework is available... no]) + fi + ;; + esac +fi + AC_ARG_ENABLE(ext_sound, AC_HELP_STRING([--enable-ext-sound], [PJMEDIA will not provide any sound device backend]), @@ -725,6 +758,119 @@ AC_ARG_ENABLE(libsamplerate, AC_MSG_RESULT([Skipping libsamplerate detection]) ) +dnl # SDL +AC_ARG_ENABLE(sdl, + AC_HELP_STRING([--disable-sdl], + [Disable SDL (default: not disabled)]), + [ + if test "$enable_sdl" = "no"; then + AC_MSG_RESULT([Checking if SDL is disabled... yes]) + fi + ], + [ + AC_MSG_CHECKING([SDL availability..]) + if sdl-config --version; then + AC_SUBST(ac_sdl_cflags) + AC_SUBST(ac_sdl_ldflags) + ac_sdl_cflags=`sdl-config --cflags` + ac_sdl_cflags="-DPJMEDIA_VIDEO_DEV_HAS_SDL=1 $ac_sdl_cflags" + ac_sdl_ldflags=`sdl-config --libs` + LIBS="$LIBS $ac_sdl_ldflags" + fi + ]) + + +dnl # FFMPEG stuffs +AC_ARG_ENABLE(ffmpeg, + AC_HELP_STRING([--disable-ffmpeg], + [Disable ffmpeg (default: not disabled)]), + [ + AC_SUBST(ac_has_ffmpeg,0) + if test "$enable_ffmpeg" = "no"; then + AC_MSG_RESULT([Checking if ffmpeg is disabled... yes]) + fi + ], + [ + AC_SUBST(ac_ffmpeg_cflags) + AC_SUBST(ac_ffmpeg_ldflags) + + FFMPEG_PREFIX="" + if test "x$with_ffmpeg" != "xno" -a "x$with_ffmpeg" != "x"; then + FFMPEG_PREFIX=$with_ffmpeg + AC_MSG_RESULT([Using ffmpeg prefix... $FFMPEG_PREFIX]) + LIBS="-L$FFMPEG_PREFIX/lib $LIBS" + LDFLAGS="-L$FFMPEG_PREFIX/lib $LDFLAGS" + CFLAGS="-I$FFMPEG_PREFIX/include $CFLAGS" + fi + + AC_CHECK_LIB(avdevice, + avdevice_version, + [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVDEVICE=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavdevice" + ] + ) + AC_CHECK_LIB(avformat, + av_register_all, + [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVFORMAT=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavformat" + ] + ) + AC_CHECK_LIB(avcodec, + avcodec_init, + [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCODEC=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavcodec" + ] + ) + AC_CHECK_LIB(swscale, + sws_scale, + [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBSWSCALE=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lswscale" + ] + ) + AC_CHECK_LIB(avutil, + av_malloc, + [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVUTIL=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavutil" + ] + ) + AC_CHECK_LIB(avcore, + avcore_version, + [ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCORE=1" + ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags -lavcore" + ] + ) + LIBS="$LIBS $ac_ffmpeg_ldflags" + ] + ) + +AC_ARG_WITH(ffmpeg, + AC_HELP_STRING([--with-ffmpeg=DIR], + [Specify alternate FFMPEG prefix]), + [], + [with_ffmpeg=no] + ) + + +dnl # Video for Linux 2 +AC_ARG_ENABLE(v4l2, + AC_HELP_STRING([--disable-v4l2], + [Disable Video4Linux2 (default: not disabled)]), + [ + if test "$enable_v4l2" = "no"; then + AC_MSG_RESULT([Checking if V4L2 is disabled... yes]) + fi + ], + [ + AC_SUBST(ac_v4l2_cflags) + AC_SUBST(ac_v4l2_ldflags) + AC_CHECK_LIB(v4l2, + v4l2_open, + [ac_v4l2_cflags="-DPJMEDIA_VIDEO_DEV_HAS_V4L2=1" + ac_v4l2_ldflags="-lv4l2" + LIBS="$LIBS -lv4l2" + ] + ) + ]) dnl ######################################################## dnl # Intel IPP support diff --git a/build.mak.in b/build.mak.in index 2f8197da..99465bfc 100644 --- a/build.mak.in +++ b/build.mak.in @@ -58,12 +58,43 @@ endif # Additional flags @ac_build_mak_vars@ +# +# Video +# Note: there are duplicated macros in pjmedia/os-auto.mak.in (and that's not +# good! + +# 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 +PJ_VIDEO_CFLAGS += $(SDL_CFLAGS) $(FFMPEG_CFLAGS) $(V4L2_CFLAGS) $(QT_CFLAGS) \ + $(IOS_CFLAGS) +PJ_VIDEO_LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) + + # CFLAGS, LDFLAGS, and LIBS to be used by applications export PJDIR := @ac_pjdir@ export APP_CC := @CC@ export APP_CXX := @CXX@ export APP_CFLAGS := -DPJ_AUTOCONF=1\ @CFLAGS@\ + $(PJ_VIDEO_CFLAGS) \ -I$(PJDIR)/pjlib/include\ -I$(PJDIR)/pjlib-util/include\ -I$(PJDIR)/pjnath/include\ @@ -76,12 +107,14 @@ export APP_LDFLAGS := -L$(PJDIR)/pjlib/lib\ -L$(PJDIR)/pjmedia/lib\ -L$(PJDIR)/pjsip/lib\ -L$(PJDIR)/third_party/lib\ + $(PJ_VIDEO_LDFLAGS) \ @LDFLAGS@ export APP_LDLIBS := -lpjsua-$(TARGET_NAME)\ -lpjsip-ua-$(TARGET_NAME)\ -lpjsip-simple-$(TARGET_NAME)\ -lpjsip-$(TARGET_NAME)\ -lpjmedia-codec-$(TARGET_NAME)\ + -lpjmedia-videodev-$(TARGET_NAME)\ -lpjmedia-$(TARGET_NAME)\ -lpjmedia-audiodev-$(TARGET_NAME)\ -lpjnath-$(TARGET_NAME)\ @@ -95,6 +128,7 @@ export APP_LIB_FILES = $(PJ_DIR)/pjsip/lib/libpjsua-$(LIB_SUFFIX) \ $(PJ_DIR)/pjsip/lib/libpjsip-simple-$(LIB_SUFFIX) \ $(PJ_DIR)/pjsip/lib/libpjsip-$(LIB_SUFFIX) \ $(PJ_DIR)/pjmedia/lib/libpjmedia-codec-$(LIB_SUFFIX) \ + $(PJ_DIR)/pjmedia/lib/libpjmedia-videodev-$(LIB_SUFFIX) \ $(PJ_DIR)/pjmedia/lib/libpjmedia-$(LIB_SUFFIX) \ $(PJ_DIR)/pjmedia/lib/libpjmedia-audiodev-$(LIB_SUFFIX) \ $(PJ_DIR)/pjnath/lib/libpjnath-$(LIB_SUFFIX) \ diff --git a/build/rules.mak b/build/rules.mak index 35cea900..660ed931 100644 --- a/build/rules.mak +++ b/build/rules.mak @@ -39,7 +39,7 @@ OBJDIRS := $(sort $(dir $(OBJS))) # # FULL_SRCS is ../src/app/file1.c ../src/app/file1.S # -FULL_SRCS = $(foreach file, $($(APP)_OBJS), $(SRCDIR)/$(basename $(file)).c $(SRCDIR)/$(basename $(file)).cpp $(SRCDIR)/$(basename $(file)).S) +FULL_SRCS = $(foreach file, $($(APP)_OBJS), $(SRCDIR)/$(basename $(file)).m $(SRCDIR)/$(basename $(file)).c $(SRCDIR)/$(basename $(file)).cpp $(SRCDIR)/$(basename $(file)).S) # # When generating dependency (gcc -MM), ideally we use only either diff --git a/pjlib-util/build/pjlib_util.dsp b/pjlib-util/build/pjlib_util.dsp deleted file mode 100644 index dfbb3318..00000000 --- a/pjlib-util/build/pjlib_util.dsp +++ /dev/null @@ -1,282 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjlib_util" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=pjlib_util - 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 "pjlib_util.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 "pjlib_util.mak" CFG="pjlib_util - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjlib_util - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "pjlib_util - 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)" == "pjlib_util - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "./output/pjlib-util-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir "./output/pjlib-util-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "./output/pjlib-util-i386-win32-vc6-release" -# PROP Intermediate_Dir "./output/pjlib-util-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 /Ob2 /I "../include" /I "../../pjlib/include" /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/pjlib-util-i386-win32-vc6-release.lib" - -!ELSEIF "$(CFG)" == "pjlib_util - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "./output/pjlib-util-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir "./output/pjlib-util-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "./output/pjlib-util-i386-win32-vc6-debug" -# PROP Intermediate_Dir "./output/pjlib-util-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" /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/pjlib-util-i386-win32-vc6-debug.lib" - -!ENDIF - -# Begin Target - -# Name "pjlib_util - Win32 Release" -# Name "pjlib_util - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE="..\src\pjlib-util\base64.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\crc32.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\dns.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\dns_dump.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\dns_server.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\errno.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\getopt.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\hmac_md5.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\hmac_sha1.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\http_client.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\md5.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\pcap.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\resolver.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\scanner.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\scanner_cis_bitwise.c" -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\scanner_cis_uint.c" -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\sha1.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\srv_resolver.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\string.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\stun_simple.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\stun_simple_client.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\symbols.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util\xml.c" -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="..\include\pjlib-util\base64.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\config.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\crc32.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\dns.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\dns_server.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\errno.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\getopt.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\hmac_md5.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\hmac_sha1.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\http_client.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\md5.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\pcap.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\resolver.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\scanner.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\scanner_cis_bitwise.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\scanner_cis_uint.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\sha1.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\srv_resolver.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\string.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\stun_simple.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\types.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib-util\xml.h" -# End Source File -# End Group -# End Target -# End Project diff --git a/pjlib-util/build/pjlib_util_test.dsp b/pjlib-util/build/pjlib_util_test.dsp deleted file mode 100644 index 0742ecdd..00000000 --- a/pjlib-util/build/pjlib_util_test.dsp +++ /dev/null @@ -1,130 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjlib_util_test" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=pjlib_util_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 "pjlib_util_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 "pjlib_util_test.mak" CFG="pjlib_util_test - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjlib_util_test - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "pjlib_util_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)" == "pjlib_util_test - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "./output/pjlib-util-test-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir "./output/pjlib-util-test-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "./output/pjlib-util-test-i386-win32-vc6-release" -# PROP Intermediate_Dir "./output/pjlib-util-test-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 /W4 /GX /O2 /I "../include" /I "../../pjlib/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /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 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/pjlib-util-test-i386-win32-vc6-release.exe" - -!ELSEIF "$(CFG)" == "pjlib_util_test - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "./output/pjlib-util-test-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir "./output/pjlib-util-test-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "./output/pjlib-util-test-i386-win32-vc6-debug" -# PROP Intermediate_Dir "./output/pjlib-util-test-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 /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /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 netapi32.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/pjlib-util-test-i386-win32-vc6-debug.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "pjlib_util_test - Win32 Release" -# Name "pjlib_util_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\pjlib-util-test\encryption.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util-test\http_client.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util-test\main.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util-test\resolver_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util-test\stun.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util-test\test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-util-test\xml.c" -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="..\src\pjlib-util-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 -# End Target -# End Project diff --git a/pjlib/build/pjlib++-test.dsp b/pjlib/build/pjlib++-test.dsp deleted file mode 100644 index 88ae04a1..00000000 --- a/pjlib/build/pjlib++-test.dsp +++ /dev/null @@ -1,102 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjlib++_test" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=pjlib++_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 "pjlib++-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 "pjlib++-test.mak" CFG="pjlib++_test - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjlib++_test - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "pjlib++_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)" == "pjlib++_test - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "./output/pjlib++-test-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir "./output/pjlib++-test-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "./output/pjlib++-test-i386-win32-vc6-release" -# PROP Intermediate_Dir "./output/pjlib++-test-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 "../include" /I "../../pjlib-util/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /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 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/pjlib++-test-i386-win32-vc6-release.exe" - -!ELSEIF "$(CFG)" == "pjlib++_test - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "./output/pjlib++-test-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir "./output/pjlib++-test-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "./output/pjlib++-test-i386-win32-vc6-debug" -# PROP Intermediate_Dir "./output/pjlib++-test-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 "../include" /I "../../pjlib-util/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /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 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/pjlib++-test-i386-win32-vc6-debug.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "pjlib++_test - Win32 Release" -# Name "pjlib++_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\pjlib++-test\main.cpp" -# 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/pjlib/build/pjlib++.dsp b/pjlib/build/pjlib++.dsp deleted file mode 100644 index 9b83235b..00000000 --- a/pjlib/build/pjlib++.dsp +++ /dev/null @@ -1,146 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjlib++" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=pjlib++ - 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 "pjlib++.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 "pjlib++.mak" CFG="pjlib++ - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjlib++ - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "pjlib++ - 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)" == "pjlib++ - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir ".\output\pjlib++-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir ".\output\pjlib++-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir ".\output\pjlib++-i386-win32-vc6-release" -# PROP Intermediate_Dir ".\output\pjlib++-i386-win32-vc6-release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MD /W3 /GX /O2 /I "../include" /I "../../pjlib-util/include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /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/pjlib++-i386-win32-vc6-release.lib" - -!ELSEIF "$(CFG)" == "pjlib++ - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir ".\output\pjlib++-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir ".\output\pjlib++-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir ".\output\pjlib++-i386-win32-vc6-debug" -# PROP Intermediate_Dir ".\output\pjlib++-i386-win32-vc6-debug" -# PROP Target_Dir "" -# 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 /W3 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib-util/include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /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/pjlib++-i386-win32-vc6-debug.lib" - -!ENDIF - -# Begin Target - -# Name "pjlib++ - Win32 Release" -# Name "pjlib++ - Win32 Debug" -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="..\include\pj++\file.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pj++\hash.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pj++\list.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pj++\lock.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pj++\os.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pjlib++.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pj++\pool.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pj++\proactor.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pj++\scanner.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pj++\sock.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pj++\string.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pj++\timer.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pj++\tree.hpp" -# End Source File -# Begin Source File - -SOURCE="..\include\pj++\types.hpp" -# End Source File -# End Group -# End Target -# End Project diff --git a/pjlib/build/pjlib.dsp b/pjlib/build/pjlib.dsp deleted file mode 100644 index d63165eb..00000000 --- a/pjlib/build/pjlib.dsp +++ /dev/null @@ -1,639 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjlib" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=pjlib - 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 "pjlib.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 "pjlib.mak" CFG="pjlib - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjlib - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "pjlib - 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)" == "pjlib - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir ".\output\pjlib-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir ".\output\pjlib-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir ".\output\pjlib-i386-win32-vc6-release" -# PROP Intermediate_Dir ".\output\pjlib-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 /Ob2 /I "../include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "PJ_WIN32" /D "PJ_M_I386" /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/pjlib-i386-win32-vc6-release.lib" - -!ELSEIF "$(CFG)" == "pjlib - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir ".\output\pjlib-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir ".\output\pjlib-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir ".\output\pjlib-i386-win32-vc6-debug" -# PROP Intermediate_Dir ".\output\pjlib-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" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "PJ_WIN32" /D "PJ_M_I386" /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/pjlib-i386-win32-vc6-debug.lib" - -!ENDIF - -# Begin Target - -# Name "pjlib - Win32 Release" -# Name "pjlib - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Group "Other Targets" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\src\pj\addr_resolv_linux_kernel.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\guid_simple.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\ioqueue_dummy.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\ioqueue_epoll.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\ip_helper_generic.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\log_writer_printk.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\os_core_linux_kernel.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\os_core_unix.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\os_error_linux_kernel.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\os_error_unix.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\os_time_linux_kernel.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\os_timestamp_linux.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\os_timestamp_linux_kernel.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\pool_policy_kmalloc.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\sock_linux_kernel.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\symbols.c -# PROP Exclude_From_Build 1 -# End Source File -# End Group -# Begin Source File - -SOURCE=..\src\pj\activesock.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\addr_resolv_sock.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\array.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\config.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\ctype.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\errno.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\except.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\fifobuf.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\file_access_win32.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\file_io_ansi.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\file_io_win32.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\guid.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\guid_win32.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\hash.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\ioqueue_common_abs.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\ioqueue_common_abs.h -# End Source File -# Begin Source File - -SOURCE=..\src\pj\ioqueue_select.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\ioqueue_winnt.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pj\ip_helper_win32.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\list.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\lock.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\log.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\log_writer_stdout.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\os_core_win32.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\os_error_win32.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\os_time_win32.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\os_timestamp_common.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\os_timestamp_win32.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\pool.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\pool_buf.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\pool_caching.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\pool_dbg.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\pool_policy_malloc.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\rand.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\rbtree.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\sock_bsd.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\sock_common.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\sock_qos_bsd.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\sock_qos_common.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\sock_select.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\ssl_sock_common.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\ssl_sock_dump.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\ssl_sock_ossl.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\string.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\timer.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\types.c -# End Source File -# Begin Source File - -SOURCE=..\src\pj\unicode_win32.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Group "compat" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\include\pj\compat\assert.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\cc_gcc.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\cc_msvc.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\ctype.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\errno.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\high_precision.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\m_alpha.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\m_i386.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\m_m68k.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\m_sparc.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\malloc.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\os_linux.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\os_linux_kernel.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\os_palmos.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\os_sunos.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\os_win32.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\rand.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\setjmp.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\size_t.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\socket.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\stdarg.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\stdfileio.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\string.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\time.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\compat\vsprintf.h -# End Source File -# End Group -# Begin Source File - -SOURCE=..\include\pj\activesock.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\addr_resolv.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\array.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\assert.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\config.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\config_site.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\config_site_sample.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\ctype.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\doxygen.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\errno.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\except.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\fifobuf.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\file_access.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\file_io.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\guid.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\hash.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\ioqueue.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\ip_helper.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\list.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\lock.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\log.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\math.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\os.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjlib.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\pool.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\pool_alt.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\pool_buf.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\rand.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\rbtree.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\sock.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\sock_qos.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\sock_select.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\ssl_sock.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\string.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\timer.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\types.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\unicode.h -# End Source File -# End Group -# Begin Group "Inline Files" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\include\pj\list_i.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\pool_i.h -# End Source File -# Begin Source File - -SOURCE=..\include\pj\string_i.h -# End Source File -# End Group -# End Target -# End Project diff --git a/pjlib/build/pjlib.dsw b/pjlib/build/pjlib.dsw deleted file mode 100644 index aa45adf6..00000000 --- a/pjlib/build/pjlib.dsw +++ /dev/null @@ -1,89 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "pjlib"=.\pjlib.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjlib++"=".\pjlib++.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjlib++_test"=".\pjlib++-test.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib++ - End Project Dependency -}}} - -############################################################################### - -Project: "pjlib_samples"=.\pjlib_samples.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency -}}} - -############################################################################### - -Project: "pjlib_test"=.\pjlib_test.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/pjlib/build/pjlib_samples.dsp b/pjlib/build/pjlib_samples.dsp deleted file mode 100644 index 40bc3179..00000000 --- a/pjlib/build/pjlib_samples.dsp +++ /dev/null @@ -1,113 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjlib_samples" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) External Target" 0x0106 - -CFG=pjlib_samples - 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 "pjlib_samples.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 "pjlib_samples.mak" CFG="pjlib_samples - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjlib_samples - Win32 Release" (based on "Win32 (x86) External Target") -!MESSAGE "pjlib_samples - Win32 Debug" (based on "Win32 (x86) External Target") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" - -!IF "$(CFG)" == "pjlib_samples - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "./output/pjlib-samples-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir "./output/pjlib-samples-i386-win32-vc6-release" -# PROP BASE Cmd_Line "NMAKE /f pjlib_samples.mak" -# PROP BASE Rebuild_Opt "/a" -# PROP BASE Target_File "pjlib_samples.exe" -# PROP BASE Bsc_Name "pjlib_samples.bsc" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "./output/pjlib-samples-i386-win32-vc6-release" -# PROP Intermediate_Dir "./output/pjlib-samples-i386-win32-vc6-release" -# PROP Cmd_Line "nmake /f "pjlib_samples.mak" MODE=release" -# PROP Rebuild_Opt "/a" -# PROP Target_File "pjlib samples" -# PROP Bsc_Name "" -# PROP Target_Dir "" - -!ELSEIF "$(CFG)" == "pjlib_samples - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "./output/pjlib-samples-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir "./output/pjlib-samples-i386-win32-vc6-debug" -# PROP BASE Cmd_Line "NMAKE /f pjlib_samples.mak" -# PROP BASE Rebuild_Opt "/a" -# PROP BASE Target_File "pjlib_samples.exe" -# PROP BASE Bsc_Name "pjlib_samples.bsc" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "./output/pjlib-samples-i386-win32-vc6-debug" -# PROP Intermediate_Dir "./output/pjlib-samples-i386-win32-vc6-debug" -# PROP Cmd_Line "nmake /nologo /f "pjlib_samples.mak" MODE=debug" -# PROP Rebuild_Opt "/a" -# PROP Target_File "pjlib samples" -# PROP Bsc_Name "" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "pjlib_samples - Win32 Release" -# Name "pjlib_samples - Win32 Debug" - -!IF "$(CFG)" == "pjlib_samples - Win32 Release" - -!ELSEIF "$(CFG)" == "pjlib_samples - Win32 Debug" - -!ENDIF - -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE="..\src\pjlib-samples\except.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-samples\list.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-samples\log.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 -# Begin Source File - -SOURCE=.\pjlib_samples.mak -# End Source File -# End Target -# End Project diff --git a/pjlib/build/pjlib_test.dsp b/pjlib/build/pjlib_test.dsp deleted file mode 100644 index 067dea1d..00000000 --- a/pjlib/build/pjlib_test.dsp +++ /dev/null @@ -1,244 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjlib_test" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=pjlib_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 "pjlib_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 "pjlib_test.mak" CFG="pjlib_test - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjlib_test - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "pjlib_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)" == "pjlib_test - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir ".\output\pjlib-test-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir ".\output\pjlib-test-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir ".\output\pjlib-test-i386-win32-vc6-release" -# PROP Intermediate_Dir ".\output\pjlib-test-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 /W4 /GX /Zi /O2 /I "../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "PJ_WIN32" /D "PJ_M_I386" /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 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 /nologo /subsystem:console /profile /debug /machine:I386 /out:"../bin/pjlib-test-i386-win32-vc6-release.exe" - -!ELSEIF "$(CFG)" == "pjlib_test - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir ".\output\pjlib-test-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir ".\output\pjlib-test-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir ".\output\pjlib-test-i386-win32-vc6-debug" -# PROP Intermediate_Dir ".\output\pjlib-test-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 /W4 /Gm /GX /ZI /Od /I "../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "PJ_WIN32" /D "PJ_M_I386" /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 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 libeay32MT.lib ssleay32MT.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjlib-test-i386-win32-vc6-debug.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "pjlib_test - Win32 Release" -# Name "pjlib_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\pjlib-test\activesock.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\atomic.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\echo_clt.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\errno.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\exception.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\fifobuf.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\file.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\hash_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\ioq_perf.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\ioq_tcp.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\ioq_udp.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\ioq_unreg.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\list.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\main.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\main_mod.c" -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\main_win32.c" -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\mutex.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\os.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\pool.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\pool_perf.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\rand.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\rbtree.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\select.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\sleep.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\sock.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\sock_perf.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\ssl_sock.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\string.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\thread.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\timer.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\timestamp.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\udp_echo_srv_ioqueue.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\udp_echo_srv_sync.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjlib-test\util.c" -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="..\src\pjlib-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 -# End Target -# End Project diff --git a/pjlib/include/pj/compat/cc_gcc.h b/pjlib/include/pj/compat/cc_gcc.h index d176a319..77d93f32 100644 --- a/pjlib/include/pj/compat/cc_gcc.h +++ b/pjlib/include/pj/compat/cc_gcc.h @@ -71,5 +71,8 @@ #define PJ_UNREACHED(x) +#define PJ_ALIGN_DATA(declaration, alignment) declaration __attribute__((aligned (alignment))) + + #endif /* __PJ_COMPAT_CC_GCC_H__ */ diff --git a/pjlib/include/pj/compat/cc_msvc.h b/pjlib/include/pj/compat/cc_msvc.h index 10d0a32b..0b1fd68a 100644 --- a/pjlib/include/pj/compat/cc_msvc.h +++ b/pjlib/include/pj/compat/cc_msvc.h @@ -80,5 +80,8 @@ typedef unsigned __int64 pj_uint64_t; #define PJ_UNREACHED(x) +#define PJ_ALIGN_DATA(declaration, alignment) __declspec(align(alignment)) declaration + + #endif /* __PJ_COMPAT_CC_MSVC_H__ */ diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h index 25758452..ec7c7449 100644 --- a/pjlib/include/pj/config.h +++ b/pjlib/include/pj/config.h @@ -20,7 +20,6 @@ #ifndef __PJ_CONFIG_H__ #define __PJ_CONFIG_H__ -#error "For PJSIP version 1.x, please switch your svn repository URL to https://svn.pjsip.org/repos/pjproject/branches/1.x" /** * @file config.h @@ -46,6 +45,10 @@ # error "Unknown compiler." #endif +/* PJ_ALIGN_DATA is compiler specific directive to align data address */ +#ifndef PJ_ALIGN_DATA +# error "PJ_ALIGN_DATA is not defined!" +#endif /******************************************************************** * Include target OS specific configuration. @@ -1122,10 +1125,10 @@ PJ_BEGIN_DECL /** PJLIB version major number. */ -#define PJ_VERSION_NUM_MAJOR 1 +#define PJ_VERSION_NUM_MAJOR 2 /** PJLIB version minor number. */ -#define PJ_VERSION_NUM_MINOR 10 +#define PJ_VERSION_NUM_MINOR 0 /** PJLIB version revision number. */ #define PJ_VERSION_NUM_REV 0 @@ -1134,7 +1137,7 @@ PJ_BEGIN_DECL * Extra suffix for the version (e.g. "-trunk"), or empty for * web release version. */ -#define PJ_VERSION_NUM_EXTRA "-svn" +#define PJ_VERSION_NUM_EXTRA "-pre-alpha-svn" /** * PJLIB version number consists of three bytes with the following format: diff --git a/pjlib/include/pj/errno.h b/pjlib/include/pj/errno.h index 49fd7bbd..66fea949 100644 --- a/pjlib/include/pj/errno.h +++ b/pjlib/include/pj/errno.h @@ -468,6 +468,8 @@ PJ_DECL(pj_status_t) pj_register_strerror(pj_status_t start_code, * - PJLIB_UTIL_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*3) * - PJNATH_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*4) * - PJMEDIA_AUDIODEV_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*5) + * - PJ_SSL_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*6) + * - PJMEDIA_VIDEODEV_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*7) */ /* Internal */ diff --git a/pjlib/include/pj/os.h b/pjlib/include/pj/os.h index 45f9ce8f..3ce877af 100644 --- a/pjlib/include/pj/os.h +++ b/pjlib/include/pj/os.h @@ -1432,6 +1432,36 @@ PJ_DECL(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start, /** @} */ +/* **************************************************************************/ +/** + * @defgroup PJ_APP_OS Application execution + * @ingroup PJ_OS + * @{ + */ + +/* Type for main function. */ +typedef int (*pj_main_func_ptr)(int argc, char *argv[]); + +/** + * Run the application. This function has to be called in the main thread + * and after doing the necessary initialization according to the flags + * provided, it will call main_func() function. + * + * @param main_func Application's main function. + * @param argc Number of arguments from the main() function, which + * will be passed to main_func() function. + * @param argv The arguments from the main() function, which will + * be passed to main_func() function. + * @param flags Flags for application execution, currently must be 0. + * + * @return main_func()'s return value. + */ +int pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], + unsigned flags); + +/** @} */ + + /* **************************************************************************/ /** * Internal PJLIB function to initialize the threading subsystem. diff --git a/pjlib/src/pj/errno.c b/pjlib/src/pj/errno.c index ed09f266..615d1450 100644 --- a/pjlib/src/pj/errno.c +++ b/pjlib/src/pj/errno.c @@ -33,7 +33,9 @@ PJ_BEGIN_DECL char *buf, pj_size_t bufsize ); PJ_END_DECL -#define PJLIB_MAX_ERR_MSG_HANDLER 8 +#ifndef PJLIB_MAX_ERR_MSG_HANDLER +# define PJLIB_MAX_ERR_MSG_HANDLER 10 +#endif /* Error message handler. */ static unsigned err_msg_hnd_cnt; diff --git a/pjlib/src/pj/os_core_darwin.m b/pjlib/src/pj/os_core_darwin.m new file mode 100644 index 00000000..686d64be --- /dev/null +++ b/pjlib/src/pj/os_core_darwin.m @@ -0,0 +1,86 @@ +/* $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 +#include +#include +#include +#include + +#define THIS_FILE "os_core_darwin.m" + +typedef struct run_app_t { + pj_main_func_ptr main_func; + int argc; + char **argv; + int retval; +} run_app_t; + +@interface DeadThread: NSObject { ;; } ++ (void)enterMultiThreadedMode; ++ (void)emptyThreadMethod:(id)obj; +@end + +@implementation DeadThread ++ (void)enterMultiThreadedMode +{ + [NSThread detachNewThreadSelector:@selector(emptyThreadMethod:) + toTarget:[DeadThread class] withObject:nil]; +} + ++ (void)emptyThreadMethod:(id)obj { ; } +@end + +static void* main_thread(void *data) +{ + run_app_t *param = (run_app_t *)data; + + param->retval = (*param->main_func)(param->argc, param->argv); + CFRunLoopStop(CFRunLoopGetMain()); + + return NULL; +} + +/* + * pj_run_app() + * This function has to be called from the main thread. The purpose of + * this function is to initialize the application's memory pool, event + * loop management, and multi-threading environment. + */ +PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], + unsigned flags) +{ + pthread_t thread; + run_app_t param; + NSAutoreleasePool *pool; + + pool = [[NSAutoreleasePool alloc] init]; + [NSApplication sharedApplication]; + [DeadThread enterMultiThreadedMode]; + + param.argc = argc; + param.argv = (char **)argv; + param.main_func = main_func; + if (pthread_create(&thread, NULL, &main_thread, ¶m) == 0) { + CFRunLoopRun(); + } + + PJ_UNUSED_ARG(pool); + + return param.retval; +} diff --git a/pjlib/src/pj/os_core_symbian.cpp b/pjlib/src/pj/os_core_symbian.cpp index abf01b85..5446df5d 100644 --- a/pjlib/src/pj/os_core_symbian.cpp +++ b/pjlib/src/pj/os_core_symbian.cpp @@ -1036,3 +1036,12 @@ PJ_DEF(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread, } #endif /* PJ_OS_HAS_CHECK_STACK */ + +/* + * pj_run_app() + */ +PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], + unsigned flags) +{ + return (*main_func)(argc, argv); +} diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c index 3fcfa565..fb0ba09e 100644 --- a/pjlib/src/pj/os_core_unix.c +++ b/pjlib/src/pj/os_core_unix.c @@ -1819,3 +1819,11 @@ PJ_DEF(pj_color_t) pj_term_get_color(void) #endif /* PJ_TERM_HAS_COLOR */ +/* + * pj_run_app() + */ +PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], + unsigned flags) +{ + return (*main_func)(argc, argv); +} diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c index 2b7c2c91..2b703e74 100644 --- a/pjlib/src/pj/os_core_win32.c +++ b/pjlib/src/pj/os_core_win32.c @@ -1421,3 +1421,12 @@ PJ_DEF(pj_color_t) pj_term_get_color(void) } #endif /* PJ_TERM_HAS_COLOR */ + +/* + * pj_run_app() + */ +PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], + unsigned flags) +{ + return (*main_func)(argc, argv); +} 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 @@ -77,6 +84,15 @@ export PJMEDIA_AUDIODEV_OBJS += audiodev.o audiotest.o errno.o \ 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. @@ -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" /> + + @@ -3110,6 +3115,14 @@ /> + + + + @@ -3462,6 +3475,18 @@ /> + + + + + + @@ -4290,64 +4315,6 @@ /> - - - - - - - - - - - - - - - - - - - - @@ -4588,6 +4555,10 @@ /> + + @@ -4720,6 +4691,30 @@ /> + + + + + + + + + + + + @@ -4965,6 +4960,14 @@ RelativePath="..\include\pjmedia\alaw_ulaw.h" > + + + + @@ -4989,6 +4992,10 @@ RelativePath="..\include\pjmedia\config.h" > + + @@ -5013,6 +5020,18 @@ RelativePath="..\include\pjmedia\errno.h" > + + + + + + @@ -5070,7 +5089,7 @@ > + + @@ -5129,6 +5152,26 @@ RelativePath="..\include\pjmedia\types.h" > + + + + + + + + + + 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 @@ -2764,6 +2764,10 @@ Name="Source Files" Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" > + + @@ -2830,6 +2834,14 @@ /> + + + + @@ -3045,6 +3057,10 @@ RelativePath="..\include\pjmedia-codec\config.h" > + + @@ -3057,6 +3073,14 @@ RelativePath="..\include\pjmedia-codec\gsm.h" > + + + + 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" /> + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 #include +#include +#include #include #include 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 #include 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 #include +#include #include #include #include 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 +#include + + +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 - /* * Include config_auto.h if autoconf is used (PJ_AUTOCONF is set) */ @@ -42,6 +41,7 @@ # include #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 + * + * 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 +#include + +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 +#include + +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_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 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 @@ -97,6 +97,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 +#include + + +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 +#include + +/** + * @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 +#include +#include +#include +#include +#include + + +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 + +/** + * @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 #include +#include #include #include #include #include #include +#include #include #include #include -#include #include +#include +#include +#include +#include #include #include #include @@ -50,13 +53,14 @@ #include #include #include -#include +//#include #include #include #include #include #include #include +#include #include #include #include @@ -64,6 +68,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include 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 + + + +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 #include #include +#include /** * @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 +#include #include #include @@ -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 @@ -514,6 +514,15 @@ # define PJMEDIA_STREAM_CHECK_RTP_PT 1 #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 @@ -567,6 +576,16 @@ #endif +/** + * 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 @@ -591,6 +610,15 @@ #endif +/** + * 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 +#include +#include +#include + + +/** + * @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 #include +#include 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 +#include +#include + +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 + * + * 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 + +/** + * @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 + * + * 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 +#include + +/** + * @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; isubframe_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>= 1; + for (i=0; i>= 1; + for (i=0; i>= 1; + for (i=0; i +#include +#include +#include +#include +#include #include #include @@ -183,6 +187,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. */ @@ -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. @@ -249,12 +373,19 @@ typedef struct pjmedia_port long ldata; /**< Long data. */ } 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 - +#include /** * @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 + +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 +#include #include PJ_BEGIN_DECL @@ -268,6 +269,19 @@ PJ_DECL(pj_status_t) pjmedia_snd_port_get_ec_tail(pjmedia_snd_port *snd_port, unsigned *p_length); +/** + * 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 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 #include #include +#include #include 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); /** @@ -230,6 +249,17 @@ PJ_DECL(pjmedia_transport*) pjmedia_stream_get_transport(pjmedia_stream *st); 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 +#include + + +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 #include +#include /** * @defgroup PJMEDIA_TRANSPORT Media Transport @@ -256,6 +257,35 @@ typedef enum pjmedia_tranport_media_option } 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. */ @@ -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 -#include /* pjmedia_sock_info */ -#include /* pj_memcpy(), pj_memset() */ +#include +#include + /** * @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>= 1; - for (i=0; i>= 1; - for (i=0; i>= 1; - for (i=0; isubframe_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; isubframe_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 + * + * 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 +#include +#include +#include +#include +#include + +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 + * + * 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 +#include + +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 +#include + +/** + * @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 +#include +#include +#include +#include +#include +#include + +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 + +/** + * @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 +#include + +#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 +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * 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 +#include + + +/* 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; ienabled && + (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; itype != 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; iop = &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 +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#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(¶m->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(¶m->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, + ¶m->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; ivfi->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 +#include +#include +#include +#include + +#if PJMEDIA_VIDEO_DEV_HAS_DSHOW + +#ifdef _MSC_VER +# pragma warning(push, 3) +#endif + +#include +#define COBJMACROS +#include + +#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(¶m->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(¶m->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, + ¶m->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 + +#if PJMEDIA_VIDEO_DEV_HAS_DSHOW + +#include +#include + +#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 +#include +#include + +/* 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 +#include +#include +#include +#include + +#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG + +#define THIS_FILE "ffmpeg.c" + +#include "../pjmedia/ffmpeg_util.h" +#include +#include + +#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(¶m->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(¶m->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 +#include +#include +#include + +#if PJMEDIA_VIDEO_DEV_HAS_IOS +#include "Availability.h" +#ifdef __IPHONE_4_0 + +#import +#import + +#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 + +{ +@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(¶m->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, + ¶m->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 +#include +#include +#include + +#if PJMEDIA_VIDEO_DEV_HAS_QT + +#include +#include + +#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(¶m->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, + ¶m->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 +#include +#include +#include +#include + +#if PJMEDIA_VIDEO_DEV_HAS_SDL + +#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 +# include +#endif + +#include +#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(¶m->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, + ¶m->window_pos); + } + if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) { + sdl_stream_set_cap(&strm->base, + PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, + ¶m->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 +#include +#include +#include +#include +#include +#include +#include + +#if PJMEDIA_VIDEO_DEV_HAS_V4L2 + +#include +#include +#include +#include +#include + +#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++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; idev_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_cntdev_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; jinfo.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(¶m->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(¶m->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(¶m->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; ifd, 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; ibuf_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 +#include +#include +#include +#include +#include + +#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; iname; \ + *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; iop->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; if) { + 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; icreate == 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; if && 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; icap_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_idxop->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, ¶m->cap_id); + make_global_index(f->sys.drv_idx, ¶m->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, ¶m->cap_id); + make_global_index(strm->sys.drv_idx, ¶m->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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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= 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 #include #include +#include +#include + +/* 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. @@ -62,26 +141,43 @@ PJ_DEF(pj_status_t) pjmedia_clock_create( pj_pool_t *pool, pjmedia_clock_callback *cb, 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, ¶m, 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; icodec_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 #include #include -#include #include #include #include +#include #include #include @@ -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 && slotmax_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; imax_ports && ciport_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 #include #include -#include #include #include #include @@ -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 +#include +#include + +#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 +#include + +#if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL + +#include "ffmpeg_util.h" +#include + +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 #include #include +#include #include #include #include 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 + * + * 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 +#include +#include + 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 #include +#include #include #include #include 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 #include +#include #include #include #include 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 #include #include +#include #include #include #include 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 #include #include +#include #include #include #include @@ -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; icodec_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 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; imedia[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 +#include +#include +#include +#include + +#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 +#include + +#if PJMEDIA_HAS_LIBAVFORMAT && PJMEDIA_HAS_LIBAVUTIL + +#include "ffmpeg_util.h" +#include + +/* 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; iid==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; ipf == 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; iid==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; icodec_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 + +#ifdef _MSC_VER +# ifndef __cplusplus +# define inline _inline +# endif +# pragma warning(disable:4244) /* possible loss of data */ +#endif + +#include +#include + +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 + * + * 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 +#include +#include +#include +#include + +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; istrides[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; istrides[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; istrides[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; iinfos[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; iinfo_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, @@ -756,6 +816,18 @@ PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb, pj_uint32_t bit_info, 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; @@ -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); } /* @@ -890,6 +963,21 @@ PJ_DEF(void) pjmedia_jbuf_get_frame2(pjmedia_jbuf *jb, pj_size_t *size, 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) { @@ -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 -#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 #include #include +#include #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 #include #include +#include #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 @@ -638,30 +654,43 @@ 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 -#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 -#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 #include #include +#include #include #include #include @@ -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; idesc.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; iattr_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; iattr_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 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 +#include + +#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 @@ -205,6 +205,22 @@ PJ_DEF(pj_status_t) pjmedia_ice_create2(pjmedia_endpt *endpt, const pjmedia_ice_cb *cb, 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; @@ -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 + * + * 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 +#include + +/** + * 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 + * + * 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 +#include +#include +#include +#include +#include + +#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; icodec_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; icodec_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; icodec_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; icodec_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; icodec_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; icodec_cnt; ++i) { + unsigned j, max; + + for (max=i, j=i+1; jcodec_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; icodec_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; icodec_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 + * + * 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 +#include +#include +#include +#include +#include + +#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 && i32) + 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; icnt; ++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; jparam[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(¶m->dec_fmtp, + &fmtp_loc); + if (status != PJ_SUCCESS) + return status; + + /* Get remote param */ + status = pjmedia_vid_codec_parse_h263_fmtp(¶m->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_sqenc_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(¶m->dec_fmtp, + &fmtp); + if (status != PJ_SUCCESS) + return status; + + vfd = pjmedia_format_get_video_format_detail(¶m->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; icnt; ++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(¶m->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(¶m->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(¶m->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(¶m->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 +#include +#include +#include +#include +#include +#include +#include + +#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, ¶m, + 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; yuser_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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* 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 +# 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; idec; + 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; idesc.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 +#include +#include +#include +#include +#include + +#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 + #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; ipcm; 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, ®1, ®1_len, ®2, ®2_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 #include +#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 +#include +#include +#include + +#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; (idec_fmt_id_cnt) && (p-str+5dec_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 + * + * 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 +#include +#include +#include + +#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; jname : "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(¶m); + + /* Create capture, set it to active (master) */ + status = pjmedia_vid_dev_default_param(pool, cap_dev_id, + ¶m.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(¶m.vidparam.fmt, PJ_TRUE); + if (vfd == NULL) { + rc = 105; goto on_return; + } + + status = pjmedia_vid_port_create(pool, ¶m, &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, + ¶m.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, ¶m, &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 + * + * 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 +#include +#include +#include + +#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(¶m); + + /* Create capture, set it to active (master) */ + status = pjmedia_vid_dev_default_param(pool, cap_dev_id, + ¶m.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(¶m.vidparam.fmt, PJ_TRUE); + if (vfd == NULL) { + rc = 105; goto on_return; + } + + status = pjmedia_vid_port_create(pool, ¶m, &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, + ¶m.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, ¶m, &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; +} diff --git a/pjnath/build/pjnath.dsp b/pjnath/build/pjnath.dsp deleted file mode 100644 index c7ec3f2d..00000000 --- a/pjnath/build/pjnath.dsp +++ /dev/null @@ -1,232 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjnath" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=pjnath - 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 "pjnath.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 "pjnath.mak" CFG="pjnath - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjnath - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "pjnath - 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)" == "pjnath - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "./output/pjnath-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir "./output/pjnath-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "./output/pjnath-i386-win32-vc6-release" -# PROP Intermediate_Dir "./output/pjnath-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 /O1 /Ob2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /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/pjnath-i386-win32-vc6-release.lib" - -!ELSEIF "$(CFG)" == "pjnath - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "./output/pjnath-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir "./output/pjnath-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "./output/pjnath-i386-win32-vc6-debug" -# PROP Intermediate_Dir "./output/pjnath-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" /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/pjnath-i386-win32-vc6-debug.lib" - -!ENDIF - -# Begin Target - -# Name "pjnath - Win32 Release" -# Name "pjnath - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\src\pjnath\errno.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjnath\ice_session.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjnath\ice_strans.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjnath\nat_detect.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjnath\stun_auth.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjnath\stun_msg.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjnath\stun_msg_dump.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjnath\stun_session.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjnath\stun_sock.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjnath\stun_transaction.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjnath\turn_session.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjnath\turn_sock.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\include\pjnath\config.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\errno.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\ice_session.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\ice_strans.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\nat_detect.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\stun_auth.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\stun_config.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\stun_msg.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\stun_session.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\stun_sock.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\stun_transaction.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\turn_session.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\turn_sock.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjnath\types.h -# End Source File -# End Group -# Begin Group "Doxygen Files" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\docs\doc_ice.h -# End Source File -# Begin Source File - -SOURCE=..\docs\doc_mainpage.h -# End Source File -# Begin Source File - -SOURCE=..\docs\doc_nat.h -# End Source File -# Begin Source File - -SOURCE=..\docs\doc_samples.h -# End Source File -# Begin Source File - -SOURCE=..\docs\doc_stun.h -# End Source File -# Begin Source File - -SOURCE=..\docs\doc_turn.h -# End Source File -# End Group -# End Target -# End Project diff --git a/pjnath/build/pjnath.dsw b/pjnath/build/pjnath.dsw deleted file mode 100644 index 0f622b61..00000000 --- a/pjnath/build/pjnath.dsw +++ /dev/null @@ -1,116 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -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: "pjnath"=.\pjnath.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjnath_test"=.\pjnath_test.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency -}}} - -############################################################################### - -Project: "pjturn_client"=.\pjturn_client.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency -}}} - -############################################################################### - -Project: "pjturn_srv"=.\pjturn_srv.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/pjnath/build/pjnath_test.dsp b/pjnath/build/pjnath_test.dsp deleted file mode 100644 index 32789682..00000000 --- a/pjnath/build/pjnath_test.dsp +++ /dev/null @@ -1,145 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjnath_test" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=pjnath_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 "pjnath_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 "pjnath_test.mak" CFG="pjnath_test - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjnath_test - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "pjnath_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)" == "pjnath_test - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "./output/pjnath-test-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir "./output/pjnath-test-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "./output/pjnath-test-i386-win32-vc6-release" -# PROP Intermediate_Dir "./output/pjnath-test-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 /W4 /GX /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /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 mswsock.lib iphlpapi.lib netapi32.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 /nologo /subsystem:console /machine:I386 /out:"../bin/pjnath-test-i386-win32-vc6-release.exe" - -!ELSEIF "$(CFG)" == "pjnath_test - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "./output/pjnath-test-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir "./output/pjnath-test-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "./output/pjnath-test-i386-win32-vc6-debug" -# PROP Intermediate_Dir "./output/pjnath-test-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 /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /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 iphlpapi.lib netapi32.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 /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjnath-test-i386-win32-vc6-debug.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "pjnath_test - Win32 Release" -# Name "pjnath_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\pjnath-test\ice_test.c" - -!IF "$(CFG)" == "pjnath_test - Win32 Release" - -!ELSEIF "$(CFG)" == "pjnath_test - Win32 Debug" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE="..\src\pjnath-test\main.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjnath-test\server.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjnath-test\sess_auth.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjnath-test\stun.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjnath-test\stun_sock_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjnath-test\test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjnath-test\turn_sock_test.c" -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="..\src\pjnath-test\server.h" -# End Source File -# Begin Source File - -SOURCE="..\src\pjnath-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 -# End Target -# End Project diff --git a/pjnath/build/pjstun_srv_test.dsp b/pjnath/build/pjstun_srv_test.dsp deleted file mode 100644 index 428467e1..00000000 --- a/pjnath/build/pjstun_srv_test.dsp +++ /dev/null @@ -1,122 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjstun_srv_test" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=pjstun_srv_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 "pjstun_srv_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 "pjstun_srv_test.mak" CFG="pjstun_srv_test - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjstun_srv_test - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "pjstun_srv_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)" == "pjstun_srv_test - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "./output/pjstun-srv-test-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir "./output/pjstun-srv-test-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "./output/pjstun-srv-test-i386-win32-vc6-release" -# PROP Intermediate_Dir "./output/pjstun-srv-test-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 /W4 /GX /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /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 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/pjstun-srv-test-i386-win32-vc6-release.exe" - -!ELSEIF "$(CFG)" == "pjstun_srv_test - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "./output/pjstun-srv-test-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir "./output/pjstun-srv-test-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "./output/pjstun-srv-test-i386-win32-vc6-debug" -# PROP Intermediate_Dir "./output/pjstun-srv-test-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 /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /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 netapi32.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/pjstun-srv-test-i386-win32-vc6-debug.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "pjstun_srv_test - Win32 Release" -# Name "pjstun_srv_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\pjstun-srv-test\bind_usage.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjstun-srv-test\main.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjstun-srv-test\server.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjstun-srv-test\turn_usage.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjstun-srv-test\usage.c" -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="..\src\pjstun-srv-test\server.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 -# End Target -# End Project diff --git a/pjnath/build/pjturn_client.dsp b/pjnath/build/pjturn_client.dsp deleted file mode 100644 index ff591c94..00000000 --- a/pjnath/build/pjturn_client.dsp +++ /dev/null @@ -1,102 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjturn_client" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=pjturn_client - 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 "pjturn_client.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 "pjturn_client.mak" CFG="pjturn_client - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjturn_client - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "pjturn_client - 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)" == "pjturn_client - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "./output/pjturn-client-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir "./output/pjturn-client-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "./output/pjturn-client-i386-win32-vc6-release" -# PROP Intermediate_Dir "./output/pjturn-client-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 "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /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 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/pjturn-client-i386-win32-vc6-release.exe" - -!ELSEIF "$(CFG)" == "pjturn_client - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "./output/pjturn-client-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir "./output/pjturn-client-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "./output/pjturn-client-i386-win32-vc6-debug" -# PROP Intermediate_Dir "./output/pjturn-client-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 "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /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 netapi32.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/pjturn-client-i386-win32-vc6-debug.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "pjturn_client - Win32 Release" -# Name "pjturn_client - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE="..\src\pjturn-client\client_main.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/pjnath/build/pjturn_srv.dsp b/pjnath/build/pjturn_srv.dsp deleted file mode 100644 index 5e79a8f6..00000000 --- a/pjnath/build/pjturn_srv.dsp +++ /dev/null @@ -1,130 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjturn_srv" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=pjturn_srv - 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 "pjturn_srv.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 "pjturn_srv.mak" CFG="pjturn_srv - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjturn_srv - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "pjturn_srv - 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)" == "pjturn_srv - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "output/pjturn-srv-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir "output/pjturn-srv-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "output/pjturn-srv-i386-win32-vc6-release" -# PROP Intermediate_Dir "output/pjturn-srv-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 /W4 /GX /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /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 mswsock.lib netapi32.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 /nologo /subsystem:console /machine:I386 /out:"../bin/pjturn-srv-i386-win32-vc6-release.exe" - -!ELSEIF "$(CFG)" == "pjturn_srv - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "output/pjturn-srv-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir "output/pjturn-srv-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "output/pjturn-srv-i386-win32-vc6-debug" -# PROP Intermediate_Dir "output/pjturn-srv-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 /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "WIN32" /D "_DEBUG" /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 netapi32.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 /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjturn-srv-i386-win32-vc6-debug.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "pjturn_srv - Win32 Release" -# Name "pjturn_srv - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE="..\src\pjturn-srv\allocation.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjturn-srv\auth.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjturn-srv\listener_tcp.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjturn-srv\listener_udp.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjturn-srv\main.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjturn-srv\server.c" -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="..\src\pjturn-srv\auth.h" -# End Source File -# Begin Source File - -SOURCE="..\src\pjturn-srv\turn.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 -# End Target -# End Project diff --git a/pjproject-vs8.sln b/pjproject-vs8.sln index 007363c2..e10a739a 100644 --- a/pjproject-vs8.sln +++ b/pjproject-vs8.sln @@ -108,24 +108,25 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsua_wince", "pjsip-apps\s EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpjproject", "pjsip-apps\build\libpjproject.vcproj", "{23D7679C-764C-4E02-8B29-BB882CEEEFE2}" ProjectSection(ProjectDependencies) = postProject - {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858} = {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858} {2BB84911-C1B4-4747-B93D-36AA82CC5031} = {2BB84911-C1B4-4747-B93D-36AA82CC5031} - {2A3F241E-682C-47E1-9543-DC28708B406A} = {2A3F241E-682C-47E1-9543-DC28708B406A} - {4BF51C21-5A30-423B-82FE-1ED410E5769D} = {4BF51C21-5A30-423B-82FE-1ED410E5769D} - {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} = {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} - {4281CA5E-1D48-45D4-A991-2718A454B4BA} = {4281CA5E-1D48-45D4-A991-2718A454B4BA} - {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} - {6794B975-4E84-4F49-B2DC-C31F2224E03E} = {6794B975-4E84-4F49-B2DC-C31F2224E03E} - {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} - {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} = {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} - {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} = {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} - {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} - {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} - {B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21} - {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} - {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} = {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} + {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858} = {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858} + {A1989FF3-9894-40F4-B5A6-6EA364476E45} = {A1989FF3-9894-40F4-B5A6-6EA364476E45} {E53AA5FF-B737-40AA-BD13-387EFA99023D} = {E53AA5FF-B737-40AA-BD13-387EFA99023D} + {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} = {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} + {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} + {B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21} + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} + {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} = {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} + {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} = {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} + {6794B975-4E84-4F49-B2DC-C31F2224E03E} = {6794B975-4E84-4F49-B2DC-C31F2224E03E} + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} + {4281CA5E-1D48-45D4-A991-2718A454B4BA} = {4281CA5E-1D48-45D4-A991-2718A454B4BA} + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} = {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} + {4BF51C21-5A30-423B-82FE-1ED410E5769D} = {4BF51C21-5A30-423B-82FE-1ED410E5769D} + {2A3F241E-682C-47E1-9543-DC28708B406A} = {2A3F241E-682C-47E1-9543-DC28708B406A} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python_pjsua", "pjsip-apps\build\python_pjsua.vcproj", "{0C91838B-3372-40B4-A764-DE075A4BC94B}" @@ -138,6 +139,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsystest", "pjsip-apps\bui {23D7679C-764C-4E02-8B29-BB882CEEEFE2} = {23D7679C-764C-4E02-8B29-BB882CEEEFE2} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjmedia_videodev", "pjmedia\build\pjmedia_videodev.vcproj", "{A1989FF3-9894-40F4-B5A6-6EA364476E45}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Pocket PC 2003 (ARMV4) = Debug|Pocket PC 2003 (ARMV4) @@ -2846,6 +2849,126 @@ Global {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Static|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Release|Windows Mobile 6 Professional SDK (ARMV4I) {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Static|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 6 Professional SDK (ARMV4I) {5E507EA2-CB39-47CA-BD39-49EB58D7A0BB}.Release-Static|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Pocket PC 2003 (ARMV4).ActiveCfg = Debug|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Pocket PC 2003 (ARMV4).Build.0 = Debug|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Pocket PC 2003 (ARMV4).Deploy.0 = Debug|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Smartphone 2003 (ARMV4).ActiveCfg = Debug|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Smartphone 2003 (ARMV4).Build.0 = Debug|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Smartphone 2003 (ARMV4).Deploy.0 = Debug|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Win32.ActiveCfg = Debug|Win32 + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Win32.Build.0 = Debug|Win32 + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I).Build.0 = Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Debug|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Debug|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Pocket PC 2003 (ARMV4).ActiveCfg = Debug-Dynamic|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Pocket PC 2003 (ARMV4).Build.0 = Debug-Dynamic|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Pocket PC 2003 (ARMV4).Deploy.0 = Debug-Dynamic|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Smartphone 2003 (ARMV4).ActiveCfg = Debug-Dynamic|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Smartphone 2003 (ARMV4).Build.0 = Debug-Dynamic|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Smartphone 2003 (ARMV4).Deploy.0 = Debug-Dynamic|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Win32.ActiveCfg = Debug-Dynamic|Win32 + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Win32.Build.0 = Debug-Dynamic|Win32 + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I).Build.0 = Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I).Deploy.0 = Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Pocket PC 2003 (ARMV4).ActiveCfg = Debug-Static|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Pocket PC 2003 (ARMV4).Build.0 = Debug-Static|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Pocket PC 2003 (ARMV4).Deploy.0 = Debug-Static|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Smartphone 2003 (ARMV4).ActiveCfg = Debug-Static|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Smartphone 2003 (ARMV4).Build.0 = Debug-Static|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Smartphone 2003 (ARMV4).Deploy.0 = Debug-Static|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Win32.ActiveCfg = Debug-Static|Win32 + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Win32.Build.0 = Debug-Static|Win32 + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I).Build.0 = Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I).Deploy.0 = Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Pocket PC 2003 (ARMV4).ActiveCfg = Release|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Pocket PC 2003 (ARMV4).Build.0 = Release|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Pocket PC 2003 (ARMV4).Deploy.0 = Release|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Smartphone 2003 (ARMV4).ActiveCfg = Release|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Smartphone 2003 (ARMV4).Build.0 = Release|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Smartphone 2003 (ARMV4).Deploy.0 = Release|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Win32.ActiveCfg = Release|Win32 + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Win32.Build.0 = Release|Win32 + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I).Build.0 = Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Release|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Release|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Pocket PC 2003 (ARMV4).ActiveCfg = Release-Dynamic|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Pocket PC 2003 (ARMV4).Build.0 = Release-Dynamic|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Pocket PC 2003 (ARMV4).Deploy.0 = Release-Dynamic|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Smartphone 2003 (ARMV4).ActiveCfg = Release-Dynamic|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Smartphone 2003 (ARMV4).Build.0 = Release-Dynamic|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Smartphone 2003 (ARMV4).Deploy.0 = Release-Dynamic|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Win32.ActiveCfg = Release-Dynamic|Win32 + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Win32.Build.0 = Release-Dynamic|Win32 + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I).Build.0 = Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I).Deploy.0 = Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Pocket PC 2003 (ARMV4).ActiveCfg = Release-Static|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Pocket PC 2003 (ARMV4).Build.0 = Release-Static|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Pocket PC 2003 (ARMV4).Deploy.0 = Release-Static|Pocket PC 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Smartphone 2003 (ARMV4).ActiveCfg = Release-Static|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Smartphone 2003 (ARMV4).Build.0 = Release-Static|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Smartphone 2003 (ARMV4).Deploy.0 = Release-Static|Smartphone 2003 (ARMV4) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Win32.ActiveCfg = Release-Static|Win32 + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Win32.Build.0 = Release-Static|Win32 + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Release-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I).Build.0 = Release-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I).Deploy.0 = Release-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Release-Static|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Release-Static|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Release-Static|Windows Mobile 6 Professional SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Release-Static|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Release-Static|Windows Mobile 6 Standard SDK (ARMV4I) + {A1989FF3-9894-40F4-B5A6-6EA364476E45}.Release-Static|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Release-Static|Windows Mobile 6 Standard SDK (ARMV4I) EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/pjproject.dsw b/pjproject.dsw deleted file mode 100644 index 7abee43a..00000000 --- a/pjproject.dsw +++ /dev/null @@ -1,680 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "libg7221codec"=.\third_party\build\g7221\libg7221codec.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libgsmcodec"=.\THIRD_PARTY\BUILD\GSM\libgsmcodec.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libilbccodec"=.\THIRD_PARTY\BUILD\ILBC\libilbccodec.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libmilenage"=.\third_party\build\milenage\libmilenage.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libportaudio"=.\THIRD_PARTY\BUILD\PORTAUDIO\libportaudio.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libresample"=.\THIRD_PARTY\BUILD\RESAMPLE\libresample.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libresample_dll"=.\THIRD_PARTY\BUILD\RESAMPLE\libresample_dll.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libspeex"=.\third_party\build\speex\libspeex.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libsrtp"=.\third_party\build\srtp\libsrtp.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjlib"=.\pjlib\build\pjlib.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjlib_test"=.\pjlib\build\pjlib_test.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency -}}} - -############################################################################### - -Project: "pjlib_util"=".\pjlib-util\build\pjlib_util.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjlib_util_test"=".\pjlib-util\build\pjlib_util_test.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency -}}} - -############################################################################### - -Project: "pjmedia"=.\pjmedia\build\pjmedia.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjmedia_audiodev"=.\pjmedia\build\pjmedia_audiodev.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjmedia_codec"=.\pjmedia\build\pjmedia_codec.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjmedia_test"=.\pjmedia\build\pjmedia_test.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name libgsmcodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libilbccodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libportaudio - End Project Dependency - Begin Project Dependency - Project_Dep_Name libresample - End Project Dependency - Begin Project Dependency - Project_Dep_Name libspeex - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsrtp - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_codec - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_audiodev - End Project Dependency - Begin Project Dependency - Project_Dep_Name libg7221codec - End Project Dependency -}}} - -############################################################################### - -Project: "pjnath"=.\pjnath\build\pjnath.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjnath_test"=.\pjnath\build\pjnath_test.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency -}}} - -############################################################################### - -Project: "pjsip_core"=.\pjsip\build\pjsip_core.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjsip_simple"=.\pjsip\build\pjsip_simple.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjsip_test"=.\pjsip\build\pjsip_test.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_core - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_simple - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_ua - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_codec - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsua_lib - End Project Dependency -}}} - -############################################################################### - -Project: "pjsip_ua"=.\pjsip\build\pjsip_ua.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjsua"=".\pjsip-apps\build\pjsua.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_codec - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_core - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_simple - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_ua - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsua_lib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency - Begin Project Dependency - Project_Dep_Name libgsmcodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libilbccodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libportaudio - End Project Dependency - Begin Project Dependency - Project_Dep_Name libspeex - End Project Dependency - Begin Project Dependency - Project_Dep_Name libresample - End Project Dependency - Begin Project Dependency - Project_Dep_Name libmilenage - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsrtp - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_audiodev - End Project Dependency - Begin Project Dependency - Project_Dep_Name libg7221 - End Project Dependency - Begin Project Dependency - Project_Dep_Name libg7221codec - End Project Dependency -}}} - -############################################################################### - -Project: "pjsua_lib"=.\pjsip\build\pjsua_lib.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjsystest"=".\pjsip-apps\build\pjsystest.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name libg7221codec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libgsmcodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libilbccodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libportaudio - End Project Dependency - Begin Project Dependency - Project_Dep_Name libresample - End Project Dependency - Begin Project Dependency - Project_Dep_Name libspeex - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsrtp - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_audiodev - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_codec - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_core - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_simple - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_ua - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsua_lib - End Project Dependency -}}} - -############################################################################### - -Project: "pjturn_client"=.\pjnath\build\pjturn_client.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency -}}} - -############################################################################### - -Project: "pjturn_srv"=.\pjnath\build\pjturn_srv.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency -}}} - -############################################################################### - -Project: "sample_debug"=".\pjsip-apps\build\sample_debug.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_codec - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_core - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_simple - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_ua - End Project Dependency - Begin Project Dependency - Project_Dep_Name libgsmcodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libilbccodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libportaudio - End Project Dependency - Begin Project Dependency - Project_Dep_Name libresample - End Project Dependency - Begin Project Dependency - Project_Dep_Name libspeex - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsrtp - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_audiodev - End Project Dependency -}}} - -############################################################################### - -Project: "samples"=".\pjsip-apps\build\samples.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_codec - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_core - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_simple - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_ua - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsua_lib - End Project Dependency - Begin Project Dependency - Project_Dep_Name libgsmcodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libilbccodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libportaudio - End Project Dependency - Begin Project Dependency - Project_Dep_Name libresample - End Project Dependency - Begin Project Dependency - Project_Dep_Name libspeex - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsrtp - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_audiodev - End Project Dependency - Begin Project Dependency - Project_Dep_Name libg7221codec - End Project Dependency -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/pjsip-apps/build/Makefile b/pjsip-apps/build/Makefile index 08e2ca00..146e841e 100644 --- a/pjsip-apps/build/Makefile +++ b/pjsip-apps/build/Makefile @@ -20,7 +20,7 @@ PJSUA_LIB_LIB=../../pjsip/lib/libpjsua-$(TARGET_NAME)$(LIBEXT) # Gather all flags. # export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ - $(CFLAGS) $(CC_INC)../../pjsip/include \ + $(PJ_CFLAGS) $(CFLAGS) $(CC_INC)../../pjsip/include \ $(CC_INC)../../pjlib/include \ $(CC_INC)../../pjlib-util/include \ $(CC_INC)../../pjnath/include \ diff --git a/pjsip-apps/build/Samples-vc.mak b/pjsip-apps/build/Samples-vc.mak index b938e2c0..b3a76e6b 100644 --- a/pjsip-apps/build/Samples-vc.mak +++ b/pjsip-apps/build/Samples-vc.mak @@ -21,6 +21,7 @@ PJNATH_LIB = ..\..\pjnath\lib\pjnath-$(TARGET)$(LIBEXT) PJMEDIA_LIB = ..\..\pjmedia\lib\pjmedia-$(TARGET)$(LIBEXT) PJMEDIA_CODEC_LIB = ..\..\pjmedia\lib\pjmedia-codec-$(TARGET)$(LIBEXT) PJMEDIA_AUDIODEV_LIB = ..\..\pjmedia\lib\pjmedia-audiodev-$(TARGET)$(LIBEXT) +PJMEDIA_VIDEODEV_LIB = ..\..\pjmedia\lib\pjmedia-videodev-$(TARGET)$(LIBEXT) PJSIP_LIB = ..\..\pjsip\lib\pjsip-core-$(TARGET)$(LIBEXT) PJSIP_UA_LIB = ..\..\pjsip\lib\pjsip-ua-$(TARGET)$(LIBEXT) PJSIP_SIMPLE_LIB = ..\..\pjsip\lib\pjsip-simple-$(TARGET)$(LIBEXT) @@ -39,6 +40,7 @@ THIRD_PARTY_LIBS = $(GSM_LIB) $(ILBC_LIB) $(PORTAUDIO_LIB) $(RESAMPLE_LIB) \ LIBS = $(PJSUA_LIB_LIB) $(PJSIP_UA_LIB) $(PJSIP_SIMPLE_LIB) \ $(PJSIP_LIB) $(PJMEDIA_CODEC_LIB) $(PJMEDIA_AUDIODEV_LIB) \ + $(PJMEDIA_VIDEODEV_LIB) \ $(PJMEDIA_LIB) $(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB) \ $(THIRD_PARTY_LIBS) @@ -60,6 +62,7 @@ BINDIR = ..\bin\samples\$(TARGET) SAMPLES = $(BINDIR)\auddemo.exe \ $(BINDIR)\aectest.exe \ + $(BINDIR)\aviplay.exe \ $(BINDIR)\confsample.exe \ $(BINDIR)\confbench.exe \ $(BINDIR)\encdec.exe \ @@ -84,7 +87,8 @@ SAMPLES = $(BINDIR)\auddemo.exe \ $(BINDIR)\stereotest.exe \ $(BINDIR)\streamutil.exe \ $(BINDIR)\strerror.exe \ - $(BINDIR)\tonegen.exe + $(BINDIR)\tonegen.exe \ + $(BINDIR)\vid_streamutil.exe all: $(BINDIR) $(OBJDIR) $(SAMPLES) diff --git a/pjsip-apps/build/Samples.mak b/pjsip-apps/build/Samples.mak index bba68b46..ee16e52e 100644 --- a/pjsip-apps/build/Samples.mak +++ b/pjsip-apps/build/Samples.mak @@ -14,7 +14,9 @@ OBJDIR := ./output/samples-$(TARGET_NAME) BINDIR := ../bin/samples/$(TARGET_NAME) SAMPLES := auddemo \ + aviplay \ aectest \ + aviplay \ confsample \ encdec \ httpdemo \ @@ -38,7 +40,8 @@ SAMPLES := auddemo \ stereotest \ streamutil \ strerror \ - tonegen + tonegen \ + vid_streamutil EXES := $(foreach file, $(SAMPLES), $(BINDIR)/$(file)$(HOST_EXE)) diff --git a/pjsip-apps/build/pjsip_apps.dsw b/pjsip-apps/build/pjsip_apps.dsw deleted file mode 100644 index 6945bc53..00000000 --- a/pjsip-apps/build/pjsip_apps.dsw +++ /dev/null @@ -1,443 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "libg7221codec"="..\..\third_party\build\g7221\libg7221codec.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libgsmcodec"="..\..\third_party\build\gsm\libgsmcodec.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libilbccodec"="..\..\third_party\build\ilbc\libilbccodec.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libportaudio"="..\..\third_party\build\portaudio\libportaudio.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libresample"="..\..\third_party\build\resample\libresample.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libspeex"="..\..\third_party\build\speex\libspeex.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libsrtp"="..\..\third_party\build\srtp\libsrtp.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -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\build\pjmedia.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjmedia_audiodev"="..\..\pjmedia\build\pjmedia_audiodev.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjmedia_codec"="..\..\pjmedia\build\pjmedia_codec.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjnath"="..\..\pjnath\build\pjnath.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjsip_core"="..\..\pjsip\build\pjsip_core.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjsip_simple"="..\..\pjsip\build\pjsip_simple.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjsip_ua"="..\..\pjsip\build\pjsip_ua.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjsua"=".\pjsua.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_codec - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_core - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_simple - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_ua - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsua_lib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency - Begin Project Dependency - Project_Dep_Name libgsmcodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libilbccodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libportaudio - End Project Dependency - Begin Project Dependency - Project_Dep_Name libresample - End Project Dependency - Begin Project Dependency - Project_Dep_Name libspeex - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_audiodev - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsrtp - End Project Dependency - Begin Project Dependency - Project_Dep_Name libg7221codec - End Project Dependency -}}} - -############################################################################### - -Project: "pjsua_lib"="..\..\pjsip\build\pjsua_lib.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "python_pjsua"=".\python_pjsua.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name libgsmcodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libilbccodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libportaudio - End Project Dependency - Begin Project Dependency - Project_Dep_Name libresample - End Project Dependency - Begin Project Dependency - Project_Dep_Name libspeex - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsrtp - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_codec - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_core - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_simple - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_ua - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsua_lib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_audiodev - End Project Dependency - Begin Project Dependency - Project_Dep_Name libg7221codec - End Project Dependency -}}} - -############################################################################### - -Project: "sample_debug"=".\sample_debug.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_codec - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_core - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_simple - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_ua - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsua_lib - End Project Dependency - Begin Project Dependency - Project_Dep_Name libgsmcodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libilbccodec - End Project Dependency - Begin Project Dependency - Project_Dep_Name libportaudio - End Project Dependency - Begin Project Dependency - Project_Dep_Name libresample - End Project Dependency - Begin Project Dependency - Project_Dep_Name libspeex - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsrtp - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_audiodev - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency -}}} - -############################################################################### - -Project: "samples"=".\samples.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_codec - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_core - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_simple - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_ua - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsua_lib - End Project Dependency -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/pjsip-apps/build/pjsua.dsp b/pjsip-apps/build/pjsua.dsp deleted file mode 100644 index 7aafa8df..00000000 --- a/pjsip-apps/build/pjsua.dsp +++ /dev/null @@ -1,109 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjsua" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=pjsua - 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 "pjsua.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 "pjsua.mak" CFG="pjsua - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjsua - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "pjsua - 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)" == "pjsua - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir ".\output\pjsua-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir ".\output\pjsua-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir ".\output\pjsua-i386-win32-vc6-release" -# PROP Intermediate_Dir ".\output\pjsua-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 /W4 /GX /Zi /O2 /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /I "../../pjnath/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /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 -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 Iphlpapi.lib ole32.lib user32.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /map:"..\bin\pjsua_vc6.map" /debug /machine:I386 /out:"../bin/pjsua_vc6.exe" /fixed:no -# SUBTRACT LINK32 /pdb:none - -!ELSEIF "$(CFG)" == "pjsua - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir ".\output\pjsua-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir ".\output\pjsua-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir ".\output\pjsua-i386-win32-vc6-debug" -# PROP Intermediate_Dir ".\output\pjsua-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 /W4 /Gm /GX /ZI /Od /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /I "../../pjnath/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /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 -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 Iphlpapi.lib ole32.lib user32.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjsua_vc6d.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "pjsua - Win32 Release" -# Name "pjsua - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\src\pjsua\main.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsua\pjsua_app.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/pjsip-apps/build/pjsua.vcproj b/pjsip-apps/build/pjsua.vcproj index 3c6c1f62..717d7dca 100644 --- a/pjsip-apps/build/pjsua.vcproj +++ b/pjsip-apps/build/pjsua.vcproj @@ -11,16 +11,16 @@ Name="Win32" /> + @@ -286,27 +289,25 @@ Name="VCBscMakeTool" /> - - + @@ -487,27 +490,25 @@ Name="VCBscMakeTool" /> - - @@ -542,13 +543,11 @@ - @@ -556,25 +555,27 @@ Name="VCBscMakeTool" /> - - + @@ -688,27 +691,25 @@ Name="VCBscMakeTool" /> - - + @@ -889,27 +892,25 @@ Name="VCBscMakeTool" /> - - @@ -1011,13 +1012,11 @@ - @@ -1025,25 +1024,27 @@ Name="VCBscMakeTool" /> - - + @@ -1090,27 +1093,25 @@ Name="VCBscMakeTool" /> - - @@ -1278,8 +1279,8 @@ /> @@ -1345,8 +1346,8 @@ /> @@ -1412,8 +1413,8 @@ /> @@ -1464,7 +1465,7 @@ /> @@ -1480,13 +1481,11 @@ - @@ -1494,23 +1493,25 @@ Name="VCBscMakeTool" /> - - @@ -1546,8 +1547,8 @@ /> @@ -1613,8 +1614,8 @@ /> @@ -1680,8 +1681,8 @@ /> @@ -1747,8 +1748,8 @@ /> @@ -1814,8 +1815,8 @@ /> @@ -1881,8 +1882,8 @@ /> @@ -1933,7 +1934,7 @@ /> @@ -1949,13 +1950,11 @@ - @@ -1963,23 +1962,25 @@ Name="VCBscMakeTool" /> - - @@ -2016,7 +2017,7 @@ @@ -2082,8 +2083,8 @@ /> @@ -2149,8 +2150,8 @@ /> @@ -2216,8 +2217,8 @@ /> @@ -2283,8 +2284,8 @@ /> @@ -2351,7 +2352,7 @@ @@ -2402,7 +2403,7 @@ /> @@ -2417,14 +2418,12 @@ /> - @@ -2432,23 +2431,25 @@ Name="VCBscMakeTool" /> - - @@ -2484,8 +2485,8 @@ /> @@ -2551,8 +2552,8 @@ /> @@ -2619,7 +2620,7 @@ @@ -2686,7 +2687,7 @@ @@ -2753,7 +2754,7 @@ @@ -2820,7 +2821,7 @@ -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=pjsystest - 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 "pjsystest.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 "pjsystest.mak" CFG="pjsystest - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjsystest - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "pjsystest - 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)" == "pjsystest - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir ".\output\pjsystest-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir ".\output\pjsystest-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir ".\output\pjsystest-i386-win32-vc6-release" -# PROP Intermediate_Dir ".\output\pjsystest-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 /W4 /GX /Zi /O2 /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /I "../../pjnath/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /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 -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 Iphlpapi.lib ole32.lib user32.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /map:"..\bin\pjsystest_vc6.map" /debug /machine:I386 /out:"../bin/pjsystest_vc6.exe" /fixed:no -# SUBTRACT LINK32 /pdb:none - -!ELSEIF "$(CFG)" == "pjsystest - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir ".\output\pjsystest-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir ".\output\pjsystest-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir ".\output\pjsystest-i386-win32-vc6-debug" -# PROP Intermediate_Dir ".\output\pjsystest-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 /W4 /Gm /GX /ZI /Od /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /I "../../pjnath/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /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 -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 Iphlpapi.lib ole32.lib user32.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjsystest_vc6d.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "pjsystest - Win32 Release" -# Name "pjsystest - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\src\pjsystest\main_console.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsystest\main_wm.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pjsystest\systest.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\src\pjsystest\gui.h -# End Source File -# Begin Source File - -SOURCE=..\src\pjsystest\systest.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 -# End Target -# End Project diff --git a/pjsip-apps/build/py_pjsua.dsp b/pjsip-apps/build/py_pjsua.dsp deleted file mode 100644 index f73ee695..00000000 --- a/pjsip-apps/build/py_pjsua.dsp +++ /dev/null @@ -1,126 +0,0 @@ -# Microsoft Developer Studio Project File - Name="py_pjsua" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=py_pjsua - 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 "py_pjsua.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 "py_pjsua.mak" CFG="py_pjsua - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "py_pjsua - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "py_pjsua - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "py_pjsua - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir ".\output\py_pjsua-i386-win32-vc6-release" -# PROP Intermediate_Dir ".\output\py_pjsua-i386-win32-vc6-release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PY_PJSUA_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\pjlib\include" /I "..\..\pjlib-util\include" /I "..\..\pjmedia\include" /I "..\..\pjsip\include" /I "../../pjnath/include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PY_PJSUA_EXPORTS" /FR /YX /FD /c -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x421 /d "NDEBUG" -# ADD RSC /l 0x421 /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 /nologo /dll /machine:I386 -# ADD LINK32 python24.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 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib iphlpapi.lib /nologo /dll /map /machine:I386 /nodefaultlib:"libcmt.lib" /out:"..\lib\py_pjsua.pyd" /libpath:"../../pjlib/lib" /libpath:"../../pjlib-util/lib" /libpath:"../../pjmedia/lib" /libpath:"../../pjsip/lib" -# SUBTRACT LINK32 /nodefaultlib - -!ELSEIF "$(CFG)" == "py_pjsua - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir ".\output\py_pjsua-i386-win32-vc6-debug" -# PROP Intermediate_Dir ".\output\py_pjsua-i386-win32-vc6-debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PY_PJSUA_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\pjlib\include" /I "..\..\pjlib-util\include" /I "..\..\pjmedia\include" /I "..\..\pjsip\include" /I "../../pjnath/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PY_PJSUA_EXPORTS" /FR /YX /FD /GZ /c -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x421 /d "_DEBUG" -# ADD RSC /l 0x421 /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 /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 python24_d.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 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib iphlpapi.lib /nologo /dll /debug /machine:I386 /out:"..\lib\py_pjsua_d.pyd" /pdbtype:sept /libpath:"../../pjlib/lib" /libpath:"../../pjlib-util/lib" /libpath:"../../pjmedia/lib" /libpath:"../../pjsip/lib" /libpath:"F:\incoming\projects\divusi\Python-2.4\Python-2.4\PCbuild" /libpath:"F:\incoming\projects\divusi\Python-2.4\Python-2.4\PC\VC6" - -!ENDIF - -# Begin Target - -# Name "py_pjsua - Win32 Release" -# Name "py_pjsua - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\src\py_pjsua\pjsua.py -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\py_pjsua\pjsua_app.py -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\py_pjsua\py_pjsua.c -# End Source File -# Begin Source File - -SOURCE=..\src\py_pjsua\py_pjsua.def -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\src\py_pjsua\py_pjsua.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 -# End Target -# End Project diff --git a/pjsip-apps/build/python_pjsua.dsp b/pjsip-apps/build/python_pjsua.dsp deleted file mode 100644 index 6b495e1c..00000000 --- a/pjsip-apps/build/python_pjsua.dsp +++ /dev/null @@ -1,116 +0,0 @@ -# Microsoft Developer Studio Project File - Name="python_pjsua" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=python_pjsua - 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 "python_pjsua.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 "python_pjsua.mak" CFG="python_pjsua - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "python_pjsua - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "python_pjsua - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "python_pjsua - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir ".\output\python_pjsua-i386-win32-vc6-release" -# PROP Intermediate_Dir ".\output\python_pjsua-i386-win32-vc6-release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PY_PJSUA_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\pjlib\include" /I "..\..\pjlib-util\include" /I "..\..\pjmedia\include" /I "..\..\pjsip\include" /I "../../pjnath/include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PY_PJSUA_EXPORTS" /FR /YX /FD /c -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x421 /d "NDEBUG" -# ADD RSC /l 0x421 /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 /nologo /dll /machine:I386 -# ADD LINK32 python24.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 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib iphlpapi.lib /nologo /dll /map /machine:I386 /nodefaultlib:"libcmt.lib" /out:"..\lib\_pjsua.pyd" /libpath:"../../pjlib/lib" /libpath:"../../pjlib-util/lib" /libpath:"../../pjmedia/lib" /libpath:"../../pjsip/lib" -# SUBTRACT LINK32 /nodefaultlib - -!ELSEIF "$(CFG)" == "python_pjsua - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir ".\output\python_pjsua-i386-win32-vc6-debug" -# PROP Intermediate_Dir ".\output\python_pjsua-i386-win32-vc6-debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PY_PJSUA_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\pjlib\include" /I "..\..\pjlib-util\include" /I "..\..\pjmedia\include" /I "..\..\pjsip\include" /I "../../pjnath/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PY_PJSUA_EXPORTS" /FR /YX /FD /GZ /c -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x421 /d "_DEBUG" -# ADD RSC /l 0x421 /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 /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 python24_d.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 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib iphlpapi.lib /nologo /dll /debug /machine:I386 /out:"..\lib\_pjsua_d.pyd" /pdbtype:sept /libpath:"../../pjlib/lib" /libpath:"../../pjlib-util/lib" /libpath:"../../pjmedia/lib" /libpath:"../../pjsip/lib" /libpath:"F:\incoming\projects\divusi\Python-2.4\Python-2.4\PCbuild" /libpath:"F:\incoming\projects\divusi\Python-2.4\Python-2.4\PC\VC6" - -!ENDIF - -# Begin Target - -# Name "python_pjsua - Win32 Release" -# Name "python_pjsua - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\src\python\_pjsua.c -# End Source File -# Begin Source File - -SOURCE=..\src\python\_pjsua.def -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\src\python\_pjsua.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 -# End Target -# End Project diff --git a/pjsip-apps/build/sample_debug.dsp b/pjsip-apps/build/sample_debug.dsp deleted file mode 100644 index 03c49d16..00000000 --- a/pjsip-apps/build/sample_debug.dsp +++ /dev/null @@ -1,104 +0,0 @@ -# Microsoft Developer Studio Project File - Name="sample_debug" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=sample_debug - 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 "sample_debug.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 "sample_debug.mak" CFG="sample_debug - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "sample_debug - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "sample_debug - 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)" == "sample_debug - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "./output/samples-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir "./output/samples-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "./output/samples-i386-win32-vc6-release" -# PROP Intermediate_Dir "./output/samples-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 /Zi /O2 /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /I "../../pjnath/include" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /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 -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 Iphlpapi.lib ole32.lib user32.lib 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 /map /debug /machine:I386 /out:"../bin/samples/sampledebug_vc6.exe" /fixed:no -# SUBTRACT LINK32 /pdb:none - -!ELSEIF "$(CFG)" == "sample_debug - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "./output/samples-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir "./output/samples-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "./output/samples-i386-win32-vc6-debug" -# PROP Intermediate_Dir "./output/samples-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 /W4 /Gm /GX /ZI /Od /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /I "../../pjnath/include" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_DEBUG" /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 Iphlpapi.lib ole32.lib user32.lib 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 /map /debug /machine:I386 /out:"../bin/samples/sampledebug_vc6d.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "sample_debug - Win32 Release" -# Name "sample_debug - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\src\samples\debug.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/pjsip-apps/build/sample_debug.vcproj b/pjsip-apps/build/sample_debug.vcproj index 0df2cd13..f9e77266 100644 --- a/pjsip-apps/build/sample_debug.vcproj +++ b/pjsip-apps/build/sample_debug.vcproj @@ -34,11 +34,11 @@ -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) External Target" 0x0106 - -CFG=samples - 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 "samples.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 "samples.mak" CFG="samples - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "samples - Win32 Release" (based on "Win32 (x86) External Target") -!MESSAGE "samples - Win32 Debug" (based on "Win32 (x86) External Target") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" - -!IF "$(CFG)" == "samples - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "./output/samples-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir "./output/samples-i386-win32-vc6-release" -# PROP BASE Cmd_Line "NMAKE /f samples.mak" -# PROP BASE Rebuild_Opt "/a" -# PROP BASE Target_File "samples.exe" -# PROP BASE Bsc_Name "samples.bsc" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "./output/samples-i386-win32-vc6-release" -# PROP Intermediate_Dir "./output/samples-i386-win32-vc6-release" -# PROP Cmd_Line "nmake /NOLOGO /S /f Samples-vc.mak BUILD_MODE=release VC_VER=6" -# PROP Rebuild_Opt "/a" -# PROP Target_File "All samples" -# PROP Bsc_Name "" -# PROP Target_Dir "" - -!ELSEIF "$(CFG)" == "samples - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "./output/samples-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir "./output/samples-i386-win32-vc6-debug" -# PROP BASE Cmd_Line "NMAKE /f samples.mak" -# PROP BASE Rebuild_Opt "/a" -# PROP BASE Target_File "samples.exe" -# PROP BASE Bsc_Name "samples.bsc" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "./output/samples-i386-win32-vc6-debug" -# PROP Intermediate_Dir "./output/samples-i386-win32-vc6-debug" -# PROP Cmd_Line "nmake /NOLOGO /S /f Samples-vc.mak BUILD_MODE=debug VC_VER=6" -# PROP Rebuild_Opt "/a" -# PROP Target_File "All samples" -# PROP Bsc_Name "" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "samples - Win32 Release" -# Name "samples - Win32 Debug" - -!IF "$(CFG)" == "samples - Win32 Release" - -!ELSEIF "$(CFG)" == "samples - Win32 Debug" - -!ENDIF - -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\src\samples\aectest.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\auddemo.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\confbench.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\confsample.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\encdec.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\footprint.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\httpdemo.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\icedemo.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\invtester.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\jbsim.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\latency.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\level.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\mix.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\pcaputil.c -# End Source File -# Begin Source File - -SOURCE="..\src\samples\pjsip-perf.c" -# End Source File -# Begin Source File - -SOURCE=..\src\samples\playfile.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\playsine.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\recfile.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\resampleplay.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\simple_pjsua.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\simpleua.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\siprtp.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\siprtp_report.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\sipstateless.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\sndinfo.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\sndtest.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\stateful_proxy.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\stateless_proxy.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\stereotest.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\streamutil.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\strerror.c -# End Source File -# Begin Source File - -SOURCE=..\src\samples\tonegen.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\src\samples\proxy.h -# End Source File -# Begin Source File - -SOURCE=..\src\samples\util.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=".\Samples-vc.mak" -# End Source File -# Begin Source File - -SOURCE=.\Samples.mak -# End Source File -# End Target -# End Project diff --git a/pjsip-apps/build/samples.vcproj b/pjsip-apps/build/samples.vcproj index 09767799..f7825e0a 100644 --- a/pjsip-apps/build/samples.vcproj +++ b/pjsip-apps/build/samples.vcproj @@ -136,32 +136,8 @@ /> - - - + + + + - - - - + + + + + + + + + + + - - - - - - - - + + @@ -1709,6 +1713,10 @@ RelativePath="..\src\samples\tonegen.c" > + + + #define THIS_FILE "main.c" @@ -84,7 +85,7 @@ static void setup_socket_signal() #endif -int main(int argc, char *argv[]) +static int main_func(int argc, char *argv[]) { setup_socket_signal(); @@ -106,3 +107,7 @@ int main(int argc, char *argv[]) return 0; } +int main(int argc, char *argv[]) +{ + return pj_run_app(&main_func, argc, argv, 0); +} diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index 81b1f2a4..e311c32a 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -124,6 +124,7 @@ static struct app_config int ring_cnt; pjmedia_port *ring_port; + int vcapture_dev, vrender_dev; } app_config; @@ -136,7 +137,13 @@ static const char *stdout_refresh_text = "STDOUT_REFRESH"; static pj_bool_t stdout_refresh_quit = PJ_FALSE; static pj_str_t uri_arg; -static char some_buf[1024 * 3]; +#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) +# define SOME_BUF_SIZE (1024 * 10) +#else +# define SOME_BUF_SIZE (1024 * 3) +#endif + +static char some_buf[SOME_BUF_SIZE]; #ifdef STEREO_DEMO static void stereo_demo(); @@ -194,6 +201,7 @@ static void usage(void) puts (" --color Use colorful logging (default yes on Win32)"); puts (" --no-color Disable colorful logging"); puts (" --light-bg Use dark colors for light background (default is dark bg)"); + puts (" --no-stderr Disable stderr"); puts (""); puts ("SIP Account options:"); @@ -267,7 +275,7 @@ static void usage(void) puts (" --tls-srv-name Specify TLS server name for multihosting server"); puts (""); - puts ("Media Options:"); + puts ("Audio Options:"); puts (" --add-codec=name Manually add codec (default is to enable all)"); puts (" --dis-codec=name Disable codec (can be specified multiple times)"); puts (" --clock-rate=N Override conference bridge clock rate"); @@ -301,6 +309,15 @@ static void usage(void) puts (" Specify N=0 for instant close when unused."); puts (" --no-tones Disable audible tones"); puts (" --jb-max-size Specify jitter buffer maximum size, in frames (default=-1)"); + puts (" --extra-audio Add one more audio stream"); + +#if PJSUA_HAS_VIDEO + puts (""); + puts ("Video Options:"); + puts (" --video Enable video"); + puts (" --vcapture-dev=id Video capture device ID (default=-1)"); + puts (" --vrender-dev=id Video render device ID (default=-1)"); +#endif puts (""); puts ("Media Transport Options:"); @@ -376,6 +393,9 @@ static void default_config(struct app_config *cfg) for (i=0; ibuddy_cfg); ++i) pjsua_buddy_config_default(&cfg->buddy_cfg[i]); + + cfg->vcapture_dev = PJSUA_INVALID_ID; + cfg->vrender_dev = PJSUA_INVALID_ID; } @@ -509,7 +529,7 @@ static pj_status_t parse_args(int argc, char *argv[], int c; int option_index; enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, - OPT_LOG_APPEND, OPT_COLOR, OPT_NO_COLOR, OPT_LIGHT_BG, + OPT_LOG_APPEND, OPT_COLOR, OPT_NO_COLOR, OPT_LIGHT_BG, OPT_NO_STDERR, OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, OPT_SND_AUTO_CLOSE, OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT, @@ -541,7 +561,9 @@ static pj_status_t parse_args(int argc, char *argv[], #endif OPT_AUTO_UPDATE_NAT,OPT_USE_COMPACT_FORM,OPT_DIS_CODEC, OPT_NO_FORCE_LR, - OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE + OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE, + OPT_VIDEO, OPT_EXTRA_AUDIO, + OPT_VCAPTURE_DEV, OPT_VRENDER_DEV, }; struct pj_getopt_option long_options[] = { { "config-file",1, 0, OPT_CONFIG_FILE}, @@ -552,6 +574,7 @@ static pj_status_t parse_args(int argc, char *argv[], { "color", 0, 0, OPT_COLOR}, { "no-color", 0, 0, OPT_NO_COLOR}, { "light-bg", 0, 0, OPT_LIGHT_BG}, + { "no-stderr", 0, 0, OPT_NO_STDERR}, { "help", 0, 0, OPT_HELP}, { "version", 0, 0, OPT_VERSION}, { "clock-rate", 1, 0, OPT_CLOCK_RATE}, @@ -660,6 +683,10 @@ static pj_status_t parse_args(int argc, char *argv[], { "timer-se", 1, 0, OPT_TIMER_SE}, { "timer-min-se", 1, 0, OPT_TIMER_MIN_SE}, { "outb-rid", 1, 0, OPT_OUTB_RID}, + { "video", 0, 0, OPT_VIDEO}, + { "extra-audio",0, 0, OPT_EXTRA_AUDIO}, + { "vcapture-dev", 1, 0, OPT_VCAPTURE_DEV}, + { "vrender-dev", 1, 0, OPT_VRENDER_DEV}, { NULL, 0, 0, 0} }; pj_status_t status; @@ -752,6 +779,10 @@ static pj_status_t parse_args(int argc, char *argv[], pj_log_set_color(77, 0); break; + case OPT_NO_STDERR: + freopen("/dev/null", "w", stderr); + break; + case OPT_HELP: usage(); return PJ_EINVAL; @@ -1417,6 +1448,26 @@ static pj_status_t parse_args(int argc, char *argv[], cfg->udp_cfg.qos_params.flags = PJ_QOS_PARAM_HAS_DSCP; cfg->udp_cfg.qos_params.dscp_val = 0x18; break; + case OPT_VIDEO: + ++cur_acc->max_video_cnt; + cur_acc->vid_in_auto_show = PJ_TRUE; + cur_acc->vid_out_auto_transmit = PJ_TRUE; + PJ_TODO(implement_pjsua_option_for_vid_auto_show_and_transmit); + break; + case OPT_EXTRA_AUDIO: + ++cur_acc->max_audio_cnt; + break; + + case OPT_VCAPTURE_DEV: + cfg->vcapture_dev = atoi(pj_optarg); + cur_acc->vid_cap_dev = cfg->vcapture_dev; + break; + + case OPT_VRENDER_DEV: + cfg->vrender_dev = atoi(pj_optarg); + cur_acc->vid_rend_dev = cfg->vrender_dev; + break; + default: PJ_LOG(1,(THIS_FILE, "Argument \"%s\" is not valid. Use --help to see help", @@ -1661,6 +1712,14 @@ static void write_account_settings(int acc_index, pj_str_t *result) /* MWI */ if (acc_cfg->mwi_enabled) pj_strcat2(result, "--mwi\n"); + + /* Video & extra audio */ + for (i=0; imax_video_cnt; ++i) { + pj_strcat2(result, "--video\n"); + } + for (i=1; imax_audio_cnt; ++i) { + pj_strcat2(result, "--extra-audio\n"); + } } @@ -1985,6 +2044,14 @@ static int write_settings(const struct app_config *config, pj_strcat2(&cfg, line); } + if (config->vcapture_dev != PJSUA_INVALID_ID) { + pj_ansi_sprintf(line, "--vcapture-dev %d\n", config->vcapture_dev); + pj_strcat2(&cfg, line); + } + if (config->vrender_dev != PJSUA_INVALID_ID) { + pj_ansi_sprintf(line, "--vrender-dev %d\n", config->vrender_dev); + pj_strcat2(&cfg, line); + } /* ptime */ if (config->media_cfg.ptime) { @@ -2584,45 +2651,60 @@ static void on_call_tsx_state(pjsua_call_id call_id, } } - -/* - * Callback on media state changed event. - * The action may connect the call to sound device, to file, or - * to loop the call. - */ -static void on_call_media_state(pjsua_call_id call_id) +/* General processing for media state. "mi" is the media index */ +static void on_call_generic_media_state(pjsua_call_info *ci, unsigned mi, + pj_bool_t *has_error) { - pjsua_call_info call_info; + const char *status_name[] = { + "None", + "Active", + "Local hold", + "Remote hold", + "Error" + }; - pjsua_call_get_info(call_id, &call_info); + pj_assert(ci->media[mi].status <= PJ_ARRAY_SIZE(status_name)); + pj_assert(PJSUA_CALL_MEDIA_ERROR == 4); + + PJ_LOG(4,(THIS_FILE, "Call %d media %d [type=%s], status is %s", + ci->id, mi, pjmedia_type_name(ci->media[mi].type), + status_name[ci->media[mi].status])); +} +/* Process audio media state. "mi" is the media index. */ +static void on_call_audio_state(pjsua_call_info *ci, unsigned mi, + pj_bool_t *has_error) +{ /* Stop ringback */ - ring_stop(call_id); + ring_stop(ci->id); /* Connect ports appropriately when media status is ACTIVE or REMOTE HOLD, * otherwise we should NOT connect the ports. */ - if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE || - call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD) + if (ci->media[mi].status == PJSUA_CALL_MEDIA_ACTIVE || + ci->media[mi].status == PJSUA_CALL_MEDIA_REMOTE_HOLD) { pj_bool_t connect_sound = PJ_TRUE; + pjsua_conf_port_id call_conf_slot; + + call_conf_slot = ci->media[mi].stream.aud.conf_slot; /* Loopback sound, if desired */ if (app_config.auto_loop) { - pjsua_conf_connect(call_info.conf_slot, call_info.conf_slot); + pjsua_conf_connect(call_conf_slot, call_conf_slot); connect_sound = PJ_FALSE; } /* Automatically record conversation, if desired */ if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) { - pjsua_conf_connect(call_info.conf_slot, app_config.rec_port); + pjsua_conf_connect(call_conf_slot, app_config.rec_port); } /* Stream a file, if desired */ if ((app_config.auto_play || app_config.auto_play_hangup) && app_config.wav_port != PJSUA_INVALID_ID) { - pjsua_conf_connect(app_config.wav_port, call_info.conf_slot); + pjsua_conf_connect(app_config.wav_port, call_conf_slot); connect_sound = PJ_FALSE; } @@ -2638,16 +2720,16 @@ static void on_call_media_state(pjsua_call_id call_id) pjsua_enum_calls(call_ids, &call_cnt); for (i=0; iid) continue; if (!pjsua_call_has_media(call_ids[i])) continue; - pjsua_conf_connect(call_info.conf_slot, + pjsua_conf_connect(call_conf_slot, pjsua_call_get_conf_port(call_ids[i])); pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]), - call_info.conf_slot); + call_conf_slot); /* Automatically record conversation, if desired */ if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) { @@ -2663,52 +2745,53 @@ static void on_call_media_state(pjsua_call_id call_id) /* Otherwise connect to sound device */ if (connect_sound) { - pjsua_conf_connect(call_info.conf_slot, 0); - pjsua_conf_connect(0, call_info.conf_slot); + pjsua_conf_connect(call_conf_slot, 0); + pjsua_conf_connect(0, call_conf_slot); /* Automatically record conversation, if desired */ if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) { - pjsua_conf_connect(call_info.conf_slot, app_config.rec_port); + pjsua_conf_connect(call_conf_slot, app_config.rec_port); pjsua_conf_connect(0, app_config.rec_port); } } } +} - /* Handle media status */ - switch (call_info.media_status) { - case PJSUA_CALL_MEDIA_ACTIVE: - PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id)); - break; +/* Process video media state. "mi" is the media index. */ +static void on_call_video_state(pjsua_call_info *ci, unsigned mi, + pj_bool_t *has_error) +{ +} - case PJSUA_CALL_MEDIA_LOCAL_HOLD: - PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by local", - call_id)); - break; +/* + * Callback on media state changed event. + * The action may connect the call to sound device, to file, or + * to loop the call. + */ +static void on_call_media_state(pjsua_call_id call_id) +{ + pjsua_call_info call_info; + unsigned mi; + pj_bool_t has_error = PJ_FALSE; - case PJSUA_CALL_MEDIA_REMOTE_HOLD: - PJ_LOG(3,(THIS_FILE, - "Media for call %d is suspended (hold) by remote", - call_id)); - break; + pjsua_call_get_info(call_id, &call_info); - case PJSUA_CALL_MEDIA_ERROR: - PJ_LOG(3,(THIS_FILE, - "Media has reported error, disconnecting call")); - { - pj_str_t reason = pj_str("ICE negotiation failed"); - pjsua_call_hangup(call_id, 500, &reason, NULL); - } - break; + for (mi=0; mitype, event_name))); +} + /* * Print buddy list. */ @@ -3193,8 +3295,12 @@ static void keystroke_help(void) puts("| * Send DTMF with INFO | cc Connect port | dd Dump detailed |"); puts("| dq Dump curr. call quality | cd Disconnect port | dc Dump config |"); puts("| | V Adjust audio Volume | f Save config |"); - puts("| S Send arbitrary REQUEST | Cp Codec priorities | f Save config |"); - puts("+------------------------------+--------------------------+-------------------+"); + puts("| S Send arbitrary REQUEST | Cp Codec priorities | |"); + puts("+-----------------------------------------------------------------------------+"); +#if PJSUA_HAS_VIDEO + puts("| Video: \"vid help\" for more info |"); + puts("+-----------------------------------------------------------------------------+"); +#endif puts("| q QUIT L ReLoad sleep MS echo [0|1|txt] n: detect NAT type |"); puts("+=============================================================================+"); @@ -3210,6 +3316,31 @@ static void keystroke_help(void) } } +/* Help screen for video */ +static void vid_show_help(void) +{ +#if PJSUA_HAS_VIDEO + puts("+=============================================================================+"); + puts("| Video commands: |"); + puts("| |"); + puts("| vid help Show this help screen |"); + puts("| vid call rx on|off N Enable/disable video rx for stream N in curr call |"); + puts("| vid call tx on|off N Enable/disable video tx for stream N in curr call |"); + puts("| vid call add Add video stream for current call |"); + puts("| vid call enable/disable N Enable/disable stream #N in current call |"); + puts("| vid call set-cap N ID Set capture dev ID for stream #N in current call |"); + puts("| vid dev list List all video devices |"); + puts("| vid dev refresh Refresh video device list |"); + puts("| vid dev prev on|off ID Enable/disable preview for specified device ID |"); + puts("| vid codec list List video codecs |"); + puts("| vid codec prio PT PRIO Set codec with pt PT priority to PRIO |"); + puts("| vid win list List all active video windows |"); + puts("| vid win show|hide ID Show/hide the specified video window ID |"); + puts("| vid win move ID X Y Move window ID to position X,Y |"); + puts("| vid win resize ID w h Resize window ID to the specified width, height |"); + puts("+=============================================================================+"); +#endif +} /* * Input simple string @@ -3481,17 +3612,30 @@ static void manage_codec_prio(void) int new_prio; pj_status_t status; - printf("List of codecs:\n"); - + printf("List of audio codecs:\n"); pjsua_enum_codecs(c, &count); for (i=0; idir == PJMEDIA_DIR_CAPTURE_RENDER) { + dirname = "capture, render"; + } else if (vdi->dir == PJMEDIA_DIR_CAPTURE) { + dirname = "capture"; + } else { + dirname = "render"; + } + + + capnames[0] = '\0'; + for (i=0; icaps & (1 << i)) { + const char *capname = pjmedia_vid_dev_cap_name(1 << i, NULL); + if (capname) { + if (*capnames) + strcat(capnames, ", "); + strncat(capnames, capname, + sizeof(capnames)-strlen(capnames)-1); + } + } + } + + formats[0] = '\0'; + for (i=0; ifmt_cnt; ++i) { + const pjmedia_video_format_info *vfi = + pjmedia_get_video_format_info(NULL, vdi->fmt[i].id); + if (vfi) { + if (*formats) + strcat(formats, ", "); + strncat(formats, vfi->name, sizeof(formats)-strlen(formats)-1); + } + } + + PJ_LOG(3,(THIS_FILE, "%3d %s [%s][%s] %s", id, vdi->name, vdi->driver, + dirname, title)); + PJ_LOG(3,(THIS_FILE, " Supported capabilities: %s", capnames)); + PJ_LOG(3,(THIS_FILE, " Supported formats: %s", formats)); +} + +static void vid_list_devs(void) +{ + unsigned i, count; + pjmedia_vid_dev_info vdi; + pj_status_t status; + + PJ_LOG(3,(THIS_FILE, "Video device list:")); + count = pjsua_vid_dev_count(); + if (count == 0) { + PJ_LOG(3,(THIS_FILE, " - no device detected -")); + return; + } else { + PJ_LOG(3,(THIS_FILE, "%d device(s) detected:", count)); + } + + status = pjsua_vid_dev_get_info(PJMEDIA_VID_DEFAULT_RENDER_DEV, &vdi); + if (status == PJ_SUCCESS) + vid_print_dev(PJMEDIA_VID_DEFAULT_RENDER_DEV, &vdi, + "(default renderer device)"); + + status = pjsua_vid_dev_get_info(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &vdi); + if (status == PJ_SUCCESS) + vid_print_dev(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &vdi, + "(default capture device)"); + + for (i=0; i= 3 && + (strcmp(argv[2], "disable")==0 || strcmp(argv[2], "enable")==0)) + { + pj_bool_t enable = (strcmp(argv[2], "enable") == 0); + pjsua_call_vid_strm_op op = enable? PJSUA_CALL_VID_STRM_CHANGE_DIR : + PJSUA_CALL_VID_STRM_REMOVE; + + param.med_idx = argc >= 4? atoi(argv[3]) : -1; + param.dir = PJMEDIA_DIR_ENCODING_DECODING; + pjsua_call_set_vid_strm(current_call, op, ¶m); + } + else if (argc >= 3 && strcmp(argv[2], "set-cap")==0) { + param.med_idx = argc >= 4? atoi(argv[3]) : -1; + param.cap_dev = argc >= 5? atoi(argv[4]) : PJMEDIA_VID_DEFAULT_CAPTURE_DEV; + pjsua_call_set_vid_strm(current_call, PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV, ¶m); + } else + goto on_error; + } else if (argc >= 3 && strcmp(argv[1], "dev")==0) { + if (strcmp(argv[2], "list")==0) { + vid_list_devs(); + } else if (strcmp(argv[2], "refresh")==0) { + pjmedia_vid_dev_refresh(); + } else if (strcmp(argv[2], "prev")==0) { + if (argc != 5) { + goto on_error; + } else { + pj_bool_t on = (strcmp(argv[3], "on") == 0); + int dev_id = atoi(argv[4]); + if (on) { + pjsua_vid_preview_start(dev_id, NULL); + } else { + pjsua_vid_preview_stop(dev_id); + } + } + } else + goto on_error; + } else if (strcmp(argv[1], "win")==0) { + if (argc==3 && strcmp(argv[2], "list")==0) { + pjsua_vid_win_id wids[PJSUA_MAX_VID_WINS]; + unsigned i, cnt = PJ_ARRAY_SIZE(wids); + + pjsua_vid_enum_wins(wids, &cnt); + + PJ_LOG(3,(THIS_FILE, "Found %d video windows:", cnt)); + PJ_LOG(3,(THIS_FILE, "WID show pos size")); + PJ_LOG(3,(THIS_FILE, "------------------------------")); + for (i = 0; i < cnt; ++i) { + pjsua_vid_win_info wi; + pjsua_vid_win_get_info(wids[i], &wi); + PJ_LOG(3,(THIS_FILE, "%3d %c (%d,%d) %dx%d", + wids[i], (wi.show?'Y':'N'), wi.pos.x, wi.pos.y, + wi.size.w, wi.size.h)); + } + } else if (argc==4 && (strcmp(argv[2], "show")==0 || + strcmp(argv[2], "hide")==0)) + { + pj_bool_t show = (strcmp(argv[2], "show")==0); + pjsua_vid_win_id wid = atoi(argv[3]); + pjsua_vid_win_set_show(wid, show); + } else if (argc==6 && strcmp(argv[2], "move")==0) { + pjsua_vid_win_id wid = atoi(argv[3]); + pjmedia_coord pos; + + pos.x = atoi(argv[4]); + pos.y = atoi(argv[5]); + pjsua_vid_win_set_pos(wid, &pos); + } else if (argc==6 && strcmp(argv[2], "resize")==0) { + pjsua_vid_win_id wid = atoi(argv[3]); + pjmedia_rect_size size; + + size.w = atoi(argv[4]); + size.h = atoi(argv[5]); + pjsua_vid_win_set_size(wid, &size); + } else + goto on_error; + } else if (strcmp(argv[1], "codec")==0) { + pjmedia_vid_codec_info ci[PJMEDIA_CODEC_MGR_MAX_CODECS]; + unsigned prio[PJMEDIA_CODEC_MGR_MAX_CODECS]; + unsigned count = PJMEDIA_CODEC_MGR_MAX_CODECS; + pj_status_t status; + + if (argc==3 && strcmp(argv[2], "list")==0) { + status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, ci, prio); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, "Error enumerating codecs")); + } else { + unsigned i; + PJ_LOG(3,(THIS_FILE, "Found %d video codecs:", count)); + PJ_LOG(3,(THIS_FILE, " PT Prio Name")); + PJ_LOG(3,(THIS_FILE, "-------------------------")); + for (i=0; imsg_info.msg, PJSIP_H_EXPIRES, NULL); + + h = rdata->msg_info.msg->hdr.next; + while (h != &rdata->msg_info.msg->hdr) { + if (h->type == PJSIP_H_CONTACT) { + const pjsip_contact_hdr *c = (const pjsip_contact_hdr*)h; + int e = c->expires; + + if (e < 0) { + if (exp) + e = exp->ivalue; + else + e = 3600; + } + + if (e > 0) { + pjsip_contact_hdr *nc = pjsip_hdr_clone(tdata->pool, h); + nc->expires = e; + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)nc); + ++cnt; + } + } + h = h->next; + } + + srv = pjsip_generic_string_hdr_create(tdata->pool, NULL, NULL); + srv->name = pj_str("Server"); + srv->hvalue = pj_str("pjsua simple registrar"); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)srv); + + pjsip_endpt_send_response2(pjsua_get_pjsip_endpt(), + rdata, tdata, NULL, NULL); +} + + + /***************************************************************************** * A simple module to handle otherwise unhandled request. We will register * this with the lowest priority. @@ -4524,10 +4992,18 @@ static pj_bool_t default_mod_on_rx_request(pjsip_rx_data *rdata) pj_status_t status; /* Don't respond to ACK! */ - if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, + if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_ack_method) == 0) return PJ_TRUE; + /* Simple registrar */ + if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, + &pjsip_register_method) == 0) + { + simple_registrar(rdata); + return PJ_TRUE; + } + /* Create basic response. */ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_notify_method) == 0) @@ -4651,6 +5127,8 @@ pj_status_t app_init(int argc, char *argv[]) app_config.cfg.cb.on_mwi_info = &on_mwi_info; app_config.cfg.cb.on_transport_state = &on_transport_state; app_config.cfg.cb.on_ice_transport_error = &on_ice_transport_error; + app_config.cfg.cb.on_snd_dev_operation = &on_snd_dev_operation; + app_config.cfg.cb.on_call_media_event = &on_call_media_event; app_config.log_cfg.cb = log_cb; /* Set sound device latency */ @@ -4929,6 +5407,7 @@ pj_status_t app_init(int argc, char *argv[]) /* Add accounts */ for (i=0; iinfo.samples_per_frame; + samples_per_frame = PJMEDIA_PIA_SPF(&wav->info); + clock_rate = PJMEDIA_PIA_SRATE(&wav->info); frm.buf = pj_pool_alloc(pool, samples_per_frame * 2); frm.size = samples_per_frame * 2; len = pjmedia_wav_player_get_len(wav); @@ -677,7 +678,7 @@ static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav, read += samples_per_frame; } - if (read < 2 * wav->info.clock_rate) { + if (read < 2 * clock_rate) { systest_perror("The WAV file is too short", PJ_SUCCESS); return -1; } @@ -685,12 +686,12 @@ static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav, /* Zero the first 500ms to remove loud click noises * (keypad press, etc.) */ - pjmedia_zero_samples(buf, wav->info.clock_rate / 2); + pjmedia_zero_samples(buf, clock_rate / 2); /* Loop to calculate latency */ start_pos = 0; first = PJ_TRUE; - while (start_pos < len/2 - wav->info.clock_rate) { + while (start_pos < len/2 - clock_rate) { int max_signal = 0; unsigned max_signal_pos = start_pos; unsigned max_echo_pos = 0; @@ -698,7 +699,7 @@ static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav, unsigned lat; /* Get the largest signal in the next 0.7s */ - for (i=start_pos; iinfo.clock_rate * 700 / 1000; ++i) { + for (i=start_pos; i max_signal) { max_signal = abs(buf[i]); max_signal_pos = i; @@ -706,24 +707,24 @@ static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav, } /* Advance 10ms from max_signal_pos */ - pos = max_signal_pos + 10 * wav->info.clock_rate / 1000; + pos = max_signal_pos + 10 * clock_rate / 1000; /* Get the largest signal in the next 800ms */ max_signal = 0; max_echo_pos = pos; - for (i=pos; iinfo.clock_rate * 8 / 10; ++i) { + for (i=pos; i max_signal) { max_signal = abs(buf[i]); max_echo_pos = i; } } - lat = (max_echo_pos - max_signal_pos) * 1000 / wav->info.clock_rate; + lat = (max_echo_pos - max_signal_pos) * 1000 / clock_rate; #if 0 PJ_LOG(4,(THIS_FILE, "Signal at %dms, echo at %d ms, latency %d ms", - max_signal_pos * 1000 / wav->info.clock_rate, - max_echo_pos * 1000 / wav->info.clock_rate, + max_signal_pos * 1000 / clock_rate, + max_echo_pos * 1000 / clock_rate, lat)); #endif @@ -736,10 +737,10 @@ static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav, /* Advance next loop */ if (first) { - start_pos = max_signal_pos + wav->info.clock_rate * 9 / 10; + start_pos = max_signal_pos + clock_rate * 9 / 10; first = PJ_FALSE; } else { - start_pos += wav->info.clock_rate; + start_pos += clock_rate; } } diff --git a/pjsip-apps/src/samples/aectest.c b/pjsip-apps/src/samples/aectest.c index 518a3e26..4168f698 100644 --- a/pjsip-apps/src/samples/aectest.c +++ b/pjsip-apps/src/samples/aectest.c @@ -197,23 +197,23 @@ int main(int argc, char *argv[]) } /* play and rec WAVs must have the same clock rate */ - if (wav_play->info.clock_rate != wav_rec->info.clock_rate) { + if (PJMEDIA_PIA_SRATE(&wav_play->info) != PJMEDIA_PIA_SRATE(&wav_rec->info)) { puts("Error: clock rate mismatch in the WAV files"); return 1; } /* .. and channel count */ - if (wav_play->info.channel_count != wav_rec->info.channel_count) { + if (PJMEDIA_PIA_CCNT(&wav_play->info) != PJMEDIA_PIA_CCNT(&wav_rec->info)) { puts("Error: clock rate mismatch in the WAV files"); return 1; } /* Create output wav */ status = pjmedia_wav_writer_port_create(pool, argv[pj_optind+2], - wav_play->info.clock_rate, - wav_play->info.channel_count, - wav_play->info.samples_per_frame, - wav_play->info.bits_per_sample, + PJMEDIA_PIA_SRATE(&wav_play->info), + PJMEDIA_PIA_CCNT(&wav_play->info), + PJMEDIA_PIA_SPF(&wav_play->info), + PJMEDIA_PIA_BITS(&wav_play->info), 0, 0, &wav_out); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error opening output WAV file", status); @@ -221,9 +221,9 @@ int main(int argc, char *argv[]) } /* Create echo canceller */ - status = pjmedia_echo_create2(pool, wav_play->info.clock_rate, - wav_play->info.channel_count, - wav_play->info.samples_per_frame, + status = pjmedia_echo_create2(pool, PJMEDIA_PIA_SRATE(&wav_play->info), + PJMEDIA_PIA_CCNT(&wav_play->info), + PJMEDIA_PIA_SPF(&wav_play->info), tail_ms, latency_ms, opt, &ec); if (status != PJ_SUCCESS) { @@ -233,19 +233,19 @@ int main(int argc, char *argv[]) /* Processing loop */ - play_frame.buf = pj_pool_alloc(pool, wav_play->info.samples_per_frame<<1); - rec_frame.buf = pj_pool_alloc(pool, wav_play->info.samples_per_frame<<1); + play_frame.buf = pj_pool_alloc(pool, PJMEDIA_PIA_SPF(&wav_play->info)<<1); + rec_frame.buf = pj_pool_alloc(pool, PJMEDIA_PIA_SPF(&wav_play->info)<<1); pj_get_timestamp(&t0); for (i=0; i < repeat; ++i) { for (;;) { - play_frame.size = wav_play->info.samples_per_frame << 1; + play_frame.size = PJMEDIA_PIA_SPF(&wav_play->info) << 1; status = pjmedia_port_get_frame(wav_play, &play_frame); if (status != PJ_SUCCESS) break; status = pjmedia_echo_playback(ec, (short*)play_frame.buf); - rec_frame.size = wav_play->info.samples_per_frame << 1; + rec_frame.size = PJMEDIA_PIA_SPF(&wav_play->info) << 1; status = pjmedia_port_get_frame(wav_rec, &rec_frame); if (status != PJ_SUCCESS) break; @@ -264,7 +264,7 @@ int main(int argc, char *argv[]) pj_get_timestamp(&t1); i = pjmedia_wav_writer_port_get_pos(wav_out) / sizeof(pj_int16_t) * 1000 / - (wav_out->info.clock_rate * wav_out->info.channel_count); + (PJMEDIA_PIA_SRATE(&wav_out->info) * PJMEDIA_PIA_CCNT(&wav_out->info)); PJ_LOG(3,(THIS_FILE, "Processed %3d.%03ds audio", i / 1000, i % 1000)); PJ_LOG(3,(THIS_FILE, "Completed in %u msec\n", pj_elapsed_msec(&t0, &t1))); diff --git a/pjsip-apps/src/samples/auddemo.c b/pjsip-apps/src/samples/auddemo.c index 3c97b0db..48bcad4e 100644 --- a/pjsip-apps/src/samples/auddemo.c +++ b/pjsip-apps/src/samples/auddemo.c @@ -144,7 +144,7 @@ static void show_dev_info(unsigned index) strcat(formats, "unknown/"); break; } - sprintf(bitrate, "%u", info.ext_fmt[i].bitrate); + sprintf(bitrate, "%u", info.ext_fmt[i].det.aud.avg_bps); strcat(formats, bitrate); strcat(formats, " "); } @@ -276,10 +276,10 @@ static void record(unsigned rec_index, const char *filename) } param.dir = PJMEDIA_DIR_CAPTURE; - param.clock_rate = wav->info.clock_rate; - param.samples_per_frame = wav->info.samples_per_frame; - param.channel_count = wav->info.channel_count; - param.bits_per_sample = wav->info.bits_per_sample; + param.clock_rate = PJMEDIA_PIA_SRATE(&wav->info); + param.samples_per_frame = PJMEDIA_PIA_SPF(&wav->info); + param.channel_count = PJMEDIA_PIA_CCNT(&wav->info); + param.bits_per_sample = PJMEDIA_PIA_BITS(&wav->info); status = pjmedia_aud_stream_create(¶m, &wav_rec_cb, NULL, wav, &strm); @@ -343,10 +343,10 @@ static void play_file(unsigned play_index, const char *filename) } param.dir = PJMEDIA_DIR_PLAYBACK; - param.clock_rate = wav->info.clock_rate; - param.samples_per_frame = wav->info.samples_per_frame; - param.channel_count = wav->info.channel_count; - param.bits_per_sample = wav->info.bits_per_sample; + param.clock_rate = PJMEDIA_PIA_SRATE(&wav->info); + param.samples_per_frame = PJMEDIA_PIA_SPF(&wav->info); + param.channel_count = PJMEDIA_PIA_CCNT(&wav->info); + param.bits_per_sample = PJMEDIA_PIA_BITS(&wav->info); status = pjmedia_aud_stream_create(¶m, NULL, &wav_play_cb, wav, &strm); diff --git a/pjsip-apps/src/samples/aviplay.c b/pjsip-apps/src/samples/aviplay.c new file mode 100644 index 00000000..a6602ba1 --- /dev/null +++ b/pjsip-apps/src/samples/aviplay.c @@ -0,0 +1,531 @@ +/* $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 +#include +#include +#include +#include +#include +#include + +#include "util.h" + +/** + * \page page_pjmedia_samples_aviplay_c Samples: Playing AVI File to + * Video and Sound Devices + * + * This is a very simple example to use the @ref PJMEDIA_FILE_PLAY, + * @ref PJMED_SND_PORT, and @ref PJMEDIA_VID_PORT. In this example, we + * open the file, video, and sound devices, then connect the file to both + * video and sound devices to play the contents of the file. + * + * + * This file is pjsip-apps/src/samples/aviplay.c + * + * \includelineno aviplay.c + */ + + +/* + * aviplay.c + * + * PURPOSE: + * Play a AVI file to video and sound devices. + * + * USAGE: + * aviplay FILE.AVI + */ + + +/* For logging purpose. */ +#define THIS_FILE "aviplay.c" + +static const char *desc = +" FILE \n" +" \n" +" aviplay.c \n" +" \n" +" PURPOSE \n" +" \n" +" Demonstrate how to play a AVI file. \n" +" \n" +" USAGE \n" +" \n" +" aviplay FILE.AVI \n"; + +struct codec_fmt { + pj_uint32_t pjmedia_id; + const char *codec_id; + /* Do we need to convert the decoded frame? */ + pj_bool_t need_conversion; + /* If conversion is needed, dst_fmt indicates the destination format */ + pjmedia_format_id dst_fmt; +} codec_fmts[] = {{PJMEDIA_FORMAT_MJPEG, "mjpeg", + PJ_TRUE , PJMEDIA_FORMAT_I420}, + {PJMEDIA_FORMAT_H263 , "h263" , + PJ_FALSE, 0}, + {PJMEDIA_FORMAT_XVID , "xvid"}, + }; + +typedef struct avi_port_t +{ + pjmedia_vid_port *vid_port; + pjmedia_snd_port *snd_port; + pj_bool_t is_running; + pj_bool_t is_quitting; +} avi_port_t; + +typedef struct codec_port_data_t +{ + pjmedia_vid_codec *codec; + pjmedia_port *src_port; + pj_uint8_t *enc_buf; + pj_size_t enc_buf_size; + + pjmedia_converter *conv; +} codec_port_data_t; + +static pj_status_t avi_event_cb(pjmedia_event_subscription *esub, + pjmedia_event *event) +{ + avi_port_t *ap = (avi_port_t *)esub->user_data; + + switch (event->type) { + case PJMEDIA_EVENT_WND_CLOSED: + ap->is_quitting = PJ_TRUE; + break; + case PJMEDIA_EVENT_MOUSE_BTN_DOWN: + if (ap->is_running) { + pjmedia_vid_port_stop(ap->vid_port); + if (ap->snd_port) + pjmedia_aud_stream_stop( + pjmedia_snd_port_get_snd_stream(ap->snd_port)); + } else { + pjmedia_vid_port_start(ap->vid_port); + if (ap->snd_port) + pjmedia_aud_stream_start( + pjmedia_snd_port_get_snd_stream(ap->snd_port)); + } + ap->is_running = !ap->is_running; + break; + default: + return PJ_SUCCESS; + } + + /* We handled the event on our own, so return non-PJ_SUCCESS here */ + return -1; +} + +static pj_status_t codec_get_frame(pjmedia_port *port, + pjmedia_frame *frame) +{ + codec_port_data_t *port_data = (codec_port_data_t*)port->port_data.pdata; + pjmedia_vid_codec *codec = port_data->codec; + pjmedia_frame enc_frame; + pj_status_t status; + + enc_frame.buf = port_data->enc_buf; + enc_frame.size = port_data->enc_buf_size; + + if (port_data->conv) { + pj_size_t frame_size = frame->size; + + status = pjmedia_port_get_frame(port_data->src_port, frame); + if (status != PJ_SUCCESS) goto on_error; + + status = pjmedia_vid_codec_decode(codec, frame, frame->size, &enc_frame); + if (status != PJ_SUCCESS) goto on_error; + + frame->size = frame_size; + status = pjmedia_converter_convert(port_data->conv, &enc_frame, frame); + if (status != PJ_SUCCESS) goto on_error; + + return PJ_SUCCESS; + } + + status = pjmedia_port_get_frame(port_data->src_port, &enc_frame); + if (status != PJ_SUCCESS) goto on_error; + + status = pjmedia_vid_codec_decode(codec, &enc_frame, frame->size, frame); + if (status != PJ_SUCCESS) goto on_error; + + return PJ_SUCCESS; + +on_error: + pj_perror(3, THIS_FILE, status, "codec_get_frame() error"); + return status; +} + +static int aviplay(pj_pool_t *pool, const char *fname) +{ + pjmedia_vid_port *renderer=NULL; + pjmedia_vid_port_param param; + const pjmedia_video_format_info *vfi; + pjmedia_video_format_detail *vfd; + pjmedia_snd_port *snd_port = NULL; + pj_status_t status; + int rc = 0; + pjmedia_avi_streams *avi_streams; + pjmedia_avi_stream *vid_stream, *aud_stream; + pjmedia_port *vid_port = NULL, *aud_port = NULL; + pjmedia_vid_codec *codec=NULL; + pjmedia_event_subscription esub; + avi_port_t avi_port; + + pj_bzero(&avi_port, sizeof(avi_port)); + + status = pjmedia_avi_player_create_streams(pool, fname, 0, &avi_streams); + if (status != PJ_SUCCESS) { + PJ_PERROR(2,("", status, " Error playing %s", fname)); + rc = 210; goto on_return; + } + + vid_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams, + 0, + PJMEDIA_TYPE_VIDEO); + vid_port = pjmedia_avi_stream_get_port(vid_stream); + + if (vid_port) { + pjmedia_vid_port_param_default(¶m); + + status = pjmedia_vid_dev_default_param(pool, + PJMEDIA_VID_DEFAULT_RENDER_DEV, + ¶m.vidparam); + if (status != PJ_SUCCESS) { + rc = 220; goto on_return; + } + + /* Create renderer, set it to active */ + param.active = PJ_TRUE; + param.vidparam.dir = PJMEDIA_DIR_RENDER; + vfd = pjmedia_format_get_video_format_detail(&vid_port->info.fmt, + PJ_TRUE); + pjmedia_format_init_video(¶m.vidparam.fmt, + vid_port->info.fmt.id, + vfd->size.w, vfd->size.h, + vfd->fps.num, vfd->fps.denum); + + vfi = pjmedia_get_video_format_info( + pjmedia_video_format_mgr_instance(), + vid_port->info.fmt.id); + /* Check whether the frame is encoded */ + if (!vfi || vfi->bpp == 0) { + /* Yes, prepare codec */ + pj_str_t codec_id_st; + unsigned info_cnt = 1, i, k; + const pjmedia_vid_codec_info *codec_info; + pj_str_t port_name = {"codec", 5}; + pj_uint8_t *enc_buf = NULL; + pj_size_t enc_buf_size = 0; + pjmedia_vid_dev_info rdr_info; + pjmedia_port codec_port; + codec_port_data_t codec_port_data; + pjmedia_vid_codec_param codec_param; + struct codec_fmt *codecp = NULL; + + /* Lookup codec */ + for (i = 0; i < sizeof(codec_fmts)/sizeof(codec_fmts[0]); i++) { + if (vid_port->info.fmt.id == codec_fmts[i].pjmedia_id) { + codecp = &codec_fmts[i]; + break; + } + } + if (!codecp) { + rc = 242; goto on_return; + } + pj_cstr(&codec_id_st, codecp->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; + } + + pjmedia_vid_dev_get_info(param.vidparam.rend_id, &rdr_info); + for (i=0; idec_fmt_id_cnt; ++i) { + for (k=0; kdec_fmt_id[i]==(int)rdr_info.fmt[k].id) + { + param.vidparam.fmt.id = codec_info->dec_fmt_id[i]; + } + } + } + + /* 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; + } + + pjmedia_format_copy(&codec_param.dec_fmt, ¶m.vidparam.fmt); + + status = pjmedia_vid_codec_open(codec, &codec_param); + if (status != PJ_SUCCESS) { + rc = 252; goto on_return; + } + + /* Alloc encoding buffer */ + enc_buf_size = codec_param.dec_fmt.det.vid.size.w * + codec_param.dec_fmt.det.vid.size.h * 4 + + 16; /*< padding, just in case */ + enc_buf = pj_pool_alloc(pool,enc_buf_size); + + /* 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; + } + pj_bzero(&codec_port_data, sizeof(codec_port_data)); + codec_port_data.codec = codec; + codec_port_data.src_port = vid_port; + codec_port_data.enc_buf = enc_buf; + codec_port_data.enc_buf_size = enc_buf_size; + + codec_port.get_frame = &codec_get_frame; + codec_port.port_data.pdata = &codec_port_data; + + /* Check whether we need to convert the decoded frame */ + if (codecp->need_conversion) { + pjmedia_conversion_param conv_param; + + pjmedia_format_copy(&conv_param.src, ¶m.vidparam.fmt); + pjmedia_format_copy(&conv_param.dst, ¶m.vidparam.fmt); + conv_param.dst.id = codecp->dst_fmt; + param.vidparam.fmt.id = conv_param.dst.id; + + status = pjmedia_converter_create(NULL, pool, &conv_param, + &codec_port_data.conv); + if (status != PJ_SUCCESS) { + rc = 270; goto on_return; + } + } + + status = pjmedia_vid_port_create(pool, ¶m, &renderer); + if (status != PJ_SUCCESS) { + rc = 230; goto on_return; + } + + status = pjmedia_vid_port_connect(renderer, &codec_port, + PJ_FALSE); + } else { + status = pjmedia_vid_port_create(pool, ¶m, &renderer); + if (status != PJ_SUCCESS) { + rc = 230; goto on_return; + } + + /* Connect avi port to renderer */ + status = pjmedia_vid_port_connect(renderer, vid_port, + PJ_FALSE); + } + + if (status != PJ_SUCCESS) { + rc = 240; goto on_return; + } + } + + aud_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams, + 0, + PJMEDIA_TYPE_AUDIO); + aud_port = pjmedia_avi_stream_get_port(aud_stream); + + if (aud_port) { + /* Create sound player port. */ + status = pjmedia_snd_port_create_player( + pool, /* pool */ + -1, /* use default dev. */ + PJMEDIA_PIA_SRATE(&aud_port->info),/* clock rate. */ + PJMEDIA_PIA_CCNT(&aud_port->info), /* # of channels. */ + PJMEDIA_PIA_SPF(&aud_port->info), /* samples per frame. */ + PJMEDIA_PIA_BITS(&aud_port->info), /* bits per sample. */ + 0, /* options */ + &snd_port /* returned port */ + ); + if (status != PJ_SUCCESS) { + rc = 310; goto on_return; + } + + /* Connect file port to the sound player. + * Stream playing will commence immediately. + */ + status = pjmedia_snd_port_connect(snd_port, aud_port); + if (status != PJ_SUCCESS) { + rc = 330; goto on_return; + } + } + + if (vid_port) { + pjmedia_vid_dev_cb cb; + + pj_bzero(&cb, sizeof(cb)); + avi_port.snd_port = snd_port; + avi_port.vid_port = renderer; + avi_port.is_running = PJ_TRUE; + pjmedia_vid_port_set_cb(renderer, &cb, &avi_port); + + /* subscribe events */ + pjmedia_event_subscription_init(&esub, &avi_event_cb, &avi_port); + pjmedia_event_subscribe( + pjmedia_vid_port_get_event_publisher(renderer), + &esub); + + if (snd_port) { + /* Synchronize video rendering and audio playback */ + pjmedia_vid_port_set_clock_src( + renderer, + pjmedia_snd_port_get_clock_src( + snd_port, PJMEDIA_DIR_PLAYBACK)); + } + + + /* Start video streaming.. */ + status = pjmedia_vid_port_start(renderer); + if (status != PJ_SUCCESS) { + rc = 270; goto on_return; + } + } + + while (!avi_port.is_quitting) { + pj_thread_sleep(100); + } + +on_return: + if (snd_port) { + pjmedia_snd_port_disconnect(snd_port); + /* Without this sleep, Windows/DirectSound will repeteadly + * play the last frame during destroy. + */ + pj_thread_sleep(100); + pjmedia_snd_port_destroy(snd_port); + } + if (renderer) + pjmedia_vid_port_destroy(renderer); + if (aud_port) + pjmedia_port_destroy(aud_port); + if (vid_port) + pjmedia_port_destroy(vid_port); + if (codec) { + pjmedia_vid_codec_close(codec); + pjmedia_vid_codec_mgr_dealloc_codec(NULL, codec); + } + + return rc; +} + + +static int main_func(int argc, char *argv[]) +{ + pj_caching_pool cp; + pj_pool_t *pool; + int rc = 0; + pj_status_t status = PJ_SUCCESS; + + if (argc != 2) { + puts("Error: filename required"); + puts(desc); + return 1; + } + + + /* Must init PJLIB first: */ + status = pj_init(); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + /* Must create a pool factory before we can allocate any memory. */ + pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); + + /* Create memory pool for our file player */ + pool = pj_pool_create( &cp.factory, /* pool factory */ + "AVI", /* pool name. */ + 4000, /* init size */ + 4000, /* increment size */ + NULL /* callback on error */ + ); + + pjmedia_video_format_mgr_create(pool, 64, 0, NULL); + pjmedia_converter_mgr_create(pool, NULL); + pjmedia_vid_codec_mgr_create(pool, NULL); + + status = pjmedia_vid_dev_subsys_init(&cp.factory); + if (status != PJ_SUCCESS) + goto on_return; + + status = pjmedia_aud_subsys_init(&cp.factory); + if (status != PJ_SUCCESS) { + goto on_return; + } + + status = pjmedia_codec_ffmpeg_init(NULL, &cp.factory); + if (status != PJ_SUCCESS) + goto on_return; + + rc = aviplay(pool, argv[1]); + + /* + * File should be playing and looping now + */ + + /* Without this sleep, Windows/DirectSound will repeteadly + * play the last frame during destroy. + */ + pj_thread_sleep(100); + +on_return: + pjmedia_codec_ffmpeg_deinit(); + pjmedia_aud_subsys_shutdown(); + pjmedia_vid_dev_subsys_shutdown(); + + 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()); + + /* Release application pool */ + pj_pool_release( pool ); + + /* Destroy pool factory */ + pj_caching_pool_destroy( &cp ); + + /* Shutdown PJLIB */ + pj_shutdown(); + + /* Done. */ + return 0; +} + +int main(int argc, char *argv[]) +{ + return pj_run_app(&main_func, argc, argv, 0); +} diff --git a/pjsip-apps/src/samples/confbench.c b/pjsip-apps/src/samples/confbench.c index 264947f9..db9cd55f 100644 --- a/pjsip-apps/src/samples/confbench.c +++ b/pjsip-apps/src/samples/confbench.c @@ -147,7 +147,7 @@ static pj_status_t sine_get_frame( pjmedia_port *port, unsigned i, count, left, right; /* Get number of samples */ - count = frame->size / 2 / port->info.channel_count; + count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info); left = 0; right = 0; @@ -156,7 +156,7 @@ static pj_status_t sine_get_frame( pjmedia_port *port, *samples++ = sine->samples[left]; ++left; - if (port->info.channel_count == 2) { + if (PJMEDIA_PIA_CCNT(&port->info) == 2) { *samples++ = sine->samples[right]; right += 2; /* higher pitch so we can distinguish left and right. */ if (right >= count) @@ -187,6 +187,7 @@ static pj_status_t create_sine_port(pj_pool_t *pool, pjmedia_port *port; unsigned i; unsigned count; + pj_str_t port_name; port_data *sine; PJ_ASSERT_RETURN(pool && channel_count > 0 && channel_count <= 2, @@ -196,17 +197,10 @@ static pj_status_t create_sine_port(pj_pool_t *pool, PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM); /* Fill in port info. */ - port->info.bits_per_sample = 16; - port->info.channel_count = channel_count; - port->info.encoding_name = pj_str("pcm"); - port->info.has_info = 1; - port->info.name = pj_str("sine generator"); - port->info.need_info = 0; - port->info.pt = 0xFF; - port->info.clock_rate = sampling_rate; - port->info.samples_per_frame = sampling_rate * SINE_PTIME / 1000 * channel_count; - port->info.bytes_per_frame = port->info.samples_per_frame * 2; - port->info.type = PJMEDIA_TYPE_AUDIO; + port_name = pj_str("sine generator"); + pjmedia_port_info_init(&port->info, &port_name, + 12345, sampling_rate, channel_count, 16, + sampling_rate * SINE_PTIME / 1000 * channel_count); /* Set the function to feed frame */ port->get_frame = &sine_get_frame; @@ -215,7 +209,7 @@ static pj_status_t create_sine_port(pj_pool_t *pool, port->port_data.pdata = sine = pj_pool_zalloc(pool, sizeof(port_data)); /* Create samples */ - count = port->info.samples_per_frame / channel_count; + count = PJMEDIA_PIA_SPF(&port->info) / channel_count; sine->samples = pj_pool_alloc(pool, count * sizeof(pj_int16_t)); PJ_ASSERT_RETURN(sine->samples != NULL, PJ_ENOMEM); diff --git a/pjsip-apps/src/samples/encdec.c b/pjsip-apps/src/samples/encdec.c index 0d38ce88..117497b9 100644 --- a/pjsip-apps/src/samples/encdec.c +++ b/pjsip-apps/src/samples/encdec.c @@ -137,8 +137,8 @@ static pj_status_t enc_dec_test(const char *codec_id, /* Alloc codec */ CHECK( pjmedia_codec_mgr_alloc_codec(cm, pci, &codec) ); - CHECK( codec->op->init(codec, pool) ); - CHECK( codec->op->open(codec, ¶m) ); + CHECK( pjmedia_codec_init(codec, pool) ); + CHECK( pjmedia_codec_open(codec, ¶m) ); for (;;) { pjmedia_frame frm_pcm, frm_bit, out_frm, frames[4]; @@ -162,7 +162,8 @@ static pj_status_t enc_dec_test(const char *codec_id, /* Encode */ frm_bit.buf = bitstream; frm_bit.size = sizeof(bitstream); - CHECK(codec->op->encode(codec, &frm_pcm, sizeof(bitstream), &frm_bit)); + CHECK(pjmedia_codec_encode(codec, &frm_pcm, sizeof(bitstream), + &frm_bit)); /* On DTX, write zero frame to wavout to maintain duration */ if (frm_bit.size == 0 || frm_bit.type != PJMEDIA_FRAME_TYPE_AUDIO) { @@ -180,8 +181,8 @@ static pj_status_t enc_dec_test(const char *codec_id, */ ts.u64 = 0; cnt = PJ_ARRAY_SIZE(frames); - CHECK( codec->op->parse(codec, bitstream, frm_bit.size, &ts, &cnt, - frames) ); + CHECK( pjmedia_codec_parse(codec, bitstream, frm_bit.size, &ts, &cnt, + frames) ); CHECK( (cnt==1 ? PJ_SUCCESS : -1) ); /* Decode or simulate packet loss */ @@ -190,11 +191,11 @@ static pj_status_t enc_dec_test(const char *codec_id, if ((pj_rand() % 100) < (int)lost_pct) { /* Simulate loss */ - CHECK( codec->op->recover(codec, sizeof(pcmbuf), &out_frm) ); + CHECK( pjmedia_codec_recover(codec, sizeof(pcmbuf), &out_frm) ); TRACE_((THIS_FILE, "%d.%03d Packet lost", T)); } else { /* Decode */ - CHECK( codec->op->decode(codec, &frames[0], sizeof(pcmbuf), + CHECK( pjmedia_codec_decode(codec, &frames[0], sizeof(pcmbuf), &out_frm) ); } @@ -210,7 +211,7 @@ static pj_status_t enc_dec_test(const char *codec_id, pjmedia_port_destroy(wavin); /* Close codec */ - codec->op->close(codec); + pjmedia_codec_close(codec); pjmedia_codec_mgr_dealloc_codec(cm, codec); /* Release pool */ @@ -238,21 +239,7 @@ int main(int argc, char *argv[]) CHECK( pjmedia_endpt_create(&cp.factory, NULL, 1, &mept) ); /* Register all codecs */ -#if PJMEDIA_HAS_G711_CODEC - CHECK( pjmedia_codec_g711_init(mept) ); -#endif -#if PJMEDIA_HAS_GSM_CODEC - CHECK( pjmedia_codec_gsm_init(mept) ); -#endif -#if PJMEDIA_HAS_ILBC_CODEC - CHECK( pjmedia_codec_ilbc_init(mept, 30) ); -#endif -#if PJMEDIA_HAS_SPEEX_CODEC - CHECK( pjmedia_codec_speex_init(mept, 0, 5, 5) ); -#endif -#if PJMEDIA_HAS_G722_CODEC - CHECK( pjmedia_codec_g722_init(mept) ); -#endif + CHECK( pjmedia_codec_register_audio_codecs(mept, NULL) ); pj_gettimeofday(&t0); status = enc_dec_test(argv[1], argv[2], argv[3]); diff --git a/pjsip-apps/src/samples/jbsim.c b/pjsip-apps/src/samples/jbsim.c index e023dd55..27b11284 100644 --- a/pjsip-apps/src/samples/jbsim.c +++ b/pjsip-apps/src/samples/jbsim.c @@ -472,31 +472,7 @@ static pj_status_t test_init(void) } /* Register codecs */ -#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC != 0 - pjmedia_codec_gsm_init(g_app.endpt); -#endif -#if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0 - pjmedia_codec_g711_init(g_app.endpt); -#endif -#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0 - pjmedia_codec_speex_init(g_app.endpt, 0, PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY, - PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY); -#endif -#if defined(PJMEDIA_HAS_G722_CODEC) && (PJMEDIA_HAS_G722_CODEC != 0) - pjmedia_codec_g722_init(g_app.endpt); -#endif -#if defined(PJMEDIA_HAS_ILBC_CODEC) && PJMEDIA_HAS_ILBC_CODEC != 0 - /* Init ILBC with mode=20 to make the losts occur at the same - * places as other codecs. - */ - pjmedia_codec_ilbc_init(g_app.endpt, 20); -#endif -#if defined(PJMEDIA_HAS_INTEL_IPP) && PJMEDIA_HAS_INTEL_IPP != 0 - pjmedia_codec_ipp_init(g_app.endpt); -#endif -#if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC != 0 - pjmedia_codec_l16_init(g_app.endpt, 0); -#endif + pjmedia_codec_register_audio_codecs(g_app.endpt, NULL); /* Create the loop transport */ status = pjmedia_transport_loop_create(g_app.endpt, &g_app.loop); @@ -530,8 +506,8 @@ static pj_status_t test_init(void) } /* Make sure stream and WAV parameters match */ - if (g_app.tx_wav->info.clock_rate != g_app.tx->port->info.clock_rate || - g_app.tx_wav->info.channel_count != g_app.tx->port->info.channel_count) + if (PJMEDIA_PIA_SRATE(&g_app.tx_wav->info) != PJMEDIA_PIA_SRATE(&g_app.tx->port->info) || + PJMEDIA_PIA_CCNT(&g_app.tx_wav->info) != PJMEDIA_PIA_CCNT(&g_app.tx->port->info)) { jbsim_perror("Error: Input WAV file has different clock rate " "or number of channels than the codec", PJ_SUCCESS); @@ -554,10 +530,10 @@ static pj_status_t test_init(void) /* Create receiver WAV */ status = pjmedia_wav_writer_port_create(g_app.pool, g_app.cfg.rx_wav_out, - g_app.rx->port->info.clock_rate, - g_app.rx->port->info.channel_count, - g_app.rx->port->info.samples_per_frame, - g_app.rx->port->info.bits_per_sample, + PJMEDIA_PIA_SRATE(&g_app.rx->port->info), + PJMEDIA_PIA_CCNT(&g_app.rx->port->info), + PJMEDIA_PIA_SPF(&g_app.rx->port->info), + PJMEDIA_PIA_BITS(&g_app.rx->port->info), 0, 0, &g_app.rx_wav); @@ -570,8 +546,8 @@ static pj_status_t test_init(void) /* Frame buffer */ g_app.framebuf = (pj_int16_t*) pj_pool_alloc(g_app.pool, - MAX(g_app.rx->port->info.samples_per_frame, - g_app.tx->port->info.samples_per_frame) * sizeof(pj_int16_t)); + MAX(PJMEDIA_PIA_SPF(&g_app.rx->port->info), + PJMEDIA_PIA_SPF(&g_app.tx->port->info)) * sizeof(pj_int16_t)); /* Set the receiver in the loop transport */ @@ -594,15 +570,15 @@ static void run_one_frame(pjmedia_port *src, pjmedia_port *dst, pj_bzero(&frame, sizeof(frame)); frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = g_app.framebuf; - frame.size = dst->info.samples_per_frame * 2; + frame.size = PJMEDIA_PIA_SPF(&dst->info) * 2; status = pjmedia_port_get_frame(src, &frame); pj_assert(status == PJ_SUCCESS); if (status!= PJ_SUCCESS || frame.type != PJMEDIA_FRAME_TYPE_AUDIO) { frame.buf = g_app.framebuf; - pjmedia_zero_samples(g_app.framebuf, src->info.samples_per_frame); - frame.size = src->info.samples_per_frame * 2; + pjmedia_zero_samples(g_app.framebuf, PJMEDIA_PIA_SPF(&src->info)); + frame.size = PJMEDIA_PIA_SPF(&src->info) * 2; if (has_frame) *has_frame = PJ_FALSE; } else { @@ -628,8 +604,8 @@ static void tx_tick(const pj_time_val *t) long pkt_interval; /* packet interval, without jitter */ - pkt_interval = port->info.samples_per_frame * 1000 / - port->info.clock_rate; + pkt_interval = PJMEDIA_PIA_SPF(&port->info) * 1000 / + PJMEDIA_PIA_SRATE(&port->info); while (PJ_TIME_VAL_GTE(*t, strm->state.tx.next_schedule)) { struct log_entry entry; @@ -777,8 +753,8 @@ static void rx_tick(const pj_time_val *t) pjmedia_port *port = g_app.rx->port; long pkt_interval; - pkt_interval = port->info.samples_per_frame * 1000 / - port->info.clock_rate * + pkt_interval = PJMEDIA_PIA_SPF(&port->info) * 1000 / + PJMEDIA_PIA_SRATE(&port->info) * g_app.cfg.rx_snd_burst; if (PJ_TIME_VAL_GTE(*t, strm->state.rx.next_schedule)) { diff --git a/pjsip-apps/src/samples/latency.c b/pjsip-apps/src/samples/latency.c index f2f0779e..297b80b7 100644 --- a/pjsip-apps/src/samples/latency.c +++ b/pjsip-apps/src/samples/latency.c @@ -60,7 +60,7 @@ static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav) lat_min = 10000, lat_max = 0; - samples_per_frame = wav->info.samples_per_frame; + samples_per_frame = PJMEDIA_PIA_SPF(&wav->info); frm.buf = pj_pool_alloc(pool, samples_per_frame * 2); frm.size = samples_per_frame * 2; len = pjmedia_wav_player_get_len(wav); @@ -76,13 +76,13 @@ static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav) read += samples_per_frame; } - if (read < 2 * wav->info.clock_rate) { + if (read < 2 * PJMEDIA_PIA_SRATE(&wav->info)) { puts("Error: too short"); return -1; } start_pos = 0; - while (start_pos < len/2 - wav->info.clock_rate) { + while (start_pos < len/2 - PJMEDIA_PIA_SRATE(&wav->info)) { int max_signal = 0; unsigned max_signal_pos = start_pos; unsigned max_echo_pos = 0; @@ -90,7 +90,7 @@ static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav) unsigned lat; /* Get the largest signal in the next 0.7s */ - for (i=start_pos; iinfo.clock_rate * 700 / 1000; ++i) { + for (i=start_pos; iinfo) * 700 / 1000; ++i) { if (abs(buf[i]) > max_signal) { max_signal = abs(buf[i]); max_signal_pos = i; @@ -98,19 +98,19 @@ static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav) } /* Advance 10ms from max_signal_pos */ - pos = max_signal_pos + 10 * wav->info.clock_rate / 1000; + pos = max_signal_pos + 10 * PJMEDIA_PIA_SRATE(&wav->info) / 1000; /* Get the largest signal in the next 500ms */ max_signal = 0; max_echo_pos = pos; - for (i=pos; iinfo.clock_rate/2; ++i) { + for (i=pos; iinfo)/2; ++i) { if (abs(buf[i]) > max_signal) { max_signal = abs(buf[i]); max_echo_pos = i; } } - lat = (max_echo_pos - max_signal_pos) * 1000 / wav->info.clock_rate; + lat = (max_echo_pos - max_signal_pos) * 1000 / PJMEDIA_PIA_SRATE(&wav->info); #if 0 printf("Latency = %u\n", lat); @@ -124,7 +124,7 @@ static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav) lat_max = lat; /* Advance next loop */ - start_pos += wav->info.clock_rate; + start_pos += PJMEDIA_PIA_SRATE(&wav->info); } printf("Latency average = %u\n", lat_sum / lat_cnt); diff --git a/pjsip-apps/src/samples/level.c b/pjsip-apps/src/samples/level.c index d4a5ae16..6bba3fe1 100644 --- a/pjsip-apps/src/samples/level.c +++ b/pjsip-apps/src/samples/level.c @@ -124,7 +124,7 @@ int main(int argc, char *argv[]) return 1; } - if (file_port->info.samples_per_frame > NSAMPLES) { + if (PJMEDIA_PIA_SPF(&file_port->info) > NSAMPLES) { app_perror(THIS_FILE, "WAV clock rate is too big", PJ_EINVAL); return 1; } @@ -145,11 +145,11 @@ int main(int argc, char *argv[]) pjmedia_port_get_frame(file_port, &frm); level32 = pjmedia_calc_avg_signal(framebuf, - file_port->info.samples_per_frame); + PJMEDIA_PIA_SPF(&file_port->info)); level = pjmedia_linear2ulaw(level32) ^ 0xFF; - ms = i * 1000 * file_port->info.samples_per_frame / - file_port->info.clock_rate; + ms = i * 1000 * PJMEDIA_PIA_SPF(&file_port->info) / + PJMEDIA_PIA_SRATE(&file_port->info); printf("%03d.%03d\t%7d\t%7d\n", ms/1000, ms%1000, level, level32); } diff --git a/pjsip-apps/src/samples/mix.c b/pjsip-apps/src/samples/mix.c index 513e5466..cc139f76 100644 --- a/pjsip-apps/src/samples/mix.c +++ b/pjsip-apps/src/samples/mix.c @@ -181,7 +181,7 @@ int main(int argc, char *argv[]) &wav_input[i].port) ); len = pjmedia_wav_player_get_len(wav_input[i].port); len = (pj_ssize_t)(len * 1.0 * clock_rate / - wav_input[i].port->info.clock_rate); + PJMEDIA_PIA_SRATE(&wav_input[i].port->info)); if (len > (pj_ssize_t)longest) longest = len; @@ -199,7 +199,7 @@ int main(int argc, char *argv[]) pjmedia_frame frame; frame.buf = framebuf; - frame.size = cp->info.samples_per_frame * 2; + frame.size = PJMEDIA_PIA_SPF(&cp->info) * 2; pj_assert(frame.size <= sizeof(framebuf)); CHECK( pjmedia_port_get_frame(cp, &frame) ); diff --git a/pjsip-apps/src/samples/pcaputil.c b/pjsip-apps/src/samples/pcaputil.c index d565bd63..1f6efb27 100644 --- a/pjsip-apps/src/samples/pcaputil.c +++ b/pjsip-apps/src/samples/pcaputil.c @@ -87,7 +87,7 @@ static void err_exit(const char *title, pj_status_t status) pj_ssize_t pos = pjmedia_wav_writer_port_get_pos(app.wav); if (pos >= 0) { unsigned msec; - msec = pos / 2 * 1000 / app.wav->info.clock_rate; + msec = pos / 2 * 1000 / PJMEDIA_PIA_SRATE(&app.wav->info); printf("Written: %dm:%02ds.%03d\n", msec / 1000 / 60, (msec / 1000) % 60, @@ -98,7 +98,7 @@ static void err_exit(const char *title, pj_status_t status) if (app.pcap) pj_pcap_close(app.pcap); if (app.codec) { pjmedia_codec_mgr *cmgr; - app.codec->op->close(app.codec); + pjmedia_codec_close(app.codec); cmgr = pjmedia_endpt_get_codec_mgr(app.mept); pjmedia_codec_mgr_dealloc_codec(cmgr, app.codec); } @@ -229,29 +229,7 @@ static void pcap2wav(const char *wav_filename, const pj_str_t *srtp_crypto, pj_status_t status; /* Initialize all codecs */ -#if PJMEDIA_HAS_SPEEX_CODEC - T( pjmedia_codec_speex_init(app.mept, 0, 10, 10) ); -#endif /* PJMEDIA_HAS_SPEEX_CODEC */ - -#if PJMEDIA_HAS_ILBC_CODEC - T( pjmedia_codec_ilbc_init(app.mept, 30) ); -#endif /* PJMEDIA_HAS_ILBC_CODEC */ - -#if PJMEDIA_HAS_GSM_CODEC - T( pjmedia_codec_gsm_init(app.mept) ); -#endif /* PJMEDIA_HAS_GSM_CODEC */ - -#if PJMEDIA_HAS_G711_CODEC - T( pjmedia_codec_g711_init(app.mept) ); -#endif /* PJMEDIA_HAS_G711_CODEC */ - -#if PJMEDIA_HAS_G722_CODEC - T( pjmedia_codec_g722_init(app.mept) ); -#endif /* PJMEDIA_HAS_G722_CODEC */ - -#if PJMEDIA_HAS_L16_CODEC - T( pjmedia_codec_l16_init(app.mept, 0) ); -#endif /* PJMEDIA_HAS_L16_CODEC */ + T( pjmedia_codec_register_audio_codecs(app.mept, NULL) ); /* Create SRTP transport is needed */ #if PJMEDIA_HAS_SRTP @@ -282,8 +260,8 @@ static void pcap2wav(const char *wav_filename, const pj_str_t *srtp_crypto, /* Alloc and init codec */ T( pjmedia_codec_mgr_alloc_codec(cmgr, ci, &app.codec) ); - T( app.codec->op->init(app.codec, app.pool) ); - T( app.codec->op->open(app.codec, ¶m) ); + T( pjmedia_codec_init(app.codec, app.pool) ); + T( pjmedia_codec_open(app.codec, ¶m) ); /* Open WAV file */ samples_per_frame = ci->clock_rate * param.info.frm_ptime / 1000; @@ -307,7 +285,7 @@ static void pcap2wav(const char *wav_filename, const pj_str_t *srtp_crypto, /* Parse first packet */ ts.u64 = 0; frame_cnt = PJ_ARRAY_SIZE(frames); - T( app.codec->op->parse(app.codec, pkt0.payload, pkt0.payload_len, + T( pjmedia_codec_parse(app.codec, pkt0.payload, pkt0.payload_len, &ts, &frame_cnt, frames) ); /* Decode and write to WAV file */ @@ -318,7 +296,7 @@ static void pcap2wav(const char *wav_filename, const pj_str_t *srtp_crypto, pcm_frame.buf = pcm; pcm_frame.size = samples_per_frame * 2; - T( app.codec->op->decode(app.codec, &frames[i], pcm_frame.size, + T( pjmedia_codec_decode(app.codec, &frames[i], pcm_frame.size, &pcm_frame) ); T( pjmedia_port_put_frame(app.wav, &pcm_frame) ); samples_cnt += samples_per_frame; @@ -337,8 +315,8 @@ static void pcap2wav(const char *wav_filename, const pj_str_t *srtp_crypto, pcm_frame.size = samples_per_frame * 2; if (app.codec->op->recover) { - T( app.codec->op->recover(app.codec, pcm_frame.size, - &pcm_frame) ); + T( pjmedia_codec_recover(app.codec, pcm_frame.size, + &pcm_frame) ); } else { pj_bzero(pcm_frame.buf, pcm_frame.size); } diff --git a/pjsip-apps/src/samples/pjsip-perf.c b/pjsip-apps/src/samples/pjsip-perf.c index 9c8fec3a..c462cf84 100644 --- a/pjsip-apps/src/samples/pjsip-perf.c +++ b/pjsip-apps/src/samples/pjsip-perf.c @@ -928,18 +928,7 @@ static pj_status_t init_media() /* Must register all codecs to be supported */ -#if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0 - pjmedia_codec_g711_init(app.med_endpt); -#endif -#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC!=0 - pjmedia_codec_gsm_init(app.med_endpt); -#endif -#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0 - pjmedia_codec_speex_init(app.med_endpt, PJMEDIA_SPEEX_NO_UWB, 3, 3); -#endif -#if defined(PJMEDIA_HAS_G722_CODEC) && PJMEDIA_HAS_G722_CODEC!=0 - pjmedia_codec_g722_init(app.med_endpt); -#endif + pjmedia_codec_register_audio_codecs(app.med_endpt, NULL); /* Init dummy socket addresses */ app.skinfo_cnt = 0; diff --git a/pjsip-apps/src/samples/playfile.c b/pjsip-apps/src/samples/playfile.c index 98e80826..10116ffa 100644 --- a/pjsip-apps/src/samples/playfile.c +++ b/pjsip-apps/src/samples/playfile.c @@ -139,10 +139,10 @@ int main(int argc, char *argv[]) status = pjmedia_snd_port_create_player( pool, /* pool */ -1, /* use default dev. */ - file_port->info.clock_rate, /* clock rate. */ - file_port->info.channel_count, /* # of channels. */ - file_port->info.samples_per_frame, /* samples per frame. */ - file_port->info.bits_per_sample, /* bits per sample. */ + PJMEDIA_PIA_SRATE(&file_port->info),/* clock rate. */ + PJMEDIA_PIA_CCNT(&file_port->info),/* # of channels. */ + PJMEDIA_PIA_SPF(&file_port->info), /* samples per frame. */ + PJMEDIA_PIA_BITS(&file_port->info),/* bits per sample. */ 0, /* options */ &snd_port /* returned port */ ); diff --git a/pjsip-apps/src/samples/playsine.c b/pjsip-apps/src/samples/playsine.c index a1370c24..00cabe47 100644 --- a/pjsip-apps/src/samples/playsine.c +++ b/pjsip-apps/src/samples/playsine.c @@ -86,7 +86,7 @@ static pj_status_t sine_get_frame( pjmedia_port *port, unsigned i, count, left, right; /* Get number of samples */ - count = frame->size / 2 / port->info.channel_count; + count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info); left = 0; right = 0; @@ -95,7 +95,7 @@ static pj_status_t sine_get_frame( pjmedia_port *port, *samples++ = sine->samples[left]; ++left; - if (port->info.channel_count == 2) { + if (PJMEDIA_PIA_CCNT(&port->info) == 2) { *samples++ = sine->samples[right]; right += 2; /* higher pitch so we can distinguish left and right. */ if (right >= count) @@ -126,6 +126,7 @@ static pj_status_t create_sine_port(pj_pool_t *pool, pjmedia_port *port; unsigned i; unsigned count; + pj_str_t name; port_data *sine; PJ_ASSERT_RETURN(pool && channel_count > 0 && channel_count <= 2, @@ -135,17 +136,12 @@ static pj_status_t create_sine_port(pj_pool_t *pool, PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM); /* Fill in port info. */ - port->info.bits_per_sample = 16; - port->info.channel_count = channel_count; - port->info.encoding_name = pj_str("pcm"); - port->info.has_info = 1; - port->info.name = pj_str("sine generator"); - port->info.need_info = 0; - port->info.pt = 0xFF; - port->info.clock_rate = sampling_rate; - port->info.samples_per_frame = sampling_rate * 20 / 1000 * channel_count; - port->info.bytes_per_frame = port->info.samples_per_frame * 2; - port->info.type = PJMEDIA_TYPE_AUDIO; + name = pj_str("sine generator"); + pjmedia_port_info_init(&port->info, &name, + PJMEDIA_SIG_CLASS_PORT_AUD('s', 'i'), + sampling_rate, + channel_count, + 16, sampling_rate * 20 / 1000 * channel_count); /* Set the function to feed frame */ port->get_frame = &sine_get_frame; @@ -154,7 +150,7 @@ static pj_status_t create_sine_port(pj_pool_t *pool, port->port_data.pdata = sine = pj_pool_zalloc(pool, sizeof(port_data)); /* Create samples */ - count = port->info.samples_per_frame / channel_count; + count = PJMEDIA_PIA_SPF(&port->info) / channel_count; sine->samples = pj_pool_alloc(pool, count * sizeof(pj_int16_t)); PJ_ASSERT_RETURN(sine->samples != NULL, PJ_ENOMEM); @@ -244,10 +240,10 @@ int main(int argc, char *argv[]) status = pjmedia_snd_port_create_player( pool, /* pool */ -1, /* use default dev. */ - sine_port->info.clock_rate, /* clock rate. */ - sine_port->info.channel_count, /* # of channels. */ - sine_port->info.samples_per_frame, /* samples per frame. */ - sine_port->info.bits_per_sample, /* bits per sample. */ + PJMEDIA_PIA_SRATE(&sine_port->info),/* clock rate. */ + PJMEDIA_PIA_CCNT(&sine_port->info),/* # of channels. */ + PJMEDIA_PIA_SPF(&sine_port->info), /* samples per frame. */ + PJMEDIA_PIA_BITS(&sine_port->info),/* bits per sample. */ 0, /* options */ &snd_port /* returned port */ ); diff --git a/pjsip-apps/src/samples/recfile.c b/pjsip-apps/src/samples/recfile.c index bd38bd80..60ca4e4f 100644 --- a/pjsip-apps/src/samples/recfile.c +++ b/pjsip-apps/src/samples/recfile.c @@ -134,10 +134,10 @@ int main(int argc, char *argv[]) status = pjmedia_snd_port_create_rec( pool, /* pool */ -1, /* use default dev. */ - file_port->info.clock_rate, /* clock rate. */ - file_port->info.channel_count, /* # of channels. */ - file_port->info.samples_per_frame, /* samples per frame. */ - file_port->info.bits_per_sample, /* bits per sample. */ + PJMEDIA_PIA_SRATE(&file_port->info),/* clock rate. */ + PJMEDIA_PIA_CCNT(&file_port->info),/* # of channels. */ + PJMEDIA_PIA_SPF(&file_port->info), /* samples per frame. */ + PJMEDIA_PIA_BITS(&file_port->info),/* bits per sample. */ 0, /* options */ &snd_port /* returned port */ ); diff --git a/pjsip-apps/src/samples/resampleplay.c b/pjsip-apps/src/samples/resampleplay.c index 2132d618..5a40e308 100644 --- a/pjsip-apps/src/samples/resampleplay.c +++ b/pjsip-apps/src/samples/resampleplay.c @@ -129,7 +129,7 @@ int main(int argc, char *argv[]) } /* File must have same number of channels. */ - if (file_port->info.channel_count != (unsigned)channel_count) { + if (PJMEDIA_PIA_CCNT(&file_port->info) != (unsigned)channel_count) { PJ_LOG(3,(THIS_FILE, "Error: file has different number of channels. " "Perhaps you'd need -c option?")); pjmedia_port_destroy(file_port); @@ -186,7 +186,8 @@ int main(int argc, char *argv[]) printf("Playing %s at sampling rate %d (original file sampling rate=%d)\n", - argv[pj_optind], sampling_rate, file_port->info.clock_rate); + argv[pj_optind], sampling_rate, + PJMEDIA_PIA_SRATE(&file_port->info)); puts(""); puts("Press to stop playing and quit"); diff --git a/pjsip-apps/src/samples/simpleua.c b/pjsip-apps/src/samples/simpleua.c index 38b13923..2ec49d37 100644 --- a/pjsip-apps/src/samples/simpleua.c +++ b/pjsip-apps/src/samples/simpleua.c @@ -67,11 +67,19 @@ /* Settings */ -#define AF pj_AF_INET() /* Change to pj_AF_INET6() for IPv6. +#define AF pj_AF_INET() /* Change to pj_AF_INET6() for IPv6. * PJ_HAS_IPV6 must be enabled and * your system must support IPv6. */ -#define SIP_PORT 5060 /* Listening SIP port */ -#define RTP_PORT 4000 /* RTP port */ +#if 0 +#define SIP_PORT 5080 /* Listening SIP port */ +#define RTP_PORT 5000 /* RTP port */ +#else +#define SIP_PORT 5060 /* Listening SIP port */ +#define RTP_PORT 4000 /* RTP port */ +#endif + +#define MAX_MEDIA_CNT 2 /* Media count, set to 1 for audio + * only or 2 for audio and video */ /* * Static variables. @@ -82,15 +90,24 @@ static pjsip_endpoint *g_endpt; /* SIP endpoint. */ static pj_caching_pool cp; /* Global pool factory. */ static pjmedia_endpt *g_med_endpt; /* Media endpoint. */ -static pjmedia_transport_info g_med_tpinfo; /* Socket info for media */ -static pjmedia_transport *g_med_transport;/* Media stream transport */ + +static pjmedia_transport_info g_med_tpinfo[MAX_MEDIA_CNT]; + /* Socket info for media */ +static pjmedia_transport *g_med_transport[MAX_MEDIA_CNT]; + /* Media stream transport */ +static pjmedia_sock_info g_sock_info[MAX_MEDIA_CNT]; + /* Socket info array */ /* Call variables: */ static pjsip_inv_session *g_inv; /* Current invite session. */ -static pjmedia_session *g_med_session; /* Call's media session. */ -static pjmedia_snd_port *g_snd_player; /* Call's sound player */ -static pjmedia_snd_port *g_snd_rec; /* Call's sound recorder. */ +static pjmedia_stream *g_med_stream; /* Call's audio stream. */ +static pjmedia_snd_port *g_snd_port; /* Sound device. */ +#if PJMEDIA_HAS_VIDEO +static pjmedia_vid_stream *g_med_vstream; /* Call's video stream. */ +static pjmedia_vid_port *g_vid_capturer;/* Call's video capturer. */ +static pjmedia_vid_port *g_vid_renderer;/* Call's video renderer. */ +#endif /* PJMEDIA_HAS_VIDEO */ /* * Prototypes: @@ -136,6 +153,68 @@ static pjsip_module mod_simpleua = }; +/* Notification on incoming messages */ +static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata) +{ + PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n" + "%.*s\n" + "--end msg--", + rdata->msg_info.len, + pjsip_rx_data_get_info(rdata), + rdata->tp_info.transport->type_name, + rdata->pkt_info.src_name, + rdata->pkt_info.src_port, + (int)rdata->msg_info.len, + rdata->msg_info.msg_buf)); + + /* Always return false, otherwise messages will not get processed! */ + return PJ_FALSE; +} + +/* Notification on outgoing messages */ +static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) +{ + + /* Important note: + * tp_info field is only valid after outgoing messages has passed + * transport layer. So don't try to access tp_info when the module + * has lower priority than transport layer. + */ + + PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n" + "%.*s\n" + "--end msg--", + (tdata->buf.cur - tdata->buf.start), + pjsip_tx_data_get_info(tdata), + tdata->tp_info.transport->type_name, + tdata->tp_info.dst_name, + tdata->tp_info.dst_port, + (int)(tdata->buf.cur - tdata->buf.start), + tdata->buf.start)); + + /* Always return success, otherwise message will not get sent! */ + return PJ_SUCCESS; +} + +/* The module instance. */ +static pjsip_module msg_logger = +{ + NULL, NULL, /* prev, next. */ + { "mod-msg-log", 13 }, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + &logging_on_rx_msg, /* on_rx_request() */ + &logging_on_rx_msg, /* on_rx_response() */ + &logging_on_tx_msg, /* on_tx_request. */ + &logging_on_tx_msg, /* on_tx_response() */ + NULL, /* on_tsx_state() */ + +}; + /* * main() @@ -145,12 +224,15 @@ static pjsip_module mod_simpleua = */ int main(int argc, char *argv[]) { + pj_pool_t *pool; pj_status_t status; + unsigned i; /* Must init PJLIB first: */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + pj_log_set_level(5); /* Then init PJLIB-UTIL: */ status = pjlib_util_init(); @@ -262,6 +344,12 @@ int main(int argc, char *argv[]) status = pjsip_endpt_register_module( g_endpt, &mod_simpleua); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + /* + * Register message logger module. + */ + status = pjsip_endpt_register_module( g_endpt, &msg_logger); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + /* * Initialize media endpoint. @@ -284,27 +372,52 @@ int main(int argc, char *argv[]) PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); #endif + + /* Init video subsystem */ +#if PJMEDIA_HAS_VIDEO + pool = pjmedia_endpt_create_pool(g_med_endpt, "Video subsystem", 512, 512); + status = pjmedia_video_format_mgr_create(pool, 64, 0, NULL); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + status = pjmedia_converter_mgr_create(pool, NULL); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + status = pjmedia_vid_codec_mgr_create(pool, NULL); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + status = pjmedia_vid_dev_subsys_init(&cp.factory); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + +# if PJMEDIA_HAS_FFMPEG_CODEC + /* Init ffmpeg video codecs */ + status = pjmedia_codec_ffmpeg_init(NULL, &cp.factory); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); +# endif /* PJMEDIA_HAS_FFMPEG_CODEC */ + +#endif /* PJMEDIA_HAS_VIDEO */ /* * Create media transport used to send/receive RTP/RTCP socket. * One media transport is needed for each call. Application may * opt to re-use the same media transport for subsequent calls. */ - status = pjmedia_transport_udp_create3(g_med_endpt, AF, NULL, NULL, - RTP_PORT, 0, &g_med_transport); - if (status != PJ_SUCCESS) { - app_perror(THIS_FILE, "Unable to create media transport", status); - return 1; - } + for (i = 0; i < PJ_ARRAY_SIZE(g_med_transport); ++i) { + status = pjmedia_transport_udp_create3(g_med_endpt, AF, NULL, NULL, + RTP_PORT + i*2, 0, + &g_med_transport[i]); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to create media transport", status); + return 1; + } - /* - * Get socket info (address, port) of the media transport. We will - * need this info to create SDP (i.e. the address and port info in - * the SDP). - */ - pjmedia_transport_info_init(&g_med_tpinfo); - pjmedia_transport_get_info(g_med_transport, &g_med_tpinfo); + /* + * Get socket info (address, port) of the media transport. We will + * need this info to create SDP (i.e. the address and port info in + * the SDP). + */ + pjmedia_transport_info_init(&g_med_tpinfo[i]); + pjmedia_transport_get_info(g_med_transport[i], &g_med_tpinfo[i]); + pj_memcpy(&g_sock_info[i], &g_med_tpinfo[i].sock_info, + sizeof(pjmedia_sock_info)); + } /* * If URL is specified, then make call immediately. @@ -360,15 +473,12 @@ int main(int argc, char *argv[]) /* Get the SDP body to be put in the outgoing INVITE, by asking - * media endpoint to create one for us. The SDP will contain all - * codecs that have been registered to it (in this case, only - * PCMA and PCMU), plus telephony event. + * media endpoint to create one for us. */ status = pjmedia_endpt_create_sdp( g_med_endpt, /* the media endpt */ dlg->pool, /* pool. */ - 1, /* # of streams */ - &g_med_tpinfo.sock_info, - /* RTP sock info */ + MAX_MEDIA_CNT, /* # of streams */ + g_sock_info, /* RTP sock info */ &local_sdp); /* the SDP result */ PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); @@ -440,6 +550,51 @@ int main(int argc, char *argv[]) /* On exit, dump current memory usage: */ dump_pool_usage(THIS_FILE, &cp); + /* Destroy audio ports. Destroy the audio port first + * before the stream since the audio port has threads + * that get/put frames to the stream. + */ + if (g_snd_port) + pjmedia_snd_port_destroy(g_snd_port); + + /* Destroy video ports */ +#if PJMEDIA_HAS_VIDEO + if (g_vid_capturer) + pjmedia_vid_port_destroy(g_vid_capturer); + if (g_vid_renderer) + pjmedia_vid_port_destroy(g_vid_renderer); +#endif + + /* Destroy streams */ + if (g_med_stream) + pjmedia_stream_destroy(g_med_stream); +#if PJMEDIA_HAS_VIDEO + if (g_med_vstream) + pjmedia_vid_stream_destroy(g_med_vstream); +#endif + + /* Destroy media transports */ + for (i = 0; i < MAX_MEDIA_CNT; ++i) { + if (g_med_transport[i]) + pjmedia_transport_close(g_med_transport[i]); + } + + /* Deinit ffmpeg codec */ +#if PJMEDIA_HAS_FFMPEG_CODEC + pjmedia_codec_ffmpeg_deinit(); +#endif + + /* Deinit pjmedia endpoint */ + if (g_med_endpt) + pjmedia_endpt_destroy(g_med_endpt); + + /* Deinit pjsip endpoint */ + if (g_endpt) + pjsip_endpt_destroy(g_endpt); + + /* Release pool */ + pj_pool_release(pool); + return 0; } @@ -574,9 +729,8 @@ static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) * Get media capability from media endpoint: */ - status = pjmedia_endpt_create_sdp( g_med_endpt, rdata->tp_info.pool, 1, - &g_med_tpinfo.sock_info, - &local_sdp); + status = pjmedia_endpt_create_sdp( g_med_endpt, rdata->tp_info.pool, + MAX_MEDIA_CNT, g_sock_info, &local_sdp); PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE); @@ -639,7 +793,7 @@ static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) static void call_on_media_update( pjsip_inv_session *inv, pj_status_t status) { - pjmedia_session_info sess_info; + pjmedia_stream_info stream_info; const pjmedia_sdp_session *local_sdp; const pjmedia_sdp_session *remote_sdp; pjmedia_port *media_port; @@ -662,88 +816,211 @@ static void call_on_media_update( pjsip_inv_session *inv, status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp); - /* Create session info based on the two SDPs. - * We only support one stream per session for now. - */ - status = pjmedia_session_info_from_sdp(inv->dlg->pool, g_med_endpt, - 1, &sess_info, - local_sdp, remote_sdp); + /* Create stream info based on the media audio SDP. */ + status = pjmedia_stream_info_from_sdp(&stream_info, inv->dlg->pool, + g_med_endpt, + local_sdp, remote_sdp, 0); if (status != PJ_SUCCESS) { - app_perror( THIS_FILE, "Unable to create media session", status); + app_perror(THIS_FILE,"Unable to create audio stream info",status); return; } - /* If required, we can also change some settings in the session info, + /* If required, we can also change some settings in the stream info, * (such as jitter buffer settings, codec settings, etc) before we - * create the session. + * create the stream. */ - /* Create new media session, passing the two SDPs, and also the + /* Create new audio media stream, passing the stream info, and also the * media socket that we created earlier. - * The media session is active immediately. */ - status = pjmedia_session_create( g_med_endpt, &sess_info, - &g_med_transport, NULL, &g_med_session ); + status = pjmedia_stream_create(g_med_endpt, inv->dlg->pool, &stream_info, + g_med_transport[0], NULL, &g_med_stream); if (status != PJ_SUCCESS) { - app_perror( THIS_FILE, "Unable to create media session", status); + app_perror( THIS_FILE, "Unable to create audio stream", status); return; } + /* Start the audio stream */ + status = pjmedia_stream_start(g_med_stream); + if (status != PJ_SUCCESS) { + app_perror( THIS_FILE, "Unable to start audio stream", status); + return; + } - /* Get the media port interface of the first stream in the session. + /* Get the media port interface of the audio stream. * Media port interface is basicly a struct containing get_frame() and * put_frame() function. With this media port interface, we can attach * the port interface to conference bridge, or directly to a sound * player/recorder device. */ - pjmedia_session_get_port(g_med_session, 0, &media_port); - + pjmedia_stream_get_port(g_med_stream, &media_port); + + /* Create sound port */ + pjmedia_snd_port_create(inv->pool, + PJMEDIA_AUD_DEFAULT_CAPTURE_DEV, + PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV, + PJMEDIA_PIA_SRATE(&media_port->info),/* clock rate */ + PJMEDIA_PIA_CCNT(&media_port->info),/* channel count */ + PJMEDIA_PIA_SPF(&media_port->info), /* samples per frame*/ + PJMEDIA_PIA_BITS(&media_port->info),/* bits per sample */ + 0, + &g_snd_port); - - /* Create a sound Player device and connect the media port to the - * sound device. - */ - status = pjmedia_snd_port_create_player( - inv->pool, /* pool */ - -1, /* sound dev id */ - media_port->info.clock_rate, /* clock rate */ - media_port->info.channel_count, /* channel count */ - media_port->info.samples_per_frame, /* samples per frame*/ - media_port->info.bits_per_sample, /* bits per sample */ - 0, /* options */ - &g_snd_player); if (status != PJ_SUCCESS) { - app_perror( THIS_FILE, "Unable to create sound player", status); + app_perror( THIS_FILE, "Unable to create sound port", status); PJ_LOG(3,(THIS_FILE, "%d %d %d %d", - media_port->info.clock_rate, /* clock rate */ - media_port->info.channel_count, /* channel count */ - media_port->info.samples_per_frame, /* samples per frame*/ - media_port->info.bits_per_sample /* bits per sample */ + PJMEDIA_PIA_SRATE(&media_port->info),/* clock rate */ + PJMEDIA_PIA_CCNT(&media_port->info),/* channel count */ + PJMEDIA_PIA_SPF(&media_port->info), /* samples per frame*/ + PJMEDIA_PIA_BITS(&media_port->info) /* bits per sample */ )); return; } - status = pjmedia_snd_port_connect(g_snd_player, media_port); + status = pjmedia_snd_port_connect(g_snd_port, media_port); - /* Create a sound recorder device and connect the media port to the - * sound device. + /* Get the media port interface of the second stream in the session, + * which is video stream. With this media port interface, we can attach + * the port directly to a renderer/capture video device. */ - status = pjmedia_snd_port_create_rec( - inv->pool, /* pool */ - -1, /* sound dev id */ - media_port->info.clock_rate, /* clock rate */ - media_port->info.channel_count, /* channel count */ - media_port->info.samples_per_frame, /* samples per frame*/ - media_port->info.bits_per_sample, /* bits per sample */ - 0, /* options */ - &g_snd_rec); - if (status != PJ_SUCCESS) { - app_perror( THIS_FILE, "Unable to create sound recorder", status); - return; - } +#if PJMEDIA_HAS_VIDEO + if (local_sdp->media_count > 1) { + pjmedia_vid_stream_info vstream_info; + pjmedia_vid_port_param vport_param; + + pjmedia_vid_port_param_default(&vport_param); + + /* Create stream info based on the media video SDP. */ + status = pjmedia_vid_stream_info_from_sdp(&vstream_info, + inv->dlg->pool, g_med_endpt, + local_sdp, remote_sdp, 1); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE,"Unable to create video stream info",status); + return; + } + + /* If required, we can also change some settings in the stream info, + * (such as jitter buffer settings, codec settings, etc) before we + * create the video stream. + */ + + /* Create new video media stream, passing the stream info, and also the + * media socket that we created earlier. + */ + status = pjmedia_vid_stream_create(g_med_endpt, NULL, &vstream_info, + g_med_transport[1], NULL, + &g_med_vstream); + if (status != PJ_SUCCESS) { + app_perror( THIS_FILE, "Unable to create video stream", status); + return; + } + + /* Start the video stream */ + status = pjmedia_vid_stream_start(g_med_vstream); + if (status != PJ_SUCCESS) { + app_perror( THIS_FILE, "Unable to start video stream", status); + return; + } + + if (vstream_info.dir & PJMEDIA_DIR_DECODING) { + status = pjmedia_vid_dev_default_param( + inv->pool, PJMEDIA_VID_DEFAULT_RENDER_DEV, + &vport_param.vidparam); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to get default param of video " + "renderer device", status); + return; + } - status = pjmedia_snd_port_connect(g_snd_rec, media_port); + /* Get video stream port for decoding direction */ + pjmedia_vid_stream_get_port(g_med_vstream, PJMEDIA_DIR_DECODING, + &media_port); + + /* Set format */ + pjmedia_format_copy(&vport_param.vidparam.fmt, + &media_port->info.fmt); + vport_param.vidparam.dir = PJMEDIA_DIR_RENDER; + vport_param.active = PJ_TRUE; + + /* Create renderer */ + status = pjmedia_vid_port_create(inv->pool, &vport_param, + &g_vid_renderer); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to create video renderer device", + status); + return; + } + + /* Connect renderer to media_port */ + status = pjmedia_vid_port_connect(g_vid_renderer, media_port, + PJ_FALSE); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to connect renderer to stream", + status); + return; + } + } + + /* Create capturer */ + if (vstream_info.dir & PJMEDIA_DIR_ENCODING) { + status = pjmedia_vid_dev_default_param( + inv->pool, PJMEDIA_VID_DEFAULT_CAPTURE_DEV, + &vport_param.vidparam); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to get default param of video " + "capture device", status); + return; + } + + /* Get video stream port for decoding direction */ + pjmedia_vid_stream_get_port(g_med_vstream, PJMEDIA_DIR_ENCODING, + &media_port); + + /* Get capturer format from stream info */ + pjmedia_format_copy(&vport_param.vidparam.fmt, + &media_port->info.fmt); + vport_param.vidparam.dir = PJMEDIA_DIR_CAPTURE; + vport_param.active = PJ_TRUE; + + /* Create capturer */ + status = pjmedia_vid_port_create(inv->pool, &vport_param, + &g_vid_capturer); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to create video capture device", + status); + return; + } + + /* Connect capturer to media_port */ + status = pjmedia_vid_port_connect(g_vid_capturer, media_port, + PJ_FALSE); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to connect capturer to stream", + status); + return; + } + } + + /* Start streaming */ + if (g_vid_renderer) { + status = pjmedia_vid_port_start(g_vid_renderer); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to start video renderer", + status); + return; + } + } + if (g_vid_capturer) { + status = pjmedia_vid_port_start(g_vid_capturer); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to start video capturer", + status); + return; + } + } + } +#endif /* PJMEDIA_HAS_VIDEO */ /* Done with media. */ } diff --git a/pjsip-apps/src/samples/stereotest.c b/pjsip-apps/src/samples/stereotest.c index f2422d22..ab552f9e 100644 --- a/pjsip-apps/src/samples/stereotest.c +++ b/pjsip-apps/src/samples/stereotest.c @@ -189,11 +189,11 @@ int main(int argc, char *argv[]) status = pjmedia_snd_port_create_player( pool, /* pool */ dev_id, /* device id. */ - file_port->info.clock_rate, /* clock rate. */ + PJMEDIA_PIA_SRATE(&file_port->info),/* clock rate. */ snd_ch_cnt, /* # of channels. */ snd_ch_cnt * PTIME * /* samples per frame. */ - file_port->info.clock_rate / 1000, - file_port->info.bits_per_sample, /* bits per sample. */ + PJMEDIA_PIA_SRATE(&file_port->info) / 1000, + PJMEDIA_PIA_BITS(&file_port->info),/* bits per sample. */ 0, /* options */ &snd_port /* returned port */ ); @@ -202,7 +202,7 @@ int main(int argc, char *argv[]) return 1; } - if (snd_ch_cnt != file_port->info.channel_count) { + if (snd_ch_cnt != PJMEDIA_PIA_CCNT(&file_port->info)) { status = pjmedia_stereo_port_create( pool, file_port, snd_ch_cnt, @@ -289,9 +289,9 @@ int main(int argc, char *argv[]) pj_thread_sleep(100); printf("Mode = %s\n", (mode == MODE_PLAY? "playing" : "recording") ); - printf("File port channel count = %d\n", file_port->info.channel_count); + printf("File port channel count = %d\n", PJMEDIA_PIA_CCNT(&file_port->info)); printf("Sound port channel count = %d\n", - pjmedia_snd_port_get_port(snd_port)->info.channel_count); + PJMEDIA_PIA_CCNT(&pjmedia_snd_port_get_port(snd_port)->info)); puts(""); puts("Press to stop and quit"); diff --git a/pjsip-apps/src/samples/streamutil.c b/pjsip-apps/src/samples/streamutil.c index 7237b7b8..7037a94e 100644 --- a/pjsip-apps/src/samples/streamutil.c +++ b/pjsip-apps/src/samples/streamutil.c @@ -100,37 +100,7 @@ int hex_string_to_octet_string(char *raw, char *hex, int len); */ static pj_status_t init_codecs(pjmedia_endpt *med_endpt) { - pj_status_t status; - - /* To suppress warning about unused var when all codecs are disabled */ - PJ_UNUSED_ARG(status); - -#if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0 - status = pjmedia_codec_g711_init(med_endpt); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); -#endif - -#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC!=0 - status = pjmedia_codec_gsm_init(med_endpt); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); -#endif - -#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0 - status = pjmedia_codec_speex_init(med_endpt, 0, -1, -1); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); -#endif - -#if defined(PJMEDIA_HAS_G722_CODEC) && PJMEDIA_HAS_G722_CODEC!=0 - status = pjmedia_codec_g722_init(med_endpt); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); -#endif - -#if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC!=0 - status = pjmedia_codec_l16_init(med_endpt, 0); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); -#endif - - return PJ_SUCCESS; + return pjmedia_codec_register_audio_codecs(med_endpt, NULL); } @@ -505,8 +475,7 @@ int main(int argc, char *argv[]) if (play_file) { unsigned wav_ptime; - wav_ptime = stream_port->info.samples_per_frame * 1000 / - stream_port->info.clock_rate; + wav_ptime = PJMEDIA_PIA_PTIME(&stream_port->info); status = pjmedia_wav_player_port_create(pool, play_file, wav_ptime, 0, -1, &play_file_port); if (status != PJ_SUCCESS) { @@ -532,10 +501,10 @@ int main(int argc, char *argv[]) } else if (rec_file) { status = pjmedia_wav_writer_port_create(pool, rec_file, - stream_port->info.clock_rate, - stream_port->info.channel_count, - stream_port->info.samples_per_frame, - stream_port->info.bits_per_sample, + PJMEDIA_PIA_SRATE(&stream_port->info), + PJMEDIA_PIA_CCNT(&stream_port->info), + PJMEDIA_PIA_SPF(&stream_port->info), + PJMEDIA_PIA_BITS(&stream_port->info), 0, 0, &rec_file_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to use file", status); @@ -562,24 +531,24 @@ int main(int argc, char *argv[]) /* Create sound device port. */ if (dir == PJMEDIA_DIR_ENCODING_DECODING) status = pjmedia_snd_port_create(pool, -1, -1, - stream_port->info.clock_rate, - stream_port->info.channel_count, - stream_port->info.samples_per_frame, - stream_port->info.bits_per_sample, + PJMEDIA_PIA_SRATE(&stream_port->info), + PJMEDIA_PIA_CCNT(&stream_port->info), + PJMEDIA_PIA_SPF(&stream_port->info), + PJMEDIA_PIA_BITS(&stream_port->info), 0, &snd_port); else if (dir == PJMEDIA_DIR_ENCODING) status = pjmedia_snd_port_create_rec(pool, -1, - stream_port->info.clock_rate, - stream_port->info.channel_count, - stream_port->info.samples_per_frame, - stream_port->info.bits_per_sample, + PJMEDIA_PIA_SRATE(&stream_port->info), + PJMEDIA_PIA_CCNT(&stream_port->info), + PJMEDIA_PIA_SPF(&stream_port->info), + PJMEDIA_PIA_BITS(&stream_port->info), 0, &snd_port); else status = pjmedia_snd_port_create_player(pool, -1, - stream_port->info.clock_rate, - stream_port->info.channel_count, - stream_port->info.samples_per_frame, - stream_port->info.bits_per_sample, + PJMEDIA_PIA_SRATE(&stream_port->info), + PJMEDIA_PIA_CCNT(&stream_port->info), + PJMEDIA_PIA_SPF(&stream_port->info), + PJMEDIA_PIA_BITS(&stream_port->info), 0, &snd_port); @@ -757,11 +726,9 @@ static void print_stream_stat(pjmedia_stream *stream, now.msec); - printf(" Info: audio %.*s@%dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n", - (int)port->info.encoding_name.slen, - port->info.encoding_name.ptr, - port->info.clock_rate, - port->info.samples_per_frame * 1000 / port->info.clock_rate, + printf(" Info: audio %dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n", + PJMEDIA_PIA_SRATE(&port->info), + PJMEDIA_PIA_PTIME(&port->info), good_number(bps, (codec_param->info.avg_bps+7)/8), good_number(ipbps, ((codec_param->info.avg_bps+7)/8) + (40 * 1000 / @@ -900,14 +867,14 @@ static void print_stream_stat(pjmedia_stream *stream, unsigned jmin, jmax, jmean, jdev; SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min, - port->info.clock_rate); + port->info.fmt.det.aud.clock_rate); SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max, - port->info.clock_rate); + port->info.fmt.det.aud.clock_rate); SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean, - port->info.clock_rate); + port->info.fmt.det.aud.clock_rate); SAMPLES_TO_USEC(jdev, pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter), - port->info.clock_rate); + port->info.fmt.det.aud.clock_rate); sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f", jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0); } else @@ -963,14 +930,14 @@ static void print_stream_stat(pjmedia_stream *stream, unsigned jmin, jmax, jmean, jdev; SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min, - port->info.clock_rate); + port->info.fmt.det.aud.clock_rate); SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max, - port->info.clock_rate); + port->info.fmt.det.aud.clock_rate); SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean, - port->info.clock_rate); + port->info.fmt.det.aud.clock_rate); SAMPLES_TO_USEC(jdev, pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter), - port->info.clock_rate); + port->info.fmt.det.aud.clock_rate); sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f", jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0); } else diff --git a/pjsip-apps/src/samples/vid_streamutil.c b/pjsip-apps/src/samples/vid_streamutil.c new file mode 100644 index 00000000..4d50b387 --- /dev/null +++ b/pjsip-apps/src/samples/vid_streamutil.c @@ -0,0 +1,929 @@ +/* $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 + */ + + +/** + * \page page_pjmedia_samples_vid_streamutil_c Samples: Video Streaming + * + * This example mainly demonstrates how to stream video to remote + * peer using RTP. + * + * This file is pjsip-apps/src/samples/vid_streamutil.c + * + * \includelineno vid_streamutil.c + */ + +#include +#include +#include +#include +#include + +#include /* atoi() */ +#include + +#include "util.h" + + +static const char *desc = + " vid_streamutil \n" + "\n" + " PURPOSE: \n" + " Demonstrate how to use pjmedia video stream component to \n" + " transmit/receive RTP packets to/from video device/file. \n" + "\n" + "\n" + " USAGE: \n" + " vid_streamutil [options] \n" + "\n" + "\n" + " Options: \n" + " --codec=CODEC Set the codec name. \n" + " --local-port=PORT Set local RTP port (default=4000) \n" + " --remote=IP:PORT Set the remote peer. If this option is set, \n" + " the program will transmit RTP audio to the \n" + " specified address. (default: recv only) \n" + " --play-file=AVI Send video from the AVI file instead of from \n" + " the video device. \n" + " --send-recv Set stream direction to bidirectional. \n" + " --send-only Set stream direction to send only \n" + " --recv-only Set stream direction to recv only (default) \n" + + " --send-width Video width to be sent \n" + " --send-height Video height to be sent \n" + " --send-width and --send-height not applicable \n" + " for file streaming (see --play-file) \n" + + " --send-pt Payload type for sending \n" + " --recv-pt Payload type for receiving \n" + +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + " --use-srtp[=NAME] Enable SRTP with crypto suite NAME \n" + " e.g: AES_CM_128_HMAC_SHA1_80 (default), \n" + " AES_CM_128_HMAC_SHA1_32 \n" + " Use this option along with the TX & RX keys, \n" + " formated of 60 hex digits (e.g: E148DA..) \n" + " --srtp-tx-key SRTP key for transmiting \n" + " --srtp-rx-key SRTP key for receiving \n" +#endif + + "\n" +; + +#define THIS_FILE "vid_streamutil.c" + + +/* If set, local renderer will be created to play original file */ +#define HAS_LOCAL_RENDERER_FOR_PLAY_FILE 1 + + +/* Default width and height for the renderer, better be set to maximum + * acceptable size. + */ +#define DEF_RENDERER_WIDTH 640 +#define DEF_RENDERER_HEIGHT 480 + + +/* Prototype */ +static void print_stream_stat(pjmedia_vid_stream *stream, + const pjmedia_vid_codec_param *codec_param); + +/* Prototype for LIBSRTP utility in file datatypes.c */ +int hex_string_to_octet_string(char *raw, char *hex, int len); + +/* + * Register all codecs. + */ +static pj_status_t init_codecs(pj_pool_factory *pf) +{ + pj_status_t status; + + /* To suppress warning about unused var when all codecs are disabled */ + PJ_UNUSED_ARG(status); + +#if defined(PJMEDIA_HAS_FFMPEG_CODEC) && PJMEDIA_HAS_FFMPEG_CODEC != 0 + status = pjmedia_codec_ffmpeg_init(NULL, pf); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); +#endif + + return PJ_SUCCESS; +} + +/* + * Register all codecs. + */ +static void deinit_codecs() +{ +#if defined(PJMEDIA_HAS_FFMPEG_CODEC) && PJMEDIA_HAS_FFMPEG_CODEC != 0 + pjmedia_codec_ffmpeg_deinit(); +#endif +} + +static pj_status_t create_file_player( pj_pool_t *pool, + const char *file_name, + pjmedia_port **p_play_port) +{ + pjmedia_avi_streams *avi_streams; + pjmedia_avi_stream *vid_stream; + pjmedia_port *play_port; + pj_status_t status; + + status = pjmedia_avi_player_create_streams(pool, file_name, 0, &avi_streams); + if (status != PJ_SUCCESS) + return status; + + vid_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams, + 0, + PJMEDIA_TYPE_VIDEO); + if (!vid_stream) + return PJ_ENOTFOUND; + + play_port = pjmedia_avi_stream_get_port(vid_stream); + pj_assert(play_port); + + *p_play_port = play_port; + + return PJ_SUCCESS; +} + +/* + * Create stream based on the codec, dir, remote address, etc. + */ +static pj_status_t create_stream( pj_pool_t *pool, + pjmedia_endpt *med_endpt, + const pjmedia_vid_codec_info *codec_info, + pjmedia_vid_codec_param *codec_param, + pjmedia_dir dir, + pj_int8_t rx_pt, + pj_int8_t tx_pt, + pj_uint16_t local_port, + const pj_sockaddr_in *rem_addr, +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + pj_bool_t use_srtp, + const pj_str_t *crypto_suite, + const pj_str_t *srtp_tx_key, + const pj_str_t *srtp_rx_key, +#endif + pjmedia_vid_stream **p_stream ) +{ + pjmedia_vid_stream_info info; + pjmedia_transport *transport = NULL; + pj_status_t status; +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + pjmedia_transport *srtp_tp = NULL; +#endif + + /* Reset stream info. */ + pj_bzero(&info, sizeof(info)); + + /* Initialize stream info formats */ + info.type = PJMEDIA_TYPE_VIDEO; + info.dir = dir; + info.codec_info = *codec_info; + info.tx_pt = (tx_pt == -1)? codec_info->pt : tx_pt; + info.rx_pt = (rx_pt == -1)? codec_info->pt : rx_pt; + info.ssrc = pj_rand(); + if (codec_param) + info.codec_param = codec_param; + + /* Copy remote address */ + pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in)); + + /* If remote address is not set, set to an arbitrary address + * (otherwise stream will assert). + */ + if (info.rem_addr.addr.sa_family == 0) { + const pj_str_t addr = pj_str("127.0.0.1"); + pj_sockaddr_in_init(&info.rem_addr.ipv4, &addr, 0); + } + + /* Create media transport */ + status = pjmedia_transport_udp_create(med_endpt, NULL, local_port, + 0, &transport); + if (status != PJ_SUCCESS) + return status; + +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* Check if SRTP enabled */ + if (use_srtp) { + pjmedia_srtp_crypto tx_plc, rx_plc; + + status = pjmedia_transport_srtp_create(med_endpt, transport, + NULL, &srtp_tp); + if (status != PJ_SUCCESS) + return status; + + pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto)); + pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto)); + + tx_plc.key = *srtp_tx_key; + tx_plc.name = *crypto_suite; + rx_plc.key = *srtp_rx_key; + rx_plc.name = *crypto_suite; + + status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc); + if (status != PJ_SUCCESS) + return status; + + transport = srtp_tp; + } +#endif + + /* Now that the stream info is initialized, we can create the + * stream. + */ + + status = pjmedia_vid_stream_create( med_endpt, pool, &info, + transport, + NULL, p_stream); + + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Error creating stream", status); + pjmedia_transport_close(transport); + return status; + } + + + return PJ_SUCCESS; +} + + +typedef struct play_file_data +{ + const char *file_name; + pjmedia_port *play_port; + pjmedia_port *stream_port; + pjmedia_vid_codec *decoder; + pjmedia_port *renderer; + void *read_buf; + pj_size_t read_buf_size; + void *dec_buf; + pj_size_t dec_buf_size; +} play_file_data; + + +static void clock_cb(const pj_timestamp *ts, void *user_data) +{ + play_file_data *play_file = (play_file_data*)user_data; + pjmedia_frame read_frame, write_frame; + pj_status_t status; + + PJ_UNUSED_ARG(ts); + + /* Read frame from file */ + read_frame.buf = play_file->read_buf; + read_frame.size = play_file->read_buf_size; + pjmedia_port_get_frame(play_file->play_port, &read_frame); + + /* Decode frame, if needed */ + if (play_file->decoder) { + pjmedia_vid_codec *decoder = play_file->decoder; + + write_frame.buf = play_file->dec_buf; + write_frame.size = play_file->dec_buf_size; + status = decoder->op->decode(decoder, &read_frame, write_frame.size, + &write_frame); + if (status != PJ_SUCCESS) + return; + } else { + write_frame = read_frame; + } + + /* Display frame locally */ + if (play_file->renderer) + pjmedia_port_put_frame(play_file->renderer, &write_frame); + + /* Send frame */ + pjmedia_port_put_frame(play_file->stream_port, &write_frame); +} + + +/* + * usage() + */ +static void usage() +{ + puts(desc); +} + +/* + * main() + */ +int main(int argc, char *argv[]) +{ + pj_caching_pool cp; + pjmedia_endpt *med_endpt; + pj_pool_t *pool; + pjmedia_vid_stream *stream = NULL; + pjmedia_port *enc_port, *dec_port; + pj_status_t status; + + pjmedia_vid_port *capture=NULL, *renderer=NULL; + pjmedia_vid_port_param vpp; + +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* SRTP variables */ + pj_bool_t use_srtp = PJ_FALSE; + char tmp_tx_key[64]; + char tmp_rx_key[64]; + pj_str_t srtp_tx_key = {NULL, 0}; + pj_str_t srtp_rx_key = {NULL, 0}; + pj_str_t srtp_crypto_suite = {NULL, 0}; + int tmp_key_len; +#endif + + /* Default values */ + const pjmedia_vid_codec_info *codec_info; + pjmedia_vid_codec_param codec_param; + pjmedia_dir dir = PJMEDIA_DIR_DECODING; + pj_sockaddr_in remote_addr; + pj_uint16_t local_port = 4000; + char *codec_id = NULL; + pjmedia_rect_size tx_size = {0}; + pj_int8_t rx_pt = -1, tx_pt = -1; + + play_file_data play_file = { NULL }; + pjmedia_port *play_port = NULL; + pjmedia_vid_codec *play_decoder = NULL; + pjmedia_clock *play_clock = NULL; + + enum { + OPT_CODEC = 'c', + OPT_LOCAL_PORT = 'p', + OPT_REMOTE = 'r', + OPT_PLAY_FILE = 'f', + OPT_SEND_RECV = 'b', + OPT_SEND_ONLY = 's', + OPT_RECV_ONLY = 'i', + OPT_SEND_WIDTH = 'W', + OPT_SEND_HEIGHT = 'H', + OPT_RECV_PT = 't', + OPT_SEND_PT = 'T', +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + OPT_USE_SRTP = 'S', +#endif + OPT_SRTP_TX_KEY = 'x', + OPT_SRTP_RX_KEY = 'y', + OPT_HELP = 'h', + }; + + struct pj_getopt_option long_options[] = { + { "codec", 1, 0, OPT_CODEC }, + { "local-port", 1, 0, OPT_LOCAL_PORT }, + { "remote", 1, 0, OPT_REMOTE }, + { "play-file", 1, 0, OPT_PLAY_FILE }, + { "send-recv", 0, 0, OPT_SEND_RECV }, + { "send-only", 0, 0, OPT_SEND_ONLY }, + { "recv-only", 0, 0, OPT_RECV_ONLY }, + { "send-width", 1, 0, OPT_SEND_WIDTH }, + { "send-height", 1, 0, OPT_SEND_HEIGHT }, + { "recv-pt", 1, 0, OPT_RECV_PT }, + { "send-pt", 1, 0, OPT_SEND_PT }, +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + { "use-srtp", 2, 0, OPT_USE_SRTP }, + { "srtp-tx-key", 1, 0, OPT_SRTP_TX_KEY }, + { "srtp-rx-key", 1, 0, OPT_SRTP_RX_KEY }, +#endif + { "help", 0, 0, OPT_HELP }, + { NULL, 0, 0, 0 }, + }; + + int c; + int option_index; + + + pj_bzero(&remote_addr, sizeof(remote_addr)); + + + /* init PJLIB : */ + status = pj_init(); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + + /* Parse arguments */ + pj_optind = 0; + while((c=pj_getopt_long(argc,argv, "h", long_options, &option_index))!=-1) + { + switch (c) { + case OPT_CODEC: + codec_id = pj_optarg; + break; + + case OPT_LOCAL_PORT: + local_port = (pj_uint16_t) atoi(pj_optarg); + if (local_port < 1) { + printf("Error: invalid local port %s\n", pj_optarg); + return 1; + } + break; + + case OPT_REMOTE: + { + pj_str_t ip = pj_str(strtok(pj_optarg, ":")); + pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":")); + + status = pj_sockaddr_in_init(&remote_addr, &ip, port); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Invalid remote address", status); + return 1; + } + } + break; + + case OPT_PLAY_FILE: + play_file.file_name = pj_optarg; + break; + + case OPT_SEND_RECV: + dir = PJMEDIA_DIR_ENCODING_DECODING; + break; + + case OPT_SEND_ONLY: + dir = PJMEDIA_DIR_ENCODING; + break; + + case OPT_RECV_ONLY: + dir = PJMEDIA_DIR_DECODING; + break; + + case OPT_SEND_WIDTH: + tx_size.w = (unsigned)atoi(pj_optarg); + break; + + case OPT_SEND_HEIGHT: + tx_size.h = (unsigned)atoi(pj_optarg); + break; + + case OPT_RECV_PT: + rx_pt = (pj_int8_t)atoi(pj_optarg); + break; + + case OPT_SEND_PT: + tx_pt = (pj_int8_t)atoi(pj_optarg); + break; + +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + case OPT_USE_SRTP: + use_srtp = PJ_TRUE; + if (pj_optarg) { + pj_strset(&srtp_crypto_suite, pj_optarg, strlen(pj_optarg)); + } else { + srtp_crypto_suite = pj_str("AES_CM_128_HMAC_SHA1_80"); + } + break; + + case OPT_SRTP_TX_KEY: + tmp_key_len = hex_string_to_octet_string(tmp_tx_key, pj_optarg, + strlen(pj_optarg)); + pj_strset(&srtp_tx_key, tmp_tx_key, tmp_key_len/2); + break; + + case OPT_SRTP_RX_KEY: + tmp_key_len = hex_string_to_octet_string(tmp_rx_key, pj_optarg, + strlen(pj_optarg)); + pj_strset(&srtp_rx_key, tmp_rx_key, tmp_key_len/2); + break; +#endif + + case OPT_HELP: + usage(); + return 1; + + default: + printf("Invalid options %s\n", argv[pj_optind]); + return 1; + } + + } + + + /* Verify arguments. */ + if (dir & PJMEDIA_DIR_ENCODING) { + if (remote_addr.sin_addr.s_addr == 0) { + printf("Error: remote address must be set\n"); + return 1; + } + } + + if (play_file.file_name != NULL && dir != PJMEDIA_DIR_ENCODING) { + printf("Direction is set to --send-only because of --play-file\n"); + dir = PJMEDIA_DIR_ENCODING; + } + +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* SRTP validation */ + if (use_srtp) { + if (!srtp_tx_key.slen || !srtp_rx_key.slen) + { + printf("Error: Key for each SRTP stream direction must be set\n"); + return 1; + } + } +#endif + + /* Must create a pool factory before we can allocate any memory. */ + pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); + + /* + * Initialize media endpoint. + * This will implicitly initialize PJMEDIA too. + */ + status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + /* Create memory pool for application purpose */ + pool = pj_pool_create( &cp.factory, /* pool factory */ + "app", /* pool name. */ + 4000, /* init size */ + 4000, /* increment size */ + NULL /* callback on error */ + ); + + /* Init video format manager */ + pjmedia_video_format_mgr_create(pool, 64, 0, NULL); + + /* Init video converter manager */ + pjmedia_converter_mgr_create(pool, NULL); + + /* Init video codec manager */ + pjmedia_vid_codec_mgr_create(pool, NULL); + + /* Init video subsystem */ + pjmedia_vid_dev_subsys_init(&cp.factory); + + /* Register all supported codecs */ + status = init_codecs(&cp.factory); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + + /* Find which codec to use. */ + if (codec_id) { + unsigned count = 1; + pj_str_t str_codec_id = pj_str(codec_id); + + status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, + &str_codec_id, &count, + &codec_info, NULL); + if (status != PJ_SUCCESS) { + printf("Error: unable to find codec %s\n", codec_id); + return 1; + } + } else { + static pjmedia_vid_codec_info info[1]; + unsigned count = PJ_ARRAY_SIZE(info); + + /* Default to first codec */ + pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, NULL); + codec_info = &info[0]; + } + + /* Get codec default param for info */ + status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info, + &codec_param); + pj_assert(status == PJ_SUCCESS); + + /* Set outgoing video size */ + if (tx_size.w && tx_size.h) + codec_param.enc_fmt.det.vid.size = tx_size; + +#if DEF_RENDERER_WIDTH && DEF_RENDERER_HEIGHT + /* Set incoming video size */ + codec_param.dec_fmt.det.vid.size.w = DEF_RENDERER_WIDTH; + codec_param.dec_fmt.det.vid.size.h = DEF_RENDERER_HEIGHT; +#endif + + if (play_file.file_name) { + pjmedia_video_format_detail *file_vfd; + pjmedia_clock_param clock_param; + + /* Create file player */ + status = create_file_player(pool, play_file.file_name, &play_port); + if (status != PJ_SUCCESS) + goto on_exit; + + /* Collect format info */ + file_vfd = pjmedia_format_get_video_format_detail(&play_port->info.fmt, + PJ_TRUE); + PJ_LOG(2, (THIS_FILE, "Reading video stream %dx%d %c%c%c%c @%.2ffps", + file_vfd->size.w, file_vfd->size.h, + ((play_port->info.fmt.id & 0x000000FF) >> 0), + ((play_port->info.fmt.id & 0x0000FF00) >> 8), + ((play_port->info.fmt.id & 0x00FF0000) >> 16), + ((play_port->info.fmt.id & 0xFF000000) >> 24), + (1.0*file_vfd->fps.num/file_vfd->fps.denum))); + + /* Allocate file read buffer */ + play_file.read_buf_size = PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE; + play_file.read_buf = pj_pool_zalloc(pool, play_file.read_buf_size); + + /* Create decoder, if the file and the stream uses different codec */ + if (codec_info->fmt_id != (pjmedia_format_id)play_port->info.fmt.id) { + const pjmedia_video_format_info *dec_vfi; + pjmedia_video_apply_fmt_param dec_vafp = {0}; + const pjmedia_vid_codec_info *codec_info2; + pjmedia_vid_codec_param codec_param2; + + /* Find decoder */ + status = pjmedia_vid_codec_mgr_get_codec_info2(NULL, + play_port->info.fmt.id, + &codec_info2); + if (status != PJ_SUCCESS) + goto on_exit; + + /* Init decoder */ + status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info2, + &play_decoder); + if (status != PJ_SUCCESS) + goto on_exit; + + status = play_decoder->op->init(play_decoder, pool); + if (status != PJ_SUCCESS) + goto on_exit; + + /* Open decoder */ + status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info2, + &codec_param2); + if (status != PJ_SUCCESS) + goto on_exit; + + codec_param2.dir = PJMEDIA_DIR_DECODING; + status = play_decoder->op->open(play_decoder, &codec_param2); + if (status != PJ_SUCCESS) + goto on_exit; + + /* Get decoder format info and apply param */ + dec_vfi = pjmedia_get_video_format_info(NULL, + codec_info2->dec_fmt_id[0]); + if (!dec_vfi || !dec_vfi->apply_fmt) { + status = PJ_ENOTSUP; + goto on_exit; + } + dec_vafp.size = file_vfd->size; + (*dec_vfi->apply_fmt)(dec_vfi, &dec_vafp); + + /* Allocate buffer to receive decoder output */ + play_file.dec_buf_size = dec_vafp.framebytes; + play_file.dec_buf = pj_pool_zalloc(pool, play_file.dec_buf_size); + } + + /* Create player clock */ + clock_param.usec_interval = PJMEDIA_PTIME(&file_vfd->fps); + clock_param.clock_rate = codec_info->clock_rate; + status = pjmedia_clock_create2(pool, &clock_param, + PJMEDIA_CLOCK_NO_HIGHEST_PRIO, + &clock_cb, &play_file, &play_clock); + if (status != PJ_SUCCESS) + goto on_exit; + + /* Override stream codec param for encoding direction */ + codec_param.enc_fmt.det.vid.size = file_vfd->size; + codec_param.enc_fmt.det.vid.fps = file_vfd->fps; + + } else { + pjmedia_vid_port_param_default(&vpp); + + /* Set as active for all video devices */ + vpp.active = PJ_TRUE; + + /* Create video device port. */ + if (dir & PJMEDIA_DIR_ENCODING) { + /* Create capture */ + status = pjmedia_vid_dev_default_param( + pool, + 0,//PJMEDIA_VID_DEFAULT_CAPTURE_DEV, + &vpp.vidparam); + if (status != PJ_SUCCESS) + goto on_exit; + + pjmedia_format_copy(&vpp.vidparam.fmt, &codec_param.enc_fmt); + vpp.vidparam.fmt.id = codec_param.dec_fmt.id; + vpp.vidparam.dir = PJMEDIA_DIR_CAPTURE; + + status = pjmedia_vid_port_create(pool, &vpp, &capture); + if (status != PJ_SUCCESS) + goto on_exit; + } + + if (dir & PJMEDIA_DIR_DECODING) { + /* Create renderer */ + status = pjmedia_vid_dev_default_param( + pool, + 1,//PJMEDIA_VID_DEFAULT_RENDER_DEV, + &vpp.vidparam); + if (status != PJ_SUCCESS) + goto on_exit; + + pjmedia_format_copy(&vpp.vidparam.fmt, &codec_param.dec_fmt); + vpp.vidparam.dir = PJMEDIA_DIR_RENDER; + vpp.vidparam.disp_size = vpp.vidparam.fmt.det.vid.size; + + status = pjmedia_vid_port_create(pool, &vpp, &renderer); + if (status != PJ_SUCCESS) + goto on_exit; + } + } + + /* Create stream based on program arguments */ + status = create_stream(pool, med_endpt, codec_info, &codec_param, + dir, rx_pt, tx_pt, local_port, &remote_addr, +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + use_srtp, &srtp_crypto_suite, + &srtp_tx_key, &srtp_rx_key, +#endif + &stream); + if (status != PJ_SUCCESS) + goto on_exit; + + /* Get the port interface of the stream */ + status = pjmedia_vid_stream_get_port(stream, PJMEDIA_DIR_ENCODING, + &enc_port); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + status = pjmedia_vid_stream_get_port(stream, PJMEDIA_DIR_DECODING, + &dec_port); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + /* Start streaming */ + status = pjmedia_vid_stream_start(stream); + if (status != PJ_SUCCESS) + goto on_exit; + + /* Start renderer */ + if (renderer) { + status = pjmedia_vid_port_connect(renderer, dec_port, PJ_FALSE); + if (status != PJ_SUCCESS) + goto on_exit; + status = pjmedia_vid_port_start(renderer); + if (status != PJ_SUCCESS) + goto on_exit; + } + + /* Start capture */ + if (capture) { + status = pjmedia_vid_port_connect(capture, enc_port, PJ_FALSE); + if (status != PJ_SUCCESS) + goto on_exit; + status = pjmedia_vid_port_start(capture); + if (status != PJ_SUCCESS) + goto on_exit; + } + + /* Start playing file */ + if (play_file.file_name) { + +#if HAS_LOCAL_RENDERER_FOR_PLAY_FILE + /* Create local renderer */ + pjmedia_vid_port_param_default(&vpp); + vpp.active = PJ_FALSE; + status = pjmedia_vid_dev_default_param( + pool, + 1,//PJMEDIA_VID_DEFAULT_RENDER_DEV, + &vpp.vidparam); + if (status != PJ_SUCCESS) + goto on_exit; + + vpp.vidparam.dir = PJMEDIA_DIR_RENDER; + pjmedia_format_copy(&vpp.vidparam.fmt, &codec_param.dec_fmt); + vpp.vidparam.fmt.det.vid.size = play_port->info.fmt.det.vid.size; + vpp.vidparam.fmt.det.vid.fps = play_port->info.fmt.det.vid.fps; + vpp.vidparam.disp_size = vpp.vidparam.fmt.det.vid.size; + + status = pjmedia_vid_port_create(pool, &vpp, &renderer); + if (status != PJ_SUCCESS) + goto on_exit; + status = pjmedia_vid_port_start(renderer); + if (status != PJ_SUCCESS) + goto on_exit; +#endif + + /* Init play file data */ + play_file.play_port = play_port; + play_file.stream_port = enc_port; + play_file.decoder = play_decoder; + if (renderer) { + play_file.renderer = pjmedia_vid_port_get_passive_port(renderer); + } + + status = pjmedia_clock_start(play_clock); + if (status != PJ_SUCCESS) + goto on_exit; + } + + /* Done */ + + if (dir == PJMEDIA_DIR_DECODING) + printf("Stream is active, dir is recv-only, local port is %d\n", + local_port); + else if (dir == PJMEDIA_DIR_ENCODING) + printf("Stream is active, dir is send-only, sending to %s:%d\n", + pj_inet_ntoa(remote_addr.sin_addr), + pj_ntohs(remote_addr.sin_port)); + else + printf("Stream is active, send/recv, local port is %d, " + "sending to %s:%d\n", + local_port, + pj_inet_ntoa(remote_addr.sin_addr), + pj_ntohs(remote_addr.sin_port)); + + if (dir & PJMEDIA_DIR_ENCODING) + PJ_LOG(2, (THIS_FILE, "Sending %dx%d %.*s @%.2ffps", + codec_param.enc_fmt.det.vid.size.w, + codec_param.enc_fmt.det.vid.size.h, + codec_info->encoding_name.slen, + codec_info->encoding_name.ptr, + (1.0*codec_param.enc_fmt.det.vid.fps.num/ + codec_param.enc_fmt.det.vid.fps.denum))); + + for (;;) { + char tmp[10]; + + puts(""); + puts("Commands:"); + puts(" q Quit"); + puts(""); + + printf("Command: "); fflush(stdout); + + if (fgets(tmp, sizeof(tmp), stdin) == NULL) { + puts("EOF while reading stdin, will quit now.."); + break; + } + + if (tmp[0] == 'q') + break; + + } + + + + /* Start deinitialization: */ +on_exit: + + /* Stop and destroy file clock */ + if (play_clock) { + pjmedia_clock_stop(play_clock); + pjmedia_clock_destroy(play_clock); + } + + /* Destroy file reader/player */ + if (play_port) + pjmedia_port_destroy(play_port); + + /* Destroy file decoder */ + if (play_decoder) { + play_decoder->op->close(play_decoder); + pjmedia_vid_codec_mgr_dealloc_codec(NULL, play_decoder); + } + + /* Destroy video devices */ + if (capture) + pjmedia_vid_port_destroy(capture); + if (renderer) + pjmedia_vid_port_destroy(renderer); + + /* Destroy stream */ + if (stream) { + pjmedia_transport *tp; + + tp = pjmedia_vid_stream_get_transport(stream); + pjmedia_vid_stream_destroy(stream); + + pjmedia_transport_close(tp); + } + + /* Deinit codecs */ + deinit_codecs(); + + /* Shutdown video subsystem */ + pjmedia_vid_dev_subsys_shutdown(); + + /* Release application pool */ + pj_pool_release( pool ); + + /* Destroy media endpoint. */ + pjmedia_endpt_destroy( med_endpt ); + + /* Destroy pool factory */ + pj_caching_pool_destroy( &cp ); + + /* Shutdown PJLIB */ + pj_shutdown(); + + return (status == PJ_SUCCESS) ? 0 : 1; +} diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile index 6466c57a..247ee7f4 100644 --- a/pjsip/build/Makefile +++ b/pjsip/build/Makefile @@ -75,8 +75,9 @@ export PJSIP_SIMPLE_CFLAGS += $(_CFLAGS) export PJSUA_LIB_SRCDIR = ../src/pjsua-lib export PJSUA_LIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ pjsua_acc.o pjsua_call.o pjsua_core.o \ - pjsua_im.o pjsua_media.o pjsua_pres.o -export PJSUA_LIB_CFLAGS += $(_CFLAGS) + pjsua_im.o pjsua_media.o pjsua_pres.o \ + pjsua_dump.o pjsua_vid.o +export PJSUA_LIB_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT diff --git a/pjsip/build/pjsip.dsw b/pjsip/build/pjsip.dsw deleted file mode 100644 index 06c45c2f..00000000 --- a/pjsip/build/pjsip.dsw +++ /dev/null @@ -1,152 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "pjlib"=..\..\pjlib\build\pjlib.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -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\build\pjmedia.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjmedia_codec"=..\..\pjmedia\build\pjmedia_codec.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjsip_core"=.\pjsip_core.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency -}}} - -############################################################################### - -Project: "pjsip_simple"=.\pjsip_simple.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjsip_ua"=.\pjsip_ua.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "pjsua_lib"=.\pjsua_lib.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "test_pjsip"=.\test_pjsip.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_core - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/pjsip/build/pjsip_core.dsp b/pjsip/build/pjsip_core.dsp deleted file mode 100644 index bd916bec..00000000 --- a/pjsip/build/pjsip_core.dsp +++ /dev/null @@ -1,385 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjsip_core" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=pjsip_core - 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 "pjsip_core.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 "pjsip_core.mak" CFG="pjsip_core - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjsip_core - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "pjsip_core - 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)" == "pjsip_core - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir ".\output\pjsip-core-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir ".\output\pjsip-core-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir ".\output\pjsip-core-i386-win32-vc6-release" -# PROP Intermediate_Dir ".\output\pjsip-core-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 "../../pjlib-util/include" /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/pjsip-core-i386-win32-vc6-release.lib" - -!ELSEIF "$(CFG)" == "pjsip_core - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir ".\output\pjsip-core-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir ".\output\pjsip-core-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir ".\output\pjsip-core-i386-win32-vc6-debug" -# PROP Intermediate_Dir ".\output\pjsip-core-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" /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/pjsip-core-i386-win32-vc6-debug.lib" - -!ENDIF - -# Begin Target - -# Name "pjsip_core - Win32 Release" -# Name "pjsip_core - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Group "Base (.c)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\src\pjsip\sip_config.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_errno.c -# End Source File -# End Group -# Begin Group "Messaging and Parsing (.c)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\src\pjsip\sip_msg.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_multipart.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_parser.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_tel_uri.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_uri.c -# End Source File -# End Group -# Begin Group "Core (.c)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\src\pjsip\sip_endpoint.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_util.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_util_proxy.c -# End Source File -# End Group -# Begin Group "Transport Layer (.c)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\src\pjsip\sip_resolve.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_transport.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_transport_loop.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_transport_tcp.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_transport_tls.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_transport_tls_ossl.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_transport_udp.c -# End Source File -# End Group -# Begin Group "Authentication (.c)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\src\pjsip\sip_auth_aka.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_auth_client.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_auth_msg.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_auth_parser.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_auth_server.c -# End Source File -# End Group -# Begin Group "Transaction Layer (.c)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\src\pjsip\sip_transaction.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_util_statefull.c -# End Source File -# End Group -# Begin Group "UA Layer (.c)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\src\pjsip\sip_dialog.c -# End Source File -# Begin Source File - -SOURCE=..\src\pjsip\sip_ua_layer.c -# End Source File -# End Group -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Group "Base Types (.h)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\include\pjsip\sip_config.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_errno.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_private.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_types.h -# End Source File -# End Group -# Begin Group "Messaging and Parsing (.h)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\include\pjsip\print_util.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_msg.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_multipart.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_parser.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_tel_uri.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_uri.h -# End Source File -# End Group -# Begin Group "Core (.h)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\include\pjsip\sip_endpoint.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_event.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_module.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_util.h -# End Source File -# End Group -# Begin Group "Transport Layer (.h)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\include\pjsip\sip_resolve.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_transport.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_transport_loop.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_transport_tcp.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_transport_tls.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_transport_udp.h -# End Source File -# End Group -# Begin Group "Authentication (.h)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\include\pjsip\sip_auth.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_auth_aka.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_auth_msg.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_auth_parser.h -# End Source File -# End Group -# Begin Group "Transaction Layer (.h)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\include\pjsip\sip_transaction.h -# End Source File -# End Group -# Begin Group "UA Layer (.h)" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\include\pjsip\sip_dialog.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip\sip_ua_layer.h -# End Source File -# End Group -# Begin Source File - -SOURCE=..\docs\doxygen.h -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip.h -# End Source File -# End Group -# Begin Group "Inline Files" - -# PROP Default_Filter "" -# End Group -# Begin Source File - -SOURCE=..\..\INSTALL.txt -# End Source File -# Begin Source File - -SOURCE=..\..\RELNOTES.txt -# End Source File -# End Target -# End Project diff --git a/pjsip/build/pjsip_simple.dsp b/pjsip/build/pjsip_simple.dsp deleted file mode 100644 index 8b584a97..00000000 --- a/pjsip/build/pjsip_simple.dsp +++ /dev/null @@ -1,186 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjsip_simple" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=pjsip_simple - 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 "pjsip_simple.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 "pjsip_simple.mak" CFG="pjsip_simple - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjsip_simple - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "pjsip_simple - 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)" == "pjsip_simple - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "./output/pjsip-simple-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir "./output/pjsip-simple-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "./output/pjsip-simple-i386-win32-vc6-release" -# PROP Intermediate_Dir "./output/pjsip-simple-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-util/include" /I "../../pjlib/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /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 -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo /out:"../lib/pjsip-simple-i386-win32-vc6-release.lib" - -!ELSEIF "$(CFG)" == "pjsip_simple - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "./output/pjsip-simple-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir "./output/pjsip-simple-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "./output/pjsip-simple-i386-win32-vc6-debug" -# PROP Intermediate_Dir "./output/pjsip-simple-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-util/include" /I "../../pjlib/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /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 -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo /out:"../lib/pjsip-simple-i386-win32-vc6-debug.lib" - -!ENDIF - -# Begin Target - -# Name "pjsip_simple - Win32 Release" -# Name "pjsip_simple - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE="..\src\pjsip-simple\errno.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-simple\evsub.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-simple\evsub_msg.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-simple\iscomposing.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-simple\mwi.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-simple\pidf.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-simple\presence.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-simple\presence_body.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-simple\publishc.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-simple\rpid.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-simple\xpidf.c" -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="..\include\pjsip-simple\errno.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-simple\evsub.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-simple\evsub_msg.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-simple\iscomposing.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-simple\mwi.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-simple\pidf.h" -# End Source File -# Begin Source File - -SOURCE=..\include\pjsip_simple.h -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-simple\presence.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-simple\publish.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-simple\rpid.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-simple\types.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-simple\xpidf.h" -# End Source File -# End Group -# End Target -# End Project diff --git a/pjsip/build/pjsip_test.dsp b/pjsip/build/pjsip_test.dsp deleted file mode 100644 index fd908070..00000000 --- a/pjsip/build/pjsip_test.dsp +++ /dev/null @@ -1,184 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjsip_test" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=pjsip_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 "pjsip_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 "pjsip_test.mak" CFG="pjsip_test - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjsip_test - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "pjsip_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)" == "pjsip_test - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir ".\output\test-pjsip-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir ".\output\test-pjsip-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir ".\output\test-pjsip-i386-win32-vc6-release" -# PROP Intermediate_Dir ".\output\test-pjsip-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 /Zi /O2 /Ob2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjnath/include" /I "../../pjmedia/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "PJ_WIN32" /D "PJ_M_I386" /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 -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 netapi32.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 /profile /map /debug /machine:I386 /out:"..\bin\test-pjsip-i386-win32-vc6-release.exe" /fixed:no - -!ELSEIF "$(CFG)" == "pjsip_test - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir ".\output\test-pjsip-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir ".\output\test-pjsip-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir ".\output\test-pjsip-i386-win32-vc6-debug" -# PROP Intermediate_Dir ".\output\test-pjsip-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 "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjnath/include" /I "../../pjmedia/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "PJ_WIN32" /D "PJ_M_I386" /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 -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 netapi32.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\test-pjsip-i386-win32-vc6-debug.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "pjsip_test - Win32 Release" -# Name "pjsip_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\dlg_core_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\dns_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\inv_offer_answer_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\main.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\msg_err_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\msg_logger.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\msg_test.c" -# End Source File -# Begin Source File - -SOURCE=..\src\test\multipart_test.c -# End Source File -# Begin Source File - -SOURCE="..\src\test\regc_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\transport_loop_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\transport_tcp_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\transport_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\transport_udp_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\tsx_basic_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\tsx_bench.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\tsx_uac_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\tsx_uas_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\txdata_test.c" -# End Source File -# Begin Source File - -SOURCE="..\src\test\uri_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 -# End Target -# End Project diff --git a/pjsip/build/pjsip_ua.dsp b/pjsip/build/pjsip_ua.dsp deleted file mode 100644 index 6a7b29f5..00000000 --- a/pjsip/build/pjsip_ua.dsp +++ /dev/null @@ -1,148 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjsip_ua" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=pjsip_ua - 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 "pjsip_ua.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 "pjsip_ua.mak" CFG="pjsip_ua - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjsip_ua - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "pjsip_ua - 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)" == "pjsip_ua - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir ".\output\pjsip-ua-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir ".\output\pjsip-ua-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir ".\output\pjsip-ua-i386-win32-vc6-release" -# PROP Intermediate_Dir ".\output\pjsip-ua-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 "../../pjlib-util/include" /I "../../pjmedia/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /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/pjsip-ua-i386-win32-vc6-release.lib" - -!ELSEIF "$(CFG)" == "pjsip_ua - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir ".\output\pjsip-ua-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir ".\output\pjsip-ua-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir ".\output\pjsip-ua-i386-win32-vc6-debug" -# PROP Intermediate_Dir ".\output\pjsip-ua-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 "../../pjmedia/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /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/pjsip-ua-i386-win32-vc6-debug.lib" - -!ENDIF - -# Begin Target - -# Name "pjsip_ua - Win32 Release" -# Name "pjsip_ua - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE="..\src\pjsip-ua\sip_100rel.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-ua\sip_inv.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-ua\sip_reg.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-ua\sip_replaces.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-ua\sip_timer.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsip-ua\sip_xfer.c" -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\include\pjsip_ua.h -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-ua\sip_100rel.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-ua\sip_inv.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-ua\sip_regc.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-ua\sip_replaces.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-ua\sip_timer.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsip-ua\sip_xfer.h" -# End Source File -# End Group -# End Target -# End Project diff --git a/pjsip/build/pjsua_lib.dsp b/pjsip/build/pjsua_lib.dsp deleted file mode 100644 index 3defda24..00000000 --- a/pjsip/build/pjsua_lib.dsp +++ /dev/null @@ -1,128 +0,0 @@ -# Microsoft Developer Studio Project File - Name="pjsua_lib" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=pjsua_lib - 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 "pjsua_lib.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 "pjsua_lib.mak" CFG="pjsua_lib - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "pjsua_lib - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "pjsua_lib - 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)" == "pjsua_lib - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir ".\output\pjsua-lib-i386-win32-vc6-release" -# PROP BASE Intermediate_Dir ".\output\pjsua-lib-i386-win32-vc6-release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir ".\output\pjsua-lib-i386-win32-vc6-release" -# PROP Intermediate_Dir ".\output\pjsua-lib-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 "../../pjmedia/include" /I "../../pjlib-util/include" /I "../../pjlib/include" /I "../../pjnath/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /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\pjsua-lib-i386-win32-vc6-release.lib" - -!ELSEIF "$(CFG)" == "pjsua_lib - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir ".\output\pjsua-lib-i386-win32-vc6-debug" -# PROP BASE Intermediate_Dir ".\output\pjsua-lib-i386-win32-vc6-debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir ".\output\pjsua-lib-i386-win32-vc6-debug" -# PROP Intermediate_Dir ".\output\pjsua-lib-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 "../../pjmedia/include" /I "../../pjlib-util/include" /I "../../pjlib/include" /I "../../pjnath/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /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\pjsua-lib-i386-win32-vc6-debug.lib" - -!ENDIF - -# Begin Target - -# Name "pjsua_lib - Win32 Release" -# Name "pjsua_lib - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE="..\src\pjsua-lib\pjsua_acc.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsua-lib\pjsua_call.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsua-lib\pjsua_core.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsua-lib\pjsua_im.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsua-lib\pjsua_media.c" -# End Source File -# Begin Source File - -SOURCE="..\src\pjsua-lib\pjsua_pres.c" -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="..\include\pjsua-lib\pjsua.h" -# End Source File -# Begin Source File - -SOURCE="..\include\pjsua-lib\pjsua_internal.h" -# End Source File -# End Group -# End Target -# End Project diff --git a/pjsip/build/pjsua_lib.vcproj b/pjsip/build/pjsua_lib.vcproj index 0add9ad3..f36b2ab3 100644 --- a/pjsip/build/pjsua_lib.vcproj +++ b/pjsip/build/pjsua_lib.vcproj @@ -11,16 +11,16 @@ Name="Win32" /> + + @@ -3112,6 +3116,10 @@ /> + +