最近因为一些原因在折腾这方面的事情,朋友负责DVD-LPCM的,我这边则是BD-LPCM的,我轻松了很多,因为BD的LPCM的比DVD的那是舒服了不少。。
不过开发环境限制死了在VS2010我很蛋疼,并且其实源工程是基于DShow的,对我这个习惯了MediaFoundation的人来说,实在过于腐朽。
好了不废话那么多,我们来看BD的LPCM如何转换为PCM。
这个转换其实很简单,LPCM是BE的格式,首先我们解码音频需要3个关键的东西:声道数、采样率、采样大小。
BD的采样大小在16\24\32这范围内,也就是不会出现8bit的情况,我们需要判断,转换也就是:
switch bits
{
case 16:
sample = BE2LE_16BIT(old_sample);
break;
case 24:
sample = BE2LE_24BIT(old_sample);
case 32:
sample = BE2LE_32BIT(old_sample);
break;
}
这个转换虽然是十分脑残,不过BD有个啰嗦的地方就是它的头,比如在m2ts格式的BDMV中,封装的是H264的视频和LPCM的音频,每一个LPCM的Packet都有一个32bit(4字节)的信息头,然后从第五个字节开始才是LPCM的数据。
这个头是这样的:
- unknown (16 bits)
- number of channels (4 bits)
- frequency (4 bits)
- bits per sample (2 bits)
- unknown (6 bits)
见下图(我用红线圈住的地方就是头,后面的就是LPCM数据):
前后2个未知我们不需要知道,其中有10个bit把我们需要的关键信息都给交出来了。
可以看到我们上图的头是03 C0 31 40,这个如何解析呢,代码在下面:
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <string>
#include <iostream>
#include <memory>
#include <Windows.h>
#include <ks.h>
#include <ksmedia.h>
#include <winsock.h>
#pragma comment(lib,"ws2_32.lib")
bool BDHeaderParse(void* ph,int* nch,int* srate,int* bits,unsigned int* ch_layout)
{
unsigned int head = ntohl((*(unsigned int*)ph));
switch ((head & 0xF000) >> 12)
{
case 1:
*nch = 1;
*ch_layout = SPEAKER_FRONT_CENTER;
break;
case 3:
*nch = 2;
*ch_layout = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT;
break;
case 4:
*nch = 3;
*ch_layout = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER;
break;
case 5:
*nch = 3;
*ch_layout = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_CENTER;
break;
case 6:
*nch = 4;
*ch_layout = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT
|SPEAKER_FRONT_CENTER|SPEAKER_BACK_CENTER;
break;
case 7:
*nch = 4;
*ch_layout = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT
|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT;
break;
case 8:
*nch = 5;
*ch_layout = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER
|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT;
break;
case 9:
*nch = 6;
*ch_layout = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER
|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_LOW_FREQUENCY;
break;
case 10:
*nch = 7;
*ch_layout = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER
|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT
|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT;
break;
case 11:
*nch = 8;
*ch_layout = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER
|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT
|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT|SPEAKER_LOW_FREQUENCY;
break;
default:
return false;
}
switch ((head >> 6) & 3)
{
case 1:
*bits = 16;
break;
case 2:
case 3:
*bits = 24;
break;
default:
return false;
}
switch ((head >> 8) & 15)
{
case 1:
*srate = 48000;
break;
case 4:
*srate = 96000;
break;
case 5:
*srate = 192000;
break;
default:
return false;
}
return true;
}
int main()
{
unsigned int i = 0x4031C003;
int nch = 0,srate = 0,bits = 0;
unsigned ch_layout = 0;
BDHeaderParse(&i,&nch,&srate,&bits,&ch_layout);
printf("nch:%d\nsrate:%d\nbits:%d\nch_layout:%d",
nch,
srate,
bits,
ch_layout);
getchar();
return 0;
}
好了,我们提取出了声道数、采样率、采样大小,就可以进行后面的LPCM->PCM转换了。
还有一个问题,就是声道布局,这个布局的问题上面我也写了。
不过在转换的时候,前面的1-5个声道,布局都对应有Windows布局,但是5.1(6声道)跟Windows布局不同,就是LFE的位置不同,在这里进行转换的时候,需要进行声道位置调整。
还有,貌似在m2ts文件中,LPCM每次的Packet大小都是一样的?