重要结构体
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帧开始。
最好使用下面的方法。