Ps流转H264流 代码实现

最近在处理海康视频流,因为要推到RTMP服务器上,所以需要把海康读取的Ps流转为H.264流。但网上代码实现的较少,或质量一般,于是参考大神博客 https://blog.csdn.net/wwyyxx26/article/details/15224879 实现了相关代码。

使用的语言为C#,使用.Net Core实现。代码是简单实现,只考虑了单线程问题。

完整代码链接在最下端

 

好了不啰嗦了,切入正题。

首先是找寻包头

           for (int i = 0; i < PsBufferCount - 4; i++)
            {
                //判断包头
                if (this.psBuffer[i] == 0x00 && this.psBuffer[i + 1] == 0x00
                    && this.psBuffer[i + 2] == 0x01 && this.psBuffer[i + 3] == 0xBA)
                {
                    if (i > 0)
                    {
                        //找到包头,把包头之前部分遗弃
                        this.psBuffer.RemoveRange(0, i);
                    }

                    int length;
                    //判断包头长度是否为完整包头
                    if (this.PsBufferCount >= 14
                        && this.PsBufferCount >= (length = 14 + (this.psBuffer[13] & 0x07)))
                    {
                        //移除包头并继续下一步
                        this.psBuffer.RemoveRange(0, length);
                        this.parseState = 1;
                        return true;
                    }
                }
            }

            //找寻失败,解析停止
            this.parseState = 0;
            return false;

然后找到包头后,需要验证是否有系统头

            //判断是否包含系统头
            if (this.psBuffer[0] == 0x00 && this.psBuffer[1] == 0x00
               && this.psBuffer[2] == 0x01 && this.psBuffer[3] == 0xBB)
            {
                int length;
                //包含系统头,判断系统头是否完整
                if (this.PsBufferCount >= 12
                    && this.PsBufferCount > (length = this.psBuffer[4] * 256 + this.psBuffer[5] + 6))
                {
                    //系统头完整,去除系统头,解析继续
                    this.psBuffer.RemoveRange(0, length);
                    this.parseState = 2;
                    return true;
                }
                else
                {
                    //系统头不完整,解析停止
                    this.parseState = 1;
                    return false;
                }
            }
            else
            {
                //不包含系统头,解析继续
                this.parseState = 2;
                return true;
            }

系统头后可能跟随Program Stream Map,然后判断是否为Program Stream Map

            //判断是否包含Program Stream Map
            if (this.psBuffer[0] == 0x00 && this.psBuffer[1] == 0x00
                && this.psBuffer[2] == 0x01 && this.psBuffer[3] == 0xBC)
            {
                int length;
                //包含Program Stream Map,判断Program Stream Map是否完整
                if (this.PsBufferCount >= 16
                    && this.PsBufferCount > (length = this.psBuffer[4] * 256 + this.psBuffer[5] + 6))
                {
                    //Program Stream Map完整,去除Program Stream Map,解析继续
                    this.psBuffer.RemoveRange(0, length);
                    this.parseState = 3;
                    return true;
                }
                else
                {
                    //Program Stream Map不完整,解析停止
                    this.parseState = 2;
                    return false;
                }
            }
            else
            {
                // 不包含Program Stream Map,解析继续
                this.parseState = 3;
                return true;
            }

最后是解析PES包

            //判断包头
            if (this.psBuffer[0] == 0x00 && this.psBuffer[1] == 0x00
                    && this.psBuffer[2] == 0x01)
            {
                int length;
                //判断是否具有完整的PES包
                if (this.PsBufferCount >= 9
                    && this.PsBufferCount > (length = this.psBuffer[4] * 256 + this.psBuffer[5] + 6))
                {
                    //获取一个完整的PES包
                    List<byte> buffer = this.psBuffer.GetRange(0, length);
                    this.psBuffer.RemoveRange(0, length);

                    //判断是否符合,符合写入H264流
                    if (buffer[3] == 0xE0 && length > 0)
                    {
                        this.h264Buffer.AddRange(buffer.GetRange(9 + buffer[8], buffer.Count - 9 - buffer[8]));
                    }
                    //else if (buffer[3] == 0xC0 && length > 0) { }

                    //解析完成,寻找下一个包,解析继续
                    this.parseState = 1;
                    return true;
                }
                else
                {
                    //PES不完整,解析停止
                    this.parseState = 3;
                    return false;
                }
            }
            else
            {
                //数据包错误,寻找包头,解析继续
                this.parseState = 0;
                return true;
            }

然后就是把整个代码串起来,需要注意一个包中可能存在多个PES包,所以,解析完PES包不能直接找包头。此处在每次循环前都添加了找包头的代码,这样就可以在包头解析和PES包解析自由切换。

            //循环解析
            while (this.PsBufferCount >= 4)
            {
                //判断是否包含包头
                if (this.psBuffer[0] == 0x00 && this.psBuffer[1] == 0x00
                    && this.psBuffer[2] == 0x01 && this.psBuffer[3] == 0xBA)
                {
                    this.parseState = 0;
                }

                switch (this.parseState)
                {
                    case 0:
                        if (!FindAndRemoveHeader()) return;
                        break;
                    case 1:
                        if (!RemoveSystemHeader()) return;
                        break;
                    case 2:
                        if (!RemoveProgramStreamMap()) return;
                        break;
                    case 3:
                        if (!ParsingPakcet()) return;
                        break;
                    default:
                        this.parseState = 0;
                        return;
                }
            }

最后赋上我完整代码的GitHub地址:https://github.com/kevinfromcn/PsToH264.git

如果对你有帮助,希望给个Star

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值