使用Gstreamer 作为数据源输出视频数据 II 媒体类型

2021-11-11  本文已影响0人  Charles_linzc

Directshow的媒体类型结构

在directshow中,对于push模式的source filter来说,有两个重要的基础类,CSource
CSourceStream。CSource是filter的基础类,CSourceStream是outputpin的基础类。这里我们就看看后者里有关媒体类型的内容。
在directshow中两个Pin(gstreamer叫做pad)相连接时,需要进行类型与格式的协商,其中就依赖CSourceStream的一个重要方法:

//Gets a media type from the output pin.
virtual HRESULT GetMediaType(
   int        iPosition,
   CMediaType *pMediaType
);

GetMediaType返回一个或多个媒体类型(media type),用于和下游的filter进行协商。下游如果是decoder/encoder,需要这些信息进行编码和解码工作;如果是render,需要更详细的信息,包括格式信息等进行渲染。详细可参考How to Write a Source Filter for DirectShow

AM_MEDIA_TYPE

在这里,可以看到媒体类型使用CMediaType类来表示, CMediaType类继承自AM_MEDIA_TYPE结构体,提供了AM_MEDIA_TYPE结构体操作方法。如下正式AM_MEDIA_TYPE的定义:

typedef struct _AMMediaType {
  GUID     majortype;
  GUID     subtype;
  BOOL     bFixedSizeSamples;
  BOOL     bTemporalCompression;
  ULONG    lSampleSize;
  GUID     formattype;
  IUnknown *pUnk;
  ULONG    cbFormat;
  BYTE     *pbFormat;
} AM_MEDIA_TYPE

majortype

majortype 的值是一个GUID, 用它来确定媒体数据的主要类型,directshow为每个GUID都定义了常量标识符,下面是它的list:

GUID Description
MEDIATYPE_AnalogAudio Analog audio.模拟音频,一般是声卡采集输入的数据类型,TV Audio Filter就接收这种类型的音频
MEDIATYPE_AnalogVideo Analog video. 模拟视频,一般是视频采集卡输入的数据类型
MEDIATYPE_Audio Audio. See Audio Subtypes. 数字音频信号
MEDIATYPE_AUXLine21Data Line 21 data. Used by closed captions. See Line 21 Media Types. 闭路电视信号(参考EIA-608)
MEDIATYPE_File File. (Obsolete) 已经废弃文件类型
MEDIATYPE_Interleaved Interleaved audio and video. Used for Digital Video (DV). DV的数据流(包括音视频数据)
MEDIATYPE_LMRT Obsolete. Do not use.
MEDIATYPE_Midi MIDI format. 乐器数字接口信号,MIDI是编曲界最广泛的音乐标准格式
MEDIATYPE_MPEG2_PES MPEG-2 PES packets. See MPEG-2 Media Types. 用于DVD或数字视频广播(DVB)
MEDIATYPE_MPEG2_SECTIONS MPEG-2 section data. See MPEG-2 Media Types. MPEG-2 Demultiplexer的output媒体类型之一
MEDIATYPE_ScriptCommand Data is a script command, used by closed captions. 闭路电视
MEDIATYPE_Stream Byte stream with no time stamps. See Stream Subtypes. 字节流,如(Pull模式)文件源的输出数据类型
MEDIATYPE_Text Text.
MEDIATYPE_Timecode Timecode data. Note: DirectShow does not provide any filters that support this media type.
MEDIATYPE_URL_STREAM Obsolete. Do not use.
MEDIATYPE_VBI Vertical blanking interval (VBI) data (for television). Same as KSDATAFORMAT_TYPE_VBI. 电视广播VBI信号
MEDIATYPE_Video Video. See Video Subtypes. 数字视频信号

参考图:PES 在 MPEG-2 系统中的位置,可以看到它是一视频编码后更外边的包。

image.png

subtype

很多著类型都有子类型,在上面的列表种我们主要关心的是MEDIATYPE_Video类型,在MEDIATYPE_Video下的subtype可以被分为几类:

我们最常见的图像表示方法是RGB和YUV; 在YUV视频分类下,我们可以看到如下常用的类型:

GUID Format Sampling Packed or planar Bits per channel
MEDIASUBTYPE_AYUV AYUV 4:4:4 Packed 8
MEDIASUBTYPE_YUY2 YUY2 4:2:2 Packed 8
MEDIASUBTYPE_UYVY UYVY 4:2:2 Packed 8
MEDIASUBTYPE_IMC1 IMC1 4:2:0 Planar 8
MEDIASUBTYPE_IMC3 IMC2 4:2:0 Planar 8
MEDIASUBTYPE_IMC2 IMC3 4:2:0 Planar 8
MEDIASUBTYPE_IMC4 IMC4 4:2:0 Planar 8
MEDIASUBTYPE_YV12 YUY2 4:2:0 Planar 8
MEDIASUBTYPE_NV12 NV12 4:2:0 Planar 8

YUV类别下的子类型 像素都是YUV格式, 主要按照 采样比、存储方式,和字节深度来区分

formattype

majortype和subtype主要描述媒体的主要类型和次要类型:majortype主要定义大概的分类,例如视频、音频、字节流等;子类型定义大分类下的更小的分类,例如YUY2, YUY2等。
但是这些信息还不足够保证两个pin(gstremer称为pad)之间的成功连接,它们还需要一些详细的格式信息,例如支持的图像宽度、高度,播放频率等。
这些信息会被保存在一个格式数据块,并且仅跟在AM_MEDIA_TYPE 结构体的后面存储,私用AM_MEDIA_TYPE结构体的pbFormat指针来指向。这个format块是不定长的,所以需要使用formattype来描述它的类别,看它是那种媒体类型的格式信息。
formattype也是使用GUID表示,directshow同样提供了一个常量列表来描述它所支持的Format类型:


image.png

在上面的媒体对于视频来说,在常用的就是FORMAT_VideoInfo 和 FORMAT_VideoInfo2。

typedef struct tagVIDEOINFOHEADER {
  RECT             rcSource;    //确定source视频窗口的长方形
  RECT             rcTarget;    //确定目标视频窗口的长方形
  DWORD            dwBitRate; //视频流按bit计算的数据速率
  DWORD            dwBitErrorRate;     //出错率
  REFERENCE_TIME   AvgTimePerFrame;   //期望的每帧平均显示时间,以100纳秒为单位
  BITMAPINFOHEADER bmiHeader;  
} VIDEOINFOHEADER;
typedef struct tagVIDEOINFOHEADER2 {
  RECT             rcSource;
  RECT             rcTarget;
  DWORD            dwBitRate;
  DWORD            dwBitErrorRate;
  REFERENCE_TIME   AvgTimePerFrame;
  DWORD            dwInterlaceFlags;  //flags that specify how the video is interlaced
  DWORD            dwCopyProtectFlags;  //用于copy保护
  DWORD            dwPictAspectRatioX; //The X dimension of picture aspect ratio
  DWORD            dwPictAspectRatioY; //he Y dimension of picture aspect ratio
  union {
    DWORD dwControlFlags;
    DWORD dwReserved1;
  };
  DWORD            dwReserved2;
  BITMAPINFOHEADER bmiHeader;
} VIDEOINFOHEADER2;
typedef struct tagBITMAPINFOHEADER {
  DWORD biSize; //本结构体大小
  LONG  biWidth;  // bitmap宽度, 单位为pixel
  LONG  biHeight; //bitmap高度, 单位为pixel
  WORD  biPlanes; //number of planes for the target device. This value must be set to 1
  WORD  biBitCount; //Specifies the number of bits per pixel (bpp)
  DWORD biCompression; //对于压缩视频和YUV格式,这个字段是 FOURCC code,对于未压缩的RGB格式,它又固定的值
  DWORD biSizeImage;   //image的大小,以byte为单位。
  LONG  biXPelsPerMeter; //Specifies the horizontal resolution
  LONG  biYPelsPerMeter; // Specifies the vertical resolution
  DWORD biClrUsed;  // Specifies the number of color indices in the color table that are actually used by the bitmap
  DWORD biClrImportant; //Specifies the number of color indices that are considered important for displaying the bitmap.
} BITMAPINFOHEADER, *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;

需要注意的是BITMAPINFOHEADER结构体后面可能会跟随palette 和 color mask信息。需要参考BITMAPINFOHEADER Color Tables的描述。

rcSource,rcTarget 和biWidth/biHeight的关系

在几个结构体中,都有关于图像宽度和高度的描述,例如VIDEOINFOHEADER 和VIDEOINFOHEADER2 中的rcSource、rcTarget ;还有BITMAPINFOHEADER中的biwidth、biHeight. 它们之间的关系可以用两个filter连接后的动作来描述:

当filter A与Filter B 相连接后,数据通过A与B之间的buffer传递。Buffer有自己的大小,它由bmiHeader.biWidth, bmiHeader.biHeight决定。FilteA在填充buffer的时候,将会使用rcSource矩形来确定哪一部分的input video应该放在buffer里;并且通过拉伸操作,将由rcTarget矩形确定的buffer位置填充满。
所以在填充时需要比较rcsource,bitWidth等信息,如果rcsource值为0,表示使用整个输入视频图像填充;如果rctarget为0,表示填满整个buffer.


gst videotestsrc 的媒体类型信息

在gststreamer种, videotestsrc被用来生成各种格式的测试视频数据,默认情况下,它可以无限的生成数据。
**gst-launch-1.0 -v videotestsrc pattern=snow ! video/x-raw,width=1280,height=720 ! autovideosink **
例如使用如上命令产生的效果:

image.png

在Gststreamer中,一般用pad模板用来描述input/output pad的能力,每个filter都有一个或多个pad(directshow中称为PIN), 用来描述这个filter能够接收的媒体类型与格式或者可以输出的媒体类型和格式。 videotestsrc作为数据生成filter,它只有一个output pad, 如下是它的pad模板:

video/x-raw:
         format: { ABGR64_LE, BGRA64_LE, AYUV64, ARGB64_LE, ARGB64, RGBA64_LE, ABGR64_BE, BGRA64_BE, ARGB64_BE, RGBA64_BE, GBRA_12LE, GBRA_12BE, Y412_LE, Y412_BE, A444_10LE, GBRA_10LE, A444_10BE, GBRA_10BE, A422_10LE, A422_10BE, A420_10LE, A420_10BE, RGB10A2_LE, BGR10A2_LE, Y410, GBRA, ABGR, VUYA, BGRA, AYUV, ARGB, RGBA, A420, AV12, Y444_16LE, Y444_16BE, v216, P016_LE, P016_BE, Y444_12LE, GBR_12LE, Y444_12BE, GBR_12BE, I422_12LE, I422_12BE, Y212_LE, Y212_BE, I420_12LE, I420_12BE, P012_LE, P012_BE, Y444_10LE, GBR_10LE, Y444_10BE, GBR_10BE, r210, I422_10LE, I422_10BE, NV16_10LE32, Y210, v210, UYVP, I420_10LE, I420_10BE, P010_10LE, NV12_10LE32, NV12_10LE40, P010_10BE, Y444, RGBP, GBR, BGRP, NV24, xBGR, BGRx, xRGB, RGBx, BGR, IYU2, v308, RGB, Y42B, NV61, NV16, VYUY, UYVY, YVYU, YUY2, I420, YV12, NV21, NV12, NV12_64Z32, NV12_4L4, NV12_32L32, Y41B, IYU1, YVU9, YUV9, RGB16, BGR16, RGB15, BGR15, RGB8P, GRAY16_LE, GRAY16_BE, GRAY10_LE32, GRAY8 }
          width: [ 1, 2147483647 ]
         height: [ 1, 2147483647 ]
      framerate: [ 0/1, 2147483647/1 ]
 multiview-mode: { (string)mono, (string)left, (string)right }
video/x-bayer:
         format: { bggr, rggb, grbg, gbrg }
          width: [ 1, 2147483647 ]
         : [ 1, 2147483647 ]
      : [ 0/1, 2147483647/1 ]
 multiview-mode: { (string)mono, (string)left, (string)right }

CSourceStream中的GetMediaType方法

了解了directshow的媒体类型信息,以及gstreamer中videotestsrc的pad 模板,我们更深一步的看看如何实现CSourceStream的GetMediaType方法,并且他的媒体信息与videotestsrc的模板能力保持一致。
GetMediaType有两个重载方法:

HRESULT CVCamStream::GetMediaType(CMediaType *pmt)
{
  //为pmt创建一个VIDEOINFOHEADER结构体指针,并为它分配内存
    DECLARE_PTR(VIDEOINFOHEADER, pvi, 
    pmt->AllocFormatBuffer(sizeof(VIDEOINFOHEADER)));
    ZeroMemory(pvi, sizeof(VIDEOINFOHEADER));

    pvi->bmiHeader.biWidth = 1280; //图像宽度
    pvi->bmiHeader.biHeight = 720;  //图像高度
    pvi->AvgTimePerFrame = 333333;//每帧平均显示时常,单位100-nanosecond
    pvi->bmiHeader.biCompression = MAKEFOURCC('Y', 'U', 'Y', '2');//对于YUV格式需要在这个字段设置它的4CC代码
    pvi->bmiHeader.biBitCount = 16; //设置YUY2格式下一个像素的bits数
    pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);//bmiHeader的size
    pvi->bmiHeader.biPlanes = 1; //总是设置1
    pvi->bmiHeader.biSizeImage = pvi->bmiHeader.biWidth * 
        pvi->bmiHeader.biHeight * 2;//YUY2一个像素相当于两个字节,设置图像的大小,单位是字节byte.
    pvi->bmiHeader.biClrImportant = 0; //0表示所有颜色都是重要的

    SetRectEmpty(&(pvi->rcSource)); //填充所有source image数据
    SetRectEmpty(&(pvi->rcTarget));  //填充满buffer

    pmt->SetType(&MEDIATYPE_Video);  //设置著类型未video 对应video/x-raw
    pmt->SetFormatType(&FORMAT_VideoInfo); //设置格式类型为FORMAT_VideoInfo
    pmt->SetTemporalCompression(FALSE);  // 每一帧无压缩
    pmt->SetSubtype(&MEDIASUBTYPE_YUY2); //设置子类型格式
    pmt->SetSampleSize(pvi->bmiHeader.biSizeImage);//设置sample的大小为固定1帧的大小
    return NOERROR;

} 

如上directshow的代码,在gstreamer中,它对应的videotestsrc的caps信息为:
video/x-raw,width=1280,height=720,format=YUY2,framerate=(fraction)30/1
这里需要注意,framerate的格式用的是我们通常理解的帧/每秒, pvi->AvgTimePerFrame用的是时间,单位是100-nanosecond。
可以使用gst-launch-1.0测试以下:

gst-launch-1.0 -v videotestsrc \
         ! video/x-raw,foramt=YUV2,width=1280,height=720,framerate=30/1 \
        !  autovideosink

显示效果:


image.png

CSourceStream中的DecideBufferSize方法

当确定了媒体类型和格式信息后,作为通常output pin还需要overwiter DecideBufferSize方法,这个方法用来设置sample buffers的大小

HRESULT CVCamStream::DecideBufferSize(IMemAllocator *pAlloc, //pAlloc为指向allocator的指针,allocator管理这buffer
    ALLOCATOR_PROPERTIES *pProperties)//ALLOCATOR_PROPERTIES 包含有input pin对buffer的需求
{
    CAutoLock cAutoLock(m_pFilter->pStateLock());
    HRESULT hr = NOERROR;

    VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *)m_mt.Format();
    pProperties->cBuffers = 1;  //设置被allocator创建的buffer的多少
    pProperties->cbBuffer = pvi->bmiHeader.biSizeImage; //设置buffer的大小,以字节为单位

    ALLOCATOR_PROPERTIES Actual;
    hr = pAlloc->SetProperties(pProperties, &Actual);//这是新属性

    if (FAILED(hr)) return hr;
    if (Actual.cbBuffer < pProperties->cbBuffer) return E_FAIL;

    return NOERROR;
}

如上面的方法,allocator被要求只创建一个buffer,且大小等于1帧(bitmap的大小)。

IAMStreamConfig接口和video Capabilities

始于视频输入设备来说,它们通常支持一些列的格式。例如一个设备可能会支持 16-bit RGB, 32-bit RGB, 和YUYV. 在每个格式里,这个设备又可能会支持一系列的帧大小.
这时候 IAMStreamConfig接口通常被用来报告这个设备支持的所有格式信息,并且允许设置格式信息, 对于capture filter来说,它的output pin需要实现这个接口。

HRESULT GetNumberOfCapabilities(
  [out] int *piCount,
  [out] int *piSize
);

这个接口返回两个数据: 通过piCount获得这个filter支持的媒体类型个数。通过piSize可以获得保存caps信息的结构体的大小,对于视频来说,这个接口体时VIDEO_STREAM_CONFIG_CAPS

HRESULT GetStreamCaps(
  [in]  int           iIndex,
  [out] AM_MEDIA_TYPE **ppmt,
  [out] BYTE          *pSCC
);

这个方法通过索引index获得具体的某个format信息(ppmt指向)和能力信息(pSCC指向),对于视频pSCC指向一个 VIDEO_STREAM_CONFIG_CAPS结构体。

typedef struct _VIDEO_STREAM_CONFIG_CAPS {
  GUID     guid;   //确定Format类型的GUID
  ULONG    VideoStandard;//支持的模拟视频标准,数字视频设为0(AnalogVideo_None)
  SIZE     InputSize;  ////输入视频的大小    ---已经废弃
  SIZE     MinCroppingSize; //允许的最小resSource   ---已经废弃
  SIZE     MaxCroppingSize; //允许的最小resSource   ---已经废弃
  int      CropGranularityX;   //resSource的水平increment的度   ---已经废弃
  int      CropGranularityY;   //resSource的垂直increment的度    ---已经废弃
  int      CropAlignX;      //resSource水平对齐  ---已经废弃
  int      CropAlignY;       //resSource垂直对齐   ---已经废弃
  SIZE     MinOutputSize;   //最小输出大小   ---已经废弃
  SIZE     MaxOutputSize;   //最大输出大小  ---已经废弃
  int      OutputGranularityX;  //Granularity of the output width  ---已经废弃
  int      OutputGranularityY;  //Granularity of output height.  ---已经废弃
  int      StretchTapsX;     //水平stretch的程度   取值[0,1,2,3...] ---已经废弃   
  int      StretchTapsY;      //垂直stretch的程度    ---已经废弃
  int      ShrinkTapsX;    //水平Shrink的程度      ---已经废弃
  int      ShrinkTapsY;     //水平Shrink的程度     ---已经废弃
  LONGLONG MinFrameInterval;   //最小帧间隔 单位100纳秒
  LONGLONG MaxFrameInterval;  //最大帧间隔   单位100纳秒
  LONG     MinBitsPerSecond;   //Minimum data rate this pin can produce. ---已经废弃
  LONG     MaxBitsPerSecond;  //Maximum data rate this pin can produce. ---已经废弃
} VIDEO_STREAM_CONFIG_CAPS;

IAMStreamConfig::GetStreamCaps将返回这个结构体,应用程序可以使用这个信息output pin的格式。关于它详细的介绍可以参考Video CapabilitiesVIDEO_STREAM_CONFIG_CAPS structure。例如对于如下的设置:
MinOutputSize: 160 x 120
MaxOutputSize: 320 x 240
OutputGranularityX: 8 pixels (horizontal step size)
OutputGranularityY: 8 pixels (vertical step size)
下图是这些属性值的解释:

image.png

最后我们来看一下,根据我们的媒体和格式类型,实现GetStreamCaps方法:

HRESULT STDMETHODCALLTYPE CVCamStream::GetStreamCaps(int iIndex, 
    AM_MEDIA_TYPE **pmt, BYTE *pSCC)
{
    if (iIndex < 0 )
        return E_INVALIDARG;

    *pmt = CreateMediaType(&m_mt); //创建媒体类型
    DECLARE_PTR(VIDEOINFOHEADER, pvi, (*pmt)->pbFormat); //创建format类型

        //设置format
    pvi->bmiHeader.biWidth = 1280; 
    pvi->bmiHeader.biHeight = 720;
    pvi->AvgTimePerFrame = 333333;
    pvi->bmiHeader.biCompression = MAKEFOURCC('Y', 'U', 'Y', '2');
    pvi->bmiHeader.biBitCount = 16;
    pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pvi->bmiHeader.biPlanes = 1;
    pvi->bmiHeader.biSizeImage = pvi->bmiHeader.biWidth * 
        pvi->bmiHeader.biHeight * 2;
    pvi->bmiHeader.biClrImportant = 0;

    SetRectEmpty(&(pvi->rcSource)); 
    SetRectEmpty(&(pvi->rcTarget)); 
         //设置meida Type
    (*pmt)->majortype = MEDIATYPE_Video;
    (*pmt)->subtype = MEDIASUBTYPE_YUY2;
    (*pmt)->formattype = FORMAT_VideoInfo;
    (*pmt)->bTemporalCompression = FALSE;
    (*pmt)->bFixedSizeSamples = FALSE;
    (*pmt)->lSampleSize = pvi->bmiHeader.biSizeImage;
    (*pmt)->cbFormat = sizeof(VIDEOINFOHEADER);
       //创建VIDEO_STREAM_CONFIG_CAPS结构体
    DECLARE_PTR(VIDEO_STREAM_CONFIG_CAPS, pvscc, pSCC);
       //设置VIDEO_STREAM_CONFIG_CAPS
    pvscc->guid = FORMAT_VideoInfo;  //媒体类型为FORMAT_VideoInfo
    pvscc->VideoStandard = AnalogVideo_None;  //不是模拟视频
    pvscc->InputSize.cx = pvi->bmiHeader.biWidth;  //输入宽度
    pvscc->InputSize.cy = pvi->bmiHeader.biHeight; //输入高度
    pvscc->MinCroppingSize.cx = pvi->bmiHeader.biWidth;  //最小可裁剪宽度 相当于不允许水平裁剪
    pvscc->MinCroppingSize.cy = pvi->bmiHeader.biHeight;//最小可裁剪高度
    pvscc->MaxCroppingSize.cx = pvi->bmiHeader.biWidth;//最大可裁剪宽度
    pvscc->MaxCroppingSize.cy = pvi->bmiHeader.biHeight;//最大可裁剪高度
    pvscc->CropGranularityX = pvi->bmiHeader.biWidth;  //水平裁剪增量
    pvscc->CropGranularityY = pvi->bmiHeader.biHeight; //水平裁剪增量
    pvscc->CropAlignX = 0;  //水平对其
    pvscc->CropAlignY = 0;  //垂直对齐

    pvscc->MinOutputSize.cx = pvi->bmiHeader.biWidth;   //最小输出宽度
    pvscc->MinOutputSize.cy = pvi->bmiHeader.biHeight;   //最小输出高度度
    pvscc->MaxOutputSize.cx = pvi->bmiHeader.biWidth;  /最大输出宽度
    pvscc->MaxOutputSize.cy = pvi->bmiHeader.biHeight;  //最大输出高度度
    pvscc->OutputGranularityX = 0;  //水平输出变化增量
    pvscc->OutputGranularityY = 0;  //垂直输出变化增量
    pvscc->StretchTapsX = 0;   //不允许tretching
    pvscc->StretchTapsY = 0;    //不允许tretching
    pvscc->ShrinkTapsX = 0;     //不允许Shrink
    pvscc->ShrinkTapsY = 0;     //不允许Shrink
    pvscc->MinFrameInterval = pvi->AvgTimePerFrame;   //最小帧间隔=平均帧播放时间
    pvscc->MaxFrameInterval = pvi->AvgTimePerFrame; //最大帧间隔=平均帧播放时间
    pvscc->MinBitsPerSecond = pvi->bmiHeader.biWidth * pvi->bmiHeader.biHeight 
        * 2 * 8 * (10000000 / pvi->AvgTimePerFrame);  //最小输出数据bit单位
    pvscc->MaxBitsPerSecond = pvi->bmiHeader.biWidth * pvi->bmiHeader.biHeight 
        * 2 * 8 * (10000000 / pvi->AvgTimePerFrame); /最大输出数据bit单位

    return S_OK;
}

上面有一个关于pin产生数据大小的计算:
pvscc->MinBitsPerSecond = pvi->bmiHeader.biWidth * pvi->bmiHeader.biHeight
* 2 * 8 * (10000000 / pvi->AvgTimePerFrame);
biWidth 是位图宽度(单位像素),biHeight是位图高度(单位像素),YUV2一个像素需要2个字节,每个字节8bit位。所以biWidth biHeight28就是bitmap的大小。10000000 / pvi->AvgTimePerFrame表示一秒中,可以播放多少帧。我们知道这个值在我们的例子里就是30.
所以最终我们获得了,每秒钟output pin可以传输的bit的大小。他就等于size(bitmap)
帧频率。

上一篇下一篇

猜你喜欢

热点阅读