如何使用 FFMPEG 设置从 RTSP 流保存的视频的开始时间

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

我使用 FFMPEG 从 RTSP 流录制视频。我的代码所做的是获取当前日期时间,创建格式为 year/month/day/hour/minute 的文件夹并将视频保存到该文件夹​​。

当新的一分钟到来时,我会根据新的一分钟创建新的文件夹,然后 运行 再次将记录放到新的文件夹中。 基本上它有效,但下一个视频开始时间是继续上一个视频的结束。例如:

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

我希望我可以为从00:00开始的所有视频设置。我可以这样做吗?

这是我的代码

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

您需要将录音包的 pts 移动到 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 ;
        // ...
    }
}