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 --- 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 +++++++++++++++++++++ 24 files changed, 2635 insertions(+), 420 deletions(-) create mode 100644 pjsip-apps/src/samples/aviplay.c create mode 100644 pjsip-apps/src/samples/vid_streamutil.c (limited to 'pjsip-apps/src') diff --git a/pjsip-apps/src/ipjsua/config.cfg b/pjsip-apps/src/ipjsua/config.cfg index 3399a807..acca9051 100644 --- a/pjsip-apps/src/ipjsua/config.cfg +++ b/pjsip-apps/src/ipjsua/config.cfg @@ -1 +1,3 @@ +--auto-answer=200 +--video --clock-rate=8000 diff --git a/pjsip-apps/src/ipjsua/ipjsua.xcodeproj/project.pbxproj b/pjsip-apps/src/ipjsua/ipjsua.xcodeproj/project.pbxproj index 25a1345d..19bf84c9 100644 --- a/pjsip-apps/src/ipjsua/ipjsua.xcodeproj/project.pbxproj +++ b/pjsip-apps/src/ipjsua/ipjsua.xcodeproj/project.pbxproj @@ -18,6 +18,22 @@ 3A0D789F121E324E009D5030 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A0D789E121E324E009D5030 /* CFNetwork.framework */; }; 3A0D7ECD123DD46C009D5030 /* MainWindow-iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3A0D7ECC123DD46C009D5030 /* MainWindow-iPad.xib */; }; 3A0D7F20123F2254009D5030 /* SecondView-iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3A0D7F1F123F2254009D5030 /* SecondView-iPad.xib */; }; + 3AE06674138E6C25008EE71A /* libpjmedia-videodev-arm-apple-darwin9.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE06673138E6C25008EE71A /* libpjmedia-videodev-arm-apple-darwin9.a */; }; + 3AE06681138E6F88008EE71A /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE06680138E6F88008EE71A /* AVFoundation.framework */; }; + 3AE06683138E6FBB008EE71A /* libSDL.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE06682138E6FBB008EE71A /* libSDL.a */; }; + 3AE06693138E7056008EE71A /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE06692138E7056008EE71A /* OpenGLES.framework */; }; + 3AE06695138E70B9008EE71A /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE06694138E70B9008EE71A /* libz.dylib */; }; + 3AE06699138E70F0008EE71A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE06696138E70F0008EE71A /* CoreGraphics.framework */; }; + 3AE0669A138E70F0008EE71A /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE06697138E70F0008EE71A /* CoreMedia.framework */; }; + 3AE0669B138E70F0008EE71A /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE06698138E70F0008EE71A /* CoreVideo.framework */; }; + 3AE0669D138E710C008EE71A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE0669C138E710C008EE71A /* QuartzCore.framework */; }; + 3AE066A5138E72A4008EE71A /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE0669E138E72A4008EE71A /* libavcodec.a */; }; + 3AE066A6138E72A4008EE71A /* libavcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE0669F138E72A4008EE71A /* libavcore.a */; }; + 3AE066A7138E72A4008EE71A /* libavdevice.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE066A0138E72A4008EE71A /* libavdevice.a */; }; + 3AE066A8138E72A4008EE71A /* libavfilter.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE066A1138E72A4008EE71A /* libavfilter.a */; }; + 3AE066A9138E72A4008EE71A /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE066A2138E72A4008EE71A /* libavformat.a */; }; + 3AE066AA138E72A4008EE71A /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE066A3138E72A4008EE71A /* libavutil.a */; }; + 3AE066AB138E72A4008EE71A /* libswscale.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE066A4138E72A4008EE71A /* libswscale.a */; }; 3AE9099D11587BB900FAEAA5 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE9099C11587BB900FAEAA5 /* AudioToolbox.framework */; }; 3AE90A2D1158B52500FAEAA5 /* pjsua_app.c in Sources */ = {isa = PBXBuildFile; fileRef = 3AE90A2C1158B52500FAEAA5 /* pjsua_app.c */; }; 3AE90A6A1158C6B400FAEAA5 /* libgsmcodec-arm-apple-darwin9.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE90A691158C6B400FAEAA5 /* libgsmcodec-arm-apple-darwin9.a */; }; @@ -59,6 +75,22 @@ 3A0D789E121E324E009D5030 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; 3A0D7ECC123DD46C009D5030 /* MainWindow-iPad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "MainWindow-iPad.xib"; path = "Resources-iPad/MainWindow-iPad.xib"; sourceTree = ""; }; 3A0D7F1F123F2254009D5030 /* SecondView-iPad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "SecondView-iPad.xib"; path = "Resources-iPad/SecondView-iPad.xib"; sourceTree = ""; }; + 3AE06673138E6C25008EE71A /* libpjmedia-videodev-arm-apple-darwin9.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libpjmedia-videodev-arm-apple-darwin9.a"; path = "../../../pjmedia/lib/libpjmedia-videodev-arm-apple-darwin9.a"; sourceTree = ""; }; + 3AE06680138E6F88008EE71A /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + 3AE06682138E6FBB008EE71A /* libSDL.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libSDL.a; path = "../../../../../Library/Developer/Xcode/DerivedData/SDLiPhoneOS-fyjdxvchuwlpnghehmebkvasdrke/Build/Products/Debug-iphoneos/libSDL.a"; sourceTree = ""; }; + 3AE06692138E7056008EE71A /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; + 3AE06694138E70B9008EE71A /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; + 3AE06696138E70F0008EE71A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 3AE06697138E70F0008EE71A /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + 3AE06698138E70F0008EE71A /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + 3AE0669C138E710C008EE71A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 3AE0669E138E72A4008EE71A /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcodec.a; path = "../../../../video/ffmpeg-iphone/lib/libavcodec.a"; sourceTree = ""; }; + 3AE0669F138E72A4008EE71A /* libavcore.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcore.a; path = "../../../../video/ffmpeg-iphone/lib/libavcore.a"; sourceTree = ""; }; + 3AE066A0138E72A4008EE71A /* libavdevice.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavdevice.a; path = "../../../../video/ffmpeg-iphone/lib/libavdevice.a"; sourceTree = ""; }; + 3AE066A1138E72A4008EE71A /* libavfilter.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavfilter.a; path = "../../../../video/ffmpeg-iphone/lib/libavfilter.a"; sourceTree = ""; }; + 3AE066A2138E72A4008EE71A /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavformat.a; path = "../../../../video/ffmpeg-iphone/lib/libavformat.a"; sourceTree = ""; }; + 3AE066A3138E72A4008EE71A /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavutil.a; path = "../../../../video/ffmpeg-iphone/lib/libavutil.a"; sourceTree = ""; }; + 3AE066A4138E72A4008EE71A /* libswscale.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libswscale.a; path = "../../../../video/ffmpeg-iphone/lib/libswscale.a"; sourceTree = ""; }; 3AE9099C11587BB900FAEAA5 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 3AE90A2C1158B52500FAEAA5 /* pjsua_app.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pjsua_app.c; path = ../pjsua/pjsua_app.c; sourceTree = SOURCE_ROOT; }; 3AE90A691158C6B400FAEAA5 /* libgsmcodec-arm-apple-darwin9.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libgsmcodec-arm-apple-darwin9.a"; path = "../../../third_party/lib/libgsmcodec-arm-apple-darwin9.a"; sourceTree = SOURCE_ROOT; }; @@ -116,6 +148,22 @@ 3AE90EBB115F7BCE00FAEAA5 /* libspeex-arm-apple-darwin9.a in Frameworks */, 3AE90EBC115F7BCE00FAEAA5 /* libsrtp-arm-apple-darwin9.a in Frameworks */, 3A0D789F121E324E009D5030 /* CFNetwork.framework in Frameworks */, + 3AE06674138E6C25008EE71A /* libpjmedia-videodev-arm-apple-darwin9.a in Frameworks */, + 3AE06681138E6F88008EE71A /* AVFoundation.framework in Frameworks */, + 3AE06683138E6FBB008EE71A /* libSDL.a in Frameworks */, + 3AE06693138E7056008EE71A /* OpenGLES.framework in Frameworks */, + 3AE06695138E70B9008EE71A /* libz.dylib in Frameworks */, + 3AE06699138E70F0008EE71A /* CoreGraphics.framework in Frameworks */, + 3AE0669A138E70F0008EE71A /* CoreMedia.framework in Frameworks */, + 3AE0669B138E70F0008EE71A /* CoreVideo.framework in Frameworks */, + 3AE0669D138E710C008EE71A /* QuartzCore.framework in Frameworks */, + 3AE066A5138E72A4008EE71A /* libavcodec.a in Frameworks */, + 3AE066A6138E72A4008EE71A /* libavcore.a in Frameworks */, + 3AE066A7138E72A4008EE71A /* libavdevice.a in Frameworks */, + 3AE066A8138E72A4008EE71A /* libavfilter.a in Frameworks */, + 3AE066A9138E72A4008EE71A /* libavformat.a in Frameworks */, + 3AE066AA138E72A4008EE71A /* libavutil.a in Frameworks */, + 3AE066AB138E72A4008EE71A /* libswscale.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -184,6 +232,13 @@ 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( + 3AE06694138E70B9008EE71A /* libz.dylib */, + 3AE0669C138E710C008EE71A /* QuartzCore.framework */, + 3AE06696138E70F0008EE71A /* CoreGraphics.framework */, + 3AE06697138E70F0008EE71A /* CoreMedia.framework */, + 3AE06698138E70F0008EE71A /* CoreVideo.framework */, + 3AE06692138E7056008EE71A /* OpenGLES.framework */, + 3AE06680138E6F88008EE71A /* AVFoundation.framework */, 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, 1D30AB110D05D00D00671497 /* Foundation.framework */, 288765070DF74369002DB57D /* CoreGraphics.framework */, @@ -205,6 +260,14 @@ 3AE909B211587D2700FAEAA5 /* Libraries */ = { isa = PBXGroup; children = ( + 3AE0669E138E72A4008EE71A /* libavcodec.a */, + 3AE0669F138E72A4008EE71A /* libavcore.a */, + 3AE066A0138E72A4008EE71A /* libavdevice.a */, + 3AE066A1138E72A4008EE71A /* libavfilter.a */, + 3AE066A2138E72A4008EE71A /* libavformat.a */, + 3AE066A3138E72A4008EE71A /* libavutil.a */, + 3AE066A4138E72A4008EE71A /* libswscale.a */, + 3AE06682138E6FBB008EE71A /* libSDL.a */, 3AE90EB1115F7BCE00FAEAA5 /* libg7221codec-arm-apple-darwin9.a */, 3AE90EB2115F7BCE00FAEAA5 /* libilbccodec-arm-apple-darwin9.a */, 3AE90EB3115F7BCE00FAEAA5 /* libmilenage-arm-apple-darwin9.a */, @@ -217,6 +280,7 @@ 3AE90A6F1158C6B400FAEAA5 /* libpjmedia-arm-apple-darwin9.a */, 3AE90A711158C6B400FAEAA5 /* libpjmedia-audiodev-arm-apple-darwin9.a */, 3AE90A731158C6B400FAEAA5 /* libpjmedia-codec-arm-apple-darwin9.a */, + 3AE06673138E6C25008EE71A /* libpjmedia-videodev-arm-apple-darwin9.a */, 3AE90A751158C6B400FAEAA5 /* libpjnath-arm-apple-darwin9.a */, 3AE90A771158C6B400FAEAA5 /* libpjsip-arm-apple-darwin9.a */, 3AE90A791158C6B400FAEAA5 /* libpjsip-simple-arm-apple-darwin9.a */, @@ -257,7 +321,11 @@ }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "ipjsua" */; compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 29B97314FDCFA39411CA2CEA /* ipjsua */; projectDirPath = ""; projectRoot = ""; @@ -319,9 +387,13 @@ "\"$(SRCROOT)/../../../pjmedia/lib\"", "\"$(SRCROOT)/../../../pjnath/lib\"", "\"$(SRCROOT)/../../../pjsip/lib\"", + "\"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/AVFoundation.framework\"", + "\"$(SRCROOT)/../../../../../Library/Developer/Xcode/DerivedData/SDLiPhoneOS-fyjdxvchuwlpnghehmebkvasdrke/Build/Products/Debug-iphoneos\"", + "\"$(SRCROOT)/../../../../video/ffmpeg-iphone/lib\"", ); + ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = ipjsua; - SDKROOT = iphoneos3.2; + SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -344,9 +416,13 @@ "\"$(SRCROOT)/../../../pjmedia/lib\"", "\"$(SRCROOT)/../../../pjnath/lib\"", "\"$(SRCROOT)/../../../pjsip/lib\"", + "\"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/AVFoundation.framework\"", + "\"$(SRCROOT)/../../../../../Library/Developer/Xcode/DerivedData/SDLiPhoneOS-fyjdxvchuwlpnghehmebkvasdrke/Build/Products/Debug-iphoneos\"", + "\"$(SRCROOT)/../../../../video/ffmpeg-iphone/lib\"", ); + ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = ipjsua; - SDKROOT = iphoneos3.2; + SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -375,9 +451,10 @@ ../../../pjsip/lib, ../../../third_party/lib, ); + ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = ""; PREBINDING = NO; - SDKROOT = iphoneos3.2; + SDKROOT = iphoneos; }; name = Debug; }; @@ -405,9 +482,10 @@ ../../../pjsip/lib, ../../../third_party/lib, ); + ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = ""; PREBINDING = NO; - SDKROOT = iphoneos3.2; + SDKROOT = iphoneos; }; name = Release; }; diff --git a/pjsip-apps/src/pjsua/main.c b/pjsip-apps/src/pjsua/main.c index f65aa03f..8469d3b7 100644 --- a/pjsip-apps/src/pjsua/main.c +++ b/pjsip-apps/src/pjsua/main.c @@ -19,6 +19,7 @@ */ #include + #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; +} -- cgit v1.2.3