• FFmpeg input与output 函数流程


    重要结构体

    AVFormatContext
    AVCodecContext
    AVCodec

    AVPacket
    AVFrame

    0.公共部分

    av_register_all();
    avfilter_register_all();
    avformat_network_init();
    avdevice_register_all();
    av_log_set_level(AV_LOG_ERROR);

    1.input部分

    avformat_alloc_context
    av_find_input_format
    avformat_open_input
    
    avformat_find_stream_info
    
    avcodec_find_decoder
    avcodec_open2
    
    av_read_frame
    
    avcodec_decode_video2
    
    
    
    avformat_close_input

    2.output部分

    avformat_alloc_output_context2
    
    avio_open2
    
    avcodec_find_encoder
    avcodec_alloc_context3
    avcodec_open2
    
    avformat_new_stream
    avcodec_copy_context ---> avcodec_find_decoder + avcodec_alloc_context3
                   + avcodec_parameters_to_context + avcodec_parameters_from_context avformat_write_header avcodec_encode_video2 av_interleaved_write_frame av_write_trailer avcodec_close avformat_close_input

    3.裁剪视频代码

      1 #include <stdlib.h>
      2 #include <libavutil/timestamp.h>
      3 #include <libavformat/avformat.h>
      4 
      5 static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag)
      6 {
      7     AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
      8 
      9     printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d
    ",
     10            tag,
     11            av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
     12            av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
     13            av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
     14            pkt->stream_index);
     15 }
     16 
     17 int cut_video(double from_seconds, double end_seconds, const char* in_filename, const char* out_filename) {
     18     AVOutputFormat *ofmt = NULL;
     19     AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
     20     AVPacket pkt;
     21     int ret, i;
     22 
     23     av_register_all();
     24 
     25     if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
     26         fprintf(stderr, "Could not open input file '%s'", in_filename);
     27         goto end;
     28     }
     29 
     30     if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
     31         fprintf(stderr, "Failed to retrieve input stream information");
     32         goto end;
     33     }
     34 
     35     av_dump_format(ifmt_ctx, 0, in_filename, 0);
     36 
     37     avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
     38     if (!ofmt_ctx) {
     39         fprintf(stderr, "Could not create output context
    ");
     40         ret = AVERROR_UNKNOWN;
     41         goto end;
     42     }
     43 
     44     ofmt = ofmt_ctx->oformat;
     45 
     46     for (i = 0; i < ifmt_ctx->nb_streams; i++) {
     47 /*        AVStream *in_stream = ifmt_ctx->streams[i];
     48         AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
     49         if (!out_stream) {
     50             fprintf(stderr, "Failed allocating output stream
    ");
     51             ret = AVERROR_UNKNOWN;
     52             goto end;
     53         }
     54 
     55         ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
     56         if (ret < 0) {
     57             fprintf(stderr, "Failed to copy context from input to output stream codec context
    ");
     58             goto end;
     59         }
     60         out_stream->codec->codec_tag = 0;
     61         if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
     62             out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
     63 */
     64         AVStream *in_stream = ifmt_ctx->streams[i];
     65         AVCodec * codec = avcodec_find_decoder(in_stream->codecpar->codec_id);
     66         AVStream *out_stream = avformat_new_stream(ofmt_ctx, codec);
     67         if (!out_stream) {
     68             fprintf(stderr, "Failed allocating output stream
    ");
     69             ret = AVERROR_UNKNOWN;
     70             goto end;
     71         }
     72         
     73         AVCodecContext * codec_ctx = avcodec_alloc_context3(codec);
     74         ret = avcodec_parameters_to_context(codec_ctx, in_stream->codecpar);
     75         if (ret < 0) {
     76             fprintf(stderr, "Failed to copy in_stream codecpar to codec context
    ");
     77             goto end;
     78         }
     79 
     80         codec_ctx->codec_tag = 0;
     81         if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
     82             codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
     83 
     84         ret = avcodec_parameters_from_context(out_stream->codecpar, codec_ctx);
     85         if (ret < 0) {
     86             fprintf(stderr, "Failed to copy codec context to out_stream codecpar context
    ");
     87             goto end;
     88         }
     89     }
     90     av_dump_format(ofmt_ctx, 0, out_filename, 1);
     91 
     92 
     93     if (!(ofmt->flags & AVFMT_NOFILE)) {
     94         ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
     95         if (ret < 0) {
     96             fprintf(stderr, "Could not open output file '%s'", out_filename);
     97             goto end;
     98         }
     99     }
    100 
    101     ret = avformat_write_header(ofmt_ctx, NULL);
    102     if (ret < 0) {
    103         fprintf(stderr, "Error occurred when opening output file
    ");
    104         goto end;
    105     }
    106 
    107     // int64_t start_from = 8*AV_TIME_BASE;
    108     ret = av_seek_frame(ifmt_ctx, -1, from_seconds*AV_TIME_BASE, AVSEEK_FLAG_ANY);
    109     if (ret < 0) {
    110         fprintf(stderr, "Error seek
    ");
    111         goto end;
    112     }
    113 
    114     int64_t *dts_start_from = malloc(sizeof(int64_t) * ifmt_ctx->nb_streams);
    115     memset(dts_start_from, 0, sizeof(int64_t) * ifmt_ctx->nb_streams);
    116     int64_t *pts_start_from = malloc(sizeof(int64_t) * ifmt_ctx->nb_streams);
    117     memset(pts_start_from, 0, sizeof(int64_t) * ifmt_ctx->nb_streams);
    118 
    119     while (1) {
    120         AVStream *in_stream, *out_stream;
    121 
    122         ret = av_read_frame(ifmt_ctx, &pkt);
    123         if (ret < 0)
    124             break;
    125 
    126         in_stream  = ifmt_ctx->streams[pkt.stream_index];
    127         out_stream = ofmt_ctx->streams[pkt.stream_index];
    128 
    129         log_packet(ifmt_ctx, &pkt, "in");
    130 
    131         if (av_q2d(in_stream->time_base) * pkt.pts > end_seconds) {
    132             av_free_packet(&pkt);
    133             break;
    134         }
    135 
    136         if (dts_start_from[pkt.stream_index] == 0) {
    137             dts_start_from[pkt.stream_index] = pkt.dts;
    138             printf("dts_start_from: %s
    ", av_ts2str(dts_start_from[pkt.stream_index]));
    139         }
    140         if (pts_start_from[pkt.stream_index] == 0) {
    141             pts_start_from[pkt.stream_index] = pkt.pts;
    142             printf("pts_start_from: %s
    ", av_ts2str(pts_start_from[pkt.stream_index]));
    143         }
    144 
    145         /* copy packet */
    146         pkt.pts = av_rescale_q_rnd(pkt.pts - pts_start_from[pkt.stream_index], in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
    147         pkt.dts = av_rescale_q_rnd(pkt.dts - dts_start_from[pkt.stream_index], in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
    148         if (pkt.pts < 0) {
    149             pkt.pts = 0;
    150         }
    151         if (pkt.dts < 0) {
    152             pkt.dts = 0;
    153         }
    154         pkt.duration = (int)av_rescale_q((int64_t)pkt.duration, in_stream->time_base, out_stream->time_base);
    155         pkt.pos = -1;
    156         log_packet(ofmt_ctx, &pkt, "out");
    157         printf("
    ");
    158 
    159         ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
    160         if (ret < 0) {
    161             fprintf(stderr, "Error muxing packet
    ");
    162             //break;    // 注释后,解决 错误 pts (6000) < dts (12000) in stream 0
    163         }
    164         av_free_packet(&pkt);
    165     }
    166     free(dts_start_from);
    167     free(pts_start_from);
    168 
    169     av_write_trailer(ofmt_ctx);
    170 end:
    171 
    172     avformat_close_input(&ifmt_ctx);
    173 
    174     /* close output */
    175     if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
    176         avio_closep(&ofmt_ctx->pb);
    177     avformat_free_context(ofmt_ctx);
    178 
    179     if (ret < 0 && ret != AVERROR_EOF) {
    180         fprintf(stderr, "Error occurred: %s
    ", av_err2str(ret));
    181         return 1;
    182     }
    183 
    184     return 0;
    185 }
    186 
    187 int main(int argc, char *argv[]){
    188     if(argc < 5){
    189         fprintf(stderr, "Usage: 
    190                 command startime, endtime, srcfile, outfile");
    191         return -1;
    192     }
    193 
    194     double startime = atoi(argv[1]);
    195     double endtime = atoi(argv[2]);
    196     cut_video(startime, endtime, argv[3], argv[4]);
    197 
    198     return 0;
    199 }

    4.问题

      问题分析:

      出现这种错误是由于视频pts大于dts。pts是视频播放时间,dts是送入解码器解码时间。所以一帧视频播放时间必须在解码时间点之后。

      产生错误的原因一般是对dts,pts操作不当。比如在进行视频分割时,常用的方法是视频截取后半段视频pts与dts减去前半段pts和dts。前半段pts可能比dts大(当解码的视频帧不是I帧时)后半段刚开始视频pts和dts刚好相等(当前帧为I帧时),两个一相减就会出现dts小于pts的情况。

      解决方法:

      1.进行判断:在av_interleaved_write_frame之前添加 if(packet.pts < packet.dts) continue; 把异常的帧简单跳过,异常帧只是极少数简单跳过不会有什么影响。这样会是的播放裁剪后的视频起始有黑屏。

      2.获取截取起始时间后的第一个I帧,从这个I帧开始。

      最好使用下面的方法。

  • 相关阅读:
    201521123038 《Java程序设计》 第五周学习总结
    201521123020 《Java程序设计》第4周学习总结
    201521123020 《Java程序设计》第3周学习总结
    201521123020《Java程序设计》第2周学习总结
    Java第十二周学习总结
    Java第十一周学习总结
    Java第十周学习总结
    Java第九周学习总结
    Java第八周学习总结
    Java第七周学习总结
  • 原文地址:https://www.cnblogs.com/diaoss/p/11582924.html
Copyright © 2020-2023  润新知