ffmpeg实现视频切割

流程

打开输入——>打开输出——>根据输入来创建流——>拷贝流设置——>循环读帧——>判断时间点是否到达切割点,并做设置——>设置pts和dts——>写入——>善后

代码

  1. /*
  2. *最简单的视频切割
  3. *本程序实现把一个视频切割为2个视频,不涉及编解码,最难理解的地方在于pts和dts的计算,要好好看看
  4. *不够完美的地方在于没有按照关键帧切割,所以会在切割点花屏,以后改善
  5. *注:只处理一个视频流和一个音频流,若流多了,估计会crash
  6. */
  7. #include "stdafx.h"
  8. #ifdef __cplusplus
  9. extern"C"
  10. {
  11. #endif
  12. #include <libavformat/avformat.h>
  13. #include "libavcodec/avcodec.h"
  14. #include "libavfilter/avfiltergraph.h"
  15. #include "libavfilter/buffersink.h"
  16. #include "libavfilter/buffersrc.h"
  17. #include "libavutil/avutil.h"
  18. #include "libavutil/opt.h"
  19. #include "libavutil/pixdesc.h"
  20. #include "libswresample\swresample.h"
  21. #include "libavutil\fifo.h"
  22. #include "libavutil/audio_fifo.h"
  23. #pragma comment(lib, "avcodec.lib")
  24. #pragma comment(lib, "avformat.lib")
  25. #pragma comment(lib, "avutil.lib")
  26. //#pragma comment(lib, "avdevice.lib")
  27. #pragma comment(lib, "avfilter.lib")
  28. //#pragma comment(lib, "postproc.lib")
  29. #pragma comment(lib, "swresample.lib")
  30. //#pragma comment(lib, "swscale.lib")
  31. #ifdef __cplusplus
  32. };
  33. #endif
  34. int _tmain(int argc, _TCHAR* argv[])
  35. {
  36. if(argc < 3)
  37. {
  38. printf("no input file!\n");
  39. return -1;
  40. }
  41. AVFormatContext *ifmt_ctx = NULL, *ofmt1_ctx = NULL, *ofmt2_ctx = NULL;
  42. AVStream *out1_vstream = NULL, *out1_astream = NULL;
  43. AVStream *out2_vstream = NULL, *out2_astream = NULL;
  44. char str_out1_filename[10];
  45. char str_out2_filename[10];
  46. sprintf(str_out1_filename, "test1.%s", argv[2]);
  47. sprintf(str_out2_filename, "test2.%s", argv[2]);
  48. int inVideo_StreamIndex = -1,inAudio_StreamIndex = -1;
  49. int ret;
  50. av_register_all();
  51. if ((ret = avformat_open_input(&ifmt_ctx, argv[1], NULL, NULL)) < 0)
  52. {
  53. printf("can not open the in put file format context!\n");
  54. return -1;
  55. }
  56. if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0)
  57. {
  58. printf("can not find the input stream info!\n");
  59. goto end;
  60. }
  61. avformat_alloc_output_context2(&ofmt1_ctx, NULL, NULL, str_out1_filename);
  62. if (!ofmt1_ctx)
  63. {
  64. printf( "Could not create output1 context\n");
  65. ret = AVERROR_UNKNOWN;
  66. goto end;
  67. }
  68. avformat_alloc_output_context2(&ofmt2_ctx, NULL, NULL, str_out2_filename);
  69. if (!ofmt2_ctx)
  70. {
  71. printf( "Could not create output2 context\n");
  72. ret = AVERROR_UNKNOWN;
  73. goto end;
  74. }
  75. for (int i = 0; i < ifmt_ctx->nb_streams; i++)
  76. {
  77. if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
  78. {
  79. inVideo_StreamIndex = i;
  80. out1_vstream = avformat_new_stream(ofmt1_ctx, NULL);
  81. out2_vstream = avformat_new_stream(ofmt2_ctx, NULL);
  82. //open decoder
  83. if(0 > avcodec_open2(ifmt_ctx->streams[i]->codec, avcodec_find_decoder(ifmt_ctx->streams[i]->codec->codec_id), NULL))
  84. {
  85. printf("can not find or open video decoder!\n");
  86. goto end;
  87. }
  88. if (!out1_vstream)
  89. {
  90. printf("Failed allocating output1 video stream\n");
  91. ret = AVERROR_UNKNOWN;
  92. goto end;
  93. }
  94. else
  95. {
  96. //copy the settings of AVCodecContext;
  97. if (avcodec_copy_context(out1_vstream->codec, ifmt_ctx->streams[i]->codec) < 0)
  98. {
  99. printf( "Failed to copy context from input to output stream codec context\n");
  100. goto end;
  101. }
  102. out1_vstream->codec->codec_tag = 0;
  103. if(ofmt1_ctx->oformat->flags & AVFMT_GLOBALHEADER)
  104. {
  105. out1_vstream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
  106. }
  107. }
  108. if (!out2_vstream)
  109. {
  110. printf("Failed allocating output1 video stream\n");
  111. ret = AVERROR_UNKNOWN;
  112. goto end;
  113. }
  114. else
  115. {
  116. //copy the settings of AVCodecContext;
  117. if (avcodec_copy_context(out2_vstream->codec, ifmt_ctx->streams[i]->codec) < 0)
  118. {
  119. printf( "Failed to copy context from input to output stream codec context\n");
  120. goto end;
  121. }
  122. out2_vstream->codec->codec_tag = 0;
  123. if(ofmt2_ctx->oformat->flags & AVFMT_GLOBALHEADER)
  124. {
  125. out2_vstream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
  126. }
  127. }
  128. }
  129. else if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
  130. {
  131. inAudio_StreamIndex = i;
  132. out1_astream = avformat_new_stream(ofmt1_ctx, NULL);
  133. out2_astream = avformat_new_stream(ofmt2_ctx, NULL);
  134. if (!out1_astream)
  135. {
  136. printf("Failed allocating output1 video stream\n");
  137. ret = AVERROR_UNKNOWN;
  138. goto end;
  139. }
  140. else
  141. {
  142. //copy the settings of AVCodecContext;
  143. if (avcodec_copy_context(out1_astream->codec, ifmt_ctx->streams[i]->codec) < 0)
  144. {
  145. printf( "Failed to copy context from input to output stream codec context\n");
  146. goto end;
  147. }
  148. out1_astream->codec->codec_tag = 0;
  149. if(ofmt1_ctx->oformat->flags & AVFMT_GLOBALHEADER)
  150. {
  151. out1_astream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
  152. }
  153. }
  154. if (!out2_astream)
  155. {
  156. printf("Failed allocating output1 video stream\n");
  157. ret = AVERROR_UNKNOWN;
  158. goto end;
  159. }
  160. else
  161. {
  162. //copy the settings of AVCodecContext;
  163. if (avcodec_copy_context(out2_astream->codec, ifmt_ctx->streams[i]->codec) < 0)
  164. {
  165. printf( "Failed to copy context from input to output stream codec context\n");
  166. goto end;
  167. }
  168. out2_astream->codec->codec_tag = 0;
  169. if(ofmt2_ctx->oformat->flags & AVFMT_GLOBALHEADER)
  170. {
  171. out2_astream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
  172. }
  173. }
  174. }
  175. }
  176. //Dump Format------------------
  177. printf("\n==============Input Video=============\n");
  178. av_dump_format(ifmt_ctx, 0, argv[1], 0);
  179. printf("\n==============Output1============\n");
  180. av_dump_format(ofmt1_ctx, 0, str_out1_filename, 1);
  181. printf("\n==============Output2============\n");
  182. av_dump_format(ofmt2_ctx, 0, str_out1_filename, 1);
  183. printf("\n======================================\n");
  184. //open output1 file
  185. if (!(ofmt1_ctx->oformat->flags & AVFMT_NOFILE))
  186. {
  187. if (avio_open(&ofmt1_ctx->pb, str_out1_filename, AVIO_FLAG_WRITE) < 0)
  188. {
  189. printf( "Could not open output file '%s'", str_out1_filename);
  190. goto end;
  191. }
  192. }
  193. //open output2 file
  194. if (!(ofmt2_ctx->oformat->flags & AVFMT_NOFILE))
  195. {
  196. if (avio_open(&ofmt2_ctx->pb, str_out2_filename, AVIO_FLAG_WRITE) < 0)
  197. {
  198. printf( "Could not open output file '%s'", str_out2_filename);
  199. goto end;
  200. }
  201. }
  202. //write out 1 file header
  203. if (avformat_write_header(ofmt1_ctx, NULL) < 0)
  204. {
  205. printf( "Error occurred when opening video output file\n");
  206. goto end;
  207. }
  208. //write out 2 file header
  209. if (avformat_write_header(ofmt2_ctx, NULL) < 0)
  210. {
  211. printf( "Error occurred when opening video output file\n");
  212. goto end;
  213. }
  214. int splitPtsV = 0;//the real split video pts
  215. int splitDtsV = 0;
  216. int splitPtsA = 0;//the real split audio pts
  217. int splitDtsA = 0;
  218. int videoIndex = 0;//the real video index
  219. int splitTime = 30;//the split time (sec)
  220. AVPacket pkt;
  221. while(1)
  222. {
  223. AVFormatContext *ofmt_ctx;
  224. AVStream *in_stream, *out_stream;
  225. if (av_read_frame(ifmt_ctx, &pkt) < 0)
  226. {
  227. break;
  228. }
  229. in_stream = ifmt_ctx->streams[pkt.stream_index];
  230. if (pkt.stream_index == inVideo_StreamIndex)
  231. {
  232. videoIndex++;
  233. int time = pkt.pts * (((float)in_stream->time_base.num) / ((float)in_stream->time_base.den));
  234. if (time <= splitTime)
  235. {
  236. splitPtsV = pkt.pts;
  237. splitDtsV = pkt.dts;
  238. out_stream = ofmt1_ctx->streams[pkt.stream_index];
  239. ofmt_ctx = ofmt1_ctx;
  240. }
  241. else
  242. {
  243. pkt.pts = pkt.pts - splitPtsV;
  244. pkt.dts = pkt.dts - splitDtsV;
  245. out_stream = ofmt2_ctx->streams[pkt.stream_index];
  246. ofmt_ctx = ofmt2_ctx;
  247. }
  248. }
  249. else if (pkt.stream_index == inAudio_StreamIndex)
  250. {
  251. int time = pkt.pts * (((float)in_stream->time_base.num) / ((float)in_stream->time_base.den));
  252. if (time <= splitTime)
  253. {
  254. splitPtsA = pkt.pts;
  255. splitDtsA = pkt.dts;
  256. out_stream = ofmt1_ctx->streams[pkt.stream_index];
  257. ofmt_ctx = ofmt1_ctx;
  258. }
  259. else
  260. {
  261. pkt.pts = pkt.pts - splitPtsA;
  262. pkt.dts = pkt.dts - splitDtsA;
  263. out_stream = ofmt2_ctx->streams[pkt.stream_index];
  264. ofmt_ctx = ofmt2_ctx;
  265. }
  266. }
  267. pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
  268. pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
  269. pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
  270. pkt.pos = -1;
  271. //write into file
  272. if (av_interleaved_write_frame(ofmt_ctx, &pkt) < 0)
  273. {
  274. printf( "Error muxing packet\n");
  275. break;
  276. }
  277. av_free_packet(&pkt);
  278. }
  279. av_write_trailer(ofmt1_ctx);
  280. av_write_trailer(ofmt2_ctx);
  281. end:
  282. avformat_close_input(&ifmt_ctx);
  283. /* close output */
  284. if (ofmt1_ctx && !(ofmt1_ctx->oformat->flags & AVFMT_NOFILE))
  285. avio_close(ofmt1_ctx->pb);
  286. avformat_free_context(ofmt1_ctx);
  287. /* close output */
  288. if (ofmt2_ctx && !(ofmt2_ctx->oformat->flags & AVFMT_NOFILE))
  289. avio_close(ofmt2_ctx->pb);
  290. avformat_free_context(ofmt2_ctx);
  291. return 0;
  292. }

ffmpeg实现视频合并

流程

打开输入——>打开输出——>根据输入来创建流——>拷贝流设置——>循环读帧——>判断第一个读完,改输入为第二个——>设置pts和dts——>写入——>善后

代码

  1. /*
  2. *最简单的视频合并
  3. *本程序实现把2个视频合并为一个视频,不涉及编解码,但是对视频源有要求,必须是相同的参数
  4. *着重理解第二个视频开始的时候的pts和dts计算
  5. *注:只处理一个视频流和一个音频流,若流多了,估计会crash
  6. */
  7. #include "stdafx.h"
  8. #ifdef __cplusplus
  9. extern"C"
  10. {
  11. #endif
  12. #include <libavformat/avformat.h>
  13. #include "libavcodec/avcodec.h"
  14. #include "libavfilter/avfiltergraph.h"
  15. #include "libavfilter/buffersink.h"
  16. #include "libavfilter/buffersrc.h"
  17. #include "libavutil/avutil.h"
  18. #include "libavutil/opt.h"
  19. #include "libavutil/pixdesc.h"
  20. #include "libswresample\swresample.h"
  21. #include "libavutil\fifo.h"
  22. #include "libavutil/audio_fifo.h"
  23. #pragma comment(lib, "avcodec.lib")
  24. #pragma comment(lib, "avformat.lib")
  25. #pragma comment(lib, "avutil.lib")
  26. //#pragma comment(lib, "avdevice.lib")
  27. #pragma comment(lib, "avfilter.lib")
  28. //#pragma comment(lib, "postproc.lib")
  29. #pragma comment(lib, "swresample.lib")
  30. //#pragma comment(lib, "swscale.lib")
  31. #ifdef __cplusplus
  32. };
  33. #endif
  34. AVFormatContext *in1_fmtctx = NULL, *in2_fmtctx = NULL, *out_fmtctx = NULL;
  35. AVStream *out_video_stream = NULL, *out_audio_stream = NULL;
  36. int video_stream_index = -1, audio_stream_index = -1;
  37. int open_input(const char * in1_name, const char * in2_name)
  38. {
  39. int ret = -1;
  40. if ((ret = avformat_open_input(&in1_fmtctx, in1_name, NULL, NULL)) < 0)
  41. {
  42. printf("can not open the first input context!\n");
  43. return ret;
  44. }
  45. if ((ret = avformat_find_stream_info(in1_fmtctx, NULL)) < 0)
  46. {
  47. printf("can not find the first input stream info!\n");
  48. return ret;
  49. }
  50. if ((ret = avformat_open_input(&in2_fmtctx, in2_name, NULL, NULL)) < 0)
  51. {
  52. printf("can not open the first input context!\n");
  53. return ret;
  54. }
  55. if ((ret = avformat_find_stream_info(in2_fmtctx, NULL)) < 0)
  56. {
  57. printf("can not find the second input stream info!\n");
  58. return ret;
  59. }
  60. }
  61. int open_output(const char * out_name)
  62. {
  63. int ret = -1;
  64. if ((ret = avformat_alloc_output_context2(&out_fmtctx, NULL, NULL, out_name)) < 0)
  65. {
  66. printf("can not alloc context for output!\n");
  67. return ret;
  68. }
  69. //new stream for out put
  70. for (int i = 0; i < in1_fmtctx->nb_streams; i++)
  71. {
  72. if (in1_fmtctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
  73. {
  74. video_stream_index = i;
  75. out_video_stream = avformat_new_stream(out_fmtctx, NULL);
  76. if (!out_video_stream)
  77. {
  78. printf("Failed allocating output1 video stream\n");
  79. ret = AVERROR_UNKNOWN;
  80. return ret;
  81. }
  82. if ((ret = avcodec_copy_context(out_video_stream->codec, in1_fmtctx->streams[i]->codec)) < 0)
  83. {
  84. printf("can not copy the video codec context!\n");
  85. return ret;
  86. }
  87. out_video_stream->codec->codec_tag = 0;
  88. if(out_fmtctx->oformat->flags & AVFMT_GLOBALHEADER)
  89. {
  90. out_video_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
  91. }
  92. }
  93. else if (in1_fmtctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
  94. {
  95. audio_stream_index = i;
  96. out_audio_stream = avformat_new_stream(out_fmtctx, NULL);
  97. if (!out_audio_stream)
  98. {
  99. printf("Failed allocating output1 video stream\n");
  100. ret = AVERROR_UNKNOWN;
  101. return ret;
  102. }
  103. if ((ret = avcodec_copy_context(out_audio_stream->codec, in1_fmtctx->streams[i]->codec)) < 0)
  104. {
  105. printf("can not copy the video codec context!\n");
  106. return ret;
  107. }
  108. out_audio_stream->codec->codec_tag = 0;
  109. if(out_fmtctx->oformat->flags & AVFMT_GLOBALHEADER)
  110. {
  111. out_audio_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
  112. }
  113. }
  114. }
  115. //open output file
  116. if (!(out_fmtctx->oformat->flags & AVFMT_NOFILE))
  117. {
  118. if ((ret = avio_open(&out_fmtctx->pb, out_name, AVIO_FLAG_WRITE)) < 0)
  119. {
  120. printf("can not open the out put file handle!\n");
  121. return ret;
  122. }
  123. }
  124. //write out file header
  125. if ((ret = avformat_write_header(out_fmtctx, NULL)) < 0)
  126. {
  127. printf( "Error occurred when opening video output file\n");
  128. return ret;
  129. }
  130. }
  131. int _tmain(int argc, _TCHAR* argv[])
  132. {
  133. if(argc < 4)
  134. {
  135. printf("no input file!\n");
  136. return -1;
  137. }
  138. char out_name[20];
  139. sprintf(out_name, "combine.%s", argv[3]);
  140. av_register_all();
  141. if (0 > open_input(argv[1], argv[2]))
  142. {
  143. goto end;
  144. }
  145. if(0 > open_output(out_name))
  146. {
  147. goto end;
  148. }
  149. AVFormatContext *input_ctx = in1_fmtctx;
  150. AVPacket pkt;
  151. int pts_v, pts_a, dts_v, dts_a;
  152. while(1)
  153. {
  154. if(0 > av_read_frame(input_ctx, &pkt))
  155. {
  156. if (input_ctx == in1_fmtctx)
  157. {
  158. float vedioDuraTime, audioDuraTime;
  159. //calc the first media dura time
  160. vedioDuraTime = ((float)input_ctx->streams[video_stream_index]->time_base.num /
  161. (float)input_ctx->streams[video_stream_index]->time_base.den) * ((float)pts_v);
  162. audioDuraTime = ((float)input_ctx->streams[audio_stream_index]->time_base.num /
  163. (float)input_ctx->streams[audio_stream_index]->time_base.den) * ((float)pts_a);
  164. //calc the pts and dts end of the first media
  165. if (audioDuraTime > vedioDuraTime)
  166. {
  167. dts_v = pts_v = audioDuraTime / ((float)input_ctx->streams[video_stream_index]->time_base.num /
  168. (float)input_ctx->streams[video_stream_index]->time_base.den);
  169. dts_a++;
  170. pts_a++;
  171. }
  172. else
  173. {
  174. dts_a = pts_a = vedioDuraTime / ((float)input_ctx->streams[audio_stream_index]->time_base.num /
  175. (float)input_ctx->streams[audio_stream_index]->time_base.den);
  176. dts_v++;
  177. pts_v++;
  178. }
  179. input_ctx = in2_fmtctx;
  180. continue;
  181. }
  182. break;
  183. }
  184. if (pkt.stream_index == video_stream_index)
  185. {
  186. if (input_ctx == in2_fmtctx)
  187. {
  188. pkt.pts += pts_v;
  189. pkt.dts += dts_v;
  190. }
  191. else
  192. {
  193. pts_v = pkt.pts;
  194. dts_v = pkt.dts;
  195. }
  196. }
  197. else if (pkt.stream_index == audio_stream_index)
  198. {
  199. if (input_ctx == in2_fmtctx)
  200. {
  201. pkt.pts += pts_a;
  202. pkt.dts += dts_a;
  203. }
  204. else
  205. {
  206. pts_a = pkt.pts;
  207. dts_a = pkt.dts;
  208. }
  209. }
  210. pkt.pts = av_rescale_q_rnd(pkt.pts, input_ctx->streams[pkt.stream_index]->time_base,
  211. out_fmtctx->streams[pkt.stream_index]->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
  212. pkt.dts = av_rescale_q_rnd(pkt.dts, input_ctx->streams[pkt.stream_index]->time_base,
  213. out_fmtctx->streams[pkt.stream_index]->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
  214. pkt.pos = -1;
  215. if (av_interleaved_write_frame(out_fmtctx, &pkt) < 0)
  216. {
  217. printf( "Error muxing packet\n");
  218. //break;
  219. }
  220. av_free_packet(&pkt);
  221. }
  222. av_write_trailer(out_fmtctx);
  223. end:
  224. avformat_close_input(&in1_fmtctx);
  225. avformat_close_input(&in2_fmtctx);
  226. /* close output */
  227. if (out_fmtctx && !(out_fmtctx->oformat->flags & AVFMT_NOFILE))
  228. avio_close(out_fmtctx->pb);
  229. avformat_free_context(out_fmtctx);
  230. return 0;
  231. }

qrcode_for_gh_e95b474fcf08_344.jpg