Search code examples
c++ffmpegrecordrtsp

How to set start time of video saved from RTSP stream with FFMPEG


I use FFMPEG to record video from a RTSP stream. What my code does is get current day time, create a folder with this format year/month/day/hour/minute and save the video to that folder.

When a new minute arrive, I create the new folder base on the new minute and run the record again to the new folder. Basically It works, but the next video start time is continue the end of previous video. For example:

video1: 00:00 -> 00:55
video2: 00:56 -> ...

I hope I can set for all videos start from 00:00. Can I do that?

Here my code

ffmpeg.h

class CtFfmpeg {
public:

    CtFfmpeg();
    ~CtFfmpeg();

    void init();
    int getInput();
    int getOutputName(const char *filename);
    int release();
    int ret;
    AVFormatContext *ifmt_ctx, *ofmt_ctx;
    AVStream *in_stream, *out_stream;
    AVPacket pkt;
    const char *in_filename;
    char *out_filename;

private:
    int setOutput(const char *outfilename);
    AVOutputFormat *ofmt;
};

ffmpeg.cpp

#include "ctffmpeg.h"

CtFfmpeg::CtFfmpeg() {
    in_filename = new char [1024];
    out_filename = new char [1024];
}

CtFfmpeg::~CtFfmpeg() {
    delete [] in_filename;
    delete [] out_filename;
}

void CtFfmpeg::init() {
    avcodec_register_all();
    av_register_all();
    avformat_network_init();
    pkt = { 0 };

    av_init_packet(&pkt);
    ofmt = NULL;
    ifmt_ctx = NULL;
    ofmt_ctx = NULL;
    return;
}

int CtFfmpeg::release() {
    av_write_trailer(ofmt_ctx);
    avcodec_close(out_stream->codec);

    // avcodec_close(in_stream->codec);
    // avformat_close_input(&ifmt_ctx);

    /* close output */
    if (!(ofmt->flags & AVFMT_NOFILE))
        avio_close(ofmt_ctx->pb);

    avformat_free_context(ofmt_ctx);
    av_free_packet(&pkt);
    if (ret < 0 && ret != AVERROR_EOF) {
        fprintf(stderr, "Error occurred\n");
        return 1;
    }
}

int CtFfmpeg::getInput() {
    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
        fprintf(stderr, "Could not open input file '%s'", in_filename);
        release();
    }

    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
        fprintf(stderr, "Failed to retrieve input stream information");
        release();
    }

    av_dump_format(ifmt_ctx, 0, in_filename, 0);
}


int CtFfmpeg::setOutput(const char *outfilename) {
    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, outfilename);
    if (!ofmt_ctx) {
        fprintf(stderr, "Could not create output context\n");
        ret = AVERROR_UNKNOWN;
        release();
    }

    ofmt = ofmt_ctx->oformat;
    for (int i = 0; i < ifmt_ctx->nb_streams; i++) {
        in_stream = ifmt_ctx->streams[i];
        out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);

        if (!out_stream) {
             fprintf(stderr, "Failed allocating output stream\n");
             ret = AVERROR_UNKNOWN;
             release();
        }
        ret = avcodec_copy_context(out_stream->codec, in_stream->codec);

        if (ret < 0) {
            fprintf(stderr, "Failed to copy context from input to output stream codec context\n");
            release();
        }

        out_stream->codec->codec_tag = 0;
        if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
            out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
    } // for

    av_dump_format(ofmt_ctx, 0, outfilename, 1);
    if (!(ofmt->flags & AVFMT_NOFILE)) {
        ret = avio_open(&ofmt_ctx->pb, outfilename, AVIO_FLAG_WRITE);
        if (ret < 0) {
            fprintf(stderr, "Could not open output file '%s'", outfilename);
            release();
        }
    }
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error occurred when opening output file\n");
        release();
    }
}

int CtFfmpeg::getOutputName(const char *filename){
    sprintf(out_filename,filename);
    setOutput(out_filename);
}

main.cpp

#include "ctfolder.h"
#include "ctffmpeg.h"

CtFfmpeg * ff;

int main(int argc, char** argv) {

    if (argc < 2) {
        printf("usage: %s <RTSP link>  \n", argv[0]);
        return 1;
    }

    ff = new CtFfmpeg();

    ff->in_filename = argv[1]; //RTSP input link
    ff->init();
    ff->getInput();

    string filename;

    videoRecorder obj;
    int start, now;
    start = obj.get_current_min();

    if(obj.create_folder(0755))
        cout << "Cannot create folder, maybe it already exists" << endl;
    else
        cout << "Create folder succesfully" << endl;

    int skip = 0;

    while(1){

        filename = obj.update_filename();
        ff->getOutputName(filename.c_str());

        while((now = obj.get_current_min()) == start) {
            ff->ret = av_read_frame(ff->ifmt_ctx, &(ff->pkt));
            skip++;
            if(skip==1)
                continue;

            if(skip>2)
                skip=2;
            if (ff->ret < 0)
                continue;
            ff->pkt.pts = av_rescale_q_rnd(ff->pkt.pts, ff->in_stream->time_base, ff->out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
            ff->pkt.dts = av_rescale_q_rnd(ff->pkt.dts, ff->in_stream->time_base, ff->out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
            ff->pkt.duration = av_rescale_q(ff->pkt.duration, ff->in_stream->time_base, ff->out_stream->time_base);

            ff->pkt.pos = -1;
            ff->ret = av_interleaved_write_frame(ff->ofmt_ctx, &(ff->pkt));
            if (ff->ret < 0) {
                fprintf(stderr, "Error muxing packet\n");
                continue;
            }
            av_free_packet(&(ff->pkt));
        }
        ff->release();

        cout << "New minute!" << endl;

        if(obj.create_folder(0755))
            cout << "Cannot create folder, something's wrong" << endl;
        else
            cout << "Create folder succesfully" << endl;
        start = now;
    }

    return 0;
}

Solution

  • You need to shift your recording packet's pts to 0.

    while(<some condition>)
    {
        //...
        int64_t pts_offset = AV_NOPTS_VALUE ;        
        while((now = obj.get_current_min()) == start)
        {
            //...
            ff.pkt.pts = ...
            //...
            if( pts_offset == AV_NOPTS_VALUE )
            {
                pts_offset = ff.pkt.pts ;
            }
            ff.pkt.pts -= pts_offset ;
            // ...
        }
    }