修正wme输出的ASF流数据
前言
Windows Media Encoder是Microsoft Media Serial中的编码、转换工具,其支持将不同的文件转换成wmv/a格式,同时他也支持直接通过硬件设备(如声卡、摄像头等)采集数据,输出wmv/a格式。在输出方面wme支持直接存入文件,推送到wms(Microsoft Media Service),或推送到本地端口功能。然而推送到本地端口的数据并不完全符合asf 规范。
问题提出
在ASF Specfication中定义ASF文件的package的大小是固定的,这就导致有一部分(实际上是大部分)package达不到package的标准尺寸,因此需要一些填充字节来增长package的大小,使其符合header object中定义的package size。 我们知道,在wme的向本地推送数据时,实际上其数据包采用的是wms Http Streaming protocol,为了减少网络占用,wme推送http streaming数据时并没有推送这些填充字节,因为如果我们自己编写程序将http streaming数据转存到文件时,就必须对这些数据进行修正,这正在本文所关注的问题。
方案
首先,我们需要分析asf文件的头数据,找出其package的大小,然后,逐个分析每个package,如果其大小小于头中设定的大小,就需要进行修正。
对数据进行修复,就必须理解asf specification中的相关说明,根据5.2ASF Data package definition一节可知,数据包的组成如下:
我们所要做的就是根据Error Correction Data和Payload Parsing Information填充Padding Data,并修改data package中相应的数据(主要是Padding Length和Package Length字段)。实际上,Erro Corection Data都是存在的。
根据asf specification可知:
Error Correction Data
Payload parsing information
首先,我们要根据Error Correction Data的第一个字节(Error Correction Data Flags)了解Error Correction Data是否存在,以及其长度。检查Flags第8位是否为1(flags & 0x80 = 0x80),即可确定是否存在;如果存在,其低4位保存了Error Correcton Data Length(flags & 0x0f)。
然后跳过Error Correction Data,开始分析Payload parsing information。逐个分析Packet Length、Sequence、Padding Length的类型和大小。通常情况下,sequence是不存在的,其Length type flags中的值为00,Packet Length在某些情况下存在。分析方法如下:
Packet Length Type:flags & 0x60 >> 5
Sequence Type:flags & 0x06 >> 1
Padding Length Type: flags & 0x18 >>3
Type定义如下:
虽然这个图是针对Sequence的,但这个定义对三个字段的含义是相同。
经过以上分析,即可确定Packet Length字段、Padding Length字段是否存在,其类型是什么(BYTE、WORD、DWORD?),对应的修改Packet Length字段为asf header中定义的长度,然后修改Padding Length为asf header中的长度-本package的长度,在package后边添加填充字节即可。
示例代码(C#)
if (data.Length < AsfHeader.MaxPacketSize) //需要修正
{
int iPaddingOffset = 0;
int iPaddingLength = 0;
int iParserStart = 0;
//Error Correction
if ((data[0] & 0x80) == 0x80) //Error Correction 存在
{
iPaddingOffset += (data[0] & 0x0f) + 1/*first byte.error correction flags*/;
iParserStart = iPaddingOffset;
}
int c;
//取得Packet Length type
c = (data[iParserStart] & 0x60) >> 5;
switch (c)
{
case 0: // does not exist
break;
case 1: // byte
iPaddingOffset += 1;
//fix length
break;
case 2: //word
iPaddingOffset += 2;
//fix length
Array.Copy(BitConverter.GetBytes((ushort)mHeader.AsfHeader.MaxPacketSize), 0,data,iPaddingOffset,2);
break;
case 3: //dword
iPaddingOffset += 4;
//fix length
Array.Copy(BitConverter.GetBytes(mHeader.AsfHeader.MaxPacketSize), 0, data, iPaddingOffset -2, 4);
break;
}
//取得Sequence type
c = (data[iParserStart] & 0x06) >> 1;
switch (c)
{
case 0: // does not exist
break;
case 1: // byte
iPaddingOffset += 1;
break;
case 2: //word
iPaddingOffset += 2;
break;
case 3: //dword
iPaddingOffset += 4;
break;
}
//取得Padding Length type
c = (data[iParserStart] & 0x18) >> 3;
switch (c)
{
case 0: // does not exist
break;
case 1: // byte
iPaddingLength = 1;
break;
case 2: //word
iPaddingLength = 2;
break;
case 3: //dword
iPaddingLength = 4;
break;
}
int iPaddings = (int)(AsfHeader.MaxPacketSize - data.Length);
iPaddingOffset += 2;//skip payload parser information
switch (iPaddingLength)
{
case 0:
break;
case 1:
data[iPaddingOffset] = (byte)iPaddings;
break;
case 2:
Array.Copy(BitConverter.GetBytes((ushort)iPaddings), 0, data, iPaddingOffset, iPaddingLength);
break;
case 3:
Array.Copy(BitConverter.GetBytes((uint)iPaddings), 0, data, iPaddingOffset, iPaddingLength);
break;
}
chunk.Data = new byte[AsfHeader.MaxPacketSize];
//chunk.Data.Initialize();
Array.Copy(data, chunk.Data, data.Length);
chunk.Length = (ushort)chunk.Data.Length;
}
else
{
chunk.Data = data;
}
参考资料