C++ LibAV 将原始 H264 混合到 MP4 容器中并保留在缓冲区中

问题描述 投票:0回答:1

我有一组从 IP 摄像机的 RTSP 获取的原始 H264 AVPackets。 所以我将把它们放入 MP4 容器中(不是将文件放入缓冲区),然后将缓冲区传输到网络上。 我在 ffmpeg 网站上阅读了示例,并在 Stackoverflow 上浏览了很多内容,因此编写了以下代码,但最后当我将缓冲区中的 mp4 结果写入文件时,它无法播放。 但是,当我使用

avio_open2
并使用 libav 写入文件时,它工作正常并且文件可以播放。

#include <iostream>
#include <vector>
#include <queue>
#include "stdlib.h"
#include <fstream>
#include <list>    

extern "C" {
  #include <libavformat/avformat.h>
  #include <libavcodec/avcodec.h>
  #include <libavutil/avutil.h>
  #include <libavutil/pixdesc.h>
  #include <libswscale/swscale.h>
  #include "libavutil/imgutils.h"
  #include <libavutil/opt.h>
  #include <libavutil/mathematics.h>

}

#define VIDEO_CODEC_ID AV_CODEC_ID_H264
#define AUDIO_CODEC_ID AV_CODEC_ID_AAC
#define BUFFER_SIZE 1024*1024*10 // 10 MB buffer size (adjust as needed)


#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/img_hash.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include <pthread.h>
#include <assert.h>
#include <algorithm>
#include <limits>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>

#include <bitset>

using namespace std;
using namespace cv;

int main(int argc, char ** argv) {
    
    av_register_all();
    avcodec_register_all();
    
    FILE *fp;
    fp = fopen("./sample_iocontext.mp4", "wb");

    // Muxer2
    AVFormatContext* muxer2 = avformat_alloc_context();
    muxer2->oformat = av_guess_format("mp4", NULL, NULL);
    AVStream* video_track2 = avformat_new_stream(muxer2, NULL);

    //Muxer2
    avcodec_parameters_from_context(video_track2->codecpar, mycodec); //sample codec from another function
    video_track2->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
    video_track2->codec->codec_id = AV_CODEC_ID_H264;
    video_track2->codec->codec_type = AVMEDIA_TYPE_VIDEO;
    video_track2->codec->width = 1280;
    video_track2->codec->height = 960;
    video_track2->codec->time_base.den = 20;
    video_track2->codec->time_base.num = 1;
    video_track2->time_base = (AVRational) {1,20};
    video_track2->avg_frame_rate = mycodec->framerate;
    
    muxer2->oformat->video_codec = AV_CODEC_ID_H264;

    avio_buffer = (uint8_t*)av_malloc(BUFFER_SIZE);
    AVIOContext* ioContext = avio_alloc_context(avio_buffer, BUFFER_SIZE, 1, nullptr, nullptr, nullptr, nullptr);
    ioContext->seekable = 1;

    muxer2->pb = ioContext;
    muxer2->flags =  AVFMT_FLAG_CUSTOM_IO;
    
    // Write MP4 container header to memory buffer
    AVDictionary *options = NULL;
    av_dict_set(&options, "live", "1", 0);
    avformat_write_header(muxer2, &options);

    int packetId=0;
    for(int ii=0;ii<100;ii++){

        AVPacket *temp_packet2; //get packet from my array of AVPacket 

        AVRational encoder_time_base =  (AVRational) {1, 20};   
        temp_packet2->stream_index = video_track2->index;
        
        int64_t scaled_pts2 = av_rescale_q(packetId, encoder_time_base, video_track2->time_base);
        temp_packet2->pts = scaled_pts2;
        int64_t scaled_dts2 = av_rescale_q(packetId, encoder_time_base, video_track2->time_base);
        temp_packet2->dts = scaled_dts2;
        
        packetId++;
        av_interleaved_write_frame(muxer2, temp_packet2);

        av_packet_free(&temp_packet2);
    }
    // Write MP4 container trailer to memory buffer
    av_write_trailer(muxer2);
    fwrite(avio_buffer, 1, ioContext->pos, fp);
    fclose(fp);
    av_freep(&avio_buffer);
    avformat_free_context(muxer2);

    return 0;

}
c++ libav gorilla
1个回答
0
投票

一周后我发现这是 mp4 容器的正常行为。 实际上 mp4 非常适合

VOD
offline playback
,而不是直播的合适选择。虽然
mp4
容器压缩比较高,使用的
B/W
较少,但
mp4 muxer
需要所有
AVPacket
来生成
meta information
,所以
LibAVFormat
等待调用
av_write_trailer
,意味着没有视频帧并写入
meta tags 
,放置
header/footer segment
。这就是为什么
seekable
记忆对于
mp4 muxer
至关重要。 我建议使用
HLS
DASH
通过网络发送直播数据包。

© www.soinside.com 2019 - 2024. All rights reserved.