跳转到内容

用户:Graywolf~zhwiki/sandbox

维基百科,自由的百科全书

前言[编辑]

  • 2011/11/24, 开启我人生的里程碑, 目前从事视讯会议, 负责VideoPhone的部分

X264vfw(Video For Windows)[编辑]

版本 : x264vfw_34_2008bm_29021
参考网址 : http://sourceforge.net/projects/x264vfw/files/x264vfw/

Encode[编辑]

Command Line[编辑]

  1. Command :--preset ultrafast --tune zerolatency --keyint 300 --scenecut 40 --ipratio 1.0 --qcomp 0.3 --cplxblur 30 --aq-mode 1 --qpstep 8 --deblock 1:-1
  • ultrafast : 设定之后会盖掉前面的参数, 因此需要优先设定
             --no-8x8dct --aq-mode 0 --b-adapt 0
             --bframes 0 --no-cabac --no-deblock
             --no-mbtree --me dia --no-mixed-refs
             --partitions none --rc-lookahead 0 --ref 1
             --scenecut 0 --subme 0 --trellis 0
             --no-weightb --weightp 0
  • zerolatency :
             --bframes 0 --force-cfr --no-mbtree
             --sync-lookahead 0 --sliced-threads
             --rc-lookahead 0
  • 参数 :
       --keyint 300(每300個Frame會有一個I-Frame), 預設250
       --scenecut 40(設定決策使用I幀、IDR幀的閾值(場景變換檢測), 預設40
       --ipratio 1.0(修改I幀量化值相比P幀量化值的目標平均增量。越大的值會提高I幀的品質), 預設1.4
       --qcomp 0.3(量化值曲線壓縮係數。0.0是固定位元率,1.0則是固定量化值), 預設0.6
       --cplxblur 30(以給定的半徑範圍套用高斯模糊(gaussian blur)於量化值曲線。這意味著分配給每個幀的量化值會被它的鄰近幀模糊掉,
                     以此來限制量化值波動), 預設20
       --aq-mode 1(彈性量化模式), 預設1
       --qpstep 8(設定兩幀之間量化值的最大變更幅度), 預設4
       --deblock 1:1(參考網路上的設定), 預設0:0
--subme 5(圖層變化細緻度, 在Pic1的A點(10, 10.25)移動到Pic2的B點(10, 12.27); 若subme設為0, 則紀錄該點的偏移量為(0,2), 若設為1, 則紀錄該點的偏移量為(0, 2.0), 2則紀錄該點的偏移量為(0, 2.02); 也就是subme越大影像越細膩), 預設7
  • 心得 : 由于发现当I-Frame在传送的时候, 所设定的Bitrate会飙高(1.5~2倍, 视场景而定), 因此希望借由X264的参数来稳定带宽. 尝试用过vbv(Video Buffering Verifier), 虽可达到准确的带宽控制, 但是却抑制了I-Frame该有的资料量, 导致I相当于P来的丑(模糊)许多而无法接受
       --vbv-maxrate Bitrate(填滿Buffer的位元率, 越大則頻寬衝的越高), 預設0
       --vbv-bufsize Bitrate/2(Buffer的大小, 也可以說是Delay Time, 以Bitrate 512kbps來講, 若bufize 512, 則Delay 1秒), 
                             預設0

Decode[编辑]

Video[编辑]

Capture[编辑]

Display[编辑]

DirectX(9.0)[编辑]

  • 格式转换

由于Decode出来的影像格式为VY12, 但不是所有的显卡都能支援此格式; 因此我们将YV12转换成大多数显卡所能绘致的RGB32格式

  1. 传统转换公式:
YV12 to RGB24:
bool YV12_to_RGB24_table(unsigned char* pYV12, unsigned char* pRGB24, int Width, int Height)
{
if(!pYV12 || !pRGB24)
return -1;
const long nYLen = long(Width * Height);
const int nHfWidth = (Width >> 1);
if((nYLen < 1) || (nHfWidth < 1))
return -1;
// Y data
unsigned char* yData = pYV12;
// v data
unsigned char* vData = &yData[nYLen];
// u data
unsigned char* uData = &vData[nYLen >> 2];
if((!uData) || (!vData))
return -1;
int rgb[3];
int i, j, m, n, x, y, pu, pv, py, rdif, invgdif, bdif;
m = -Width;
n = -nHfWidth;
bool addhalf = true;
for(y = 0; y < Height; y++)
{
m += Width;
if(addhalf)
{
n += nHfWidth;
addhalf = false;
}
else
{
addhalf = true;
}
for(x = 0; x < Width; x++)
{
i = m + x;
j = n + (x >> 1);
py = yData[i];
// search tables to get rdif invgdif and bidif
rdif = Table_fv1[vData[j]]; // fv1
invgdif = Table_fu1[uData[j]] + Table_fv2[vData[j]]; // fu1+fv2
bdif = Table_fu2[uData[j]]; // fu2
rgb[2] = py+rdif; // R
rgb[1] = py-invgdif; // G
rgb[0] = py+bdif; // B
j = nYLen - Width - m + x;
i = (j << 1) + j;
// copy this pixel to rgb data
for(j = 0; j < 3; j++)
{
if((rgb[j] >= 0) && (rgb[j] <= 255))
{
pRGB24[i + j] = rgb[j];
}
else
{
pRGB24[i + j] = (rgb[j] < 0)? 0 : 255;
}
}
}
}
return 0;
}
RGB24 to RGB32
bool RGB24_to_RGB32(unsigned char* pRGB24, unsigned char* pRGB32, int Width, int Height)
{
for(int i = 0; i < Height ; i++) // RGB24转RGB32
{
for(int j = 0; j < Width ; j++)
{
pRGB32[(i * Width * 4) + (j * 4)] = pRGB24[(i * Width * 3) + (j * 3)];
pRGB32[(i * Width * 4) + (j * 4) + 1] = pRGB24[(i * Width * 3) + (j * 3) + 1];
pRGB32[(i * Width * 4) + (j * 4) + 2] = pRGB24[(i * Width * 3) + (j * 3) + 2];
pRGB32[(i * Width * 4) + (j * 4) + 3] = 0;
}
}
return 0;
}

2. 使用Surface转换:
设定被转换的参数(格式), CreateOffscreenPlainSurface
  • 绘图

在绘图的部分采用Texture与Surface二种方式; 主要取决于来源的颜色格式

1. 建立装置CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface);
/*
Adapter, D3DADAPTER_DEFAULT
DeviceType, D3DDEVTYPE_HAL 硬件绘制(GPU) or D3DDEVTYPE_SW 软件模拟绘制(CPU)
hFocusWindow, 绘制视窗
BehaviorFlags, 处理顶点方式 硬件(D3DCREATE_HARDWARE_VERTEXPROCESSING) 或软件(D3DCREATE_SOFTWARE_VERTEXPROCESSING) 或其他相关设定
pPresentationParameters, 设定相关环境参数 例如Windowed(是否视窗模式) BackBufferFormat(资料格式) BackBufferWidth(资料宽度)...
ppReturnedDeviceInterface, 储存DirectX Device的界面
*/
2. 设定绘制相关参数SetRenderState(D3DRENDERSTATETYPE State, DWORD Value);
/*
D3DRS_LIGHTING, 灯光控制
D3DRS_ALPHABLENDENABLE, 透明贴图
D3DRS_SRCBLEND, SRC混色模式
D3DRS_DESTBLEND, DST混色模式
D3DRS_BLENDOP, 混色模式
D3DRS_CULLMODE, 指定画面描绘的模式
*/
3. 建立Texture CreateTexture(UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture9** ppTexture, HANDLE* pHandle

);

/*
Width, 资料宽度
Height, 资料高度
Levels, 1 可借由GetLevelCount()来取得
Usage, 0
Format, 资料格式
Pool, D3DPOOL_MANAGED 在系统内存建立一份备份 可以无视装置遗失
ppTexture, 储存Texture元件
pHandle, 0
*/
4. 建立顶点资讯 CreateVertexBuffer(UINT Length, DWORD Usage,DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pHandle);
/*
Length, 设定4个点座标长度即为4(需要再乘点座标资讯的结构大小)
Usage, 0
FVF, 设定顶点座标的环境 例如D3DFVF_XYZ 3维空间表示座标
Pool, D3DPOOL_MANAGED 在系统内存建立一份备份 可以无视装置遗失
ppVertexBuffer, 储存ppVertexBuffer元件
pHandle, 0
*/
CreateIndexBuffer(UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pHandle);
/*
Length, 设定4个点座标长度即为4(需要再乘点座标资讯的结构大小)
Usage, 0
Format, D3DFMT_INDEX16(Indices are 16 bits each)
Pool, D3DPOOL_MANAGED 在系统内存建立一份备份 可以无视装置遗失
ppIndexBuffer, 储存ppIndexBuffer元件
pHandle, 0
*/
5. 开始绘图
Clear(DWORD Count, const D3DRECT *pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil);
/*
Count, 0
pRects, NULL
Color, D3DCLEAR_TARGET Clear the color
Z, 1.0f
Stencil, 0
*/
BeginScene()
SetTexture(DWORD Stage,IDirect3DBaseTexture9 *pTexture);
/*
Stage, 0 pTexture对应第0个
pTexture, 储存pTexture元件
*/
LockRect(UINT Level, D3DLOCKED_RECT *pLockedRect, CONST RECT *pRect, DWORD Flags);
memcpy(pLockedRect->pBits, pData, size);
UnlockRect(UINT Level)
SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9 *pStreamData, UINT OffsetInBytes, UINT Stride);
/*
StreamNumber, 0
pStreamData, ppVertexBuffer
OffsetInBytes, 0
Stride, 座标资讯的结构大小
*/
SetFVF(DWORD FVF);
SetIndices(IDirect3DIndexBuffer9 *pIndexData);
DrawIndexedPrimitive(D3DPRIMITIVETYPE Type, INT BaseVertexIndex, UINT MinIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount);
/*
Type, DrawIndexedPrimitive 照Index Buffer顺序画出三角形
BaseVertexIndex, 0 从第0个顶点开始
MinIndex, 0 最小的Index值为何
NumVertices, 4 包含了几个点
StartIndex, 0 从第BaseVertexIndex个点的第0的Index开始
PrimitiveCount, 2 有2个Type
*/
EndScene();
Present(NULL, NULL, NULL, NULL);
  • 贴字

OpenGL[编辑]

Color Space[编辑]

1. RGB24
每一个Pixel均是由相同位置的三个相同长宽的R, G, B平面所组成; 例如图片上的点P(a, b)是由三个平面R(a, b), G(a, b), B(a, b)所组成
2. YV12
YV12影像格式的图片(Width, Height)是由Y(Width, Height), V(Width/2, Height/2), U(Width/2, Height/2)三个平面所组成, 大小及相对位置如图1.1~1.3
 例如YV12圖片上的點P(a, b)組成關係如下:
 P(0, 0)由Y(0, 0), V(0, 0), U(0, 0)所組成
P(0, 1)由Y(0, 1), V(0, 0), U(0, 0)所組成
P(1, 0)由Y(1, 0), V(0, 0), U(0, 0)所組成
P(1, 1)由Y(1, 1), V(0, 0), U(0, 0)所組成
内存排序的方式如图1.4, Y平面大小为Width * Height, V平面大小为Width/2 * Height/2, U平面大小为Width/2 * Height/2
3. I420
I420影像格式的图片与YV12雷同(平面大小); 唯独差在V平面与U平面的内存位置, 也就是VU平面互换, 内存排序的方式如图2.1,
 例如YV12圖片上的點P(a, b)組成關係如下:
 P(0, 0)由Y(0, 0), U(0, 0), V(0, 0)所組成
P(0, 1)由Y(0, 1), U(0, 0), V(0, 0)所組成
P(1, 0)由Y(1, 0), U(0, 0), V(0, 0)所組成
P(1, 1)由Y(1, 1), U(0, 0), V(0, 0)所組成
4. 转换
在Color Format转换上采用FFMpeg方式
(1)YV12转YUY2
SwsContext *img_convert_ctx;
unsigned char *YUY2Buf;
// Initialize
img_convert_ctx = sws_getContext(Width, Height, PIX_FMT_YUV420P, Width, Height, PIX_FMT_YUYV422, SWS_POINT, NULL, NULL, NULL);
// Transfer
unsigned char *inbuf[3];
unsigned char *outbuf[3];
int inlinesize[3] = {Width, Width/2, Width/2};
int outlinesize[3] = {Width* 2, 0, 0};
inbuf[0] = (unsigned char* )malloc(Width * Height);
inbuf[1] = (unsigned char* )malloc(Width * Height >> 2 );
inbuf[2] = (unsigned char* )malloc(Width * Height >> 2);
outbuf[0] = (unsigned char* )malloc(Width * mHeight * 2);
outbuf[1] = NULL;
outbuf[2] = NULL;
memcpy(inbuf[0], YV12Buf, Width * Height);
memcpy(inbuf[1], YV12Buf+ (Width * m_lVideoHeight), Width * Height >> 2);
memcpy(inbuf[2], YV12Buf+ (Width * m_lVideoHeight*5>>2), Width * Height >> 2);
sws_scale(img_convert_ctx, inbuf, inlinesize, 0, Height, outbuf, outlinesize);
memcpy(YUY2Buf, outbuf[0], Width * Height * 2);
free(*inbuf);
free(*outbuf);
// Release
sws_freeContext(img_convert_ctx);

Audio[编辑]

RTP Info[编辑]

1. Marker bit & PayLoad type
我们可以借由RTP Header的第二BYTE来得到Marker bit及PayLoad type, Marker bit表示此封包为Frame的最后一个包也就是结尾; Video的PayLoad type为96 ~ 127
unsigned char marker:1;
unsigned char payloadtype:7;


2. 指数Golomb码
在影像上, 我们较关切的如何取得长宽资讯, 在H264 bitstream中长宽资讯是以指数Golomb的编码方式存取; 而长宽资讯是编码在SPS(Sequence Parameter Set)中
指数Golomb码简易来说就是移动n个Bits后遇到"1", 即往后取n个Bits; 参数藉游编码所占的Bits数举例如下
占1Bit-> 1
占3Bit-> 01, 此参数的值占3Bits; 例如011, 移动1个Bit后遇到"1"因此往后多取1Bit, 值为"3", 010, 值为"2"
占5Bit-> 001, 此参数的值占5Bits; 例如00101, 移动2个Bit后遇到"1"因此往后多取2Bit, 值为"3"
占7Bit-> 0001, 此参数的值占7Bits; 例如0001011, 移动3个Bit后遇到"1"因此往后多取3Bit, 值为"11"
长宽资讯从Wireshark分析的话, 名称为"pic_width_in_mbs_minus1"与"pic_height_in_mbs_minus1"; 意思为取出的值+1在乘16即为该Frame的长宽