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幀開始。

  最好使用下面的方法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM