• FFmpeg 音视频转封装(MP4与FLV互转,流数据转FLV、MP4)


    1.简介

    音视频文件转封装操作就是把一种格式转换为另外一种格式,例如从 flv 转到 MP4,或者把流地址数据转换为MP4。

    本文主要讲解 流地址数据 转为 flv文件。

    2.流程

    2.1 在使用FFmpeg API之前,需要先注册API,然后才能使用API。当然,新版本的库不需要再调用下面的方法。

    av_register_all()

    2.2构建输入AVFormatContext

    声明输入的封装结构体,通过输入文件或者流地址作为封装结构的句柄。韩国电视台的流地址rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp。

    1. AVFormatContext* inputFmtCtx = nullptr;
    2. const char* inputUrl = "rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp";
    3. ///打开输入的流,获取数据 begin
    4. int ret = avformat_open_input(&inputFmtCtx, inputUrl, NULL, NULL);

    2.3 查找音视频流信息,通过下面的接口与AVFormatContext中建立输入文件对应的流信息。

    1. //查找;
    2. if (avformat_find_stream_info(inputFmtCtx, NULL) < 0)
    3. {
    4. printf("Couldn't find stream information.\n");
    5. return -1;
    6. }

    2.4构建输出AVFormatContext

    1. //输出的文件
    2. AVOutputFormat *ofmt = NULL;
    3. AVFormatContext *ofmt_ctx = NULL;
    4. const char* out_filename = "out.flv";
    5. avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
    6. if (!ofmt_ctx)
    7. {
    8. return -1;
    9. }

    2.5申请输出的stream信息。

    1. AVStream* out_stream = NULL;
    2. //创建一个新的流
    3. out_stream = avformat_new_stream(ofmt_ctx, NULL);
    4. if (!out_stream)
    5. {
    6. return -1;
    7. }

    2.6信息的复制,输出的stream信息建立完成之后,需要从输入的stream中将信息复制到输出的stream中。

    1. //输入的流 视频、音频、字幕等
    2. AVStream* in_stream = ifmt_ctx->streams[i];
    3. AVCodecParameters* in_codecpar = in_stream->codecpar;
    4. //复制输入的流信息到输出流中
    5. ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
    6. if (ret < 0)
    7. {
    8. return -1;
    9. }

    2.7然后打开文件

    1. //打开输出文件
    2. ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
    3. if (ret < 0)
    4. {
    5. return -1;
    6. }

    2.8写文件头

    1. //写入头
    2. ret = avformat_write_header(ofmt_ctx, NULL);
    3. if (ret < 0)
    4. {
    5. return -1;
    6. }

    2.9数据包读取和写入

    输入和输出均已打开,接下来可以从输入格式中读取数据包,然后将数据包写入到输出文件中,当然,随着输入的封装格式与输出的封装格式差别化,时间戳也需要进行计算改变。

    1. AVPacket pkt;
    2. while (1)
    3. {
    4. AVStream* in_stream = NULL;
    5. AVStream* out_stream = NULL;
    6. //从输入流中读取数据到pkt中
    7. ret = av_read_frame(ifmt_ctx, &pkt);
    8. if (ret < 0)
    9. break;
    10. in_stream = ifmt_ctx->streams[pkt.stream_index];
    11. if (pkt.stream_index >= stream_mapping_size || stream_mapping[pkt.stream_index] < 0)
    12. {
    13. av_packet_unref(&pkt);
    14. continue;
    15. }
    16. pkt.stream_index = stream_mapping[pkt.stream_index];
    17. out_stream = ofmt_ctx->streams[pkt.stream_index];
    18. /* copy packet */
    19. 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));
    20. 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));
    21. pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
    22. pkt.pos = -1;
    23. ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
    24. if (ret < 0)
    25. {
    26. fprintf(stderr, "Error muxing packet\n");
    27. break;
    28. }
    29. av_packet_unref(&pkt);
    30. }

    2.10写文件尾

    1. //写文件尾
    2. av_write_trailer(ofmt_ctx);

    2.11收尾,关闭输入输出,释放资源。

    1. //关闭
    2. avformat_close_input(&ifmt_ctx);
    3. if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
    4. avio_closep(&ofmt_ctx->pb);
    5. avformat_free_context(ofmt_ctx);
    6. av_freep(&stream_mapping);
    7. if (ret < 0 && ret != AVERROR_EOF)
    8. {
    9. return -1;
    10. }

    3.源码

    1. #include "pch.h"
    2. #include
    3. extern "C"
    4. {
    5. #include "libavformat/avformat.h"
    6. #include "libavutil/dict.h"
    7. #include "libavutil/opt.h"
    8. #include "libavutil/timestamp.h"
    9. #include "libswscale/swscale.h"
    10. #include "libswresample/swresample.h"
    11. };
    12. int main()
    13. {
    14. //av_register_all();
    15. avformat_network_init();
    16. AVFormatContext* ifmt_ctx = NULL;
    17. const char* inputUrl = "rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp";
    18. ///打开输入的流
    19. int ret = avformat_open_input(&ifmt_ctx, inputUrl, NULL, NULL);
    20. if (ret != 0)
    21. {
    22. printf("Couldn't open input stream.\n");
    23. return -1;
    24. }
    25. //查找流信息
    26. if (avformat_find_stream_info(ifmt_ctx, NULL) < 0)
    27. {
    28. printf("Couldn't find stream information.\n");
    29. return -1;
    30. }
    31. //输出的文件
    32. AVOutputFormat *ofmt = NULL;
    33. AVFormatContext *ofmt_ctx = NULL;
    34. const char* out_filename = "out.flv";
    35. avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
    36. if (!ofmt_ctx)
    37. {
    38. return -1;
    39. }
    40. int stream_mapping_size = ifmt_ctx->nb_streams;
    41. //为数组分配内存
    42. int* stream_mapping = (int *)av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));
    43. if (!stream_mapping)
    44. {
    45. return -1;
    46. }
    47. int stream_index = 0;
    48. ofmt = ofmt_ctx->oformat;
    49. for (int i = 0; i < ifmt_ctx->nb_streams; i++)
    50. {
    51. //输出的流
    52. AVStream* out_stream = NULL;
    53. //输入的流 视频、音频、字幕等
    54. AVStream* in_stream = ifmt_ctx->streams[i];
    55. AVCodecParameters* in_codecpar = in_stream->codecpar;
    56. if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO && in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO && in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
    57. {
    58. stream_mapping[i] = -1;
    59. continue;
    60. }
    61. stream_mapping[i] = stream_index++;
    62. //创建一个新的流
    63. out_stream = avformat_new_stream(ofmt_ctx, NULL);
    64. if (!out_stream)
    65. {
    66. return -1;
    67. }
    68. //复制输入的流信息到输出流中
    69. ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
    70. if (ret < 0)
    71. {
    72. return -1;
    73. }
    74. out_stream->codecpar->codec_tag = 0;
    75. }
    76. if (!(ofmt->flags & AVFMT_NOFILE))
    77. {
    78. //打开输出文件
    79. ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
    80. if (ret < 0)
    81. {
    82. return -1;
    83. }
    84. }
    85. //写入头
    86. ret = avformat_write_header(ofmt_ctx, NULL);
    87. if (ret < 0)
    88. {
    89. return -1;
    90. }
    91. AVPacket pkt;
    92. while (1)
    93. {
    94. AVStream* in_stream = NULL;
    95. AVStream* out_stream = NULL;
    96. //从输入流中读取数据到pkt中
    97. ret = av_read_frame(ifmt_ctx, &pkt);
    98. if (ret < 0)
    99. break;
    100. in_stream = ifmt_ctx->streams[pkt.stream_index];
    101. if (pkt.stream_index >= stream_mapping_size || stream_mapping[pkt.stream_index] < 0)
    102. {
    103. av_packet_unref(&pkt);
    104. continue;
    105. }
    106. pkt.stream_index = stream_mapping[pkt.stream_index];
    107. out_stream = ofmt_ctx->streams[pkt.stream_index];
    108. /* copy packet */
    109. 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));
    110. 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));
    111. pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
    112. pkt.pos = -1;
    113. ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
    114. if (ret < 0)
    115. {
    116. fprintf(stderr, "Error muxing packet\n");
    117. break;
    118. }
    119. av_packet_unref(&pkt);
    120. }
    121. //写文件尾
    122. av_write_trailer(ofmt_ctx);
    123. //关闭
    124. avformat_close_input(&ifmt_ctx);
    125. if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
    126. avio_closep(&ofmt_ctx->pb);
    127. avformat_free_context(ofmt_ctx);
    128. av_freep(&stream_mapping);
    129. if (ret < 0 && ret != AVERROR_EOF)
    130. {
    131. return -1;
    132. }
    133. return 0;
    134. }

  • 相关阅读:
    Stable Diffusion中的embedding
    Cadence Allegro PCB设计88问解析(十七) 之 Allegro中焊盘的全连接和花焊盘
    Sulfo-CY3 azide水溶性荧光探针 星戈瑞
    【OpenCV】- 多边形将轮廓包围
    postgis 地理化函数
    质量小议15 -- 催
    《向量数据库指南》——Milvus Cloud 版本迭代的依据的是用户的反馈和对市场趋势的判断
    CompletableFuture异步编排(两任务组合——两个任务必须都完成才触发另一个任务 )
    网络安全笔记-WebShell与文件上传
    【LeetCode】Day132-单词搜索
  • 原文地址:https://blog.csdn.net/wzz953200463/article/details/125793912