前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++】FFmpeg:音视频库介绍与使用示例

【C++】FFmpeg:音视频库介绍与使用示例

作者头像
DevFrank
发布2024-07-24 15:14:26
2360
发布2024-07-24 15:14:26
举报
文章被收录于专栏:C++开发学习交流

😏1. FFmpeg音视频库介绍

ffmpeg官网:http://www.ffmpeg.org/

FFmpeg是一款开源的音视频库,提供了处理音视频文件、转码、解码、编码、播放等功能。它是一个完整的跨平台解决方案,支持多种音视频格式,并提供多种API和工具来处理音视频数据。

FFmpeg框架的基本组成包括:AVFormet封装模块、AVCodec编解码模块、AVFilter滤镜模块、AVDeviceAVUtil等模块库。

下面简单介绍一些FFmpeg库的基础知识:

1.编码器与解码器 FFmpeg提供了多种编码器和解码器来处理不同的音视频格式,例如H.264、MPEG-4、AAC等。可以使用avcodec_find_encoder和avcodec_find_decoder函数查找可用的编码器和解码器,并使用avcodec_open2函数打开需要使用的编码器或解码器。

2.格式封装与解封装 FFmpeg可以处理多种音视频文件格式,例如MP4、AVI、WAV等。它使用封装格式来将音视频流打包到一个容器中。常见的封装格式有MP4、AVI、FLV、MKV等。可以使用avformat_open_input函数打开音视频文件,并使用av_read_frame函数读取文件中的音视频数据。

3.帧与数据包 在FFmpeg中,音视频数据被组织成帧和数据包。音频数据通常被组织成PCM数据,每个样本对应一帧数据;而视频数据则被组织成一系列关键帧和非关键帧。

4.协议 FFmpeg可以处理不同的音视频流传输协议,例如RTSP、RTMP、HTTP等。可以使用avformat_open_input函数打开网络音视频流,并使用av_read_frame函数读取数据包。

😊2. 环境配置

下面进行环境配置:

代码语言:javascript
复制
# apt安装
sudo apt install ffmpeg
ffmpeg -version
# 也可选择源码安装

# windows可从官网下载

编译示例:

代码语言:javascript
复制
g++ -o main main.cpp -lavformat -lavcodec -lavutil

CMakeLists.txt示例:

代码语言:javascript
复制
cmake_minimum_required(VERSION 3.19)
project(ffmpeg_demo)

set(CMAKE_CXX_STANDARD 14)

set(FFMPEG_ROOT "D:/develop/ffmpeg611_mingw/mingw64")
include_directories(${FFMPEG_ROOT}/include)
link_directories(${FFMPEG_ROOT}/lib)
set(FFMPEG_LIBS avcodec avformat avutil avdevice avfilter postproc swresample swscale)
#link_directories(${FFMPEG_ROOT}/bin)
#set(FFMPEG_LIBS avcodec-60 avformat-60 avutil-58 swscale-7)

add_executable(ffmpeg_demo main.cpp)
target_link_libraries(ffmpeg_demo ${FFMPEG_LIBS})

😆3. 应用实例

视频

采集摄像头:

代码语言:javascript
复制
ffmpeg -f video4linux2 -s  640x480 -pixel_format yuyv422  -i /dev/video0  out.mp4 -loglevel debug
ffmpeg -f x11grab -framerate 25 -video_size 1280*720 -i :0.0 out.mp4	# 采集x11桌面

视频格式转换:

代码语言:javascript
复制
ffmpeg -i input.avi output.mp4

帧率转换:

代码语言:javascript
复制
ffmpeg -i input.avi -r 24 output.mp4

多路视频拼接命令行:

代码语言:javascript
复制
# 两路视频横向拼接
ffmpeg -i out1.mp4 -i out2.mp4 -filter_complex "[0:v]pad=iw*2:ih*1[a];[a][1:v]overlay=w" out.mp4
# 两路视频纵向拼接
ffmpeg -i out1.mp4 -i out2.mp4 -filter_complex "[0:v]pad=iw:ih*2[a];[a][1:v]overlay=0:h" out.mp4
# 三路视频横向拼接
ffmpeg -i out1.mp4 -i out2.mp4 -i out3.mp4 -filter_complex "[0:v]pad=iw*3:ih*1[a];[a][1:v]overlay=w[b];[b][2:v]overlay=2.0*w" out.mp4 
# 三路视频纵向拼接
ffmpeg -i out1.mp4 -i out2.mp4 -i out3.mp4 -filter_complex "[0:v]pad=iw:ih*3[a];[a][1:v]overlay=0:h[b];[b][2:v]overlay=0:2.0*h" out.mp4
# 四路视频2X2排列
ffmpeg -i out1.mp4 -i out2.mp4 -i out3.mp4 -i out4.mp4 -filter_complex "[0:v]pad=iw*2:ih*2[a];[a][1:v]overlay=w[b];[b][2:v]overlay=0:h[c];[c][3:v]overlay=w:h" out.mp4

视频推流:

代码语言:javascript
复制
ffmpeg -r 100 -i /dev/video0 -f flv udp://192.168.111.121:9666
ffplay -max_delay 10 udp://192.168.111.121:9666
# 原始音视频推流
ffmpeg  -f alsa -ar 11025 -ac 2 -i hw:0 -i /dev/video0 -f flv udp://192.168.111.121:9666
音频

音频格式转换:

代码语言:javascript
复制
ffmpeg64 -i null.ape -ar 44100 -ac 2 -ab 16k -vol 50 -f mp3 null.mp3
注:-i代表输入参数
          -acodec aac(音频编码用AAC) 
          -ar 设置音频采样频率
          -ac  设置音频通道数
          -ab 设定声音比特率
          -vol <百分比> 设定音量

# 或创建list包含所需音频
ffmpeg -safe 0 -f concat -i list.txt -c copy out.wav

音频拼接:

代码语言:javascript
复制
# 新建列表,包含所需音频
touch list.txt
file '1.mp3'
file '2.mp3'
ffmpeg -f concat -i list.txt -c copy out.mp3

音频录制:

代码语言:javascript
复制
ffmpeg -y -f alsa -i hw:0 -t 00:00:03 -ar 8000 -ac 1 out.mp3

音频推流:

代码语言:javascript
复制
ffmpeg  -f alsa -i hw:0 -ar 11025 -ac 1 -f flv udp://192.168.111.121:9666

其他参考:

代码语言:javascript
复制
视频播放器的实现:http://t.csdn.cn/N6Vuo

音视频播放器实现:http://t.csdn.cn/zJuXn

通过opencv读取摄像头:http://t.csdn.cn/mGCog

推送摄像头 rtsp 流:http://t.csdn.cn/YrLMm
C++示例
代码语言:javascript
复制
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
}
#include <map>

int main(int argc, char *argv[])
{
    if (argc < 3) {
        printf("remux <input> <output>\n");
        return -1;
    }

    const char *in_filename  = argv[1];
    const char *out_filename = argv[2];

    // input
    AVFormatContext *decoder_fmt_ctx = nullptr;
    if (avformat_open_input(&decoder_fmt_ctx, in_filename, nullptr, nullptr) < 0) {
        fprintf(stderr, "failed to open the %s file.\n", in_filename);
        return -1;
    }

    if (avformat_find_stream_info(decoder_fmt_ctx, nullptr) < 0) {
        fprintf(stderr, "not find stream info.\n");
        return -1;
    }

    av_dump_format(decoder_fmt_ctx, 0, in_filename, 0);

    // output
    AVFormatContext *encoder_fmt_ctx = nullptr;
    if (avformat_alloc_output_context2(&encoder_fmt_ctx, nullptr, nullptr, out_filename) < 0) {
        fprintf(stderr, "failed to alloc output-context memory.\n");
        return -1;
    }

    // map streams
    //
    // a media file always contains several streams, like video and audio streams.
    std::map<unsigned int, int> stream_mapping{};
    int stream_idx = 0;
    for (unsigned int i = 0; i < decoder_fmt_ctx->nb_streams; i++) {
        AVCodecParameters *decode_params = decoder_fmt_ctx->streams[i]->codecpar;
        if (decode_params->codec_type != AVMEDIA_TYPE_VIDEO &&
            decode_params->codec_type != AVMEDIA_TYPE_AUDIO &&
            decode_params->codec_type != AVMEDIA_TYPE_SUBTITLE) {
            stream_mapping[i] = -1;
            continue;
        }

        stream_mapping[i] = stream_idx++;

        AVStream *encode_stream = avformat_new_stream(encoder_fmt_ctx, nullptr);
        if (encode_stream == nullptr) {
            fprintf(stderr, "failed to create a stream for output.\n");
            return -1;
        }

        if (avcodec_parameters_copy(encode_stream->codecpar, decode_params) < 0) {
            fprintf(stderr, "failed to copy parameters.\n");
            return -1;
        }
    }

    // open the output file
    if (!(encoder_fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
        if (avio_open(&encoder_fmt_ctx->pb, out_filename, AVIO_FLAG_WRITE) < 0) {
            printf("can not open the output file : %s.\n", out_filename);
            return -1;
        }
    }

    // header
    if (avformat_write_header(encoder_fmt_ctx, nullptr) < 0) {
        fprintf(stderr, "can not write header to the output file.\n");
        return -1;
    }

    av_dump_format(encoder_fmt_ctx, 0, out_filename, 1);

    // copy streams
    AVPacket *packet     = av_packet_alloc();
    int64_t frame_number = 0;
    while (av_read_frame(decoder_fmt_ctx, packet) >= 0) {
        if (stream_mapping[packet->stream_index] < 0) {
            // the packet is reference-counted.
            // the ffmpeg is a C library, we need unreference the buffer manually
            av_packet_unref(packet);
            continue;
        }

        // convert the timestamps from the time base of input stream to the time base of output stream
        //
        // In simple terms, a time base is a rescaling of the unit second, like 1/100s, 1001/24000s, and
        // each stream has its own time base. The timestamps of the packets are based on the time base of
        // their streams, timestamps of the same value have different meanings depending on the time unit.
        //
        // It is not necessary in this example since the time base of input and output streams is the same.
        av_packet_rescale_ts(packet, decoder_fmt_ctx->streams[packet->stream_index]->time_base,
                             encoder_fmt_ctx->streams[stream_mapping[packet->stream_index]]->time_base);

        printf(
                " -- %s] packet = %6ld, pts: %6ld, dts: %6ld, duration: %5ld\n",
                av_get_media_type_string(encoder_fmt_ctx->streams[packet->stream_index]->codecpar->codec_type),
                frame_number++, packet->pts, packet->dts, packet->duration);

        packet->stream_index = stream_mapping[packet->stream_index];
        // write the packet to the output file
        // this function will take the owership of the packet, and packet will be blank
        if (av_interleaved_write_frame(encoder_fmt_ctx, packet) != 0) {
            fprintf(stderr, "failed to write frame.\n");
            return -1;
        }
    }

    // write the trailer to the output file and close it
    av_write_trailer(encoder_fmt_ctx);
    if (encoder_fmt_ctx && !(encoder_fmt_ctx->oformat->flags & AVFMT_NOFILE))
        avio_closep(&encoder_fmt_ctx->pb);

    // release the resources
    // let's not think about the abnormal exit for simplicity
    av_packet_free(&packet);
    avformat_close_input(&decoder_fmt_ctx);
    avformat_free_context(encoder_fmt_ctx);

    return 0;
}

😆4. 视频播放器示例

视频播放器项目Github地址:https://github.com/pockethook/player.git

视频播放器项目主要使用FFmpeg做视频编解码,用SDL做渲染。

编译运行:

代码语言:javascript
复制
make
./player xxx.mp4
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-02-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 😏1. FFmpeg音视频库介绍
  • 😊2. 环境配置
  • 😆3. 应用实例
    • 视频
      • 音频
        • C++示例
        • 😆4. 视频播放器示例
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档