FFMPEG4.1源码分析之 内存管理APIs av_strdup() && av_strndup()

1 av_strdup()


av_strdup() 声明:

  • 所属库:libavutil(lavu),lavu是ffmpeg中的功能库,本函数属于内存管理功能
  • 头文件:libavutil/mem.h
  • 声明:拷贝一份字符串。注意,该函数使用了av_malloc_attrib宏进行了属性修饰,该属性的作用见另外一篇文章
    FFMPEG4.1源码分析之 内存管理APIs av_malloc() && av_mallocz()
    /**
     * Duplicate a string.
     *
     * //入参s指向需要拷贝的字符串
     * @param s String to be duplicated 
     *
     * //返回一个指向新分配的内存,该内存拷贝了一份字符串,如果无法分配出空间,则返回NULL 
     * @return Pointer to a newly-allocated string containing a  
     *         copy of `s` or `NULL` if the string cannot be allocated
     * @see av_strndup()
     */
    char *av_strdup(const char *s) av_malloc_attrib;

av_strdup() 源码:

  • 源文件:mem.c
    char *av_strdup(const char *s)
    {
        char *ptr = NULL;
        if (s) {
            // 求取存储字符串的长度,注意c串后的需要"\0",因此需要长度+1
            size_t len = strlen(s) + 1; 
            // av_realloc()分配空间,为什么不是av_malloc()?
            ptr = av_realloc(NULL, len);
            // 如果空间分配成功,则memcpy进行内存拷贝
            if (ptr)
                memcpy(ptr, s, len);
        }
        return ptr;
    }
  1.  上述代码相当简单易懂,见代码注释即可

  2. 问题再于给ptr分配空间的时候为什么不使用av_malloc(),而使用av_realloc()?这里涉及的c库的内存分配函数malloc()和realloc()函数的区别。malloc()是重新分配一块地址,而realloc()是在入参ptr指向的地址空间处进行内存的扩大或者缩小。对于本函数的功能来说,使用av_realloc()传入ptr为NULL,因此与av_malloc()个人感觉是没有太大区别。或许是对c库的内存管理函数还理解不太深刻的缘故。因此,在后续的ffmpeg学习中会专门开个内存管理的专门章节,进一步详细的研究ffmpeg中的内存管理以及对应的底层c库,以及更底层的系统调用sbrk(),brk()等等。

2 av_strndup()


av_strndup() 声明:

  • 所属库:libavutil(lavu),lavu是ffmpeg中的功能库,本函数属于内存管理功能
  • 头文件:libavutil/mem.h
  • 声明:拷贝字串。
               重点:入参len指代了结果串的长度,那么需要考虑len与源串长度对比情况,大于,等于,小于的各种情况下都如何处理。
    /**
     * Duplicate a substring of a string.
     *
     * @param s   String to be duplicated
     * @param len Maximum length of the resulting string (not counting the
     *            terminating byte)
     * @return Pointer to a newly-allocated string containing a
     *         substring of `s` or `NULL` if the string cannot be allocated
     */
    char *av_strndup(const char *s, size_t len) av_malloc_attrib;

av_strndup() 源码:

  • 源文件:libavutil/mem.c
    char *av_strndup(const char *s, size_t len)
    {
        char *ret = NULL, *end;
    
        if (!s)
            return NULL;
    
        end = memchr(s, 0, len);  // 计算'\0'的位置
        if (end)                  // 重新计算可拷贝字符串的长度
            len = end - s;
    
        ret = av_realloc(NULL, len + 1); // 分配内存
        if (!ret)
            return NULL;
    
        memcpy(ret, s, len);  // 拷贝内存
        ret[len] = 0;         // 最后一个字节赋值'\0'
        return ret;           // 返回新串地址
    }
  1.  检查参数的有效性:如果源串s是空串,则目的串直接为NULL
  2.  匹配源串长度与len的大小:memchr()函数提供这样的功能:“C 库函数 void *memchr(const void *str, int c, size_t n) 在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c(一个无符号字符)的位置”,具体该函数的用法,这儿提供一个很不错的手册类查询网站,提供多种语言的api查询:https://www.runoob.com/。此处,查询0的位置,其实就是字符"\0",C类字符串的结尾。
           1)如果返回值不为空,说明len的长度比源串的长度要长,需要重新计算可拷贝的字符串len的长度:len = end - s
           2)如果返回值为空,说明len比源串的长度要短,拷贝len个字符是安全的,因此不需要重新计算len
  3.  分配内存,拷贝串,返回新串地址。
以下是一个简单的示例代码,可以将 OpenCV 的 `cv::Mat` 对象转换为 FFmpeg 的 `AVFrame`,并将其编码为 YUV 420P 格式。 ```c++ #include <opencv2/opencv.hpp> #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> #include <libswscale/swscale.h> int main() { // 初始化 FFmpeg av_register_all(); // 创建格式上下文 AVFormatContext* format_ctx = avformat_alloc_context(); if (!format_ctx) { std::cerr << "Failed to allocate format context" << std::endl; return -1; } // 设置输出格式 AVOutputFormat* output_fmt = av_guess_format("mp4", nullptr, nullptr); if (!output_fmt) { std::cerr << "Failed to guess output format" << std::endl; return -1; } format_ctx->oformat = output_fmt; // 打开输出文件 AVIOContext* io_ctx = nullptr; if (avio_open(&io_ctx, "output.mp4", AVIO_FLAG_WRITE) < 0) { std::cerr << "Failed to open output file" << std::endl; return -1; } format_ctx->pb = io_ctx; // 创建视频流 AVStream* video_stream = avformat_new_stream(format_ctx, nullptr); if (!video_stream) { std::cerr << "Failed to create video stream" << std::endl; return -1; } // 设置编码器参数 AVCodecParameters* codec_params = video_stream->codecpar; codec_params->codec_type = AVMEDIA_TYPE_VIDEO; codec_params->codec_id = output_fmt->video_codec; codec_params->width = 640; codec_params->height = 480; codec_params->format = AV_PIX_FMT_YUV420P; // 查找编码器 AVCodec* codec = avcodec_find_encoder(output_fmt->video_codec); if (!codec) { std::cerr << "Failed to find encoder" << std::endl; return -1; } // 创建编码器上下文 AVCodecContext* codec_ctx = avcodec_alloc_context3(codec); if (!codec_ctx) { std::cerr << "Failed to allocate codec context" << std::endl; return -1; } codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO; codec_ctx->width = codec_params->width; codec_ctx->height = codec_params->height; codec_ctx->pix_fmt = codec_params->format; codec_ctx->time_base = {1, 25}; // 打开编码器 if (avcodec_open2(codec_ctx, codec, nullptr) < 0) { std::cerr << "Failed to open codec" << std::endl; return -1; } // 创建帧 AVFrame* frame = av_frame_alloc(); if (!frame) { std::cerr << "Failed to allocate frame" << std::endl; return -1; } frame->format = codec_ctx->pix_fmt; frame->width = codec_ctx->width; frame->height = codec_ctx->height; // 分配帧数据空间 if (av_frame_get_buffer(frame, 0) < 0) { std::cerr << "Failed to allocate frame data" << std::endl; return -1; } // 创建格式转换器 SwsContext* sws_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, AV_PIX_FMT_BGR24, codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr); if (!sws_ctx) { std::cerr << "Failed to create format converter" << std::endl; return -1; } // 读取输入帧 cv::Mat input_frame = cv::imread("input.jpg"); if (input_frame.empty()) { std::cerr << "Failed to read input frame" << std::endl; return -1; } // 转换输入帧 uint8_t* input_data[AV_NUM_DATA_POINTERS] = {0}; input_data[0] = input_frame.data; int input_linesize[AV_NUM_DATA_POINTERS] = {0}; input_linesize[0] = input_frame.step; sws_scale(sws_ctx, input_data, input_linesize, 0, codec_ctx->height, frame->data, frame->linesize); // 编码帧 AVPacket pkt; av_init_packet(&pkt); pkt.data = nullptr; pkt.size = 0; int got_packet = 0; if (avcodec_encode_video2(codec_ctx, &pkt, frame, &got_packet) < 0) { std::cerr << "Failed to encode frame" << std::endl; return -1; } // 写入输出文件 if (got_packet) { av_packet_rescale_ts(&pkt, codec_ctx->time_base, video_stream->time_base); pkt.stream_index = video_stream->index; if (av_interleaved_write_frame(format_ctx, &pkt) < 0) { std::cerr << "Failed to write packet" << std::endl; return -1; } av_packet_unref(&pkt); } // 写入文件尾 av_write_trailer(format_ctx); // 释放资源 avcodec_free_context(&codec_ctx); av_frame_free(&frame); avio_closep(&format_ctx->pb); avformat_free_context(format_ctx); sws_freeContext(sws_ctx); return 0; } ``` 需要注意的是,上述代码中的 `AV_PIX_FMT_BGR24` 表示输入图像的像素格式,如果您的输入图像格式不是 BGR24,需要相应地修改代码。另外,上述代码中的像素格式硬编码为 YUV420P,如果您需要使用其他像素格式,也需要相应地修改代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值