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\n",
 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\n");
 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\n");
 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\n");
 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\n");
 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\n");
 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\n");
 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\n");
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\n");
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\n", 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\n", 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("\n");
158 
159         ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
160         if (ret < 0) {
161             fprintf(stderr, "Error muxing packet\n");
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\n", 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帧开始。

  最好使用下面的方法。

 

posted @ 2019-09-25 10:34  希广闻  阅读(1894)  评论(0编辑  收藏  举报