Search code examples
c++libavgorilla

C++ LibAV muxing raw H264 into MP4 container and keeping on the buffer


I have an array of raw H264 AVPackets which get from IP Camera's RTSP. so I'm going to put them into MP4 container (not to the file into the buffer) then stream the buffer on the network. I read example on ffmpeg site and surfe alot on Stackoverflow so write the following code but finally when I write the mp4 result from the bufer to the file, it's not playable. event I write the buufer inside the loop it can not be played and seems there's empty packet inside the buffer. however when I use avio_open2 and write to the file with libav it works fine and the file is playable.

#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;

}

Solution

  • after one week I find that it's a normal behavior of mp4 container. actually mp4 is very fit for VOD oroffline playback, and not a sutible option for live streaming. Althogh mp4 container has higher compression ratio and use less B/W, mp4 muxer needs all AVPacket to generate meta information, so LibAVFormat wait to call av_write_trailer ,means there's no video frame and write meta tags, to put header/footer segment. that's why seekable memory is essential for mp4 muxer. I suggest to useHLS or DASH to send live streaming packet over the network.