使用ffmpeg将mp4文件中的h264码流转为raw h264文件格式或ts文件格式



下面这两条命令行实现文件格式的转变:

ffmpeg -i h264.mp4 -c:v copy -bsf:v h264_mp4toannexb -an out.h264

ffmpeg -i INPUT.mp4 -codec copy -bsf:v h264_mp4toannexb OUTPUT.ts

第一条命令行将包含h264编码的mp4文件中的h264视频流提取出来保存为raw h264格式

第二条命令行将包含h264编码的mp4文件转为ts文件格式

两者都用到了bitstream filter : h264_mp4toannexb 其作用是把字节长度的前缀转为start code的前缀,其实现在ffmpeg/libavcodec/h264_mp4toannexb_bsf.c文件中。

h264的码流分为VCL层和NAL层,NAL是由一系列NALU单元组成,其中nalu type为1-5的称为VCL的NALU,其他称为非VCL的NALU

NAL层在VCL层之上,解码器在解码时,必须从NAL层到VCL层,而编码过程则相反。

基本上是这样的过程NALU -> RBSP -> SODB

NALU就是一个nalu type加上EBSP,为什么是EBSP而不是直接就RBSP?因为raw h264格式的码流也叫h264 ES流是用start code来分割一个个NALU的,如果RBSP中含有类似start code的字节就麻烦了。这时候需要作emulation prevention处理,也叫escape,即

0x000000  ->  0x00000300
0x000001  ->  0x00000301
0x000002  ->  0x00000302
0x000003  ->  0x00000303

编码时加03和解码时去03,实际上这种情况出现的几率很小1:2^22。对非VCL的NALU也需要作这样处理。

最后总结一下,实际上在处理码流的协议复用和信道间传输与介质存储都不需要考虑escape过程,这是解码器和编码器处理了的。

下面引用几处开源的代码里的实现:

live555代码H264VideoStreamFramer.cpp中,

void H264VideoStreamParser::removeEmulationBytes(u_int8_t* nalUnitCopy, unsigned maxSize, unsigned& nalUnitCopySize) {
  u_int8_t* nalUnitOrig = fStartOfFrame + fOutputStartCodeSize;
  unsigned const NumBytesInNALunit = fTo - nalUnitOrig;
  nalUnitCopySize = 0;
  if (NumBytesInNALunit > maxSize) return;
  for (unsigned i = 0; i < NumBytesInNALunit; ++i) {
    if (i+2 < NumBytesInNALunit && nalUnitOrig[i] == 0 && nalUnitOrig[i+1] == 0 && nalUnitOrig[i+2] == 3) {
      nalUnitCopy[nalUnitCopySize++] = nalUnitOrig[i++];
      nalUnitCopy[nalUnitCopySize++] = nalUnitOrig[i++];
    } else {
      nalUnitCopy[nalUnitCopySize++] = nalUnitOrig[i];
    }
  }
}

在ffmpeg/libavcodec/h264.c文件中的函数ff_h264_decode_nal的实现代码里,有去03的escape过程,

while (si + 2 < length) {
    // remove escapes (very rare 1:2^22)
    if (src[si + 2] > 3) {
        dst[di++] = src[si++];
        dst[di++] = src[si++];
    } else if (src[si] == 0 && src[si + 1] == 0 && src[si + 2] != 0) {
        if (src[si + 2] == 3) { // escape
            dst[di++]  = 0;
            dst[di++]  = 0;
            si        += 3;
            continue;
        } else // next start code
            goto nsc;
    }

    dst[di++] = src[si++];
}

在x264/common/bitstream.c文件的函数x264_nal_encode里有加03的escape处理,

/* nal header */
*dst++ = ( 0x00 << 7 ) | ( nal->i_ref_idc << 5 ) | nal->i_type;

dst = h->bsf.nal_escape( dst, src, end );
int size = dst - orig_dst;

它调用了一个函数,

static uint8_t *x264_nal_escape_c( uint8_t *dst, uint8_t *src, uint8_t *end )
{
    if( src < end ) *dst++ = *src++;
    if( src < end ) *dst++ = *src++;
    while( src < end )
    {
        if( src[0] <= 0x03 && !dst[-2] && !dst[-1] )
            *dst++ = 0x03;
        *dst++ = *src++;
    }
    return dst;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值