【live555】WISInput类分析

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



(4)还有一个:

可能视频的输入设备会有好多个??好多路同时输入?

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

重要就在于我们非常关心的获取到内存中实时数据的功能,就在他们的虚函数readFromeFile中实现。

这里的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;
  }
}

看到fTo被填充了:

  // 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


你可能感兴趣的:(多媒体/流媒体/live555,c/c++)