滴滴滴滴哒

滴滴滴滴哒

0个粉丝

8

问答

0

专栏

0

资料

滴滴滴滴哒  发布于  2024-10-29 15:02:13
采纳率 13%
8个问答
53

求助!hi3518dv500使用ffmpeg封装音视频为mp4格式中间的时间戳计算问题

使用ffmpeg封装海思的音视频,想法是调海思的函数,在venc里面拿到视频流,在音频的aenc里拿到aac音频,目前已经封装出了mp4文件,但是由于小弟第一次做这块领域,不明白海思的时间戳如何计算,如何能保证音视频同步,现在我封装出来mp4里面音频是要比视频慢一些。而且视频有点卡顿,不知道有没有大佬知道海思时间戳要如何计算呢。
下面附上计算代码

td_s32 nd_HI_PDT_WriteVideo(ot_venc_chn VeChn, ot_venc_stream *pstStream, nd_FfmpegConf *fc)
{
    unsigned int i = 0;
    unsigned char *pPackVirtAddr = NULL; // 码流首地址
    unsigned int u32PackLen = 0;         // 码流长度
    int ret = 0;
    AVStream *Vpst = NULL; // 视频流指针
    AVPacket pkt;           // 音视频包结构体,这个包不是海思的包,填充之后,用于最终写入数据
    uint8_t sps_buf[32];
    uint8_t pps_buf[32];
    uint8_t sps_pps_buf[64];
    td_s32 small_increment = 1;
    unsigned int pps_len = 0;
    unsigned int sps_len = 0;
    if (NULL == pstStream) // 裸码流有效判断
    {
        return TD_SUCCESS;
    }
    // u32PackCount是海思中记录此码流结构体中码流包数量,一般含I帧的是4个包,P帧1个
    for (i = 0; i < pstStream->pack_cnt; i++)
    {
        // 从海思码流包中获取数据地址,长度
        pPackVirtAddr = pstStream->pack[i].addr + pstStream->pack[i].offset;
        u32PackLen = pstStream->pack[i].len - pstStream->pack[i].offset;

        // 最重要的数据要给AVpack包

        av_init_packet(&pkt);         // 初始化AVpack包
        pkt.flags = AV_PKT_FLAG_KEY; // 默认是关键帧,关不关键好像都没问题

        switch (pstStream->pack[i].data_type.h264_type)
        {
        case OT_VENC_H264_NALU_SPS:                  // 如果这个包是SPS
            pkt.flags = 0;                          // 不是关键帧
            if (fc->b_First_IDR_Find[VeChn] == 2) // 如果不是第一个SPS帧
            {
                continue; // 不处理,丢弃
                          // 我只要新建文件之后的第一个SPS PPS信息,后面都是一样的,只要第一个即可
            }
            else // 如果是第一个SPS帧
            {
                sps_len = u32PackLen;
                memcpy(sps_buf, pPackVirtAddr, sps_len);
                if (fc->b_First_IDR_Find[VeChn] == 1) // 如果PPS帧已经收到
                {
                    memcpy(sps_pps_buf, sps_buf, sps_len);             // 复制sps
                    memcpy(sps_pps_buf + sps_len, pps_buf, pps_len); // 加上pps
                    // 去添加视频流,和SPS PPS信息,这步之后才开始写入视频帧
                    ret = nd_HI_ADD_SPS_PPS(VeChn, sps_pps_buf, sps_len + pps_len, fc);
                    if (ret < 0)
                        return TD_FAILURE;
                }
                fc->b_First_IDR_Find[VeChn]++;
            }
            continue; // 继续
        case OT_VENC_H264_NALU_PPS:
            pkt.flags = 0;                          // 不是关键帧
            if (fc->b_First_IDR_Find[VeChn] == 2) // 如果不是第一个PPS帧
            {
                continue;
            }
            else // 是第一个PPS帧
            {
                pps_len = u32PackLen;
                memcpy(pps_buf, pPackVirtAddr, pps_len); // 复制
                if (fc->b_First_IDR_Find[VeChn] == 1)     // 如果SPS帧已经收到
                {
                    memcpy(sps_pps_buf, sps_buf, sps_len);
                    memcpy(sps_pps_buf + sps_len, pps_buf, pps_len);
                    // 这里和SPS那里互斥,只有一个会执行,主要是看SPS和PPS包谁排在后面
                    ret = nd_HI_ADD_SPS_PPS(VeChn, sps_pps_buf, sps_len + pps_len, fc);
                    if (ret < 0)
                        return TD_FAILURE;
                }
                fc->b_First_IDR_Find[VeChn]++;
            }
            continue;
        case OT_VENC_H264_NALU_SEI:        // 增强帧,不含图像数据信息,只是对图像数据信息和视频流的补充
            continue;                    // 不稀罕这个帧
        case OT_VENC_H264_NALU_P_SLICE: // P帧
        case OT_VENC_H264_NALU_I_SLICE: // I帧

            if (fc->b_First_IDR_Find[VeChn] != 2) // 如果这个文件还没有收到过sps和pps帧
            {
                continue; // 跳过,不处理这帧
            }
            break;
        default:
            break;
        }

        if (fc->vi[VeChn] < 0) // 流索引号,如果g_OutFmt_Ctx里面还没有新建视频流,也就是说还没收到I帧
        {
            printf("vi less than 0 \n");

            return TD_SUCCESS;
        }
        if (fc->Vfirst[VeChn] == 0)
        {
            fc->Vfirst[VeChn] = 1;
            // fc->Video_PTS[VeChn] = pstStream->seq; // 记录初始序号
            fc->Video_PTS[VeChn] = pstStream->pack[i].pts;
            // printf("Computed pstStream->seq: %lld\n", pstStream->seq);
        }

        Vpst = fc->g_OutFmt_Ctx[VeChn]->streams[fc->vi[VeChn]];
        pkt.stream_index = Vpst->index;
        pkt.data = pPackVirtAddr;
        pkt.size = u32PackLen;

        // pkt.pts = av_rescale_q_rnd(pstStream->pack[i].pts - fc->Video_PTS[VeChn], (AVRational){1, STREAM_FRAME_RATE}, Vpst->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt.pts = av_rescale_q_rnd(fc->Video_PTS[VeChn]++, (AVRational){1, STREAM_FRAME_RATE}, Vpst->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt.dts = pkt.pts; // 只有I、P帧,相等就行了

        pkt.duration = av_rescale_q(40, Vpst->time_base, Vpst->time_base);
        pkt.pos = -1;

        // printf("Computed SEQ: %lld\n", pstStream->seq);
        // printf("Computed pstStream->seq - fc->Video_PTS[VeChn]: %lld\n", pstStream->seq - fc->Video_PTS[VeChn]);

        ret = av_interleaved_write_frame(fc->g_OutFmt_Ctx[VeChn], &pkt);
        if (ret < 0)
        {
            printf("cannot write video frame\n");
            return TD_FAILURE;
        }
        printf(" write video frame\n");
    }
    return TD_SUCCESS;
}

td_s32 nd_HI_PDT_WriteAudio(ot_aenc_chn AeChn, ot_audio_stream *pstStream, nd_FfmpegConf *fc)
{

    unsigned char *pPackVirtAddr = NULL; // 码流数据首地址
    unsigned int u32PackLen = 0;         // 码流数据长度
    int ret = 0;

    AVStream *Apst = NULL; // 用于指向音频流
    AVPacket pkt;           // 定义一个包
    pkt.data = NULL;

    if (NULL == pstStream)
    {
        return TD_SUCCESS;
    }
    if (fc->ai[AeChn] < 0)
    { // 如果文件里面没有音频流
#ifdef DEBUG
        printf("ai[%d] less than 0 \n", AeChn);
#endif
        return TD_SUCCESS;
    }

    // 获取音频码流信息
    pPackVirtAddr = pstStream->stream; // 带7字节ADTS头
    u32PackLen = pstStream->len;
    av_init_packet(&pkt);                                    // 初始化AVpack包
    Apst = fc->g_OutFmt_Ctx[AeChn]->streams[fc->ai[AeChn]]; // ai[AeChn]代表音频流索引号
    if (fc->Afirst[AeChn] == 0)                                // 如果是第一个音频帧
    {
        fc->Afirst[AeChn] = 1;

        // fc->Audio_PTS[AeChn] = pstStream->seq; // 记录下初始序列号
        fc->Audio_PTS[AeChn] = pstStream->time_stamp; // 记录下开始时间戳

        // fc->Audio_PTS[AeChn] = pstStream->time_stamp; // 记录下开始时间戳
    }

    // pkt.pts = pkt.dts = av_rescale_q_rnd(pstStream->seq - fc->Audio_PTS[AeChn],
    //                                      (AVRational){1000, 15625}, // 16000/1024*1000=15625
    //                                      Apst->time_base,
    //                                      (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));

    pkt.pts = pkt.dts = (pstStream->time_stamp - fc->Audio_PTS[AeChn]) / (1000000 / AUDIO_RATE); // 时间基转换
    // pkt.pts = pkt.dts = (fc->Audio_PTS[AeChn]++) / (1000000 / AUDIO_RATE); // 时间基转换

    pkt.duration = 40;                                                             // 64ms
    pkt.duration = av_rescale_q(pkt.duration, Apst->time_base, Apst->time_base); // 转换

    pkt.pos = -1;
    pkt.stream_index = Apst->index; // 音频流的索引号 赋给 包里面流索引号,表示这个包属于音频流
    pkt.data = pPackVirtAddr;        // 接受音频数据
    pkt.size = u32PackLen;            // 音频数据长度

    // 写入一帧
    ret = av_interleaved_write_frame(fc->g_OutFmt_Ctx[AeChn], &pkt);
    if (ret < 0)
    {
        printf("Muxing: cannot write audio frame\n");
        return TD_FAILURE;
    }
    printf("write audio frame\n");

    return TD_SUCCESS;
}
我来回答
回答5个
时间排序
认可量排序

MA_NONG

4个粉丝

7

问答

2

专栏

0

资料

MA_NONG 2024-10-29 16:16:15
认可0

3516DV500 / 3519DV500 中间件有MP4封装的.a 库很小巧,还有sameple。为何不用?

滴滴滴滴哒
滴滴滴滴哒   回复   MA_NONG  2024-10-29 17:18:21
0

您说的是xunayuan这个中间件吗哥 有现成的咱肯定不费劲写ffmpeg了 您能告诉我具体是哪个中间件吗

滴滴滴滴哒
滴滴滴滴哒   回复   MA_NONG  2024-10-29 17:23:31
0

哥上次也是您告诉我封装的步骤的,我按照您说的确实写出来了,但是我使用ffmpeg很多细节没处理好,视频有点问题。

MA_NONG
MA_NONG   回复   滴滴滴滴哒  2024-10-29 21:28:02
0

HiXuanyuanV100R001C01SPC020\platform\middleware\sample\mp4
mp4_demuxer / mp4_muxer 都有

滴滴滴滴哒
滴滴滴滴哒   回复   MA_NONG  2024-10-30 09:17:05
0

好的谢谢您

或将文件直接拖到这里
悬赏:
E币
网盘
* 网盘链接:
* 提取码:
悬赏:
E币

Markdown 语法

  • 加粗**内容**
  • 斜体*内容*
  • 删除线~~内容~~
  • 引用> 引用内容
  • 代码`代码`
  • 代码块```编程语言↵代码```
  • 链接[链接标题](url)
  • 无序列表- 内容
  • 有序列表1. 内容
  • 缩进内容
  • 图片![alt](url)
+ 添加网盘链接/附件

Markdown 语法

  • 加粗**内容**
  • 斜体*内容*
  • 删除线~~内容~~
  • 引用> 引用内容
  • 代码`代码`
  • 代码块```编程语言↵代码```
  • 链接[链接标题](url)
  • 无序列表- 内容
  • 有序列表1. 内容
  • 缩进内容
  • 图片![alt](url)
举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

易百纳技术社区