WebRTC音视频引擎研究(3)--临界区设计、临界资源访问--CriticalSectionWrapper

大笑微笑生气吐舌头吐舌头吐舌头吐舌头吐舌头吐舌头吐舌头吐舌头吐舌头吐舌头吐舌头生气微笑大笑


偷笑我爱北京偷笑


偷笑上太阳升偷笑


偷笑伟大领袖偷笑


偷笑指引我们向前进偷笑

偷笑大笑大笑大笑大笑大笑大笑大笑大笑大笑大笑大笑大笑大笑大笑大笑大笑偷笑


WebRTC技术交流群:234795279

原文地址:http://blog.youkuaiyun.com/temotemo/article/details/7609642


1、临界区与临界资源相关概念

临界区
不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它进行访问。每个进程中访问临界资源的那段代码称为临界区(Critical Section)。
  每个进程中访问临界资源的那段程序称为临界区(Critical Section)(临界资源是一次仅允许一个进程使用的共享资源)。每次只准许一个进程进入临界区,进入后不允许其他进程进入。不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它进行访问。
  多个进程中涉及到同一个临界资源的临界区称为相关临界区。
  进程进入临界区的调度原则是:

①如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。

②任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待。

③进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。

④如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。
  如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。

  临界区在使用时以CRITICAL_SECTION结构对象保护共享资源,并分别用EnterCriticalSection()和LeaveCriticalSection()函数去标识和释放一个临界区。所用到的CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作用,共享资源依然有被破坏的可能。

2、WebRTC中临界区和临界资源

在WebRTC源码里,对线程互斥访问临界资源设计一个良好的面向对象的实现方法。

大体框图:

图示说明:

(1)CriticalSectionWrapper类用于封装临界区的操作,包括windows平台和非windows平台的;直接使用CriticalSectionWrapper定义临界区;

(2)CriticalSectionWrapper类是CriticalSectionWindows和CriticalSectionPosix的父类;CriticalSectionWindows表示Windows平台下使用Windows API定义临界区,同理CriticalSectionPosix表示非Windows平台下使用Posix定义临界区;

(3)CriticalSectionScoped类的构造函数为CriticalSectionScoped(CriticalSectionWrapper& critsec),CriticalSectionWrapper的引用作为参数,(注意:CriticalSectionWrapper构造函数返回的并不是它本身,而是根据平台选择返回它的派生类对象,返回CriticalSectionWindows对象或者CriticalSectionPosix对象,以此达到CriticalSectionWrapper封装的目的)并且在定义CriticalSectionScoped的同时进入临界区;CriticalSectionScoped析构函数离开临界区;


定义CriticalSectionWrapper类和CriticalSectionScoped类:(critical_section_wrapper.h)

//=========critical_section_wrapper.h
#ifndef CRITICAL_SECTION_WRAPPER_H
#define CRITICAL_SECTION_WRAPPER_H

namespace webrtc {
	class CriticalSectionWrapper
	{
	public:
		// Factory method, constructor disabled
		static CriticalSectionWrapper* CreateCriticalSection();//工厂方法,创建临界区,返回指针

		virtual ~CriticalSectionWrapper() {}

		// Tries to grab lock, beginning of a critical section. Will wait for the
		// lock to become available if the grab failed.
		virtual void Enter() = 0;

		// Returns a grabbed lock, end of critical section.
		virtual void Leave() = 0;
	};

	// RAII extension of the critical section. Prevents Enter/Leave missmatches and
	// provides more compact critical section syntax.
	class CriticalSectionScoped
	{
	public:
		CriticalSectionScoped(CriticalSectionWrapper& critsec) //构造函数,进入临界区
			:
		_ptrCritSec(&critsec)
		{
			_ptrCritSec->Enter();
		}

		~CriticalSectionScoped() //析构函数,保证离开临界区
		{
			if (_ptrCritSec)
			{
				Leave();
			}
		}

	private:
		void Leave()
		{
			_ptrCritSec->Leave();
			_ptrCritSec = 0;
		}

		CriticalSectionWrapper* _ptrCritSec;
	};
} // namespace
#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_CRITICAL_SECTION_WRAPPER_H_

CriticalSectionWrapper类实现:(critical_section.cpp)

//=========== critical_section.cpp
#if defined(_WIN32)
#include <windows.h> 
#include "critical_section_windows.h" //使用Windows平台API
#else
#include "critical_section_posix.h" //使用posix
#endif

namespace webrtc {
	CriticalSectionWrapper* CriticalSectionWrapper::CreateCriticalSection()
	{
#ifdef _WIN32
		return new CriticalSectionWindows();//Windows平台,返回CriticalSectionWindows对象
#else
		return new CriticalSectionPosix(); //非Windows平台,返回CriticalSectionPosix对象
#endif
	}
} // namespace webrt



Windows平台下临界区操作CriticalSectionWindows API 定义头文件(critical_section_windows.h)

//============== critical_section_windows.h
#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_CRITICAL_SECTION_WINDOWS_H_
#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_CRITICAL_SECTION_WINDOWS_H_

//#include "typedefs.h"
#include "critical_section_wrapper.h"
#include <windows.h>  //使用Windows平台API

namespace webrtc {
	class CriticalSectionWindows : public CriticalSectionWrapper
	{
	public:
		CriticalSectionWindows();

		virtual ~CriticalSectionWindows();

		virtual void Enter();
		virtual void Leave();

	private:
		CRITICAL_SECTION crit;

		//friend class ConditionVariableWindows;
	};
} // namespace webrtc

#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_CRITICAL_SECTION_WINDOWS_H_

Windows平台下临界区操作CriticalSectionWindows类 API 实现文件(critical_section_windows.cpp)

//============= critical_section_windows.cpp
#include "critical_section_windows.h"

namespace webrtc {
	CriticalSectionWindows::CriticalSectionWindows()
	{
		InitializeCriticalSection(&crit);	//构造函数初始化临界区
	}

	CriticalSectionWindows::~CriticalSectionWindows()
	{
		DeleteCriticalSection(&crit);//析构函数删除临界区,利用类来管理资源的思想,Effective C++思想
	}

	void
		CriticalSectionWindows::Enter()
	{
		EnterCriticalSection(&crit);		//进入临界资源
	}

	void
		CriticalSectionWindows::Leave()
	{
		LeaveCriticalSection(&crit);		//离开临界资源
	}
} // namespace webrtc


非Windows平台下临界区操作CriticalSectionPosix API 定义头文件(critical_section_posix.h)

#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_CRITICAL_SECTION_POSIX_H_
#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_CRITICAL_SECTION_POSIX_H_

//非Windows平台使用Posix的临界资源

#include "critical_section_wrapper.h"

#include <pthread.h> //使用Posix

namespace webrtc {
class CriticalSectionPosix : public CriticalSectionWrapper
{
public:
    CriticalSectionPosix();

    virtual ~CriticalSectionPosix();

    virtual void Enter();
    virtual void Leave();

private:
    pthread_mutex_t _mutex;
    //friend class ConditionVariablePosix;
};
} // namespace webrtc

#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_CRITICAL_SECTION_POSIX_H_

非Windows平台下临界区操作CriticalSectionPosix类 API 实现文件(critical_section_posix.cpp)


#include "critical_section_posix.h"

namespace webrtc {
CriticalSectionPosix::CriticalSectionPosix()
{
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&_mutex, &attr);
}

CriticalSectionPosix::~CriticalSectionPosix()
{
    pthread_mutex_destroy(&_mutex);
}

void
CriticalSectionPosix::Enter()
{
    pthread_mutex_lock(&_mutex);
}

void
CriticalSectionPosix::Leave()
{
    pthread_mutex_unlock(&_mutex);
}
} // namespace webrtc

3、使用CriticalSectionWrapper和CriticalSectionScoped示例

设备池数据是临界资源,为保证临界资源不被破坏,必须使用临界区实现互斥访问:

头文件:...src\video_engine\main\test\WindowsTest\CaptureDevicePool.h

#pragma once

#include "common_types.h"

#include "vie_base.h"
#include "vie_capture.h"
#include "vie_file.h"
#include "map_wrapper.h"

namespace webrtc {
class CriticalSectionWrapper;
}
using namespace webrtc;
class CaptureDevicePool
{
public:
    CaptureDevicePool(VideoEngine* videoEngine);
    ~CaptureDevicePool(void);
    WebRtc_Word32 GetCaptureDevice(int& captureId, const char uniqeDeviceName[256]);
    WebRtc_Word32 ReturnCaptureDevice(int captureId);

    private: 
        struct DeviceItem
        {
            int captureId;
            WebRtc_Word32 refCount;
            char uniqeDeviceName[256];
            DeviceItem()
            {
                captureId=-1;
                refCount=0;
            }
        };
        CriticalSectionWrapper& _critSect;
        ViECapture* _vieCapture;
        ViEFile*    _vieFile;
        MapWrapper _deviceMap;

};

实现:...src\video_engine\main\test\WindowsTest\CaptureDevicePool.cpp

#include "CaptureDevicePool.h"
#include "map_wrapper.h"
#include <string.h>
#include <assert.h>
#include "critical_section_wrapper.h"
#include "vie_file.h"

CaptureDevicePool::CaptureDevicePool(VideoEngine* videoEngine):
_critSect(*CriticalSectionWrapper::CreateCriticalSection()),
_vieCapture(ViECapture::GetInterface(videoEngine)),
_vieFile(ViEFile::GetInterface(videoEngine))
{
}

CaptureDevicePool::~CaptureDevicePool(void)
{
    assert(_deviceMap.Size()==0);
    _vieCapture->Release();
    _vieFile->Release();
    delete &_critSect;
}

WebRtc_Word32 CaptureDevicePool::GetCaptureDevice(int& captureId, const char* uniqeDeviceName)
{
    CriticalSectionScoped cs(_critSect); //创建CriticalSectionScoped cs即立刻进入临界区,它的析构函数保证释放临界资源,保护对设备信息MapWrapper _deviceMap的访问
    DeviceItem* device=NULL;
    
    for(MapItem* item=_deviceMap.First();
        item!=NULL;
        item=_deviceMap.Next(item))
    {
        //Found the device?先从临界资源_deviceMap中查找Device,如果没有的话就定位一个新的DeviceIterm
        if(strcmp(uniqeDeviceName,(static_cast<DeviceItem*>( item->GetItem()))->uniqeDeviceName)==0)
        {
            device=static_cast<DeviceItem*>( item->GetItem());
            device->refCount++;
            captureId=device->captureId;
            return 0;
        }
    }
	//没找到Device定位一个新的Device
    device = new DeviceItem;
    strncpy(device->uniqeDeviceName,uniqeDeviceName,255);


    // Device does not exist. Create it.并将获取到的设备放到DeviceItem device中去
    WebRtc_Word32 result=_vieCapture->AllocateCaptureDevice(device->uniqeDeviceName,strlen(device->uniqeDeviceName),device->captureId);
    if(result==0)
    {
        //配置设备的属性
		//CaptureCapability cap;
        /*cap.height=1080;
        cap.width=1920;
        cap.maxFPS=25;    
        cap.interlaced=true;*/
     //   result=_vieCapture->StartCapture(device->captureId,cap);
		//设置捕捉设备初始图片
        result=_vieFile->SetCaptureDeviceImage(device->captureId,"captureDeviceImage.jpg");
    }
    captureId=device->captureId;			//返回新设备的ID给函数
    _deviceMap.Insert(captureId,device);//插入到MapWrapper _deviceMap中去
    device->refCount++;						//引用加1
    
    return result;


}
WebRtc_Word32 CaptureDevicePool::ReturnCaptureDevice(int captureId)
{
    CriticalSectionScoped cs(_critSect);

	MapItem* mapItem=_deviceMap.Find(captureId);
	if(!mapItem)
		return -1;

    DeviceItem* item=static_cast<DeviceItem*> (mapItem->GetItem());
    if(!item)
        return 0;
    item->refCount--;
    WebRtc_Word32 result=0;

    if(item->refCount==0)
    {
        result=_vieCapture->ReleaseCaptureDevice(captureId);
        
        _deviceMap.Erase(mapItem);
        delete item;

    }
    return result;
}


/==================================/

愚人愚见,欢迎拍砖,你的砖头是我的面包。

zengxijin@qq.com

/=================================/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值