From: <bc...@us...> - 2007-10-31 17:31:25
|
Revision: 1250 http://iaxclient.svn.sourceforge.net/iaxclient/?rev=1250&view=rev Author: bcholew Date: 2007-10-31 10:31:21 -0700 (Wed, 31 Oct 2007) Log Message: ----------- Add video capture device notification feature - for device plug-n-play. Add two new events: IAXC_EVENT_VIDCAP_ERROR (to signify a vidcap device error) and IAXC_EVENT_VIDCAP_DEVICE (to signify a potential change to the vidcap device list). Two new functions: iaxc_video_devices_get() and iaxc_video_devices_set() to retrieve a vidcap device list, and select one for capture. Don't acquire a vidcap device until asked to capture. Release device when capture is stopped. Update vtestcall to take advantage of video device selection. Modified Paths: -------------- trunk/README.VisualStudio trunk/contrib/win/vs2005/iaxclient.sln trunk/lib/iaxclient.h trunk/lib/video.c trunk/simpleclient/vtestcall/vtestcall.c Added Paths: ----------- trunk/contrib/win/vs2005/libvidcap.vcproj Modified: trunk/README.VisualStudio =================================================================== --- trunk/README.VisualStudio 2007-10-31 13:10:33 UTC (rev 1249) +++ trunk/README.VisualStudio 2007-10-31 17:31:21 UTC (rev 1250) @@ -28,18 +28,19 @@ http://www.microsoft.com/downloads/details.aspx?FamilyID=c2b1e300-f358-4523-b479-f53d234cdccf 2) Obtain dependencies. At the time of this writing, portaudio-v19, - libogg 1.1.3, speex 1.2beta1, and libtheora 1.0alpha7 are required. + libogg 1.1.3, speex 1.2beta1, libtheora 1.0alpha7, and libvidcap are required. Source for these dependencies are available here: http://portaudio.com/archives/pa_snapshot_v19.tar.gz http://downloads.xiph.org/releases/ogg/libogg-1.1.3.tar.gz http://downloads.xiph.org/releases/speex/speex-1.2beta1.tar.gz http://downloads.xiph.org/releases/theora/libtheora-1.0alpha7.tar.gz + http://downloads.sourceforge.net/libvidcap/libvidcap-0.1.tar.gz In order to build with the vcproj files provided by iaxclient, these dependent libraries must be moved to be peer directories to the iaxclient source directory. They also must have the following - names: libogg, speex, and libtheora. So the final directory + names: libogg, speex, libtheora and libvidcap. So the final directory layout would be as follows: C:\...\whereever\iaxclient @@ -47,6 +48,7 @@ C:\...\whereever\libogg C:\...\whereever\speex C:\...\whereever\libtheora + C:\...\whereever\libvidcap 2) Open the solution file: contrib/win/vs2005/iaxclient.sln Modified: trunk/contrib/win/vs2005/iaxclient.sln =================================================================== --- trunk/contrib/win/vs2005/iaxclient.sln 2007-10-31 13:10:33 UTC (rev 1249) +++ trunk/contrib/win/vs2005/iaxclient.sln 2007-10-31 17:31:21 UTC (rev 1250) @@ -7,14 +7,14 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libiaxclient", "libiaxclient.vcproj", "{9A9C003E-EAF6-4D0E-896F-E3994503C7E4}" ProjectSection(ProjectDependencies) = postProject + {82C9BD79-9796-405F-8A28-3F538514AC3A} = {82C9BD79-9796-405F-8A28-3F538514AC3A} + {3A76129B-55AB-4D54-BAA7-08F63ED52569} = {3A76129B-55AB-4D54-BAA7-08F63ED52569} + {5CC054B7-6DAA-46BF-9A08-3B33B83E8D3E} = {5CC054B7-6DAA-46BF-9A08-3B33B83E8D3E} + {2F463562-375D-481E-A6E0-7C7D0DC1ED7A} = {2F463562-375D-481E-A6E0-7C7D0DC1ED7A} + {E972C52F-9E85-4D65-B19C-031E511E9DB4} = {E972C52F-9E85-4D65-B19C-031E511E9DB4} + {E2C6AD95-7A61-41FE-8754-A4623C891BF8} = {E2C6AD95-7A61-41FE-8754-A4623C891BF8} + {F5166D99-32BB-40D5-BE95-6F97F72C44CE} = {F5166D99-32BB-40D5-BE95-6F97F72C44CE} {3B023516-2C69-4CCB-9302-239991B6EC2C} = {3B023516-2C69-4CCB-9302-239991B6EC2C} - {F5166D99-32BB-40D5-BE95-6F97F72C44CE} = {F5166D99-32BB-40D5-BE95-6F97F72C44CE} - {E2C6AD95-7A61-41FE-8754-A4623C891BF8} = {E2C6AD95-7A61-41FE-8754-A4623C891BF8} - {E972C52F-9E85-4D65-B19C-031E511E9DB4} = {E972C52F-9E85-4D65-B19C-031E511E9DB4} - {2F463562-375D-481E-A6E0-7C7D0DC1ED7A} = {2F463562-375D-481E-A6E0-7C7D0DC1ED7A} - {5CC054B7-6DAA-46BF-9A08-3B33B83E8D3E} = {5CC054B7-6DAA-46BF-9A08-3B33B83E8D3E} - {3A76129B-55AB-4D54-BAA7-08F63ED52569} = {3A76129B-55AB-4D54-BAA7-08F63ED52569} - {82C9BD79-9796-405F-8A28-3F538514AC3A} = {82C9BD79-9796-405F-8A28-3F538514AC3A} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libiax2", "libiax2.vcproj", "{5CC054B7-6DAA-46BF-9A08-3B33B83E8D3E}" @@ -34,6 +34,11 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libvidcap", "..\..\..\..\libvidcap\contrib\win\vs2005\libvidcap.vcproj", "{F5166D99-32BB-40D5-BE95-6F97F72C44CE}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vtestcall", "..\..\..\simpleclient\vtestcall\vtestcall.vcproj", "{B5F8E725-85A8-4CB1-8824-B82127BB2B1E}" + ProjectSection(ProjectDependencies) = postProject + {9A9C003E-EAF6-4D0E-896F-E3994503C7E4} = {9A9C003E-EAF6-4D0E-896F-E3994503C7E4} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug_dll|Win32 = Debug_dll|Win32 @@ -122,6 +127,14 @@ {F5166D99-32BB-40D5-BE95-6F97F72C44CE}.Release_dll|Win32.Build.0 = Release|Win32 {F5166D99-32BB-40D5-BE95-6F97F72C44CE}.Release|Win32.ActiveCfg = Release|Win32 {F5166D99-32BB-40D5-BE95-6F97F72C44CE}.Release|Win32.Build.0 = Release|Win32 + {B5F8E725-85A8-4CB1-8824-B82127BB2B1E}.Debug_dll|Win32.ActiveCfg = Debug|Win32 + {B5F8E725-85A8-4CB1-8824-B82127BB2B1E}.Debug_dll|Win32.Build.0 = Debug|Win32 + {B5F8E725-85A8-4CB1-8824-B82127BB2B1E}.Debug|Win32.ActiveCfg = Debug|Win32 + {B5F8E725-85A8-4CB1-8824-B82127BB2B1E}.Debug|Win32.Build.0 = Debug|Win32 + {B5F8E725-85A8-4CB1-8824-B82127BB2B1E}.Release_dll|Win32.ActiveCfg = Release|Win32 + {B5F8E725-85A8-4CB1-8824-B82127BB2B1E}.Release_dll|Win32.Build.0 = Release|Win32 + {B5F8E725-85A8-4CB1-8824-B82127BB2B1E}.Release|Win32.ActiveCfg = Release|Win32 + {B5F8E725-85A8-4CB1-8824-B82127BB2B1E}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE Added: trunk/contrib/win/vs2005/libvidcap.vcproj =================================================================== --- trunk/contrib/win/vs2005/libvidcap.vcproj (rev 0) +++ trunk/contrib/win/vs2005/libvidcap.vcproj 2007-10-31 17:31:21 UTC (rev 1250) @@ -0,0 +1,361 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="libvidcap" + ProjectGUID="{F5166D99-32BB-40D5-BE95-6F97F72C44CE}" + RootNamespace="libvidcap" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)\$(ConfigurationName)\$(ProjectName)" + IntermediateDirectory="$(OutDir)" + ConfigurationType="4" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\..\..\..\libvidcap\src;..\..\..\..\libvidcap\src\directshow;..\..\..\..\libvidcap\include;"$(DXSDK_DIR)\Include"" + PreprocessorDefinitions="WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;HAVE_DIRECTSHOW;HAVE_SLEEP" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="4" + CompileAs="1" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)\$(ConfigurationName)\$(ProjectName)" + IntermediateDirectory="$(OutDir)" + ConfigurationType="4" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="..\..\..\..\libvidcap\src;..\..\..\..\libvidcap\src\directshow;..\..\..\..\libvidcap\include;"$(DXSDK_DIR)\Include"" + PreprocessorDefinitions="WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;HAVE_DIRECTSHOW;HAVE_SLEEP" + RuntimeLibrary="2" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="true" + DebugInformationFormat="3" + CompileAs="1" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath="..\..\..\..\libvidcap\src\conv.c" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\conv_to_i420.c" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\conv_to_rgb.c" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\conv_to_yuy2.c" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\directshow\DevMonitor.cpp" + > + <FileConfiguration + Name="Debug|Win32" + > + <Tool + Name="VCCLCompilerTool" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32" + > + <Tool + Name="VCCLCompilerTool" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\directshow\DirectShowSource.cpp" + > + <FileConfiguration + Name="Debug|Win32" + > + <Tool + Name="VCCLCompilerTool" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32" + > + <Tool + Name="VCCLCompilerTool" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\directshow\DShowSrcManager.cpp" + > + <FileConfiguration + Name="Debug|Win32" + > + <Tool + Name="VCCLCompilerTool" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32" + > + <Tool + Name="VCCLCompilerTool" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\directshow\GraphMonitor.cpp" + > + <FileConfiguration + Name="Debug|Win32" + > + <Tool + Name="VCCLCompilerTool" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32" + > + <Tool + Name="VCCLCompilerTool" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\hotlist.c" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\logging.c" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\sapi.c" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\sapi_dshow.cpp" + > + <FileConfiguration + Name="Debug|Win32" + > + <Tool + Name="VCCLCompilerTool" + CompileAs="2" + /> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32" + > + <Tool + Name="VCCLCompilerTool" + CompileAs="2" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\sliding_window.c" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\vidcap.c" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\..\..\..\libvidcap\src\conv.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\include\vidcap\converters.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\directshow\DevMonitor.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\directshow\DirectShowSource.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\directshow\DShowSrcManager.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\directshow\GraphMonitor.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\hotlist.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\directshow\LocklessQueue.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\logging.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\logging.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\sapi.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\sapi_context.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\src\sliding_window.h" + > + </File> + <File + RelativePath="..\..\..\..\libvidcap\include\vidcap\vidcap.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> Modified: trunk/lib/iaxclient.h =================================================================== --- trunk/lib/iaxclient.h 2007-10-31 13:10:33 UTC (rev 1249) +++ trunk/lib/iaxclient.h 2007-10-31 17:31:21 UTC (rev 1250) @@ -132,6 +132,8 @@ #define IAXC_EVENT_DTMF 9 /*!< Indicates a DTMF event */ #define IAXC_EVENT_AUDIO 10 /*!< Indicates an audio event */ #define IAXC_EVENT_VIDEOSTATS 11 /*!< Indicates a video statistics update event */ +#define IAXC_EVENT_VIDCAP_ERROR 12 /*!< Indicates a video capture error occurred */ +#define IAXC_EVENT_VIDCAP_DEVICE 13 /*!< Indicates a possible video capture device insertion/removal */ #define IAXC_CALL_STATE_FREE 0 /*!< Indicates a call slot is free */ #define IAXC_CALL_STATE_ACTIVE (1<<1) /*!< Indicates a call is active */ @@ -920,6 +922,26 @@ */ EXPORT int iaxc_get_netstats(int call, int *rtt, struct iaxc_netstat *local, struct iaxc_netstat *remote); +/*! + A structure containing information about a video capture device. +*/ +struct iaxc_video_device { + /*! + The "human readable" name of the device + */ + const char *name; + + /*! + unique id of the device + */ + const char *id_string; + + /*! + iaxclient id of the device + */ + int id; +}; + #define IAXC_AD_INPUT (1<<0) /*!< Device is usable for input*/ #define IAXC_AD_OUTPUT (1<<1) /*!< Device is usable for output */ #define IAXC_AD_RING (1<<2) /*!< Device is usable for ring */ @@ -1111,6 +1133,23 @@ */ EXPORT int iaxc_set_audio_prefs(unsigned int prefs); +/*! + Get video capture device information: + \param devs Returns an array of iaxc_video_device structures. + The array will will be valid as long as iaxc is initialized. + \param nDevs Returns the number of devices in the devs array + \param devId Returns the id of the currently selected video capture device + + \return -1 on error, 0 if no change to the device list, 1 if it's been updated + */ +EXPORT int iaxc_video_devices_get(struct iaxc_video_device **devs, int *nDevs, int *devId); + +/*! + Sets the current video capture device + \param devId The id of the device to use for video capture + */ +EXPORT int iaxc_video_device_set(int devId); + /* * Acceptable range for video rezolution */ Modified: trunk/lib/video.c =================================================================== --- trunk/lib/video.c 2007-10-31 13:10:33 UTC (rev 1249) +++ trunk/lib/video.c 2007-10-31 17:31:21 UTC (rev 1250) @@ -31,13 +31,16 @@ #include "codec_theora.h" #endif +#if defined(WIN32) +#define strdup _strdup +#endif + struct video_info { vidcap_state * vc; vidcap_sapi * sapi; vidcap_src * src; struct vidcap_sapi_info sapi_info; - struct vidcap_src_info src_info; struct vidcap_fmt_info fmt_info; /* these are the requested (post-scaling) dimensions */ @@ -63,6 +66,15 @@ int prefs; struct slicer_context * sc; + + /* these two struct arrays are correlated by index */ + struct vidcap_src_info * vc_src_info; + struct iaxc_video_device * devices; + MUTEX dev_list_lock; + + int device_count; + int selected_device_id; + int next_id; }; struct video_format_info @@ -492,6 +504,24 @@ */ } +static int video_device_notification_callback(vidcap_sapi *sapi, + void * user_context) +{ + iaxc_event evt; + + if ( sapi != vinfo.sapi ) + { + fprintf(stderr, "ERROR: wrong sapi in device notification\n"); + return -1; + } + + /* notify application that device list has been updated */ + evt.type = IAXC_EVENT_VIDCAP_DEVICE; + iaxci_post_event(evt); + + return 0; +} + static int capture_callback(vidcap_src * src, void * user_data, struct vidcap_capture_info * cap_info) { @@ -517,6 +547,8 @@ const char * source_buf = 0; int source_buf_size = 0; + iaxc_event evt; + int i; if ( cap_info->error_status ) @@ -524,6 +556,9 @@ fprintf(stderr, "vidcap capture error %d\n", cap_info->error_status); vinfo.capturing = 0; + + evt.type = IAXC_EVENT_VIDCAP_ERROR; + iaxci_post_event(evt); return -1; } @@ -732,30 +767,10 @@ }; static const int fourcc_list_len = sizeof(fourcc_list) / sizeof(int); - int i; static const int max_factor = 2; int scale_factor; + int i; - if ( !vinfo.src ) - { - /* Acquire the default source */ - if ( !(vinfo.src = vidcap_src_acquire(vinfo.sapi, 0)) ) - { - fprintf(stderr, "failed to acquire video source\n"); - return -1; - } - - if ( vidcap_src_info_get(vinfo.src, &vinfo.src_info) ) - { - fprintf(stderr, "failed to get video source info\n"); - return -1; - } - - fprintf(stderr, "acquired vidcap source %s (%s)\n", - vinfo.src_info.description, - vinfo.src_info.identifier); - } - vinfo.width = vfinfo.width; vinfo.height = vfinfo.height; vinfo.fmt_info.fps_numerator = vfinfo.framerate; @@ -878,6 +893,46 @@ return 0; } +static int ensure_acquired(int dev_id) +{ + int dev_num; + + if ( !vinfo.src ) + { + MUTEXLOCK(&vinfo.dev_list_lock); + + for ( dev_num = 0; dev_num < vinfo.device_count; dev_num++ ) + { + if ( vinfo.devices[dev_num].id == dev_id ) + break; + } + + if ( dev_num == vinfo.device_count ) + { + MUTEXUNLOCK(&vinfo.dev_list_lock); + fprintf(stderr, "invalid vidcap dev id: %d\n", dev_id); + return -1; + } + + if ( !(vinfo.src = vidcap_src_acquire(vinfo.sapi, + &vinfo.vc_src_info[dev_num])) ) + { + vinfo.src = 0; + MUTEXUNLOCK(&vinfo.dev_list_lock); + fprintf(stderr, "failed to acquire video source\n"); + return -1; + } + + fprintf(stderr, "acquired vidcap source %s (%s)\n", + vinfo.vc_src_info[dev_num].description, + vinfo.vc_src_info[dev_num].identifier); + + MUTEXUNLOCK(&vinfo.dev_list_lock); + } + + return 0; +} + EXPORT int iaxc_set_video_prefs(unsigned int prefs) { const unsigned int prefs_mask = @@ -888,9 +943,14 @@ IAXC_VIDEO_PREF_SEND_DISABLE | IAXC_VIDEO_PREF_RECV_RGB32 | IAXC_VIDEO_PREF_CAPTURE_DISABLE; + int ret; if ( prefs & ~prefs_mask ) + { + fprintf(stderr, "ERROR: unexpected video preference: 0x%0x\n", + prefs); return -1; + } vinfo.prefs = prefs; @@ -911,7 +971,15 @@ { if ( vidcap_src_capture_stop(vinfo.src) ) fprintf(stderr, "failed vidcap_src_capture_stop\n"); + vinfo.capturing = 0; + + if ( vinfo.src && vidcap_src_release(vinfo.src) ) + { + fprintf(stderr, "failed to release a video source\n"); + } + + vinfo.src = 0; } MUTEXUNLOCK(&vinfo.camera_lock); } @@ -920,17 +988,30 @@ MUTEXLOCK(&vinfo.camera_lock); if ( !vinfo.capturing ) { + if ( vinfo.selected_device_id < 0 ) + { + MUTEXUNLOCK(&vinfo.camera_lock); + return -1; + } + + if ( ensure_acquired(vinfo.selected_device_id) ) + { + MUTEXUNLOCK(&vinfo.camera_lock); + return -1; + } + if ( prepare_for_capture() ) { MUTEXUNLOCK(&vinfo.camera_lock); return -1; } - if ( vidcap_src_capture_start(vinfo.src, - capture_callback, 0) ) + if ( (ret = vidcap_src_capture_start(vinfo.src, + capture_callback, 0)) ) { MUTEXUNLOCK(&vinfo.camera_lock); - fprintf(stderr, "failed to start video capture\n"); + fprintf(stderr, "failed to start video capture: %d\n", + ret); return -1; } @@ -1231,10 +1312,230 @@ return 0; } +EXPORT int iaxc_video_devices_get(struct iaxc_video_device **devs, + int *num_devs, int *id_selected) +{ + int new_device_count; + int old_device_count; + struct vidcap_src_info *new_src_list; + struct vidcap_src_info *old_src_list; + struct iaxc_video_device *new_iaxc_dev_list; + struct iaxc_video_device *old_iaxc_dev_list; + int found_selected_device = 0; + int list_changed = 0; + int i, n; + + /* update libvidcap's device list */ + new_device_count = vidcap_src_list_update(vinfo.sapi); + if ( new_device_count != vinfo.device_count ) + list_changed = 1; + + if ( new_device_count < 0 ) + { + fprintf(stderr, "ERROR: failed getting updated vidcap device list: %d\n", + new_device_count); + return -1; + } + + new_src_list = (struct vidcap_src_info *)malloc(new_device_count * + sizeof(struct vidcap_src_info)); + if ( !new_src_list ) + { + fprintf(stderr, "ERROR: failed updated source allocation\n"); + return -1; + } + + new_iaxc_dev_list = (struct iaxc_video_device *)malloc( + new_device_count * sizeof(struct iaxc_video_device)); + if ( !new_iaxc_dev_list ) + { + free(new_src_list); + fprintf(stderr, "ERROR: failed source allocation update\n"); + return -1; + } + + /* get an updated libvidcap device list */ + if ( vidcap_src_list_get(vinfo.sapi, new_device_count, new_src_list) ) + { + fprintf(stderr, "ERROR: failed vidcap_srcList_get()\n"); + + free(new_src_list); + free(new_iaxc_dev_list); + return -1; + } + + /* build a new iaxclient video source list */ + found_selected_device = 0; + for ( n = 0; n < new_device_count; n++ ) + { + new_iaxc_dev_list[n].name = strdup(new_src_list[n].description); + new_iaxc_dev_list[n].id_string = strdup(new_src_list[n].identifier); + + /* this device may have been here all along + * Check if it has, and re-assign that device id + * else assign a new id + */ + for ( i = 0; i < vinfo.device_count; i++ ) + { + if ( !strcmp(new_iaxc_dev_list[n].name, vinfo.devices[i].name) ) + { + /*fprintf(stderr, "EXISTING DEVICE: %s - (id=%d) '%s'\n", + new_iaxc_dev_list[n].name, + vinfo.devices[i].id, + new_iaxc_dev_list[n].id_string); + */ + new_iaxc_dev_list[n].id = vinfo.devices[i].id; + + if ( vinfo.selected_device_id == new_iaxc_dev_list[n].id ) + found_selected_device = 1; + break; + } + } + if ( i == vinfo.device_count ) + { + new_iaxc_dev_list[n].id = vinfo.next_id++; + fprintf(stderr, "NEW DEVICE: %s - (id=%d) '%s'\n", + new_iaxc_dev_list[n].name, + new_iaxc_dev_list[n].id, + new_iaxc_dev_list[n].id_string); + + list_changed = 1; + } + } + + if ( !list_changed ) + { + /* Free new lists. Nothing's really changed */ + free(new_src_list); + for ( i = 0; i < new_device_count; i++ ) + { + free((void *)new_iaxc_dev_list[i].name); + free((void *)new_iaxc_dev_list[i].id_string); + } + free(new_iaxc_dev_list); + } + else + { + old_device_count = vinfo.device_count; + old_src_list = vinfo.vc_src_info; + old_iaxc_dev_list = vinfo.devices; + + /* Update iaxclient's device list info */ + /* Lock since other iaxclient funcs use these fields */ + MUTEXLOCK(&vinfo.dev_list_lock); + + vinfo.device_count = new_device_count; + vinfo.vc_src_info = new_src_list; + vinfo.devices = new_iaxc_dev_list; + + MUTEXUNLOCK(&vinfo.dev_list_lock); + + /* free old lists */ + free(old_src_list); + for ( i = 0; i < old_device_count; i++ ) + { + free((void *)old_iaxc_dev_list[i].name); + free((void *)old_iaxc_dev_list[i].id_string); + } + free(old_iaxc_dev_list); + } + + *devs = vinfo.devices; + *num_devs = vinfo.device_count; + *id_selected = found_selected_device ? vinfo.selected_device_id : -1; + + return list_changed; +} + +EXPORT int iaxc_video_device_set(int capture_dev_id) +{ + int ret = 0; + int dev_num = 0; + + MUTEXLOCK(&vinfo.camera_lock); + + if ( capture_dev_id == vinfo.selected_device_id ) + { + MUTEXUNLOCK(&vinfo.camera_lock); + return 0; + } + + if ( capture_dev_id < 0 ) + { + MUTEXUNLOCK(&vinfo.camera_lock); + fprintf(stderr, "invalid video device id ( < 0 )\n"); + return -1; + } + + MUTEXLOCK(&vinfo.dev_list_lock); + + for ( dev_num = 0; dev_num < vinfo.device_count; dev_num++ ) + { + if ( vinfo.devices[dev_num].id == capture_dev_id ) + break; + } + + if ( dev_num == vinfo.device_count ) + { + MUTEXUNLOCK(&vinfo.dev_list_lock); + MUTEXUNLOCK(&vinfo.camera_lock); + fprintf(stderr, "invalid video device id: %d\n", + capture_dev_id); + return -1; + } + + vinfo.selected_device_id = capture_dev_id; + + if ( vinfo.src && vidcap_src_release(vinfo.src) ) + { + fprintf(stderr, "failed to release video source\n"); + } + + vinfo.src = 0; + + MUTEXUNLOCK(&vinfo.dev_list_lock); + + if ( vinfo.capturing ) + { + if ( ensure_acquired(capture_dev_id) ) + { + MUTEXUNLOCK(&vinfo.camera_lock); + return -1; + } + + if ( prepare_for_capture() ) + { + MUTEXUNLOCK(&vinfo.camera_lock); + return -1; + } + + if ( (ret = vidcap_src_capture_start(vinfo.src, + capture_callback, 0)) ) + { + MUTEXUNLOCK(&vinfo.camera_lock); + fprintf(stderr, "failed to restart video capture: %d\n", ret); + return -1; + } + } + + MUTEXUNLOCK(&vinfo.camera_lock); + + return 0; +} + int video_initialize(void) { + int i; + const int starting_id = 50; + memset(&vinfo, 0, sizeof(vinfo)); + MUTEXINIT(&vinfo.camera_lock); + MUTEXINIT(&vinfo.dev_list_lock); + + vinfo.width = vfinfo.width; + vinfo.height = vfinfo.height; + if ( !(vinfo.vc = vidcap_initialize()) ) { fprintf(stderr, "ERROR: failed vidcap_initialize\n"); @@ -1257,23 +1558,89 @@ vinfo.sapi_info.description, vinfo.sapi_info.identifier); - /* TODO: Maybe we should reevaluate these defaults. Once could - * make an argument that reasonable users of iaxclient might - * not want video to be delivered as soon as they iaxc_initialize(). + vinfo.selected_device_id = -1; + + vinfo.device_count = vidcap_src_list_update(vinfo.sapi); + if ( vinfo.device_count < 0 ) + { + fprintf(stderr, + "ERROR: failed updating video capture devices list\n"); + goto bail; + } + + vinfo.vc_src_info = (struct vidcap_src_info *)malloc(vinfo.device_count * + sizeof(struct vidcap_src_info)); + if ( !vinfo.vc_src_info ) + { + fprintf(stderr, "ERROR: failed vinfo field allocations\n"); + goto bail; + } + + vinfo.devices = (struct iaxc_video_device *)malloc(vinfo.device_count * + sizeof(struct iaxc_video_device)); + if ( !vinfo.devices ) + { + fprintf(stderr, "ERROR: failed vinfo field allocation\n"); + free(vinfo.vc_src_info); + goto bail; + } + + if ( vidcap_src_list_get(vinfo.sapi, vinfo.device_count, + vinfo.vc_src_info) ) + { + fprintf(stderr, "ERROR: failed vidcap_src_list_get()\n"); + free(vinfo.vc_src_info); + free(vinfo.devices); + goto bail; + } + + /* build initial iaxclient video source list */ + for ( i = 0; i < vinfo.device_count; i++ ) + { + vinfo.devices[i].name = strdup(vinfo.vc_src_info[i].description); + vinfo.devices[i].id_string = strdup(vinfo.vc_src_info[i].identifier); + /* Let's be clear that the device id is not some + * base-zero index. Once plug-n-play is implemented, + * these ids may diverge as devices are added + * and removed. + */ + vinfo.devices[i].id = i + starting_id; + } + vinfo.next_id = vinfo.devices[vinfo.device_count - 1].id + 1; + + /* set default source - the first device */ + if ( vinfo.device_count ) + { + iaxc_video_device_set(vinfo.devices[0].id); + } + + /* setup device notification callback + * for device insertion and removal */ + if ( vidcap_srcs_notify(vinfo.sapi, &video_device_notification_callback, 0) ) + { + fprintf(stderr, "ERROR: failed vidcap_srcs_notify()\n"); + goto late_bail; + } + vinfo.prefs = IAXC_VIDEO_PREF_RECV_LOCAL_RAW | - IAXC_VIDEO_PREF_RECV_REMOTE_RAW; + IAXC_VIDEO_PREF_RECV_REMOTE_RAW | + IAXC_VIDEO_PREF_CAPTURE_DISABLE; - MUTEXINIT(&vinfo.camera_lock); + return 0; - /* We reset the existing video preferences to yield the side-effect - * of potentially starting or stopping the video capture. - */ - iaxc_set_video_prefs(vinfo.prefs); +late_bail: + free(vinfo.vc_src_info); - return 0; + for ( i = 0; i < vinfo.device_count; i++ ) + { + free((void *)vinfo.devices[i].name); + free((void *)vinfo.devices[i].id_string); + } + free(vinfo.devices); + bail: vidcap_destroy(vinfo.vc); vinfo.vc = 0; @@ -1289,6 +1656,7 @@ vinfo.vc = 0; MUTEXDESTROY(&vinfo.camera_lock); + MUTEXDESTROY(&vinfo.dev_list_lock); if ( vinfo.converted_i420_buf ) { @@ -1324,7 +1692,7 @@ * we are saying that the "camera is working" if there exists * more than zero cameras. */ - return vidcap_src_list_update(vinfo.sapi); + return vidcap_src_list_update(vinfo.sapi) > 0; } int video_send_stats(struct iaxc_call * call) Modified: trunk/simpleclient/vtestcall/vtestcall.c =================================================================== --- trunk/simpleclient/vtestcall/vtestcall.c 2007-10-31 13:10:33 UTC (rev 1249) +++ trunk/simpleclient/vtestcall/vtestcall.c 2007-10-31 17:31:21 UTC (rev 1250) @@ -238,6 +238,9 @@ e.ev.audio.source == IAXC_SOURCE_REMOTE ? "remote" : "local", e.ev.audio.encoded ? "encoded" : "raw"); break; + case IAXC_EVENT_VIDCAP_ERROR: + fprintf(stderr, "\nVIDEO CAPTURE DEVICE ERROR\n"); + break; default: break; } @@ -275,12 +278,28 @@ int nDevs, input, output,ring; int i; + int vinput; + int nVdevs; + struct iaxc_video_device *vDevs; + iaxc_audio_devices_get(&devs,&nDevs, &input, &output, &ring); + printf("\nThere are %d audio devices:\n", nDevs); for(i=0;i<nDevs;i++) { - fprintf(stderr, "DEVICE ID=%d NAME=%s CAPS=%lx\n", devs[i].devID, devs[i].name, devs[i].capabilities); + fprintf(stderr, "AUDIO DEVICE ID = %d NAME = %s CAPS=%lx\n", + devs[i].devID, devs[i].name, devs[i].capabilities); iaxc_audio_devices_set(input,output,ring); } + + iaxc_video_devices_get(&vDevs, &nVdevs, &vinput); + printf("\nThere are %d video capture devices:\n", nVdevs); + for ( i = 0; i < nVdevs; i++ ) + { + printf("VIDEO DEVICE ID = %d NAME = %s\n", + vDevs[i].id, vDevs[i].name); + } + printf("Currently selected VIDEO device id: %d\n", vinput); + } void usage() @@ -363,7 +382,11 @@ exit(-1); } - if ( v.size <= 0 ) fprintf(stderr, "WARNING: size %d in callback\n", v.size); + if ( v.size <= 0 ) + { + fprintf(stderr, "WARNING: size %d in callback\n", v.size); + return 0; + } if ( !remote ) { @@ -528,12 +551,18 @@ //iaxc_set_audio_prefs(IAXC_AUDIO_PREF_RECV_LOCAL_RAW | IAXC_AUDIO_PREF_RECV_REMOTE_RAW); fprintf(f, "\n\ - TestCall accept some keyboard input while it's running.\n\ + vtestcall accepts keyboard input while it's running.\n\ You must hit 'enter' for your keypresses to be recognized,\n\ - although you can type more than one key on a line\n\ + although you can type more than one key on a line:\n\ \n\ - q: drop the call and hangup.\n\ - 0-9 * or #: dial those DTMF digits.\n"); + s: switch video capture devices \n\ + b: bypass video jitter stuff\n\ + r: reject incoming call\n\ + d: dial\n\ + c: set caller id info\n\ + w: video window toggle\n\ + 0-9 * or #: dial those DTMF digits\n\ + q: drop the call and hangup\n"); printf("Starting processing thread...\n"); iaxc_start_processing_thread(); @@ -615,6 +644,39 @@ iaxc_set_callerid(myCIDname,myCIDnumber); } break; + case SDLK_s: + { + int input; + int ndevs; + struct iaxc_video_device *vDevs; + int newVideoDevId; + + iaxc_video_devices_get(&vDevs, &ndevs, &input); + + printf("There are %d video capture devices:\n", ndevs); + for ( i = 0; i < ndevs; i++ ) + { + printf("VIDEO DEVICE ID = %d NAME = %s\n", vDevs[i].id, vDevs[i].name); + } + printf("Currently selected device id: %d\n", input); + + printf("Select video capture device: "); + fflush(stdin); + fscanf(stdin,"%d", &newVideoDevId); + + if ( iaxc_video_device_set(newVideoDevId) ) + { + printf("Error selecting device id %d\n", + newVideoDevId); + break; + } + + /* explicitly set the prefs again in case + * previous capture device has failed + */ + iaxc_set_video_prefs( iaxc_get_video_prefs() ); + } + break; case SDLK_t: /* transmit-only */ printf("transmit mode active\n"); break; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |