类WISInput继承自Medum,但是和Medium差异很大。
WISInput中大量使用了static的成员函数和属性。
1.
createNew依旧是调用构造函数来创建一个类对象的指针。createNew永远都是静态函数,永远都要传入UsageEnvrionment。
static WISInput* createNew(UsageEnvironment& env);
2.
WISInput类要执行初始化工作:
Boolean WISInput::initialize(UsageEnvironment& env) {
do {
if (!openFiles(env)) break;
if (!initALSA(env)) break;
if (!initV4L(env)) break;
return True;
} while (0);
// An error occurred
return False;
}
(1)打开文件? 这里的文件应该是和音视频设备有关系,可能是和WIS驱动有关系。
(2)执行v4l2设备的初始化
这里打开视频输入设备,比如/dev/video0的摄像头,比如tunner,设置捕获的格式,设置帧率(提供给WIS编码用?),设置亮度、对比度等其他的一些视频参数,设置一些压缩的参数,设置码率,设置MMAP方式讲帧的缓冲映射到用户空间。
基本上,这些初始化与V4L2的标准初始化流程是一样的。
(3)执行音频设备ALSA的初始化
Boolean WISInput::initALSA(UsageEnvironment& env) {
do {
int arg;
arg = AFMT_S16_LE;
if (ioctl(fOurAudioFileNo, SNDCTL_DSP_SETFMT, &arg) < 0) {
printErr(env, "SNDCTL_DSP_SETFMT");
break;
}
arg = audioSamplingFrequency;
if (ioctl(fOurAudioFileNo, SNDCTL_DSP_SPEED, &arg) < 0) {
printErr(env, "SNDCTL_DSP_SPEED");
break;
}
arg = audioNumChannels > 1 ? 1 : 0;
if (ioctl(fOurAudioFileNo, SNDCTL_DSP_STEREO, &arg) < 0) {
printErr(env, "SNDCTL_DSP_STEREO");
break;
}
return True;
} while (0);
可能视频的输入设备会有好多个??好多路同时输入?
void WISInput::listVideoInputDevices(UsageEnvironment& env) {
env << "Input devices available:\n";
for (int i = 0; ; ++i) {
struct v4l2_input inp;
memset(&inp, 0, sizeof inp);
inp.index = i;
if (ioctl(fOurVideoFileNo, VIDIOC_ENUMINPUT, &inp) < 0) break; // no more
env << "\tdevice #" << i << ": " << (char*)(inp.name) << " (";
for (int j = 0; ; ++j) {
struct v4l2_standard s;
memset(&s, 0, sizeof s);
s.index = j;
if (ioctl(fOurVideoFileNo, VIDIOC_ENUMSTD, &s) < 0) break;
if (j > 0) env << ", ";
env << (char*)(s.name);
}
env << ")\n";
}
}
3. 有俩非常重要的友元函数
//私有的
private:
//友类,是啥??
//打开视频文件源
friend class WISVideoOpenFileSource;
//音频
friend class WISAudioOpenFileSource;
WISInput和WISVideoOpenFileSource、WISAudioOpenFileSource 是可以相互访问的。
这俩分别打开视频的FileSource和音频的FileSource的函数非常的重要。
////////// WISVideoOpenFileSource definition //////////
class WISVideoOpenFileSource: public WISOpenFileSource {
public:
WISVideoOpenFileSource(UsageEnvironment& env, WISInput& input);
virtual ~WISVideoOpenFileSource();
protected: // redefined virtual functions:
virtual void readFromFile();
};
////////// WISAudioOpenFileSource definition //////////
class WISAudioOpenFileSource: public WISOpenFileSource {
public:
WISAudioOpenFileSource(UsageEnvironment& env, WISInput& input);
virtual ~WISAudioOpenFileSource();
protected: // redefined virtual functions:
virtual void readFromFile();
};
这里的file不一定是本地文件,/dev/下的设备也是文件(对于linux来说)。
4. 为了能与WISVideoOpenFileSource 和 WISAudioOpenFileSource 俩交互。
不仅仅是上面设置了友元,而且还有:
static FramedSource* fOurVideoSource;
static FramedSource* fOurAudioSource;
FramedSource* videoSource();
FramedSource* audioSource();
比如说,属性fOurVideoSource是一个静态的类对象指针,居然是FrameSource这样的抽象基类的对象(难道是要启用多态机制?)
而静态的成员函数videoSource()就是返回这样的一个指针的。
FramedSource* WISInput::videoSource() {
if (fOurVideoSource == NULL) {
fOurVideoSource = new WISVideoOpenFileSource(envir(), *this);
}
return fOurVideoSource;
}
FramedSource* WISInput::audioSource() {
if (fOurAudioSource == NULL) {
fOurAudioSource = new WISAudioOpenFileSource(envir(), *this);
}
return fOurAudioSource;
}
由此,可见,WISVideoOpenFileSource 和 WISAudioOpenFileSource 的重要性。
5 . 开始分析这俩非常重要的类 WISVideoOpenFileSource 和WISAudioOpenFileSource
这俩类都继承自 WISOpenFileSource ,而WISOpenFileSource 又继承自FramedSource,
这就可以解释,为啥videoSource()和audioSource()都是返回FramedSource类的对象指针了。
但为啥要用其爷爷类的对象指针呢??真的为了多态???
// A common "FramedSource" subclass, used for reading from an open file:
//一个通用的FrameSource 子类,用于从一个打开的文件中读取
class WISOpenFileSource: public FramedSource {
//保护
protected:
WISOpenFileSource(UsageEnvironment& env, WISInput& input, int fileNo);
virtual ~WISOpenFileSource();
virtual void readFromFile() = 0;
private: // redefined virtual functions:
virtual void doGetNextFrame();
private:
static void incomingDataHandler(WISOpenFileSource* source, int mask);
void incomingDataHandler1();
protected:
WISInput& fInput;
int fFileNo;
};
这个类里头,非常引人注目的是方法:
private:
static void incomingDataHandler(WISOpenFileSource* source, int mask);
void incomingDataHandler1();
这俩方法干啥用的呢???
同时,居然还有一个引用?
这个引用是 WISInput的。
虽然还不知道为啥他们俩扯上了这样的剪不断、理还乱的联系,但是似乎非常需要,尽管类间的层次感有种瞬间坍塌的感觉。
6. 继续分析这俩非常重要的类 WISVideoOpenFileSource 和WISAudioOpenFileSource
他们的readFromeFile中,获取到了一帧数据,
从v4l2接口中获取到一帧实时传递来的摄像头得到的视频帧:
void WISVideoOpenFileSource::readFromFile() {
// Retrieve a filled video buffer from the kernel:
unsigned i;
struct v4l2_buffer buf;
if (capture_start) {
capture_start = 0;
for (i = 0; i < MAX_BUFFERS; ++i) {
memset(&buf, 0, sizeof buf);
buf.index = i;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (ioctl(fFileNo, VIDIOC_QBUF, &buf) < 0) {
printErr(envir(), "VIDIOC_QBUF");
return;
}
}
// Start capturing:
i = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fFileNo, VIDIOC_STREAMON, &i) < 0) {
printErr(envir(), "VIDIOC_STREAMON");
return;
}
}
memset(&buf, 0, sizeof buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (ioctl(fFileNo, VIDIOC_DQBUF, &buf) < 0) {
printErr(envir(), "VIDIOC_DQBUF");
return;
}
// Note the timestamp and size:
fPresentationTime = buf.timestamp;
fFrameSize = buf.bytesused;
if (fFrameSize > fMaxSize) {
fNumTruncatedBytes = fFrameSize - fMaxSize;
fFrameSize = fMaxSize;
} else {
fNumTruncatedBytes = 0;
}
// Copy to the desired place:
memmove(fTo, buffers[buf.index].addr, fFrameSize);
// Send the buffer back to the kernel to be filled in again:
if (ioctl(fFileNo, VIDIOC_QBUF, &buf) < 0) {
printErr(envir(), "VIDIOC_QBUF");
return;
}
}
// Copy to the desired place:
memmove(fTo, buffers[buf.index].addr, fFrameSize);
这个函数每次只取出了一帧。
音频是这样做的,看不太懂还,等以后再分析了:
void WISAudioOpenFileSource::readFromFile() {
// Read available audio data:
int timeinc;
int ret = read(fInput.fOurAudioFileNo, fTo, fMaxSize);
if (ret < 0) ret = 0;
fFrameSize = (unsigned)ret;
gettimeofday(&fPresentationTime, NULL);
/* PR#2665 fix from Robin
* Assuming audio format = AFMT_S16_LE
* Get the current time
* Substract the time increment of the audio oss buffer, which is equal to
* buffer_size / channel_number / sample_rate / sample_size ==> 400+ millisec
*/
timeinc = fFrameSize * 1000 / audioNumChannels / (audioSamplingFrequency/1000) / 2;
while (fPresentationTime.tv_usec < timeinc)
{
fPresentationTime.tv_sec -= 1;
timeinc -= 1000000;
}
fPresentationTime.tv_usec -= timeinc;
}
7 。他们的爷爷类FramedSource中,曾要求实现 纯虚函数 doGetNextFrame,
virtual void doGetNextFrame() = 0;
// called by getNextFrame()
还要求填充这些值:
protected:
// The following variables are typically accessed/set by doGetNextFrame()
unsigned char* fTo; // in
unsigned fMaxSize; // in
unsigned fFrameSize; // out
unsigned fNumTruncatedBytes; // out
struct timeval fPresentationTime; // out
unsigned fDurationInMicroseconds; // out
貌似readFromeFile已经实现了这个函数的功能,可,这让人家doGetNextFrame()怎么做呢?
WIS中,还需要这个函数么???
8.另外一个非常重视的就是对RTPSink的缓冲大小的设置。
!!!!
9 WISInput.hh
/*
* Copyright (C) 2005-2006 WIS Technologies International Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and the associated README documentation file (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// An interface to the WIS GO7007 capture device.
// C++ header
#ifndef _WIS_INPUT_HH
#define _WIS_INPUT_HH
#include
//Medium的子类。
class WISInput: public Medium {
public:
static WISInput* createNew(UsageEnvironment& env);
//一帧数据的源头
FramedSource* videoSource();
FramedSource* audioSource();
//私有的。
private:
//创建的时候,由createNew来调用这个构造函数?
WISInput(UsageEnvironment& env); // called only by createNew()
virtual ~WISInput();
//初始化,打开文件,初始化ALSA和V4L
static Boolean initialize(UsageEnvironment& env);
static Boolean openFiles(UsageEnvironment& env);
static Boolean initALSA(UsageEnvironment& env);
static Boolean initV4L(UsageEnvironment& env);
//初始化视频输入设备
static void listVideoInputDevices(UsageEnvironment& env);
//私有的
private:
//友类,是啥??
//打开视频文件源
friend class WISVideoOpenFileSource;
//音频
friend class WISAudioOpenFileSource;
//是否已经初始化了。
static Boolean fHaveInitialized;
//这个是?视频文件号?
static int fOurVideoFileNo;
//又是一帧数据的源头,视频的??
static FramedSource* fOurVideoSource;
//
static int fOurAudioFileNo;
static FramedSource* fOurAudioSource;
};
//为RTP sink对象设置最佳的缓冲的大小的函数
// Functions to set the optimal buffer size for RTP sink objects.
//在每个RTPSink创建的时候被调用
// These should be called before each RTPSink is created.
//音视频帧的最大大小,然后给音视频帧的输出缓冲设置最大的值。
#define AUDIO_MAX_FRAME_SIZE 20480
#define VIDEO_MAX_FRAME_SIZE 250000
inline void setAudioRTPSinkBufferSize() { OutPacketBuffer::maxSize = AUDIO_MAX_FRAME_SIZE; }
inline void setVideoRTPSinkBufferSize() { OutPacketBuffer::maxSize = VIDEO_MAX_FRAME_SIZE; }
#endif