【live555】WISInput类及其重要相关类分析

来源:互联网 发布:脑白金的网络推广案例 编辑:程序博客网 时间:2024/05/16 01:28

之前写过一篇 http://blog.csdn.net/commshare/article/details/14652363

是11月9号的,20天过去了,再看一遍,有何新的认识呢.....请看下文:


(1)WISInput作为WIS-STREAMER的最为重要的类,代表数据输入和获取。

该类处理了很多V4L2的视频输入,这里暂时不做分析。

// An interface to the WIS GO7007 capture device.// C++ header#ifndef _WIS_INPUT_HH#define _WIS_INPUT_HH#include <MediaSink.hh>//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;};#endif



(2)这个WISInput.hh中,还有两个函数的声明,这是给sink们用的,与输出的缓冲区大小有关系:

//为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 250000inline void setAudioRTPSinkBufferSize() { OutPacketBuffer::maxSize = AUDIO_MAX_FRAME_SIZE; }inline void setVideoRTPSinkBufferSize() { OutPacketBuffer::maxSize = VIDEO_MAX_FRAME_SIZE; }


(3)

WISInput有两个友元类,


  friend class WISVideoOpenFileSource;
  friend class WISAudioOpenFileSource;

这俩类非常重要,随后还会分析。



这俩类需要访问WISInput的私有属性和方法有:


//私有的属性

private:  static Boolean fHaveInitialized;  static int fOurVideoFileNo;  static FramedSource* fOurVideoSource;  static int fOurAudioFileNo;  static FramedSource* fOurAudioSource;


//私有的函数

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);




(4)WISInput类分析:

【1】 父类是Medium,表明WISInput类的功能将会是liveMedia的一部分,而且将作为WIS-STREAMDER中的基类存在。


////////// WISInput implementation //////////


WISInput* WISInput::createNew(UsageEnvironment& env) {
  if (!fHaveInitialized) {
    if (!initialize(env)) return NULL;
    fHaveInitialized = True;
  }


  return new WISInput(env);
}




WISInput::WISInput(UsageEnvironment& env)
  : Medium(env) {
}


WISInput::~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;
}


static void printErr(UsageEnvironment& env, char const* str = NULL) {
  if (str != NULL) err(env) << str;
  env << ": " << strerror(env.getErrno()) << "\n";
}


【2】 public的创建函数,欢迎大家使用她。

public:
  static WISInput* createNew(UsageEnvironment& env);

【3】与俩友元有关的俩函数:

这里也是public的:难道是欢迎大家得到数据源(音频和视频的数据源是分开提供的)

//一帧数据的源头
  FramedSource* videoSource();
  FramedSource* audioSource();


private的:

而且是静态的啊。

  static FramedSource* fOurVideoSource;

  static FramedSource* fOurAudioSource;


以上俩public的函数,就是用来给外部访问这俩private的数据源(属性)用的方法。


友元是:

  friend class WISVideoOpenFileSource;
  friend class WISAudioOpenFileSource;


只是不理解,为啥返回的是FrameSource *类型的,这是他们的爷爷类啊。

难道是为了虚函数?,一个指向爷爷的指针,操控各个成员,得到的是爷爷类的成员实现?

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;}




【4】静态函数们是类内部的功能代码:

//初始化,打开文件,初始化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);


(5)在WISInput.cpp中,有三个;类的声明和实现:

【1】

作为一个通用类,专门处理打开文件(获取一帧数据)的类WISOpenFileSource

////////// WISOpenFileSource definition ////////////文件源// 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;};


1)
注意到他的纯虚函数
virtual void readFromFile() = 0;

这个必须由子类实现,这个是获取一帧的关键函数。


2)

该类继承自FrameSource,可以参考我的http://blog.csdn.net/commshare/article/details/17072581 【live555】代表帧类型输入的媒体源的类FramedSource浅析】


因此定义了一个虚函数  doGetNextFrame(),要重新实现。



4)私有的 静态的 函数  incomingDataHandler是一个回调函数,看起来是只在WISOpenFileSource中使用的。

还有一个非staitc的函数incomingDataHandler1是实际实现incomingDataHandler的函数。

private:  static void incomingDataHandler(WISOpenFileSource* source, int mask);  void incomingDataHandler1();


5) 居然有一个数据域,是一个引用,指向WISInput类。

protected:
  WISInput& fInput;   //
  int fFileNo;


6)WISOpenFileSource的实现:

////////// WISOpenFileSource implementation //////////WISOpenFileSource::WISOpenFileSource(UsageEnvironment& env, WISInput& input, int fileNo)  : FramedSource(env),    fInput(input), fFileNo(fileNo) {}WISOpenFileSource::~WISOpenFileSource() {  envir().taskScheduler().turnOffBackgroundReadHandling(fFileNo);}void WISOpenFileSource::doGetNextFrame() {  // Await the next incoming data on our FID:  //在这里调用了自己实现的incomingDataHandler函数来处理。  envir().taskScheduler().turnOnBackgroundReadHandling(fFileNo,       (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this);}void WISOpenFileSource::incomingDataHandler(WISOpenFileSource* source, int /*mask*/) {//又做了一层封装。  source->incomingDataHandler1();}void WISOpenFileSource::incomingDataHandler1() {  // Read the data from our file into the client's buffer:  //调用了这个函数,读取一帧数据  readFromFile();  // Stop handling any more input, until we're ready again:  envir().taskScheduler().turnOffBackgroundReadHandling(fFileNo);  // Tell our client that we have new data:  这个是说有新的数据来了?  afterGetting(this);}

这里,我们最关心的是doGetNextFrame的实现了,这是来自父类FramedSource的纯虚函数

  1.   virtual void doGetNextFrame() = 0;  
  2.       // called by getNextFrame()  

子类WISOpenFileSource必须实现:


看上去是在调用一个static的函数?

void WISOpenFileSource::doGetNextFrame() {
  // Await the next incoming data on our FID:
  //在这里调用了自己实现的incomingDataHandler函数来处理。
  envir().taskScheduler().turnOnBackgroundReadHandling(fFileNo,
      (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this);
}


这个数据处理是一个static的函数incomingDataHandler,实际调用的是一个非static的incomingDataHandler1:

void WISOpenFileSource
::incomingDataHandler(WISOpenFileSource* source, int /*mask*/) {
//又做了一层封装。
  source->incomingDataHandler1();
}


/***********incomingDataHandler1()的实现*****************/

void WISOpenFileSource::incomingDataHandler1() {
  // Read the data from our file into the client's buffer:
  //调用了这个函数,读取一帧数据
  readFromFile();



  // Stop handling any more input, until we're ready again:
  envir().taskScheduler().turnOffBackgroundReadHandling(fFileNo);


  // Tell our client that we have new data:  这个是说有新的数据来了?
  afterGetting(this);

}


这个afterGetting函数,看起来是是使用的FramedSource的:


void FramedSource::afterGetting(FramedSource* source) {  source->fIsCurrentlyAwaitingData = False;      // indicates that we can be read again      // Note that this needs to be done here, in case the "fAfterFunc"      // called below tries to read another frame (which it usually will)  if (source->fAfterGettingFunc != NULL) {    (*(source->fAfterGettingFunc))(source->fAfterGettingClientData,  source->fFrameSize, source->fNumTruncatedBytes,  source->fPresentationTime,  source->fDurationInMicroseconds);  }}

看起来需要执行一个以函数指针提供的函数啊:


  1. private:  
  2.   afterGettingFunc* fAfterGettingFunc;   //处理获取到一帧之后的事儿?  

可这个函数指针,又是在哪里的呢??




【2】WISOpenFileSource 有两个子类,分别处理音频和视频的打开。

他们主要是根据要求,实现WISOpenFileSource的纯虚函数readFromFIle()

视频:

////////// 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();};


实际读取一帧数据的活,就是由这俩子类所实现的父类WISOpenFileSource的纯虚函数readFromFile做的。


1)读取一帧视频


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;  }}



2)读取一帧音频

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;}


因此,核心的重要的需要修改的功能就在于如何去实现这俩函数。




原创粉丝点击