FFmpeg学习(五)-- libswresample使用说明及函数介绍

libswresample Audio合成和重采样

libswresample库用来进行audio数据的合成和重采样操作。调用流程:

  1. 调用 swr_alloc 创建SwrContext结构体。
  2. 设置SwrContext参数,有两种方法: 调用av_opt_set_xx函数逐项设置参数;swr_alloc_set_opts2同时设置多个参数(如果传入参数为null,swr_alloc_set_opts2中会调用swr_alloc创建SwrContext结构体).
  3. 调用swr_init利用设置的参数初始化SwrContext结构体的内部参数。
  4. 调用 swr_convert 或 swr_convert_frame 转换audio数据。
  5. 数据转换完成后,调用 swr_free 释放资源。如果需要多次此结构体,可以调用 swr_close 清理当前上下文,然后重复步骤2.

如果基于AVFrame进行audio数据的合成和重采样操作。调用流程:

  1. 调用 swr_alloc 创建SwrContext结构体。
  2. 设置输入AVFrame中的channel_layout, sample_rate和format
  3. 调用swr_convert_frame进行数据转换。
  4. 数据转换完成后,调用 swr_free 释放资源。如果数据格式发生变化,可以swr_config_frame重新设置参数,然后重复步骤3.

函数

SwrContext结构体处理函数

  • struct SwrContext *swr_alloc(void)
    • 创建SwrContext结构体,在swr_init调用之前必须要设置转换参数。
  • int swr_init(struct SwrContext *s)
    • 设置完参数后,初始化上下文变量。
  • int swr_is_initialized(struct SwrContext *s)
    • 检测是否已经初始化,0为没有初始化。正数为已经初始化。
  • int swr_alloc_set_opts2(struct SwrContext **ps, const AVChannelLayout *out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate, const AVChannelLayout *in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate, int log_offset, void *log_ctx)
    • 使用AVChannelLayout直接创建SwrContext结构体,并设置输入、输出audio数据的参数。log_offset为转换时log level,log_ctx为log的上下文。
  • void swr_free(struct SwrContext **s)
    • 释放SwrContext结构体。
  • void swr_close(struct SwrContext *s)
    • 清理swr_init时设置的内部参数,没有清理用户设置的参数。调用此函数后,可以修改参数,然后重新调用swr_init。

转换函数

  • int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
    const uint8_t **in , int in_count)
    • 转换audio数据。当in 和 in_count 设置为0时,表示输入数据已结束,会将剩余的少量数据输出到out中。返回值为每个channel的采样数量。负数表示错误。
  • int64_t swr_next_pts(struct SwrContext *s, int64_t pts)
    • 获取输入的pts对应的输出pts的值,单位:1/(in_sample_rate * out_sample_rate) 。
    • swr_set_compensation 是swr_next_pts内部调用的函数,不建议在其他地方调用。

底层可选设置函数

  • int swr_set_channel_mapping(struct SwrContext *s, const int *channel_map)
    • 设置一个输入channel的映射数组,不需要输出的channel设置为-1.
  • int swr_build_matrix2(const AVChannelLayout *in_layout, const AVChannelLayout *out_layout, double center_mix_level, double surround_mix_level, double lfe_mix_level, double maxval, double rematrix_volume, double *matrix, ptrdiff_t stride, enum AVMatrixEncoding matrix_encoding, void *log_context)
    • 创建一个channel的合成矩阵。一般只是内部使用,也可用来创建自定义混合矩阵。
  • int swr_set_matrix(struct SwrContext *s, const double *matrix, int stride)
    • 将swr_build_matrix2创建的合成矩阵设置到SwrContext中。

采样处理函数

  • int swr_drop_output(struct SwrContext *s, int count)
    • 丢弃指定数量的输出数据,
  • int swr_inject_silence(struct SwrContext *s, int count)
    • 注入指定数量的静音输出数据,
    • 如果需要强制补偿,swr_next_pts 调用 swr_drop_output 或 swr_inject_silence 进行数据对齐。
  • int64_t swr_get_delay(struct SwrContext *s, int64_t base)
    • 获取下一个输入采样数据相对于下一个输出采样数据将经历的延迟。延时单位根据base的值进行计算,为 1/base
  • int swr_get_out_samples(struct SwrContext *s, int in_samples)
    • 根据输入的输入采样率in_samples计算输出采样率的上限,SwrContext的内部状态不同,即使in_samples的值相同,也有可能返回不同的值。

配置信息函数

  • unsigned swresample_version(void)
    • 返回swresample的版本
  • const char *swresample_configuration(void)
    • 返回编译时的配置信息
  • const char *swresample_license(void)
    • 返回swresample的授权信息

基于AVFrame的处理函数

  • int swr_convert_frame(SwrContext *swr,AVFrame *output, const AVFrame *input);

    • 转换输入AVFrame中的数据并将其写入输出AVFrame。输入和输出AVFrames必须有channel_layout, sample_rate和format。如果输出AVFrame没有分配nb_samples的数据指针,将使用av_frame_get_buffer()分配数据并设置字段。
    • 输出AVFrame可以为NULL或分配的样本比所需的少。在这样的情况下,将添加未写入输出的所有剩余数据到内部FIFO缓冲区,在下次调用该函数或swr_convert时返回
    • 如果转换采样率可能会有数据留在内部重采样延迟缓冲器。调用Swr_get_delay()可以获取剩余的数量。如果需要获取剩余数据,请调用此函数或将swr_convert的输入设置为NULL。
    • 如果SwrContext配置不匹配输出和输入AVFrame设置,不会转换数据,并会报错。
    • 如果SwrContext没有初始化,此函数会利用输入、输出AVFrame中的参数初始化SwrContext,并进行数据转换。
    • 此函数不会创建SwrContext,也不会检测swr的值,调用之前必须通过确保swr的值为合法值
  • int swr_config_frame(SwrContext *swr, const AVFrame *out, const AVFrame *in);

    • 使用输入的AVFrame重新配置SwrContext的用户参数。此函数调用后必须调用swr_init初始化SwrContext。

结构体

  • SwrContext定义在swresample_internel.h中,属于非公开结构体,因此不同版本的定义可能不同。
  • swr_alloc、swr_get_class及SwrContext结构体对应的AVClass在libswresample/options.c中定义,此类的option项较多,请自行参考此文件。
  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用 C++ 和 FFmpeg 创建 RTSP 服务器的示例代码: ```c++ #include <iostream> #include <thread> #include <chrono> #include <sstream> #include <cstring> #include <cstdio> #include <cstdlib> #include <ctime> extern "C" { #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> #include <libavutil/avutil.h> #include <libavutil/opt.h> #include <libavutil/time.h> #include <libavutil/imgutils.h> #include <libswscale/swscale.h> #include <libswresample/swresample.h> #include <libavutil/audio_fifo.h> #include <libavutil/audioconvert.h> } #define WIDTH 640 #define HEIGHT 480 #define FPS 25 static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt, FILE *outfile) { int ret; /* send the frame to the encoder */ ret = avcodec_send_frame(enc_ctx, frame); if (ret < 0) { std::cerr << "Error sending a frame for encoding" << std::endl; exit(1); } /* receive packet from encoder */ while (ret >= 0) { ret = avcodec_receive_packet(enc_ctx, pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return; else if (ret < 0) { std::cerr << "Error during encoding" << std::endl; exit(1); } /* write the compressed frame to the media file */ fwrite(pkt->data, 1, pkt->size, outfile); av_packet_unref(pkt); } } static void encode_thread(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt, FILE *outfile) { while (true) { encode(enc_ctx, frame, pkt, outfile); } } int main(int argc, char *argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " output_url" << std::endl; return 1; } const char *output_url = argv[1]; // Initialize FFmpeg av_register_all(); avcodec_register_all(); // Create format context AVFormatContext *format_ctx = nullptr; int ret = avformat_alloc_output_context2(&format_ctx, nullptr, "rtsp", output_url); if (ret < 0) { std::cerr << "Error creating output context" << std::endl; return 1; } // Create video stream AVStream *video_stream = avformat_new_stream(format_ctx, nullptr); if (video_stream == nullptr) { std::cerr << "Error creating video stream" << std::endl; return 1; } // Set codec parameters AVCodecParameters *codec_params = video_stream->codecpar; codec_params->codec_id = AV_CODEC_ID_H264; codec_params->codec_type = AVMEDIA_TYPE_VIDEO; codec_params->width = WIDTH; codec_params->height = HEIGHT; codec_params->format = AV_PIX_FMT_YUV420P; codec_params->bit_rate = 1000000; codec_params->fps_num = FPS; codec_params->fps_den = 1; // Find codec AVCodec *codec = avcodec_find_encoder(codec_params->codec_id); if (codec == nullptr) { std::cerr << "Error finding codec" << std::endl; return 1; } // Create codec context AVCodecContext *codec_ctx = avcodec_alloc_context3(codec); if (codec_ctx == nullptr) { std::cerr << "Error creating codec context" << std::endl; return 1; } // Set codec options av_opt_set(codec_ctx->priv_data, "preset", "ultrafast", 0); // Open codec ret = avcodec_open2(codec_ctx, codec, nullptr); if (ret < 0) { std::cerr << "Error opening codec" << std::endl; return 1; } // Copy codec parameters to codec context ret = avcodec_parameters_to_context(codec_ctx, codec_params); if (ret < 0) { std::cerr << "Error copying codec parameters" << std::endl; return 1; } // Allocate frame AVFrame *frame = av_frame_alloc(); if (frame == nullptr) { std::cerr << "Error allocating frame" << std::endl; return 1; } // Set frame options frame->format = codec_ctx->pix_fmt; frame->width = codec_ctx->width; frame->height = codec_ctx->height; // Allocate frame buffer ret = av_frame_get_buffer(frame, 0); if (ret < 0) { std::cerr << "Error allocating frame buffer" << std::endl; return 1; } // Open output file ret = avio_open(&format_ctx->pb, output_url, AVIO_FLAG_WRITE); if (ret < 0) { std::cerr << "Error opening output file" << std::endl; return 1; } // Write format header ret = avformat_write_header(format_ctx, nullptr); if (ret < 0) { std::cerr << "Error writing format header" << std::endl; return 1; } // Create packet AVPacket *pkt = av_packet_alloc(); if (pkt == nullptr) { std::cerr << "Error creating packet" << std::endl; return 1; } // Create encoding thread std::thread encode_thread(encode_thread, codec_ctx, frame, pkt, format_ctx->pb); // Generate video frames AVFrame *rgb_frame = av_frame_alloc(); if (rgb_frame == nullptr) { std::cerr << "Error allocating RGB frame" << std::endl; return 1; } uint8_t *buffer = reinterpret_cast<uint8_t *>(av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, WIDTH, HEIGHT, 1))); if (buffer == nullptr) { std::cerr << "Error allocating buffer" << std::endl; return 1; } av_image_fill_arrays(rgb_frame->data, rgb_frame->linesize, buffer, AV_PIX_FMT_RGB24, WIDTH, HEIGHT, 1); for (int i = 0; i < 1000; ++i) { // Generate random image for (int y = 0; y < HEIGHT; ++y) { for (int x = 0; x < WIDTH; ++x) { rgb_frame->data[0][y * rgb_frame->linesize[0] + x * 3] = rand() % 256; rgb_frame->data[0][y * rgb_frame->linesize[0] + x * 3 + 1] = rand() % 256; rgb_frame->data[0][y * rgb_frame->linesize[0] + x * 3 + 2] = rand() % 256; } } // Convert RGB to YUV SwsContext *sws_ctx = sws_getContext(WIDTH, HEIGHT, AV_PIX_FMT_RGB24, WIDTH, HEIGHT, AV_PIX_FMT_YUV420P, 0, nullptr, nullptr, nullptr); sws_scale(sws_ctx, rgb_frame->data, rgb_frame->linesize, 0, HEIGHT, frame->data, frame->linesize); sws_freeContext(sws_ctx); // Set frame timestamp frame->pts = i * (codec_ctx->time_base.den) / (codec_ctx->time_base.num * FPS); // Encode frame encode(codec_ctx, frame, pkt, format_ctx->pb); std::this_thread::sleep_for(std::chrono::milliseconds(1000 / FPS)); } // Flush encoder encode(codec_ctx, nullptr, pkt, format_ctx->pb); // Write format trailer av_write_trailer(format_ctx); // Join encoding thread encode_thread.join(); // Free resources av_frame_free(&frame); av_frame_free(&rgb_frame); av_packet_free(&pkt); avcodec_free_context(&codec_ctx); avformat_free_context(format_ctx); return 0; } ``` 在上面的示例中,我们使用FFmpeg 库来生成随机视频帧并将其编码为 H.264 格式,并使用 RTSP 协议将其流式传输到给定的 URL。首先,我们使用 `avformat_alloc_output_context2` 函数创建一个输出上下文,并将其格式设置为 RTSP。然后,我们创建一个视频流并设置其编解码器参数。接下来,我们使用 `avcodec_find_encoder` 函数查找 H.264 编解码器,并使用 `avcodec_alloc_context3` 函数创建编解码器上下文。然后,我们设置编解码器的选项并打开它。接下来,我们将编解码器参数复制到编解码器上下文中,并使用 `av_frame_alloc` 函数分配一个帧。然后,我们设置帧的选项并使用 `av_frame_get_buffer` 函数分配其缓冲区。接下来,我们使用 `avio_open` 函数打开输出文件,并使用 `avformat_write_header` 函数写入格式头。然后,我们创建一个数据包并启动一个编码线程。接下来,我们使用 `av_frame_alloc` 函数分配一个 RGB 帧,并使用 `av_malloc` 函数分配一个缓冲区。然后,我们生成随机 RGB 像素,并使用 `sws_getContext` 函数创建一个缩放上下文。然后,我们使用 `sws_scale` 函数将 RGB 帧转换为 YUV 帧。接下来,我们在帧上设置时间戳,并使用 `encode` 函数将其编码为 H.264 格式并写入输出文件。最后,我们使用 `av_write_trailer` 函数写入格式尾,并等待编码线程完成。在退出之前,我们释放所有使用的资源,并返回 0。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值