h264和aac数据打包ps包之二

        首先说一下整体思路:

        1.将h264和AAC数据分别编码NAL单元和ADTS帧

        2.构建PES包,依次将每个NAL单元和ADTS帧添加到对应的PES包中,并加入头部信息

        3.构建PS包,将所有的PES包按照一定的规则排序并加入头部信息。

        分开实现H264的PES打包,AAC的PES打包,最终是整个PS包的打包,数据调用直接一步

int PackPS(const unsigned char *h264_data, int h264_len, const unsigned char *aac_data, int aac_len, unsigned char *ps_data) 

        填入h264数据和aac数据直接生成ps封包数据,之后再用于rtp over udp还是其他方式传输,读者可以自行填充。

        上代码!!!!!!!!!!!!!

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define H264_HEADER_LENGTH 4
#define AAC_HEADER_LENGTH 4

using namespace std;

// 计算校验和
unsigned short checksum(const unsigned char *buf, int size) {
    unsigned int cksum = 0;
    for (int i = 0; i < size; i += 2) {
        cksum += (buf[i] << 8) | buf[i+1];
    }
    while ((cksum >> 16) != 0) {
        cksum = (cksum & 0xffff) + (cksum >> 16);
    }
    return (~cksum) & 0xffff;
}

// 打包H264数据
int PackH264(const unsigned char *h264_data, int h264_len, unsigned char *ps_buffer) {
    // 初始化PS头部信息
    PSHeader ps_header = {0};
    ps_header.sync_byte = 0x47;
    ps_header.id = 0x40;
    ps_header.PES_start_flag = 1;
    ps_header.PES_header_data_length = 0x0c;

    // 用于存放PES数据的buffer
    unsigned char pes_buffer[65536];

    // 分离NAL单元
    const unsigned char *ptr = h264_data;
    while (ptr < h264_data + h264_len) {
        // 查找下一个NAL单元的开始位置
        while (*(unsigned int*)ptr != 0x00000001) {
            ++ptr;
            if (ptr >= h264_data + h264_len - 3) {
                // 剩余的数据不足一个NAL单元
                break;
            }
        }

        NALHeader *nal_header = (NALHeader*)(ptr + 1);
        int nal_unit_type = nal_header->nal_unit_type;

        // 构建PES包头部信息
        PSHeader pes_header = ps_header;
        pes_header.PES_scrambling_control = 0; // 不加密
        pes_header.PES_header_data_length = 0x08; // 固定为8个字节
        pes_header.packet_length = htons(0x08 + nal_unit_size);
        pes_header.reserved_1 = 0x02; // PTS和DTS都存在

        // 添加NAL单元到PES包中
        int nal_unit_size = 0;
        const unsigned char *nal_start = ptr + 4;
        if (nal_unit_type == 7 || nal_unit_type == 8) {
            nal_start = ptr + 2;
            nal_unit_size = ptr + 2 - nal_start;
        } else {
            nal_unit_size = ptr + 4 - nal_start;
        }
        unsigned char *pes_packet = new unsigned char[0x08 + nal_unit_size];
        memcpy(pes_packet, "\x00\x00\x01", 3); // 添加NAL单元的header标识
        memcpy(pes_packet + 3, &pes_header.packet_length, 2);
        memset(pes_packet + 5, 0xff, 1); // 固定为0xff
        pes_packet[6] = 0xe0 | (nal_unit_type & 0x1f); // Streaming ID,固定为0xe0
        memcpy(pes_packet + 7, &pts, 5); // PTS和DTS都设置为当前时间戳
        memcpy(pes_packet + 12, nal_start, nal_unit_size);

        // 计算校验和
        unsigned short cksum = checksum(pes_packet, 0x08 + nal_unit_size);

        // 添加PES包到PS包中
        int packet_len = 0x08 + nal_unit_size + 2;
        memcpy(ps_buffer, &ps_header, sizeof(ps_header));
        memcpy(ps_buffer + sizeof(ps_header), pes_packet, 0x08 + nal_unit_size);
        memcpy(ps_buffer + sizeof(ps_header) + 0x08 + nal_unit_size, &cksum, 2);

        ps_buffer += sizeof(ps_header) + packet_len;

        delete[] pes_packet;

        ptr += nal_unit_size;
    }

    return ps_buffer - ps_buffer_start;
}

// 打包AAC数据
int PackAAC(const unsigned char *aac_data, int aac_len, unsigned char *ps_buffer) {
    // 初始化PS头部信息
    PSHeader ps_header = {0};
    ps_header.sync_byte = 0x47;
    ps_header.id = 0x40;
    ps_header.PES_start_flag = 1;
    ps_header.PES_header_data_length = 0x0c;

    // 用于存放PES数据的buffer
    unsigned char pes_buffer[65536];

    // 分离ADTS帧
    const unsigned char *ptr = aac_data;
    while (ptr < aac_data + aac_len) {
        if (*(unsigned int*)ptr == 0xfff00000) {
            ADTSHeader *adts_header = (ADTSHeader*)ptr;

            // 构建PES包头部信息
            PSHeader pes_header = ps_header;
            pes_header.PES_scrambling_control = 0; // 不加密
            pes_header.PES_header_data_length = 0x05; // 固定为5个字节
            pes_header.packet_length = htons(0x05 + adts_header->frame_length + 7);
            pes_header.reserved_1 = 0x03; // PTS和DTS都存在

            // 添加ADTS帧到PES包中
            unsigned char *pes_packet = new unsigned char[0x05 + adts_header->frame_length];
            memcpy(pes_packet, "\x00\x00\x01", 3); // 添加ADTS帧的header标识
            memcpy(pes_packet + 3, &pes_header.packet_length, 2);
            memset(pes_packet + 5, 0xff, 1); // 固定为0xff
            pes_packet[6] = 0xc0; // Streaming ID,固定为0xc0
            memcpy(pes_packet + 7, &pts, 5); // PTS和DTS都设置为当前时间戳
            memcpy(pes_packet + 12, ptr, adts_header->frame_length);

            // 计算校验和
            unsigned short cksum = checksum(pes_packet, 0x05 + adts_header->frame_length);

            // 添加PES包到PS包中
            int packet_len = 0x05 + adts_header->frame_length + 2;
            memcpy(ps_buffer, &ps_header, sizeof(ps_header));
            memcpy(ps_buffer + sizeof(ps_header), pes_packet, 0x05 + adts_header->frame_length);
            memcpy(ps_buffer + sizeof(ps_header) + 0x05 + adts_header->frame_length, &cksum, 2);

            ps_buffer += sizeof(ps_header) + packet_len;

            delete[] pes_packet;

            ptr += adts_header->frame_length;
        } else {
            ++ptr;
        }
    }

    return ps_buffer - ps_buffer_start;
}

// 打包H264和AAC数据为PS(Program Stream)
int PackPS(const unsigned char *h264_data, int h264_len, const unsigned char *aac_data, int aac_len, unsigned char *ps_data) {
    // 初始化PS头部信息

……

……

 return sizeof(pat_header) + sizeof(pat_buffer) +
           sizeof(pmt_header) + sizeof(pmt_buffer) +
           h264_size + aac_size + sizeof(end_buffer);
}
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
RTMP(Real-Time Messaging Protocol)是一种用于音频、视频和数据传输的通信协议,通常用于流媒体传输。 在RTMP推送H264AAC数据之前,需要先准备好H264AAC数据流。以下是RTMP推送H264AAC数据的步骤: 1. 建立RTMP连接 使用RTMP协议的应用程序需要首先建立到媒体服务器的连接。可以使用第三方库如librtmp来实现。连接的过程中需要设置一些连接参数,如服务器地址、端口号、应用程序名称、流名称等。 2. 发送RTMP元数据 在发送音视频数据前,需要先发送一些元数据,用于描述音视频数据的格式及相关信息。RTMP协议中用AMF0或AMF3格式来传递元数据信息。例如,可以发送视频的宽度、高度、帧率、编码格式等信息。 3. 发送H264视频数据 发送H264视频数据时,需要将视频数据封装为FLV格式。FLV格式是一种常用的流媒体视频格式,由头部、tag和数据三部分组成。其中,tag含了视频的时间戳和数据大小等信息。每个tag的前4个字节表示tag类型,其中8表示视频tag类型。 4. 发送AAC音频数据 发送AAC音频数据也需要将音频数据封装为FLV格式。每个tag的前4个字节表示tag类型,其中10表示音频tag类型。音频tag中需要含音频的时间戳和数据大小等信息。 以下是一个推送H264AAC数据的示例代码: ``` AVFormatContext *fmt_ctx; AVCodecContext *video_enc_ctx, *audio_enc_ctx; AVOutputFormat *ofmt; AVStream *video_stream, *audio_stream; AVPacket video_pkt, audio_pkt; int video_frame_count = 0, audio_frame_count = 0; // 初始化FFmpeg av_register_all(); avformat_network_init(); // 打开输出流 avformat_alloc_output_context2(&fmt_ctx, NULL, "flv", "rtmp://server/live/stream"); ofmt = fmt_ctx->oformat; if (avio_open(&fmt_ctx->pb, fmt_ctx->filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Failed to open output file\n"); exit(1); } // 添加视频流和音频流 video_stream = avformat_new_stream(fmt_ctx, NULL); audio_stream = avformat_new_stream(fmt_ctx, NULL); video_enc_ctx = video_stream->codec; audio_enc_ctx = audio_stream->codec; // 设置视频编码器 video_enc_ctx->codec_id = AV_CODEC_ID_H264; video_enc_ctx->bit_rate = 400000; video_enc_ctx->width = 640; video_enc_ctx->height = 480; video_enc_ctx->time_base = (AVRational){1, 25}; video_enc_ctx->gop_size = 10; video_stream->time_base = video_enc_ctx->time_base; // 设置音频编码器 audio_enc_ctx->codec_id = AV_CODEC_ID_AAC; audio_enc_ctx->bit_rate = 64000; audio_enc_ctx->sample_rate = 44100; audio_enc_ctx->channels = 2; audio_stream->time_base = (AVRational){1, audio_enc_ctx->sample_rate}; // 写入文件头 avformat_write_header(fmt_ctx, NULL); // 发送视频元数据 AVDictionary *video_options = NULL; av_dict_set(&video_options, "profile", "main", 0); av_dict_set(&video_options, "preset", "medium", 0); av_dict_set(&video_options, "tune", "zerolatency", 0); av_dict_set(&video_options, "crf", "23", 0); av_dict_set(&video_options, "level", "4.0", 0); av_dict_set(&video_options, "refs", "4", 0); av_dict_set(&video_options, "bufsize", "2M", 0); av_dict_set(&video_options, "maxrate", "400k", 0); av_dict_set(&video_options, "zerolatency", "1", 0); av_dict_set(&video_options, "threads", "4", 0); av_dict_set(&video_options, "bframes", "0", 0); av_dict_set(&video_options, "slices", "8", 0); avformat_write_header(fmt_ctx, &video_options); // 发送音频元数据 AVDictionary *audio_options = NULL; av_dict_set(&audio_options, "profile", "aac_low", 0); av_dict_set(&audio_options, "strict", "-2", 0); avformat_write_header(fmt_ctx, &audio_options); // 发送视频数据 while (1) { // 生成视频帧 AVFrame *video_frame = av_frame_alloc(); video_frame->width = video_enc_ctx->width; video_frame->height = video_enc_ctx->height; video_frame->format = AV_PIX_FMT_YUV420P; av_image_alloc(video_frame->data, video_frame->linesize, video_frame->width, video_frame->height, video_frame->format, 32); // 编码视频帧 avcodec_send_frame(video_enc_ctx, video_frame); while (avcodec_receive_packet(video_enc_ctx, &video_pkt) == 0) { // 将视频数据封装为FLV格式 video_pkt.pts = av_rescale_q(video_pkt.pts, video_enc_ctx->time_base, video_stream->time_base); video_pkt.dts = av_rescale_q(video_pkt.dts, video_enc_ctx->time_base, video_stream->time_base); video_pkt.duration = av_rescale_q(video_pkt.duration, video_enc_ctx->time_base, video_stream->time_base); video_pkt.stream_index = video_stream->index; av_interleaved_write_frame(fmt_ctx, &video_pkt); av_packet_unref(&video_pkt); } // 发送音频数据 AVFrame *audio_frame = av_frame_alloc(); audio_frame->nb_samples = audio_enc_ctx->frame_size; audio_frame->format = audio_enc_ctx->sample_fmt; audio_frame->channel_layout = audio_enc_ctx->channel_layout; av_frame_get_buffer(audio_frame, 0); // 填充音频数据 ... // 编码音频帧 avcodec_send_frame(audio_enc_ctx, audio_frame); while (avcodec_receive_packet(audio_enc_ctx, &audio_pkt) == 0) { // 将音频数据封装为FLV格式 audio_pkt.pts = av_rescale_q(audio_pkt.pts, audio_enc_ctx->time_base, audio_stream->time_base); audio_pkt.dts = av_rescale_q(audio_pkt.dts, audio_enc_ctx->time_base, audio_stream->time_base); audio_pkt.duration = av_rescale_q(audio_pkt.duration, audio_enc_ctx->time_base, audio_stream->time_base); audio_pkt.stream_index = audio_stream->index; av_interleaved_write_frame(fmt_ctx, &audio_pkt); av_packet_unref(&audio_pkt); } // 释放帧内存 av_frame_free(&video_frame); av_frame_free(&audio_frame); } // 写入文件尾 av_write_trailer(fmt_ctx); // 关闭输出流 avio_close(fmt_ctx->pb); // 释放FFmpeg资源 avformat_free_context(fmt_ctx); ``` 注意,以上代码仅为示例,实际情况下需要根据具体的视频编码器和音频编码器来设置编码参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_1015787417

Show you code

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值