当前位置:   article > 正文

FFmpeg AAC文件和H264文件合成MP4/FLV文件

FFmpeg AAC文件和H264文件合成MP4/FLV文件

使用FFmpeg库把AAC文件和H264文件合成MP4/FLV文件,FFmpeg版本为4.4.2-0。

需要aac和h264测试文件的,可以从我上传的MP4文件中用ffmpeg提取,命令如下:

ffmpeg -i <input.mp4> -map 0:v -c:v copy <output.h264> -map 0:a -c:a copy <output.aac>

代码如下:
 

  1. #include <stdio.h>
  2. #include "libavformat/avformat.h"
  3. // 打开输入文件并查找流信息
  4. int open_input_file(const char *filename, AVFormatContext **ifmt_ctx)
  5. {
  6. // 打开输入文件
  7. if (avformat_open_input(ifmt_ctx, filename, 0, 0) < 0)
  8. {
  9. fprintf(stderr, "open %s file failed\n", filename);
  10. return -1;
  11. }
  12. // 查找流信息
  13. if (avformat_find_stream_info(*ifmt_ctx, 0) < 0)
  14. {
  15. fprintf(stderr, "avformat_find_stream_info failed\n");
  16. return -1;
  17. }
  18. return 0;
  19. }
  20. // 创建输出流
  21. int create_output_stream(AVFormatContext *ofmt_ctx, AVFormatContext *ifmt_ctx, enum AVMediaType type,
  22. int *index_in, int *index_out)
  23. {
  24. for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++)
  25. {
  26. // 查找指定类型的流
  27. if (ifmt_ctx->streams[i]->codecpar->codec_type == type)
  28. {
  29. AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL);
  30. if (!out_stream)
  31. {
  32. fprintf(stderr, "avformat_new_stream failed\n");
  33. return -1;
  34. }
  35. *index_in = i;
  36. *index_out = out_stream->index;
  37. // 复制输入流的编码参数到输出流
  38. if (avcodec_parameters_copy(out_stream->codecpar, ifmt_ctx->streams[i]->codecpar) < 0)
  39. {
  40. fprintf(stderr, "avcodec_parameters_copy failed\n");
  41. return -1;
  42. }
  43. // 如果是音频流,设置相关标志
  44. if (type == AVMEDIA_TYPE_AUDIO)
  45. {
  46. /*
  47. * codec_tag 是一个标识符,用于指定特定的编解码器。
  48. * 将其设置为0表示在输出文件中不使用特定的编解码器标识符。
  49. */
  50. out_stream->codecpar->codec_tag = 0;
  51. /*
  52. * 检查输出格式的标志是否包含 AVFMT_GLOBALHEADER,
  53. * AVFMT_GLOBALHEADER 是一个标志,表示编解码器的头部信息应存储在文件的全局头部,
  54. * 而不是每个帧的头部。常用于某些格式(例如MP4),以减少每个帧的开销。
  55. */
  56. if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
  57. {
  58. /*
  59. * 设置全局头部标志,如果输出格式需要全局头部,
  60. * 则在输出格式上下文的标志中添加 AV_CODEC_FLAG_GLOBAL_HEADER,
  61. * 这会通知编码器将头部信息写入文件的全局头部,而不是每个帧的头部。
  62. */
  63. ofmt_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
  64. }
  65. }
  66. return 0;
  67. }
  68. }
  69. return -1;
  70. }
  71. // 读取并写入帧
  72. int rw_frame(AVFormatContext *ifmt_ctx, AVFormatContext *ofmt_ctx,
  73. int stream_index_in, int stream_index_out, int *frame_index, int64_t *cur_pts)
  74. {
  75. int ret;
  76. AVPacket pkt;
  77. AVStream *in_stream, *out_stream;
  78. // 读取帧
  79. if ((ret = av_read_frame(ifmt_ctx, &pkt)) < 0)
  80. {
  81. if (ret == AVERROR_EOF) // 读到文件尾
  82. {
  83. return -2;
  84. }
  85. else
  86. {
  87. fprintf(stderr, "av_read_frame failed\n");
  88. return -1;
  89. }
  90. }
  91. in_stream = ifmt_ctx->streams[pkt.stream_index];
  92. out_stream = ofmt_ctx->streams[stream_index_out];
  93. // 处理指定的流
  94. if (pkt.stream_index == stream_index_in)
  95. {
  96. // 如果PTS值无效,计算并设置PTS和DTS
  97. if (pkt.pts == AV_NOPTS_VALUE)
  98. {
  99. AVRational time_base = in_stream->time_base;
  100. int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
  101. pkt.pts = (double)(*frame_index * calc_duration) / (double)(av_q2d(time_base) * AV_TIME_BASE);
  102. pkt.dts = pkt.pts;
  103. pkt.duration = (double)calc_duration / (double)(av_q2d(time_base) * AV_TIME_BASE);
  104. (*frame_index)++;
  105. }
  106. *cur_pts = pkt.pts;
  107. // 转换PTS和DTS
  108. pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base,
  109. (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
  110. pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base,
  111. (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
  112. pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
  113. pkt.pos = -1;
  114. pkt.stream_index = stream_index_out;
  115. if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
  116. {
  117. printf("Write 1 video Packet. size:%d pts:%ld\n", pkt.size, pkt.pts);
  118. }
  119. else if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
  120. {
  121. printf("Write 1 audio Packet. size:%d pts:%ld\n", pkt.size, pkt.pts);
  122. }
  123. // 写入帧
  124. if ((ret = av_interleaved_write_frame(ofmt_ctx, &pkt)) < 0)
  125. {
  126. fprintf(stderr, "av_interleaved_write_frame failed\n");
  127. av_packet_unref(&pkt);
  128. return -1;
  129. }
  130. }
  131. av_packet_unref(&pkt);
  132. return 0;
  133. }
  134. int main(int argc, char *argv[])
  135. {
  136. int ret = -1, value = -1;
  137. const char *in_filename_v = argv[1];
  138. const char *in_filename_a = argv[2];
  139. const char *out_filename = argv[3];
  140. int videoindex_v = -1, videoindex_out = -1;
  141. int audioindex_a = -1, audioindex_out = -1;
  142. int frame_index = 0;
  143. int64_t cur_pts_v = 0, cur_pts_a = 0;
  144. int writing_v = 1, writing_a = 1;
  145. const AVOutputFormat *ofmt = NULL;
  146. AVFormatContext *ifmt_ctx_v = NULL, *ifmt_ctx_a = NULL, *ofmt_ctx = NULL;
  147. if (argc < 3)
  148. {
  149. fprintf(stderr, "Usage: %s <h264 filename> <aac filename> <output filename>\n", argv[0]);
  150. return -1;
  151. }
  152. // 打开视频输入文件
  153. if (open_input_file(in_filename_v, &ifmt_ctx_v) < 0)
  154. {
  155. goto end;
  156. }
  157. // 打开音频输入文件
  158. if (open_input_file(in_filename_a, &ifmt_ctx_a) < 0)
  159. {
  160. goto end;
  161. }
  162. // 分配输出上下文
  163. avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
  164. if (!ofmt_ctx)
  165. {
  166. fprintf(stderr, "avformat_alloc_output_context2 failed\n");
  167. goto end;
  168. }
  169. ofmt = ofmt_ctx->oformat;
  170. // 创建视频输出流
  171. if (create_output_stream(ofmt_ctx, ifmt_ctx_v, AVMEDIA_TYPE_VIDEO, &videoindex_v, &videoindex_out) < 0)
  172. goto end;
  173. // 创建音频输出流
  174. if (create_output_stream(ofmt_ctx, ifmt_ctx_a, AVMEDIA_TYPE_AUDIO, &audioindex_a, &audioindex_out) < 0)
  175. goto end;
  176. // 打开输出文件
  177. if (!(ofmt->flags & AVFMT_NOFILE)) // 检查输出格式是否需要文件存储
  178. {
  179. if (avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE))
  180. {
  181. fprintf(stderr, "open %s file failed\n", out_filename);
  182. goto end;
  183. }
  184. }
  185. // 写入文件头
  186. if (avformat_write_header(ofmt_ctx, NULL) < 0)
  187. {
  188. fprintf(stderr, "avformat_write_header failed\n");
  189. goto end;
  190. }
  191. // 循环写入视频和音频帧
  192. while (writing_v || writing_a)
  193. {
  194. // 如果还在写视频帧,且(不写音频帧或者视频帧的PTS小于等于音频帧的PTS)
  195. if (writing_v &&
  196. (!writing_a ||
  197. av_compare_ts(cur_pts_v, ifmt_ctx_v->streams[videoindex_v]->time_base, cur_pts_a,
  198. ifmt_ctx_a->streams[audioindex_a]->time_base) <= 0))
  199. {
  200. // 读取并写入视频帧
  201. value = rw_frame(ifmt_ctx_v, ofmt_ctx, videoindex_v,
  202. videoindex_out, &frame_index, &cur_pts_v);
  203. if (value == -2)
  204. {
  205. writing_v = 0;
  206. }
  207. else if (value < 0)
  208. {
  209. goto end;
  210. }
  211. }
  212. else
  213. { // 读取并写入音频帧
  214. value = rw_frame(ifmt_ctx_a, ofmt_ctx, audioindex_a,
  215. audioindex_out, &frame_index, &cur_pts_a);
  216. if (value == -2)
  217. {
  218. writing_a = 0;
  219. }
  220. else if (value < 0)
  221. {
  222. goto end;
  223. }
  224. }
  225. }
  226. // 写入文件尾
  227. av_write_trailer(ofmt_ctx);
  228. ret = 0;
  229. end:
  230. if (ifmt_ctx_v)
  231. avformat_close_input(&ifmt_ctx_v);
  232. if (ifmt_ctx_a)
  233. avformat_close_input(&ifmt_ctx_a);
  234. if (ofmt && !(ofmt->flags & AVFMT_NOFILE))
  235. avio_close(ofmt_ctx->pb);
  236. if (ofmt_ctx)
  237. avformat_free_context(ofmt_ctx);
  238. return ret;
  239. }

参考博客链接:FFMPEG库实现mp4/flv文件(H264+AAC)的封装与分离_ffmpeg mp4 flv-CSDN博客 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/752050
推荐阅读
相关标签
  

闽ICP备14008679号