Procházet zdrojové kódy

#IQRV #comment libaudioframework 编译

80374374 před 1 rokem
rodič
revize
479c595985

+ 2 - 0
Other/win/CMakeLists.txt

@@ -16,6 +16,8 @@ add_subdirectory(libmediaplayer)
 add_subdirectory(libaudioqueue)
 # lack of SDL
 add_subdirectory(libpictureplayer)
+add_subdirectory(libaudiorender)
+add_subdirectory(libaudioframework)
 
 # 汇总要依赖拷贝的第三方库
 set(RVC_CONAN_DEP_LIBS ${RVC_CONAN_DEP_LIBS} ${OTHER_CONAN_DEP_LIBS} PARENT_SCOPE)

+ 150 - 0
Other/win/libaudioframework/CMakeLists.txt

@@ -0,0 +1,150 @@
+set(MODULE_NAME "libaudioframework")
+set(MODULE_PREFIX "LIB_AUDIOFRAMEWORK_FUNC")
+add_compile_options(-fPIC)
+
+if(RVC_DEBUG_MODE)
+    set(SPBASE_LIB spbased)
+	set(RVCCOMM_LIB RVCCommD)
+else()
+    set(SPBASE_LIB spbase)
+	set(RVCCOMM_LIB RVCComm)
+endif(RVC_DEBUG_MODE)
+
+set(${MODULE_PREFIX}_SRCS
+	audioaec.c
+	audiobridge.c
+	audiocache.c
+	audiocodec.c
+	audiocommon.c
+	audioconf.c
+	audiocontext.c
+	audiocustom.c
+	audiodbmeter.c
+	audiodriver.c
+	audiodrivergroup.c
+	audiodsp.c
+	audiodtmf.c
+	audiodtmfbridge.c
+	audioengine.c
+	audiofax.c
+	audiofile.c
+	audiolog.c
+	audiomicspk2.c
+	audiomixer.c
+	audiorecply.c
+	audioresample.c
+	audioresize.c
+	audiortp.c
+	audiosilencedet.c
+	audiostream.c
+	audiotone.c
+	audiovoxfile.c
+	audiowavfile.c
+	codec/g729acodec.c
+	precompile.c
+	codec/adpcmcodec.c
+	codec/bvcodec.c
+	codec/codecmgr.c
+	codec/g711codec.c
+	codec/gsmcodec.c
+	other/delaybuf.c
+	other/jbuf.c
+	other/plc.c
+	other/silencedet.c
+	other/wsola.c
+)
+
+add_library(${MODULE_NAME} STATIC ${${MODULE_PREFIX}_SRCS})
+
+if(WIN32)
+target_include_directories(${MODULE_NAME} PRIVATE
+	${RVC_COMMON_INCLUDE_DIR}
+	${CONAN_INCLUDE_DIRS_FFMPEG}
+	${CONAN_INCLUDE_DIRS_PORTAUDIO}
+	${CONAN_RVCFRAMEWORK_ROOT}/include
+	${CONAN_INCLUDE_DIRS_SPEEXDSP}
+	${RVC_TOOLKIT_INCLUDE_DIR}
+	${CONAN_INCLUDE_DIRS_LIB8K}
+	${CONAN_INCLUDE_DIRS_LIBTIFF}
+	${CONAN_INCLUDE_DIRS_APACHE-APR}/include
+	${CONAN_INCLUDE_DIRS_SPANDSP}/src
+	)
+else(WIN32)
+target_include_directories(${MODULE_NAME} PRIVATE
+	${RVC_COMMON_INCLUDE_DIR}
+	${CONAN_INCLUDE_DIRS_FFMPEG}
+	${CONAN_INCLUDE_DIRS_PULSEAUDIO}
+	${CONAN_RVCFRAMEWORK_ROOT}/include
+	${CONAN_INCLUDE_DIRS_SPEEXDSP}
+	${RVC_TOOLKIT_INCLUDE_DIR}
+	${CONAN_INCLUDE_DIRS_LIB8K}
+	${CONAN_INCLUDE_DIRS_LIBTIFF}
+	${CONAN_INCLUDE_DIRS_APR}/apr-1
+	${CONAN_RVCFRAMEWORK_ROOT}/include/winpr
+	${CONAN_INCLUDE_DIRS_SPANDSP}
+	)
+endif(WIN32)
+
+
+target_link_directories(${MODULE_NAME} PRIVATE
+	${CONAN_LIB_DIRS_FFMPEG}
+	${CONAN_LIB_DIRS_RVCFRAMEWORK}
+	${CONAN_LIB_DIRS_SPANDSP}
+	${CONAN_LIB_DIRS_SPEEXDSP}
+	${CONAN_LIB_DIRS_LIB8K}
+	${CONAN_LIB_DIRS_LIBTIFF}
+	if(WIN32)
+	${CONAN_LIB_DIRS_APACHE-APR}
+	${CONAN_LIB_DIRS_PORTAUDIO}
+	else
+	${CONAN_LIB_DIRS_APR}
+	${CONAN_LIB_DIRS_PULSEAUDIO}
+	endif(WIN32)
+	)
+
+
+	# 添加实体需要依赖的其他共享库(包括系统库)
+if(WIN32)
+set(${MODULE_PREFIX}_LIBS PRIVATE
+	${CONAN_PKG_LIBS_PORTAUDIO}
+	${CONAN_PKG_LIBS_FFMPEG}
+	${CONAN_LIBS_OPENCV}
+	${SPBASE_LIB}
+	${TOOLKIT_LIB}
+	${CONAN_PKG_LIBS_APACHE-APR}
+	${CONAN_PKG_LIBS_LIB8K}
+	${CONAN_PKG_LIBS_SPANDSP}
+	${CONAN_PKG_LIBS_SOFIA}
+	${CONAN_PKG_LIBS_IPP}
+	${CONAN_PKG_LIBS_SPEEXDSP}
+
+)
+else(WIN32)
+set(${MODULE_PREFIX}_LIBS PRIVATE
+	${CONAN_PKG_LIBS_FFMPEG}
+	${CONAN_PKG_LIBS_PULSEAUDIO}
+	${CONAN_LIBS_OPENCV}
+	${SPBASE_LIB}
+	${TOOLKIT_LIB}
+	${CONAN_PKG_LIBS_APR}
+	${CONAN_PKG_LIBS_SPANDSP}
+	${CONAN_PKG_LIBS_SOFIA}
+	${CONAN_PKG_LIBS_SPEEXDSP}
+	asound
+	pthread
+	winpr2
+	${RVCCOMM_LIB}
+)
+endif(WIN32)
+
+target_link_libraries(${MODULE_NAME} PRIVATE ${${MODULE_PREFIX}_LIBS})  
+
+
+if(MSVC)
+set(OTHER_CONAN_DEP_LIBS ${OTHER_CONAN_DEP_LIBS} ${CONAN_BIN_DIRS} PARENT_SCOPE)
+else()
+set(OTHER_CONAN_DEP_LIBS ${OTHER_CONAN_DEP_LIBS} ${CONAN_LIB_DIRS} PARENT_SCOPE)
+ENDIF(MSVC)
+
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "other/")
+

+ 44 - 0
Other/win/libaudiorender/CMakeLists.txt

@@ -0,0 +1,44 @@
+set(MODULE_NAME "audiorender")
+set(MODULE_PREFIX "LIB_AUDIORENDER_FUNC")
+
+set(${MODULE_PREFIX}_SRCS
+    iaudiorenderinterface.cpp
+	libaudiorender.cpp
+	stdafx.cpp
+)
+
+add_library(${MODULE_NAME} SHARED ${${MODULE_PREFIX}_SRCS})
+
+
+target_include_directories(${MODULE_NAME} PRIVATE
+		${CONAN_INCLUDE_DIRS_SPEEX}/include
+		${OTHER_LIB_PLATFORM_BASE_DIR}/libaudioqueue
+	)
+
+
+target_link_directories(${MODULE_NAME} PRIVATE
+	${CONAN_LIB_DIRS_SPEEX}
+	)
+
+
+target_link_libraries(${MODULE_NAME} PRIVATE
+	audioqueue
+	${CONAN_PKG_LIBS_SPEEX}
+)  
+
+
+target_compile_definitions(${MODULE_NAME} PUBLIC "LIBAUDIORENDER_EXPORTS")
+
+if(MSVC)
+	install(TARGETS ${MODULE_NAME} 
+    RUNTIME DESTINATION "${RVC_RUNTIME_PATH}" COMPONENT libraries
+    ARCHIVE DESTINATION "${RVC_LIBRARY_PATH}" COMPONENT develops EXCLUDE_FROM_ALL
+    LIBRARY DESTINATION "${RVC_LIBRARY_PATH}" COMPONENT libraries
+    )
+else()
+install(TARGETS ${MODULE_NAME} 
+    RUNTIME DESTINATION "${RVC_RUNTIME_PATH}"
+    ARCHIVE DESTINATION "${RVC_LIBRARY_PATH}"
+    LIBRARY DESTINATION "${RVC_RUNTIME_PATH}"
+    COMPONENT libraries)
+endif(MSVC)

+ 48 - 0
Other/win/libaudiorender/ReadMe.txt

@@ -0,0 +1,48 @@
+========================================================================
+    DYNAMIC LINK LIBRARY : libaudiorender Project Overview
+========================================================================
+
+AppWizard has created this libaudiorender DLL for you.
+
+This file contains a summary of what you will find in each of the files that
+make up your libaudiorender application.
+
+
+libaudiorender.vcxproj
+    This is the main project file for VC++ projects generated using an Application Wizard.
+    It contains information about the version of Visual C++ that generated the file, and
+    information about the platforms, configurations, and project features selected with the
+    Application Wizard.
+
+libaudiorender.vcxproj.filters
+    This is the filters file for VC++ projects generated using an Application Wizard. 
+    It contains information about the association between the files in your project 
+    and the filters. This association is used in the IDE to show grouping of files with
+    similar extensions under a specific node (for e.g. ".cpp" files are associated with the
+    "Source Files" filter).
+
+libaudiorender.cpp
+    This is the main DLL source file.
+
+	When created, this DLL does not export any symbols. As a result, it
+	will not produce a .lib file when it is built. If you wish this project
+	to be a project dependency of some other project, you will either need to
+	add code to export some symbols from the DLL so that an export library
+	will be produced, or you can set the Ignore Input Library property to Yes
+	on the General propert page of the Linker folder in the project's Property
+	Pages dialog box.
+
+/////////////////////////////////////////////////////////////////////////////
+Other standard files:
+
+StdAfx.h, StdAfx.cpp
+    These files are used to build a precompiled header (PCH) file
+    named libaudiorender.pch and a precompiled types file named StdAfx.obj.
+
+/////////////////////////////////////////////////////////////////////////////
+Other notes:
+
+AppWizard uses "TODO:" comments to indicate parts of the source code you
+should add to or customize.
+
+/////////////////////////////////////////////////////////////////////////////

+ 19 - 0
Other/win/libaudiorender/dllmain.cpp

@@ -0,0 +1,19 @@
+// dllmain.cpp : Defines the entry point for the DLL application.
+#include "stdafx.h"
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+	DWORD  ul_reason_for_call,
+	LPVOID lpReserved
+	)
+{
+	switch (ul_reason_for_call)
+	{
+	case DLL_PROCESS_ATTACH:
+	case DLL_THREAD_ATTACH:
+	case DLL_THREAD_DETACH:
+	case DLL_PROCESS_DETACH:
+		break;
+	}
+	return TRUE;
+}
+

+ 16 - 0
Other/win/libaudiorender/iaudiorenderinterface.cpp

@@ -0,0 +1,16 @@
+#include "StdAfx.h"
+#include "iaudiorenderinterface.h"
+#include "libaudiorender.h"
+
+
+IAudioRender* CreateIAudioRenderObj(audiorender_callback_t* pCallback)
+{
+	return new AudioRenderImpl(pCallback);
+}
+
+
+//这里不能直接delete pAudioRender,因为没有把IAudioRender的析构函数定义为虚函数
+void  DestroyIAudioRenderObj(IAudioRender* pAudioRender)
+{
+	pAudioRender->Release();
+}

+ 29 - 0
Other/win/libaudiorender/iaudiorenderinterface.h

@@ -0,0 +1,29 @@
+#pragma  once
+
+#ifdef LIBAUDIORENDER_EXPORTS
+#define IAUDIORENDERINTERFACE_API __declspec(dllexport)
+#else
+#define IAUDIORENDERINTERFACE_API __declspec(dllimport)
+#endif
+
+#include <stdarg.h>
+
+typedef struct audiorender_callback_s {
+	void (*debug)(void* user_data, const char* fmt, va_list arg);
+	void (*on_render_failed)();
+	void (*on_audio_render_excption)();
+	void* user_data;
+}audiorender_callback_t;
+
+
+class IAudioRender 
+{
+public:
+	virtual int StartRender(int iQueue, void* pFrameformat, const char*pSwitchName) = 0;
+	virtual int StopRender() = 0; 
+	virtual int ReStartRender() = 0;
+	virtual void Release() = 0;
+};
+
+extern "C" IAUDIORENDERINTERFACE_API IAudioRender* CreateIAudioRenderObj(audiorender_callback_t* pCallback);
+extern "C" IAUDIORENDERINTERFACE_API void DestroyIAudioRenderObj(IAudioRender* pIAudioRender);

+ 627 - 0
Other/win/libaudiorender/libaudiorender.cpp

@@ -0,0 +1,627 @@
+// libaudiorender.cpp : Defines the exported functions for the DLL application.
+//
+
+#include "stdafx.h"
+#define _CRT_SECURE_NO_WARNINGS
+
+#include "libaudiorender.h"
+#include "../rvcmediacommon/rvc_media_common.h"
+
+
+#ifndef RVC_AUDIO_FRAME_SIZE
+#define RVC_AUDIO_FRAME_SIZE	320
+#endif
+
+#ifndef RVC_AUDIO_FREQUENCY
+#define RVC_AUDIO_FREQUENCY		8000
+#endif
+
+#ifndef	RVC_AUDIO_BUFFER_LEN
+#define RVC_AUDIO_BUFFER_LEN	512
+#endif
+
+#ifndef RVC_DEFAULT_BITPERSAMPLE
+#define RVC_DEFAULT_BITPERSAMPLE	16
+#endif
+
+
+AudioRenderImpl::AudioRenderImpl(audiorender_callback_t* pCallback)
+{
+	memcpy(&m_audiorender_cb, pCallback, sizeof(audiorender_callback_t));
+	m_pAudioCaptureClient = NULL;
+	m_pAudioClient = NULL;
+	m_pMMDevice = NULL;
+	m_hEventStop = NULL;
+	m_hTimerWakeUp = NULL;
+	m_hTask = NULL;
+	m_pwfx = NULL;
+	m_iQueueNumber = 0;
+	m_audio_cap = NULL;
+	m_frame_format = NULL;
+	memset(m_strFilePathName, 0, MAX_PATH);
+	m_bRecordPCM = false;
+}
+
+bool AudioRenderImpl::InitAudioFrame(audio_frame* pframe)
+{
+	bool bret = false;
+	if (NULL != pframe){
+		pframe->bitspersample = RVC_DEFAULT_BITPERSAMPLE;
+		pframe->format = 1;
+		pframe->nchannels = 1;
+		pframe->samplespersec = RVC_AUDIO_FREQUENCY;
+		pframe->framesize = RVC_AUDIO_FRAME_SIZE;
+		pframe->data = NULL;
+
+		bret = true;
+	}
+
+	return bret;
+}
+
+
+DWORD AudioRenderImpl::pfThreadFunc(LPVOID lpThreadParameter)
+{
+	AudioRenderImpl* pCapture = (AudioRenderImpl*)lpThreadParameter;
+	pCapture->RenderFunc();
+	return 0;
+}
+
+void AudioRenderImpl::LogRenderInfo()
+{
+	uint32_t uConvertRatio = 1;
+	if (NULL != m_frame_format){
+		if (m_frame_format->samplespersec&&m_frame_format->nchannels&&m_frame_format->bitspersample){
+			uConvertRatio = (m_pwfx->nSamplesPerSec*m_pwfx->nChannels*m_pwfx->wBitsPerSample)/(m_frame_format->samplespersec*m_frame_format->nchannels*m_frame_format->bitspersample);
+		}
+	}
+	RenderLog("current speaker render audio convert ratio is %d.", uConvertRatio);
+
+	eSpeakerSamplingDepthRate eSampleType = GetSpeakerSampleRate(m_pwfx->wBitsPerSample, m_pwfx->nSamplesPerSec);
+	RenderLog("Speaker Sample Type is %d.", eSampleType);
+}
+
+void AudioRenderImpl::RenderLog( const char *fmt, ... )
+{
+	if (m_audiorender_cb.debug){
+		va_list arg;
+		va_start(arg, fmt);
+		if (*m_audiorender_cb.debug){
+			(*m_audiorender_cb.debug)(m_audiorender_cb.user_data, fmt, arg);
+		}
+		va_end(arg);
+	}
+}
+
+void AudioRenderImpl::OnRenderFailed()
+{
+
+}
+
+void AudioRenderImpl::OnAudioRenderExcption()
+{
+
+}
+
+int AudioRenderImpl::StartRender(int iQueue, void* pFrameformat, const char* pSwitchName)
+{
+	m_iQueueNumber = iQueue;
+
+	if (NULL != m_frame_format){
+		delete m_frame_format;
+		m_frame_format = NULL;
+	}
+
+	m_frame_format = new audio_frame();
+	if (NULL != m_frame_format){
+		if (NULL != pFrameformat){
+			m_frame_format->bitspersample = ((audio_frame*)pFrameformat)->bitspersample;
+			m_frame_format->nchannels = ((audio_frame*)pFrameformat)->nchannels;
+			m_frame_format->format = ((audio_frame*)pFrameformat)->format;
+			m_frame_format->samplespersec = ((audio_frame*)pFrameformat)->samplespersec;
+			m_frame_format->framesize = ((audio_frame*)pFrameformat)->framesize;
+		}
+		else{
+			InitAudioFrame(m_frame_format);
+		}
+	}
+
+	if (NULL != pSwitchName){
+		size_t ulen = strlen(pSwitchName);
+		if (ulen >= MAX_PATH){
+			ulen = MAX_PATH - 1;
+		}
+		memcpy(m_strFilePathName, pSwitchName, ulen);
+		if (strlen(m_strFilePathName) > 0){
+			m_bRecordPCM = true;
+		}
+	}
+
+	if (InitQueueInfo(iQueue)){
+		return -1;
+	}
+
+	CoInitialize(NULL);
+
+	IMMDeviceEnumerator *pMMDeviceEnumerator = NULL;
+	HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,
+		__uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator);
+	if (FAILED(hr)){
+		CoUninitialize();		
+		return -1;
+	}
+
+	// get the default render endpoint 
+	hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &m_pMMDevice);
+	if (FAILED(hr)){
+		CoUninitialize();
+		return -1;
+	}
+
+	pMMDeviceEnumerator->Release();
+
+	m_hEventStop = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if (m_hEventStop == NULL){
+		CoUninitialize();
+		return -1;
+	}
+
+	hr = m_pMMDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&m_pAudioClient);
+	if (FAILED(hr)){
+		goto error;
+	}
+
+	REFERENCE_TIME hnsDefaultDevicePeriod(0);
+	
+	if (NULL != m_pAudioClient){
+		hr = m_pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL);
+		if (FAILED(hr)){
+			goto error;
+		}
+	}
+	else{
+		goto error;
+	}
+
+
+	hr = m_pAudioClient->GetMixFormat(&m_pwfx);
+	if (FAILED(hr)){
+		goto error;
+	}
+	else{
+		RenderLog("speaker render format is %0x, channels is %d, samples rate is %d, buffer size is %d, block size of data is %d.",m_pwfx->wFormatTag, m_pwfx->nChannels, m_pwfx->nSamplesPerSec, m_pwfx->nAvgBytesPerSec, m_pwfx->nBlockAlign);   
+		RenderLog("destination audio frame format is %d, samples rate is %d, bits per sample is %d, channels number is %d.", m_frame_format->format, m_frame_format->samplespersec, m_frame_format->bitspersample, m_frame_format->nchannels);
+	}
+
+	if (!AdjustFormatTo16Bits(m_pwfx)){
+		goto error;
+	}
+
+	m_hTimerWakeUp = CreateWaitableTimer(NULL, FALSE, NULL);
+	if (m_hTimerWakeUp == NULL){
+		goto error;
+	}
+
+	if (NULL != m_pAudioClient){
+		hr = m_pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, 0, 0, m_pwfx, 0);
+		if (FAILED(hr)){
+			goto error;
+		}
+	}
+	else{
+		goto error;
+	}
+
+	if (NULL != m_pAudioClient){
+		hr = m_pAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&m_pAudioCaptureClient);
+		if (FAILED(hr)){
+			goto error;
+		}
+	}
+	else{
+		goto error;
+	}
+
+
+	DWORD nTaskIndex = 0;
+	m_hTask = AvSetMmThreadCharacteristics("Capture", &nTaskIndex);
+	if (NULL == m_hTask){
+		goto error;
+	}
+
+	LARGE_INTEGER liFirstFire;
+	liFirstFire.QuadPart = -hnsDefaultDevicePeriod / 2;							// negative means relative time
+	LONG lTimeBetweenFires = (LONG)hnsDefaultDevicePeriod / 2 / (10 * 1000);	// convert to milliseconds
+
+	BOOL bOK = SetWaitableTimer(m_hTimerWakeUp, &liFirstFire, lTimeBetweenFires, NULL, NULL, FALSE);
+	if (!bOK){
+		goto error;
+	}
+
+	if (NULL != m_pAudioClient){
+		hr = m_pAudioClient->Start();
+		if (FAILED(hr)){
+			goto error;
+		}
+	}
+	else{
+		goto error;
+	}
+
+	m_hThread = CreateThread(NULL, 0, pfThreadFunc, this, 0, 0);
+
+	if (m_hThread == NULL){
+		goto error;
+	}
+
+	CoUninitialize();
+
+	return 0;
+
+error:
+	Close();
+	CoUninitialize();
+
+	return -1;
+}
+
+uint32_t AudioRenderImpl::ConvertDouble2SingleChannel(char* pDstBuf, const uint32_t uDstLen, const unsigned char* pSrcBuf, uint32_t uSrcLen, uint32_t uBitDeepth)
+{
+	uint32_t uRet = 0;
+	uint32_t uOneChannelLen = uSrcLen/2;
+	uint32_t i = 0;
+
+	for (; i < uOneChannelLen/2 && i < uDstLen/uBitDeepth; i++){
+		memcpy((uint16_t*)pDstBuf + i, ((uint32_t*)(pSrcBuf))+i, uBitDeepth);
+	}
+
+	if (i == uOneChannelLen/2){
+		uRet = uOneChannelLen;
+	}
+
+	return uRet;
+}
+
+
+eSpeakerSamplingDepthRate AudioRenderImpl::GetSpeakerSampleRate(uint32_t udepth, uint32_t usamplerate)
+{
+	eSpeakerSamplingDepthRate eType = eSixteenBitsDVD;
+
+	if (16 == udepth){
+		switch(usamplerate)
+		{
+		case 44100:
+			eType = eSixteenBitsCD;
+			break;
+		case 48000:
+			eType = eSixteenBitsDVD;
+			break;
+		case 96000:
+			eType = eSixteenBitsStatdioLow;
+			break;
+		case 192000:
+			eType = eSixteenBitsStatdioHigh;
+			break;
+		}
+	}
+	else if (24 == udepth){
+		switch(usamplerate)
+		{
+		case 44100:
+			eType = eTwentyfourStatdioA;
+			break;
+		case 48000:
+			eType = eTwentyfourStatdioB;
+			break;
+		case 96000:
+			eType = eTwentyfourStatdioC;
+			break;
+		case 192000:
+			eType = eTwentyfourStatdioD;
+			break;
+		}
+	}
+
+	return eType;
+}
+	
+int AudioRenderImpl::InitQueueInfo(int iQueue)
+{
+	int iRet = -1;
+	char* pQueueName = NULL;
+
+	if (0 == iQueue){
+		pQueueName = REC_COMMON_REMOTEAUDIO_SHM_QUEUE;
+	}
+	else {
+		pQueueName = REC_COMMON_AUDIO_SALES_SHM_QUEUE;
+	}
+
+	if (NULL != pQueueName){
+		m_audio_cap = new Clibaudioqueue(pQueueName);
+		if (NULL != m_audio_cap){
+			iRet = 0;
+			RenderLog("audio render insert queue name is %s.", pQueueName);
+		}
+	}
+	
+	return iRet;
+}
+
+uint32_t AudioRenderImpl::TranslateBuffer2DestFrameFormat(spx_int16_t* pOutAudio, spx_uint32_t* pAudioLen, spx_uint32_t uAudioBufferLen, unsigned char* pCbBuffer, const uint32_t uBufferLen, SpeexResamplerState *st, const audio_frame* pDestFrameFormat)
+{
+	uint32_t uRet = 0;
+	
+	uint32_t uSingleChannelDataLen = uBufferLen;
+	uint32_t uSingleChannelBufferLen = 0;
+	char* pSingleChannelBuf = (char*)malloc(uBufferLen*sizeof(char));
+	if (NULL != pSingleChannelBuf){
+		memset(pSingleChannelBuf, 0, uBufferLen*sizeof(char));
+		uSingleChannelBufferLen = uBufferLen*sizeof(char);
+	}
+
+	if (eDoubleChannel == m_pwfx->nChannels){
+		if (eSingleChannel == pDestFrameFormat->nchannels){
+			uSingleChannelDataLen = ConvertDouble2SingleChannel(pSingleChannelBuf, uSingleChannelBufferLen, pCbBuffer, uBufferLen, m_pwfx->wBitsPerSample/8);
+		}
+		else{
+			memcpy(pSingleChannelBuf, pCbBuffer, uBufferLen);
+		}
+	}
+	else{
+		if (eSingleChannel == pDestFrameFormat->nchannels){
+			memcpy(pSingleChannelBuf, pCbBuffer, uBufferLen);
+		}
+		else{
+			RenderLog("not support single channel convert to double channels.");
+		}
+	}
+
+	spx_uint32_t uInLen = uSingleChannelDataLen;
+
+	int iRet = speex_resampler_process_int(st, 0, (spx_int16_t*)pSingleChannelBuf, &uInLen, pOutAudio, &uAudioBufferLen);
+	if (RESAMPLER_ERR_SUCCESS == iRet){
+		*pAudioLen = uAudioBufferLen;
+		uRet = uAudioBufferLen;
+	}
+
+	if (NULL != pSingleChannelBuf){
+		free(pSingleChannelBuf);
+		pSingleChannelBuf = NULL;
+	}
+
+	return uRet;
+}
+
+void AudioRenderImpl::RenderFunc()
+{
+	HANDLE waitArray[2] = { m_hEventStop, m_hTimerWakeUp };
+	DWORD dwWaitResult;
+	UINT32 nNextPacketSize(0);
+	BYTE *pData = NULL;
+	UINT32 nNumFramesToRead;
+	DWORD dwFlags;
+	CoInitialize(NULL);
+
+	SpeexResamplerState *st = NULL;
+	int err = 0;
+
+	spx_int16_t	OutAudioBuffer[RVC_AUDIO_BUFFER_LEN] = {0};
+	spx_uint32_t uIndex = 0;
+	spx_uint32_t uValidAudioLen = 0;
+	spx_uint32_t uLeftBufferLen = RVC_AUDIO_BUFFER_LEN;
+	int iseriesnumber = 0;
+
+	FILE* pRecord = NULL;
+	if (m_bRecordPCM){
+		pRecord = fopen(m_strFilePathName, "wb+");
+	}
+
+	LogRenderInfo();
+
+	st = speex_resampler_init_frac(1, m_pwfx->nSamplesPerSec, m_frame_format->samplespersec, m_pwfx->nSamplesPerSec, m_frame_format->samplespersec, 0, &err);
+
+	while (TRUE){
+		dwWaitResult = WaitForMultipleObjects(sizeof(waitArray) / sizeof(waitArray[0]), waitArray, FALSE, INFINITE);
+		if (WAIT_OBJECT_0 == dwWaitResult) {
+			RenderLog("%s","exit circle for set event stop.");
+			break;
+		}
+
+		if (WAIT_OBJECT_0 + 1 != dwWaitResult){
+			RenderLog("%s","exit circle for time wake up.");
+			break;
+		}
+
+		HRESULT hr = S_OK;
+		if (NULL != m_pAudioCaptureClient){
+			m_pAudioCaptureClient->GetNextPacketSize(&nNextPacketSize);
+			if (FAILED(hr)){
+				break;
+			}
+		}else{
+			break;
+		}
+
+		if (nNextPacketSize == 0) {
+			continue;
+		}
+
+		if (NULL != m_pAudioCaptureClient){
+			hr = m_pAudioCaptureClient->GetBuffer(&pData, &nNumFramesToRead, &dwFlags, NULL, NULL);
+			if (FAILED(hr)){
+				break;
+			}
+		}
+		else{
+			break;
+		}
+
+
+		if (0 != nNumFramesToRead){
+			uIndex = TranslateBuffer2DestFrameFormat((spx_int16_t*)((char*)OutAudioBuffer+uIndex), &uValidAudioLen, uLeftBufferLen, pData, nNumFramesToRead*m_pwfx->nBlockAlign, st, m_frame_format);
+			uLeftBufferLen -= uValidAudioLen;
+			//Debug("translate valid audio len is %d,left buffer len is %d.", uValidAudioLen, uLeftBufferLen);
+			if (uLeftBufferLen <= RVC_AUDIO_BUFFER_LEN - m_frame_format->framesize){
+				//Debug("audio len = %d.", RVC_AUDIO_BUFFER_LEN - uLeftBufferLen);
+				if (m_audio_cap){
+					audio_frame framedata;
+					framedata.bitspersample = m_frame_format->bitspersample;
+					framedata.format = m_frame_format->format;
+					framedata.nchannels = m_frame_format->nchannels;
+					framedata.samplespersec = m_frame_format->samplespersec;
+					framedata.framesize = m_frame_format->framesize;
+					framedata.data = (char*)OutAudioBuffer;
+					framedata.iseriesnumber = iseriesnumber++;
+					BOOL bRet = m_audio_cap->InsertAudio(&framedata);
+					if (bRet){
+						if (0 == framedata.iseriesnumber % 300){
+							RenderLog("speaker audio[%d] InsertAudio success.", framedata.iseriesnumber);
+						}
+					} 
+					else{
+						RenderLog("%s","speaker audio InsertAudio failed.");
+					}
+
+					if (m_bRecordPCM){
+						if (NULL != pRecord){
+							fwrite(framedata.data, framedata.framesize, 1, pRecord);
+						}
+					}
+				}
+				uLeftBufferLen = RVC_AUDIO_BUFFER_LEN;
+				memset(OutAudioBuffer, 0 , RVC_AUDIO_BUFFER_LEN);
+				uIndex = 0;
+			}
+			else{
+				uValidAudioLen = 0;
+				//Debug("continue next capture.");
+			}
+		}
+
+		if (NULL != m_pAudioCaptureClient){
+			m_pAudioCaptureClient->ReleaseBuffer(nNumFramesToRead);
+		}
+	}
+
+	if (NULL != pRecord){
+		fclose(pRecord);
+		pRecord = NULL;
+	}
+
+	speex_resampler_destroy(st);
+	CoUninitialize();
+}
+
+
+BOOL AudioRenderImpl::AdjustFormatTo16Bits(WAVEFORMATEX *pwfx)
+{
+	BOOL bRet = FALSE;
+
+	if (NULL != pwfx){
+		if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT){
+			pwfx->wFormatTag = WAVE_FORMAT_PCM;
+			pwfx->wBitsPerSample = 16;
+			pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
+			pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
+			bRet = TRUE;
+		}
+		else if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
+			PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pwfx);
+			if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)){
+				pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+				pEx->Samples.wValidBitsPerSample = 16;
+				pwfx->wBitsPerSample = 16;
+				pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
+				pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
+				bRet = TRUE;
+			}
+		}
+	}
+
+	return bRet;
+}
+
+int AudioRenderImpl::StopRender()
+{
+	RenderLog("%s","stop audio render.");
+	
+	m_bRecordPCM = false;
+	memset(m_strFilePathName, 0, MAX_PATH);
+
+	if (m_pAudioClient){
+		m_pAudioClient->Stop();
+	}
+
+	SetEvent(m_hEventStop);
+	RenderLog("%s","m_hEventStop SetEvent.");
+	WaitForSingleObject(m_hThread, -1);
+
+	if (m_audio_cap){
+		Sleep(10);
+		m_audio_cap->ClearAudioQueue();
+	}
+
+	if (m_frame_format){
+		delete m_frame_format;
+		m_frame_format = NULL;
+	}
+
+	Close();
+
+	return 0;
+}
+
+int AudioRenderImpl::ReStartRender()
+{
+	if (0 == StopRender()){
+		StartRender(m_iQueueNumber, m_frame_format, m_strFilePathName);
+	}
+
+	return 0;
+}
+
+void AudioRenderImpl::Close()
+{
+	if (m_hEventStop != NULL)
+	{
+		CloseHandle(m_hEventStop);
+		m_hEventStop = NULL;
+	}
+	if (m_pAudioClient)
+	{
+		m_pAudioClient->Release();
+		m_pAudioClient = NULL;
+	}
+	if (m_pwfx != NULL)
+	{
+		CoTaskMemFree(m_pwfx);
+		m_pwfx = NULL;
+	}
+	if (m_hTimerWakeUp != NULL)
+	{
+		CancelWaitableTimer(m_hTimerWakeUp);
+		CloseHandle(m_hTimerWakeUp);
+		m_hTimerWakeUp = NULL;
+	}
+	if (m_hTask != NULL)
+	{
+		AvRevertMmThreadCharacteristics(m_hTask);
+		m_hTask = NULL;
+	}
+	if (m_pAudioCaptureClient != NULL)
+	{
+		m_pAudioCaptureClient->Release();
+		m_pAudioCaptureClient = NULL;
+	}
+}
+
+void AudioRenderImpl::Release()
+{
+	delete this;
+}
+
+AudioRenderImpl::~AudioRenderImpl()
+{
+	
+}

+ 72 - 0
Other/win/libaudiorender/libaudiorender.h

@@ -0,0 +1,72 @@
+
+#pragma once
+
+#include <speex/speex_resampler.h>
+#include "iaudiorenderinterface.h"
+#include "libaudioqueue.h"
+
+#pragma comment(lib,"avrt.lib")
+#include<Audioclient.h>
+#include <mmdeviceapi.h>
+#include<avrt.h>
+#include <stdio.h>
+#include <stdint.h>
+
+enum eSpeakerSamplingDepthRate{
+	eSixteenBitsCD,					//2通道,16位,44100Hz,每秒采样大小176400字节,采集声音每次返回buffer大小448*4=1792字节,转为8k采样率,单声道后buffer大小162字节和163字节交替
+	eSixteenBitsDVD,				//2通道,16位,48000Hz,每秒采样大小192000字节,采集声音每次返回buffer大小480*4=1920字节,转为8k采样率,单声道后buffer大小160字节
+	eSixteenBitsStatdioLow,			//2通道,16位,96000Hz,每秒采样大小384000字节,采集声音每次返回buffer大小960*4=3840字节,转为8k采样率,单声道后buffer大小160字节
+	eSixteenBitsStatdioHigh,		//2通道,16位,192000Hz,每秒采样大小768000字节,采集声音每次返回buffer大小1920*4=7680字节,转为8k采样率,单声道后buffer大小160字节
+	eTwentyfourStatdioA,			//2通道,24位,44100Hz
+	eTwentyfourStatdioB,			//2通道,24位,48000Hz
+	eTwentyfourStatdioC,			//2通道,24位,96000Hz
+	eTwentyfourStatdioD				//2通道,24位,192000Hz
+};
+
+enum eChannelNumber{
+	eSingleChannel = 1,	
+	eDoubleChannel = 2
+};
+
+class AudioRenderImpl : public IAudioRender
+{
+public:
+	AudioRenderImpl(audiorender_callback_t* pCallback);
+	virtual int StartRender(int iQueue, void* pFrameformat, const char*pSwitchName);
+	BOOL AdjustFormatTo16Bits(WAVEFORMATEX *pwfx);
+	static DWORD WINAPI pfThreadFunc(LPVOID lpThreadParameter);
+	uint32_t ConvertDouble2SingleChannel(char* pDstBuf, const uint32_t uDstLen, const unsigned char* pSrcBuf, uint32_t uSrcLen, uint32_t uBitDeepth);
+	eSpeakerSamplingDepthRate GetSpeakerSampleRate(uint32_t udepth, uint32_t usamplerate);
+	uint32_t TranslateBuffer2DestFrameFormat(spx_int16_t* pOutAudio, spx_uint32_t* pAudioLen, spx_uint32_t uAudioBufferLen, unsigned char* pCbBuffer, const uint32_t uBufferLen, SpeexResamplerState *st, const audio_frame* pDestFrameFormat);
+	bool InitAudioFrame(audio_frame* pframe);
+
+	void RenderFunc();
+	int InitQueueInfo(int iQueue);
+	virtual int StopRender();
+	virtual int ReStartRender();
+	void Close();
+	void LogRenderInfo(); 
+
+	virtual void RenderLog(const char *fmt, ...);
+	virtual void OnRenderFailed();
+	virtual void OnAudioRenderExcption();
+
+	virtual void Release();
+	~AudioRenderImpl();
+
+private:
+	HANDLE m_hThread;
+	HANDLE m_hTask;
+	HANDLE m_hTimerWakeUp;
+	IAudioCaptureClient * m_pAudioCaptureClient;
+	IAudioClient * m_pAudioClient;
+	WAVEFORMATEX * m_pwfx;
+	HANDLE m_hEventStop;
+	IMMDevice* m_pMMDevice;
+	int m_iQueueNumber;
+	audio_frame* m_frame_format;
+	Clibaudioqueue *m_audio_cap;
+	char m_strFilePathName[MAX_PATH];
+	bool m_bRecordPCM;
+	audiorender_callback_t m_audiorender_cb;
+};

+ 8 - 0
Other/win/libaudiorender/stdafx.cpp

@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// libaudiorender.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file

+ 18 - 0
Other/win/libaudiorender/stdafx.h

@@ -0,0 +1,18 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
+#endif //WIN32_LEAN_AND_MEAN
+// Windows Header Files:
+#include <windows.h>
+
+
+
+// TODO: reference additional headers your program requires here

+ 8 - 0
Other/win/libaudiorender/targetver.h

@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>

+ 58 - 0
Other/win/libmediadeviceinfo/CMakeLists.txt

@@ -0,0 +1,58 @@
+set(MODULE_NAME "mediadeviceinfo")
+set(MODULE_PREFIX "MEDIADEVICEINFO_FUNC")
+
+if(RVC_DEBUG_MODE)
+    set(TOOLKIT_LIB libtoolkitd)
+else()
+    set(TOOLKIT_LIB libtoolkit)
+endif(RVC_DEBUG_MODE)
+
+set(${MODULE_PREFIX}_SRCS
+	imediadeviceinfo.h
+	./linux/videodevice_info_linux.cpp
+)
+
+add_library(${MODULE_NAME} SHARED ${${MODULE_PREFIX}_SRCS})
+
+target_include_directories(${MODULE_NAME} PRIVATE
+	${RVC_TOOLKIT_INCLUDE_DIR}
+	${CONAN_RVCFRAMEWORK_ROOT}/include
+	)
+
+target_link_directories(${MODULE_NAME} PRIVATE
+	${CONAN_LIB_DIRS_RVCFRAMEWORK}
+	)
+
+
+# 添加实体需要依赖的其他共享库(包括系统库)
+if(WIN32)
+set(${MODULE_PREFIX}_LIBS  ${MODULE_BASE_LIBS} 
+
+)
+else(WIN32)
+set(${MODULE_PREFIX}_LIBS  ${MODULE_BASE_LIBS} 
+	${TOOLKIT_LIB}
+)
+endif(WIN32)
+
+
+target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}
+	
+)
+
+
+target_compile_definitions(${MODULE_NAME} PUBLIC "LIBMEDIADEVICEINFO_EXPORTS")
+
+if(MSVC)
+	install(TARGETS ${MODULE_NAME} 
+    RUNTIME DESTINATION "${RVC_RUNTIME_PATH}" COMPONENT libraries
+    ARCHIVE DESTINATION "${RVC_LIBRARY_PATH}" COMPONENT develops EXCLUDE_FROM_ALL
+    LIBRARY DESTINATION "${RVC_LIBRARY_PATH}" COMPONENT libraries
+    )
+else()
+install(TARGETS ${MODULE_NAME} 
+    RUNTIME DESTINATION "${RVC_RUNTIME_PATH}"
+    ARCHIVE DESTINATION "${RVC_LIBRARY_PATH}"
+    LIBRARY DESTINATION "${RVC_RUNTIME_PATH}"
+    COMPONENT libraries)
+endif(MSVC)

+ 52 - 0
Other/win/libmediadeviceinfo/imediadeviceinfo.h

@@ -0,0 +1,52 @@
+#pragma once
+
+#ifdef _WIN32
+#ifdef LIBMEDIADEVICEINFO_EXPORTS
+#define LIBMEDIADEVICEINFO_API __declspec(dllexport)
+#else
+#define LIBMEDIADEVICEINFO_API __declspec(dllimport)
+#endif
+# elif ( defined(__GNUC__) &&  __GNUC__ >= 4 )
+#define LIBMEDIADEVICEINFO_API __attribute__((visibility("default")))
+#else // RVC_OS_WIN
+#define LIBMEDIADEVICEINFO_API
+#endif // RVC_OS_WIN
+
+
+/**
+ * get video camera devices count, return device count numbers
+ */
+extern "C" LIBMEDIADEVICEINFO_API int rvc_videocap_get_device_count();
+
+/**
+ * get video camera name, buf length should not be larger than 255,
+ * return -1 if error
+ * if buf == null, len is ignored, return value is the required buffer size
+ * else return value is copy to buf, including null-terminated char
+ */
+extern "C" LIBMEDIADEVICEINFO_API int rvc_videocap_get_device_name(int device_id, char* buf, int len);
+
+/**
+ * get video device path string, buf length should not be larger than 255,
+ * return -1 on error, if buf == null, len is ignored, return value is the required buffer size
+ */
+extern "C" LIBMEDIADEVICEINFO_API int rvc_videocap_get_device_path(int device_id, char* buf, int len);
+
+/**
+ * get video camera name and path, buf length should not be larger than 255,
+ * return -1 if error
+ */
+extern "C" LIBMEDIADEVICEINFO_API int rvc_videocap_get_device_info(int device_id, char* namebuf, int namelen, char* pathbuf, int pathlen);
+
+
+/**
+ * get video camera recognise name, buf length should not be larger than 255,
+ * return -1 if error
+ */
+extern "C" LIBMEDIADEVICEINFO_API int rvc_videocap_get_device_fullpathname(int device_id, char* fullnamebuf, int fulllen);
+
+/**
+ * get video device id
+ * return -1 if error
+ */
+extern "C" LIBMEDIADEVICEINFO_API int rvc_videocap_get_video_device_id(const char* dev_name);

+ 297 - 0
Other/win/libmediadeviceinfo/linux/videodevice_info_linux.cpp

@@ -0,0 +1,297 @@
+#include "../imediadeviceinfo.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <md5.h>
+
+//v4l includes
+#include <linux/videodev2.h>
+
+
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif // !MAX_PATH
+
+
+int  rvc_videocap_get_device_count()
+{
+	int icount = 0;
+	int fd = -1;
+	
+	for (int index = 0; index < 64; index++)
+	{
+		char device[MAX_PATH] = { 0 };
+		snprintf(device, MAX_PATH, "/dev/video%d", index);
+		if (-1 != (fd = open(device, O_RDONLY)) )
+		{
+			// query device capabilities
+			struct v4l2_capability cap = {0};
+			if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0){
+				close(fd);
+				continue;
+			}
+
+			if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)){
+				continue;
+			}
+
+			if (cap.capabilities & V4L2_CAP_STREAMING) {
+
+			}
+
+			if (cap.capabilities & V4L2_CAP_READWRITE) {
+
+			}
+
+			/* 查询支持的格式 */
+			struct v4l2_fmtdesc tFmtDesc = {0};
+			int iPixelFormat = 0;
+
+			tFmtDesc.index = 0;
+			tFmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+			while ((ioctl(fd, VIDIOC_ENUM_FMT, &tFmtDesc)) == 0) {
+				if (V4L2_PIX_FMT_YUYV == tFmtDesc.pixelformat || V4L2_PIX_FMT_MJPEG == tFmtDesc.pixelformat)
+				{
+					iPixelFormat = tFmtDesc.pixelformat;
+					icount++;
+					break;
+				}
+
+				tFmtDesc.index++;
+			}
+		}
+	}
+	return icount;
+}
+
+
+int  rvc_videocap_get_device_name(int device_id, char* buf, int len)
+{
+	int iret = -1;
+
+	char device[MAX_PATH] = { 0 };
+	int fd = -1;
+
+	snprintf(device, MAX_PATH, "/dev/video%d", device_id);
+	if (-1 != (fd = open(device, O_RDONLY)))
+	{
+		// query device capabilities
+		struct v4l2_capability cap = { 0 };
+		if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0){
+			close(fd);
+			return iret;
+		}
+
+		if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)){
+			return iret;
+		}
+
+		if (cap.capabilities & V4L2_CAP_STREAMING) {
+
+		}
+
+		if (cap.capabilities & V4L2_CAP_READWRITE) {
+
+		}
+
+		/* 查询支持的格式 */
+		struct v4l2_fmtdesc tFmtDesc = { 0 };
+		int iPixelFormat = 0;
+
+		tFmtDesc.index = 0;
+		tFmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+		while ((ioctl(fd, VIDIOC_ENUM_FMT, &tFmtDesc)) == 0) {
+			if (V4L2_PIX_FMT_YUYV == tFmtDesc.pixelformat || V4L2_PIX_FMT_MJPEG == tFmtDesc.pixelformat){
+				iPixelFormat = tFmtDesc.pixelformat;
+				break;
+			}
+			tFmtDesc.index++;
+		}
+
+		if (0 != iPixelFormat)
+		{
+			snprintf(buf, len, "%s%s%s", cap.card, ";", cap.bus_info);
+			iret = 0;
+		}
+	}
+	return iret;
+}
+
+
+int  rvc_videocap_get_device_path(int device_id, char* buf, int len)
+{
+	return 0;
+}
+
+
+int  rvc_videocap_get_device_info(int device_id, char* namebuf, int namelen, char* pathbuf, int pathlen)
+{
+	int iret = -1;
+
+	char device[MAX_PATH] = { 0 };
+	int fd = -1;
+
+	snprintf(device, MAX_PATH, "/dev/video%d", device_id);
+	if (-1 != (fd = open(device, O_RDONLY)))
+	{
+		// query device capabilities
+		struct v4l2_capability cap = { 0 };
+		if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
+		{
+			close(fd);
+			return iret;
+		}
+
+		if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
+		{
+			return iret;
+		}
+
+		if (cap.capabilities & V4L2_CAP_STREAMING) {
+
+		}
+
+		if (cap.capabilities & V4L2_CAP_READWRITE) {
+
+		}
+
+		/* 查询支持的格式 */
+		struct v4l2_fmtdesc tFmtDesc = { 0 };
+		int iPixelFormat = 0;
+
+		tFmtDesc.index = 0;
+		tFmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+		while ((ioctl(fd, VIDIOC_ENUM_FMT, &tFmtDesc)) == 0) {
+			if (V4L2_PIX_FMT_YUYV == tFmtDesc.pixelformat || V4L2_PIX_FMT_RGB565 == tFmtDesc.pixelformat)
+			{
+				iPixelFormat = tFmtDesc.pixelformat;
+				break;
+			}
+			tFmtDesc.index++;
+		}
+
+		if (0 != iPixelFormat)
+		{
+			snprintf(namebuf, namelen, "%s", cap.card);
+			snprintf(pathbuf, pathlen, "%s", cap.bus_info);
+			iret = 0;
+		}
+	}
+	return iret;
+}
+
+static int bin2str(unsigned char* x, int xlen, char* str, int str_size)
+{
+	static const char* hex2char = "0123456789ABCDEF";
+	int i, k = 0;
+	if (str_size <= xlen * 2)
+		return -1;
+	for (i = 0; i < xlen; ++i) {
+		int h = x[i] >> 4;
+		int l = x[i] & 0xf;
+		str[k++] = hex2char[h];
+		str[k++] = hex2char[l];
+	}
+	str[k] = 0;
+	return k;
+}
+
+
+int  rvc_videocap_get_device_fullpathname(int device_id, char* fullnamebuf, int fulllen)
+{
+	int iret = -1;
+
+	char device[MAX_PATH] = { 0 };
+	int fd = -1;
+
+	snprintf(device, MAX_PATH, "/dev/video%d", device_id);
+	if (-1 != (fd = open(device, O_RDONLY)))
+	{
+		// query device capabilities
+		struct v4l2_capability cap = { 0 };
+		if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
+		{
+			close(fd);
+			return iret;
+		}
+
+		if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
+		{
+			return iret;
+		}
+
+		if (cap.capabilities & V4L2_CAP_STREAMING) {
+
+		}
+
+		if (cap.capabilities & V4L2_CAP_READWRITE) {
+
+		}
+
+		/* 查询支持的格式 */
+		struct v4l2_fmtdesc tFmtDesc = { 0 };
+		int iPixelFormat = 0;
+
+		tFmtDesc.index = 0;
+		tFmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+		while ((ioctl(fd, VIDIOC_ENUM_FMT, &tFmtDesc)) == 0) {
+			if (V4L2_PIX_FMT_YUYV == tFmtDesc.pixelformat || V4L2_PIX_FMT_RGB565 == tFmtDesc.pixelformat)
+			{
+				iPixelFormat = tFmtDesc.pixelformat;
+				break;
+			}
+			tFmtDesc.index++;
+		}
+
+		if (0 != iPixelFormat)
+		{
+			if (NULL != cap.bus_info && NULL != cap.card)
+			{
+				char strmd5[MAX_PATH] = { 0 };
+				snprintf(strmd5, MAX_PATH, "%s", cap.bus_info);
+				unsigned char x[MD5_DIGESTSIZE];
+				md5_ctx_t ctx;
+				md5_init(&ctx);
+				md5(x, strmd5, strlen(strmd5));
+				bin2str(x, sizeof(x), strmd5, sizeof(strmd5));
+
+				snprintf(fullnamebuf, fulllen, "%s:%s", cap.card, strmd5);
+				iret = 0;
+			}
+		}
+	}
+	return iret;
+}
+
+int  rvc_videocap_get_video_device_id(const char* dev_name)
+{
+	int iret = -1;
+	if (NULL == dev_name){
+		return iret;
+	}
+
+	int icount = rvc_videocap_get_device_count();
+	int ifound = 0;
+	for (int i = 0; i < 64 && ifound < icount; ++i) {
+		char strfullname[2*MAX_PATH] = { 0 };
+		if (0 == rvc_videocap_get_device_fullpathname(i, strfullname, 2 * MAX_PATH)){
+			ifound++;
+			printf("camera name is: %s\n", strfullname);
+			printf("   dev name is: %s\n", dev_name);
+			if (0 == strcasecmp(strfullname, dev_name))
+			{
+				iret = i;
+			}
+		}
+	}
+	return iret;
+}

+ 272 - 0
Other/win/libmediadeviceinfo/win/videodevice_info_win.cpp

@@ -0,0 +1,272 @@
+#include "../imediadeviceinfo.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <md5.h>
+#include <dshow.h>
+
+
+
+#ifndef MAX_PATH_EX
+#define MAX_PATH_EX 512
+#endif
+
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif // !MAX_PATH
+
+
+static HRESULT WalkFilterCategory(REFCLSID category, 
+	HRESULT (*OnDeviceCB)(int index,IMoniker *pMoniker,VOID *pUserData1,VOID *pUserData2),
+	VOID * pUserData1, 
+	VOID* pUserData2)
+{
+	HRESULT hr;
+	ICreateDevEnum *pDevEnum = NULL;
+	IEnumMoniker *pEnumMoniker = NULL;
+	IMoniker *pMoniker = NULL;
+	BOOL quit;
+	int i = 0;
+
+	if (!OnDeviceCB)
+		return E_INVALIDARG;
+
+	hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, 
+		IID_ICreateDevEnum, (void**)&pDevEnum);
+	if (FAILED(hr)){ 
+		return hr;
+	}
+
+	hr = pDevEnum->CreateClassEnumerator(category, &pEnumMoniker, 0);
+	if (FAILED(hr)) {
+		pDevEnum->Release();
+		return hr;
+	} else if (hr == S_OK) {
+		pEnumMoniker->Reset();
+		quit = FALSE;
+		while (pEnumMoniker->Next(1, &pMoniker, NULL) == S_OK && !quit) {
+			hr = OnDeviceCB(i++, pMoniker, pUserData1, pUserData2);
+			quit = FAILED(hr) || (hr == S_OK);
+			pMoniker->Release();
+		}
+		pEnumMoniker->Release();
+		pDevEnum->Release();
+	} else {
+		if (pEnumMoniker)
+			pEnumMoniker->Release();
+		pDevEnum->Release();
+	}
+	return hr;
+}
+
+static HRESULT get_device_count_cb(int index, IMoniker *pMoniker, void *pUserData1, void *pUserData2)
+{
+	if (pMoniker)
+		++*(int*)pUserData1;
+	return S_FALSE;
+}
+
+
+int  rvc_videocap_get_device_count()
+{
+	int iret = 0;
+
+	int count = 0;
+
+	CoInitialize(NULL);
+
+	HRESULT hr = WalkFilterCategory((const IID)CLSID_VideoInputDeviceCategory, &get_device_count_cb, &count, NULL);
+
+	if (SUCCEEDED(hr)){
+		iret = count;
+	}
+
+	CoUninitialize();
+
+	return iret;
+}
+
+
+struct _buf {
+	char *buf;
+	int len;
+};
+
+
+static HRESULT get_device_name_cb(int index, IMoniker *pMoniker, void *pUserData1, void *pUserData2)
+{
+	if (pMoniker) {
+		int *p_id = (int*)pUserData1;
+		struct _buf *buf = (struct _buf*)pUserData2;
+		HRESULT hr;
+		if (index == *p_id) {
+			IPropertyBag *pPropBag;
+			VARIANT name;
+			hr = pMoniker->BindToStorage(NULL, NULL, 
+				IID_IPropertyBag, (void**)&pPropBag);
+			if (FAILED(hr)) {
+				buf->len = -1; /* failed */
+				return hr;
+			}
+			VariantInit(&name);
+			name.vt = VT_BSTR;
+			hr = pPropBag->Read(L"FriendlyName", &name, NULL);
+			if (FAILED(hr)) {
+				pPropBag->Release();
+				VariantClear(&name);
+				buf->len = -1;
+				return hr;
+			}
+			buf->len = SysStringByteLen(name.bstrVal) + 2;
+			if (buf)
+				memcpy(buf->buf, name.bstrVal, buf->len);
+			pPropBag->Release();
+			VariantClear(&name);
+			return S_OK;
+		}
+	}
+	return S_FALSE;
+}
+
+
+int  rvc_videocap_get_device_name(int device_id, char* buf, int len)
+{
+	int iret = -1;
+
+	struct _buf x = {buf, len};
+	int dev_id = device_id;
+
+	CoInitialize(NULL);
+
+	HRESULT hr = WalkFilterCategory(CLSID_VideoInputDeviceCategory, 
+		&get_device_name_cb, &dev_id, &x);
+
+	if (hr == S_OK){
+		iret = x.len;
+	}
+
+	CoUninitialize();
+
+	return iret;
+}
+
+
+static HRESULT get_device_path_cb(int index, IMoniker *pMoniker, void *pUserData1, void *pUserData2)
+{
+	if (pMoniker) {
+		int *p_id = (int*)pUserData1;
+		struct _buf *buf = (struct _buf*)pUserData2;
+		HRESULT hr;
+		if (index == *p_id) {
+			IPropertyBag *pPropBag;
+			VARIANT path;
+			hr = pMoniker->BindToStorage(NULL, NULL, 
+				IID_IPropertyBag, (void**)&pPropBag);
+			if (FAILED(hr)) {
+				buf->len = -1; /* failed */
+				return hr;
+			}
+			VariantInit(&path);
+			path.vt = VT_BSTR;
+			hr = pPropBag->Read(L"DevicePath", &path, NULL);
+			if (FAILED(hr)) {
+				pPropBag->Release();
+				VariantClear(&path);
+				buf->len = -1;
+				return hr;
+			}
+			buf->len = SysStringByteLen(path.bstrVal) + 2;
+			if (buf)
+				memcpy(buf->buf, path.bstrVal, buf->len);
+			pPropBag->Release();
+			VariantClear(&path);
+			return S_OK;
+		}
+	}
+	return S_FALSE;
+}
+
+
+int  rvc_videocap_get_device_path(int device_id, char* buf, int len)
+{
+	int iret = -1;
+
+	struct _buf x = {buf, len};
+	int dev_id = device_id;
+	CoInitialize(NULL);
+
+	HRESULT hr = WalkFilterCategory((const IID)CLSID_VideoInputDeviceCategory, 
+		&get_device_path_cb, &dev_id, &x);
+	if (hr == S_OK){
+		iret = x.len;
+	}
+	CoUninitialize();
+
+	return iret;
+}
+
+
+int  rvc_videocap_get_device_info(int device_id, char* namebuf, int namelen, char* pathbuf, int pathlen)
+{
+	int iret = -1;
+
+	return iret;
+}
+
+static int bin2str(unsigned char* x, int xlen, char* str, int str_size)
+{
+	static const char* hex2char = "0123456789ABCDEF";
+	int i, k = 0;
+	if (str_size <= xlen * 2)
+		return -1;
+	for (i = 0; i < xlen; ++i) {
+		int h = x[i] >> 4;
+		int l = x[i] & 0xf;
+		str[k++] = hex2char[h];
+		str[k++] = hex2char[l];
+	}
+	str[k] = 0;
+	return k;
+}
+
+
+int  rvc_videocap_get_device_fullpathname(int device_id, char* fullnamebuf, int fulllen)
+{
+	int iret = -1;
+
+	char tmp[MAX_PATH] = {0};
+	int ilen = rvc_videocap_get_device_name(device_id, tmp, sizeof(tmp)/sizeof(tmp[0]));
+	wchar_t strname[MAX_PATH] = {0};
+	memcpy(strname, tmp, ilen);
+	char strdevname[MAX_PATH] = {0};
+	WideCharToMultiByte(CP_ACP, 0, strname, -1, strdevname, sizeof(strdevname), 0, NULL);
+
+	char tmppath[MAX_PATH_EX] = {0};
+	ilen = rvc_videocap_get_device_path(device_id, tmppath, sizeof(tmppath)/sizeof(tmppath[0]));
+	wchar_t strpath[MAX_PATH_EX] = {0};
+	memcpy(strpath, tmppath, ilen);
+	char strdevpath[MAX_PATH_EX] = {0};
+	WideCharToMultiByte(CP_ACP, 0, strpath, -1, strdevpath, sizeof(strdevpath), 0, NULL);
+
+	unsigned char x[MD5_DIGESTSIZE];
+	md5_ctx_t ctx;
+	md5_init(&ctx);
+	md5(x, strdevpath, strlen(strdevpath));
+	bin2str(x, sizeof(x), strdevpath, sizeof(strdevpath));
+	
+	iret = sprintf_s(fullnamebuf, fulllen, "%s;%s", strdevname, strdevpath);
+
+	return iret;
+}
+
+int  rvc_videocap_get_video_device_id(const char* dev_name)
+{
+	int iret = -1;
+	if (NULL == dev_name){
+		return iret;
+	}
+
+	return iret;
+}