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

原创 2013年12月02日 16:43:02

之前写过一篇 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 250000
inline 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;
}


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




版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

live555 流程重要函数整理

live555 流程重要函数整理 服务器启动 DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB); e...

live555学习笔记16-几个重要对象的生命期

十六 几个重要对象的生命期 live555中很多类,类与类之间的关系复杂,从属关系不明显,层次上看起来也有些乱.所以源代码读起来比较困难,对于一些对象生命的来龙去脉也很难厘清. 但这并不能...

live555 流程重要函数整理

http://hi.baidu.com/amixyue/blog/item/df5d0a37a87f96305ab5f50e.html live555 流程重要函数整理 2010-02-0...

live555源码分析----mpg文件的处理

live555支持的文件格式多为单流的文件,仅支持*.mpg、*.mkv、*.webm几种音视频混合类型的文件。其实我的目的是扩展其支持的格式,如avi等, 所以来分析一下mpg文件的处理。 ...
  • gavinr
  • gavinr
  • 2011-12-06 17:55
  • 5135

Live555源码分析RTSP客户端

  • 2013-11-29 09:55
  • 139KB
  • 下载

live555学习笔记8-RTSPClient分析

八 RTSPClient分析 有RTSPServer,当然就要有RTSPClient。 如果按照Server端的架构,想一下Client端各部分的组成可能是这样: 因为要连接RTSP server,所...

live555源码分析----RTP的打包与发送

这里主要分析一下,live555中关于RTP打包发送的部分。在处理完PLAY命令之后,就开始发送RTP数据包了(其实在发送PLAY命令的response包之前,就会发送一个RTP包,这里传输就已经开始...
  • gavinr
  • gavinr
  • 2011-12-02 17:10
  • 16941

live555从RTSP服务器读取数据到使用接收到的数据流程分析

本文在linux环境下编译live555工程,并用cgdb调试工具对live555工程中的testProgs目录下的openRTSP的执行过程进行了跟踪分析,直到将从socket端读取视频数据并保存为...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)