ffmpeg源码简析(三)av_malloc(),AVIOContext,AVFrame,avio_open2()等

内存操作的常见函数位于libavutil\mem.c

av_malloc()

av_malloc()是FFmpeg中最常见的内存分配函数。

av_malloc()的代码可以简化成如下形式。

void *av_malloc(size_t size)  
{  
    void *ptr = NULL;  
    /* let's disallow possibly ambiguous cases */  
    if (size > (max_alloc_size - 32))  
        return NULL;  
    ptr = malloc(size);  
    if(!ptr && !size) {  
        size = 1;  
        ptr= av_malloc(1);  
    }  
    return ptr;  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

可以看出,此时的av_malloc()就是简单的封装了系统函数malloc(),并做了一些错误检查工作。 
关于size_t 
size _t 这个类型在FFmpeg中多次出现,简单解释一下其作用。size _t是为了增强程序的可移植性而定义的。不同系统上,定义size_t可能不一样。它实际上就是unsigned int。

av_realloc()

av_realloc()用于对申请的内存的大小进行调整。

    void *av_realloc(void *ptr, size_t size)  
    {  
    #if CONFIG_MEMALIGN_HACK  
        int diff;  
    #endif  


        /* let's disallow possibly ambiguous cases */  
        if (size > (max_alloc_size - 32))  
            return NULL;  


    #if CONFIG_MEMALIGN_HACK  
        //FIXME this isn't aligned correctly, though it probably isn't needed  
        if (!ptr)  
            return av_malloc(size);  
        diff = ((char *)ptr)[-1];  
        av_assert0(diff>0 && diff<=ALIGN);  
        ptr = realloc((char *)ptr - diff, size + diff);  
        if (ptr)  
            ptr = (char *)ptr + diff;  
        return ptr;  
    #elif HAVE_ALIGNED_MALLOC  
        return _aligned_realloc(ptr, size + !size, ALIGN);  
    #else  
        return realloc(ptr, size + !size);  
    #endif  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

默认情况下(CONFIG_MEMALIGN_HACK这些宏使用默认值0)的代码:

void *av_realloc(void *ptr, size_t size)  
{  
    /* let's disallow possibly ambiguous cases */  
    if (size > (max_alloc_size - 32))  
        return NULL;  
    return realloc(ptr, size + !size);  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

可以看出av_realloc()简单封装了系统的realloc()函数。

av_mallocz()

av_mallocz()可以理解为av_malloc()+zeromemory。代码如下。

    void *av_mallocz(size_t size)  
    {  
        void *ptr = av_malloc(size);  
        if (ptr)  
            memset(ptr, 0, size);  
        return ptr;  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

av_mallocz()中调用了av_malloc()之后,又调用memset()将分配的内存设置为0。

av_calloc()

av_calloc()则是简单封装了av_mallocz(),定义如下所示。

    void *av_calloc(size_t nmemb, size_t size)  
    {  
        if (size <= 0 || nmemb >= INT_MAX / size)  
            return NULL;  
        return av_mallocz(nmemb * size);  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

它调用av_mallocz()分配了nmemb*size个字节的内存。

av_free()

av_free()用于释放申请的内存。它的定义如下。

    void av_free(void *ptr)  
    {  
    #if CONFIG_MEMALIGN_HACK  
        if (ptr) {  
            int v= ((char *)ptr)[-1];  
            av_assert0(v>0 && v<=ALIGN);  
            free((char *)ptr - v);  
        }  
    #elif HAVE_ALIGNED_MALLOC  
        _aligned_free(ptr);  
    #else  
        free(ptr);  
    #endif  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

默认情况下(CONFIG_MEMALIGN_HACK这些宏使用默认值0)的代码:

    void av_free(void *ptr)  
    {  
        free(ptr);  
    } 
  • 1
  • 2
  • 3
  • 4

可以看出av_free()简单的封装了free()。

av_freep()

av_freep()简单封装了av_free()。并且在释放内存之后将目标指针设置为NULL。

    void av_freep(void *arg)  
    {  
        void **ptr = (void **)arg;  
        av_free(*ptr);  
        *ptr = NULL;  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

FFmpeg常见结构体的初始化和销毁函数

AVFormatContext:统领全局的基本结构体。主要用于处理封装格式(FLV/MKV/RMVB等)。

AVIOContext:输入输出对应的结构体,用于输入输出(读写文件,RTMP协议等)。

AVStream,AVCodecContext:视音频流对应的结构体,用于视音频编解码。

AVFrame:存储非压缩的数据(视频对应RGB/YUV像素数据,音频对应PCM采样数据)

AVPacket:存储压缩数据(视频对应H.264等码流数据,音频对应AAC/MP3等码流数据)

这里写图片描述

avio_open2() (libavformat\aviobuf.c)

int avio_open2(AVIOContext **s, const char *url, int flags,  
               const AVIOInterruptCB *int_cb, AVDictionary **options);  
  • 1
  • 2

avio_open2()函数参数的含义如下:

s:函数调用成功之后创建的AVIOContext结构体。
url:输入输出协议的地址(文件也是一种“广义”的协议,对于文件来说就是文件的路径)。
flags:打开地址的方式。可以选择只读,只写,或者读写。取值如下。
    AVIO_FLAG_READ:只读。
    AVIO_FLAG_WRITE:只写。
    AVIO_FLAG_READ_WRITE:读写。
int_cb:目前还没有用过。

options:目前还没有用过

avio_open2()的源代码,位于libavformat\aviobuf.c文件中

    int avio_open2(AVIOContext **s, const char *filename, int flags,  
                   const AVIOInterruptCB *int_cb, AVDictionary **options)  
    {  
        URLContext *h;  
        int err;  


        err = ffurl_open(&h, filename, flags, int_cb, options);  
        if (err < 0)  
            return err;  
        err = ffio_fdopen(s, h);  
        if (err < 0) {  
            ffurl_close(h);  
            return err;  
        }  
        return 0;  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

从avio_open2()的源代码可以看出,它主要调用了2个函数:ffurl_open()和ffio_fdopen()。其中ffurl_open()用于初始化URLContext,ffio_fdopen()用于根据URLContext初始化AVIOContext。URLContext中包含的URLProtocol完成了具体的协议读写等工作。AVIOContext则是在URLContext的读写函数外面加上了一层“包装”(通过retry_transfer_wrapper()函数)。

以下是在MFC中使用FFmpeg将YUY2格式的字节流转换为图片的代码示例: ```c++ #include <Windows.h> #include <iostream> #include <fstream> #include <vector> #include <string> #include <exception> #include <algorithm> #include <functional> #include <codecvt> #include <locale> #include "ffmpeg.h" // YUV420P转RGB24 void YUV420P_to_RGB24(unsigned char* yuvData, unsigned char* rgbData, int width, int height) { unsigned char* yData = yuvData; unsigned char* uData = yuvData + width * height; unsigned char* vData = yuvData + width * height * 5 / 4; int r, g, b, y, u, v; for (int i = 0, j = 0; i < width * height; i++, j += 3) { y = (int)(yData[i] - 16); u = (int)(uData[i / 4] - 128); v = (int)(vData[i / 4] - 128); r = (int)(1.164 * y + 1.596 * v); g = (int)(1.164 * y - 0.813 * v - 0.391 * u); b = (int)(1.164 * y + 2.018 * u); r = std::max(0, std::min(255, r)); g = std::max(0, std::min(255, g)); b = std::max(0, std::min(255, b)); rgbData[j] = r; rgbData[j + 1] = g; rgbData[j + 2] = b; } } // YUY2转RGB24 void YUY2_to_RGB24(unsigned char* yuy2Data, unsigned char* rgbData, int width, int height) { unsigned char* yData = yuy2Data; unsigned char* uData = yuy2Data + 1; unsigned char* vData = yuy2Data + 3; int r, g, b, y, u, v; for (int i = 0, j = 0; i < width * height / 2; i++, j += 6) { y = (int)(yData[i * 2] - 16); u = (int)(uData[i] - 128); v = (int)(vData[i] - 128); r = (int)(1.164 * y + 1.596 * v); g = (int)(1.164 * y - 0.813 * v - 0.391 * u); b = (int)(1.164 * y + 2.018 * u); r = std::max(0, std::min(255, r)); g = std::max(0, std::min(255, g)); b = std::max(0, std::min(255, b)); rgbData[j] = r; rgbData[j + 1] = g; rgbData[j + 2] = b; y = (int)(yData[i * 2 + 1] - 16); r = (int)(1.164 * y + 1.596 * v); g = (int)(1.164 * y - 0.813 * v - 0.391 * u); b = (int)(1.164 * y + 2.018 * u); r = std::max(0, std::min(255, r)); g = std::max(0, std::min(255, g)); b = std::max(0, std::min(255, b)); rgbData[j + 3] = r; rgbData[j + 4] = g; rgbData[j + 5] = b; } } // YUY2字节流转换为图片 bool YUY2ToImage(const char* yuy2Data, int dataSize, int width, int height, const char* imageFile) { bool ret = false; try { // 初始化FFmpeg av_register_all(); avcodec_register_all(); // 获取YUY2格式解码器 AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_YUYV422); if (!codec) { throw std::exception("avcodec_find_decoder failed"); } // 创建解码器上下文 AVCodecContext* codecCtx = avcodec_alloc_context3(codec); if (!codecCtx) { throw std::exception("avcodec_alloc_context3 failed"); } // 打开解码器 if (avcodec_open2(codecCtx, codec, NULL) < 0) { throw std::exception("avcodec_open2 failed"); } // 创建AVPacket和AVFrame AVPacket pkt; av_init_packet(&pkt); pkt.data = (uint8_t*)yuy2Data; pkt.size = dataSize; AVFrame* frame = av_frame_alloc(); if (!frame) { throw std::exception("av_frame_alloc failed"); } // 填充AVFrame数据 frame->width = width; frame->height = height; frame->format = AV_PIX_FMT_YUYV422; avpicture_fill((AVPicture*)frame, (uint8_t*)yuy2Data, AV_PIX_FMT_YUYV422, width, height); // 创建AVFrame用于存储转换后的RGB24数据 AVFrame* rgbFrame = av_frame_alloc(); if (!rgbFrame) { throw std::exception("av_frame_alloc failed"); } // 设置AVFrame的参数 rgbFrame->width = width; rgbFrame->height = height; rgbFrame->format = AV_PIX_FMT_RGB24; int numBytes = avpicture_get_size(AV_PIX_FMT_RGB24, width, height); uint8_t* buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t)); avpicture_fill((AVPicture*)rgbFrame, buffer, AV_PIX_FMT_RGB24, width, height); // 转换YUY2到RGB24 YUY2_to_RGB24(yuy2Data, rgbFrame->data[0], width, height); // 创建输出文件 FILE* outFile = NULL; fopen_s(&outFile, imageFile, "wb"); if (!outFile) { throw std::exception("open output file failed"); } // 初始化AVFormatContext AVFormatContext* formatCtx = avformat_alloc_context(); if (!formatCtx) { throw std::exception("avformat_alloc_context failed"); } // 设置输出格式 AVOutputFormat* outputFmt = av_guess_format(NULL, imageFile, NULL); if (!outputFmt) { throw std::exception("av_guess_format failed"); } formatCtx->oformat = outputFmt; // 创建AVIOContext if (avio_open(&formatCtx->pb, imageFile, AVIO_FLAG_WRITE) < 0) { throw std::exception("avio_open failed"); } // 创建AVStream AVStream* stream = avformat_new_stream(formatCtx, codec); if (!stream) { throw std::exception("avformat_new_stream failed"); } // 设置AVCodecContext AVCodecContext* outCodecCtx = stream->codec; outCodecCtx->codec = codec; outCodecCtx->codec_id = codec->id; outCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO; outCodecCtx->width = width; outCodecCtx->height = height; outCodecCtx->pix_fmt = AV_PIX_FMT_RGB24; outCodecCtx->time_base.num = 1; outCodecCtx->time_base.den = 25; // 写入文件头 avformat_write_header(formatCtx, NULL); // 写入视频帧 AVPacket packet; av_init_packet(&packet); packet.data = rgbFrame->data[0]; packet.size = numBytes; packet.pts = 0; packet.dts = 0; packet.duration = 1; packet.stream_index = stream->index; av_interleaved_write_frame(formatCtx, &packet); // 写入文件尾 av_write_trailer(formatCtx); // 释放资源 av_frame_free(&rgbFrame); av_free(buffer); av_frame_free(&frame); avcodec_close(codecCtx); avcodec_free_context(&codecCtx); avformat_free_context(formatCtx); fclose(outFile); ret = true; } catch (std::exception& e) { std::cout << e.what() << std::endl; } return ret; } ``` 在函数 `YUY2ToImage` 中,首先初始化FFmpeg库,并获取YUY2格式的解码器。然后创建解码器上下文,并打开解码器。接着创建AVPacket和AVFrame,填充AVFrame数据,并创建AVFrame用于存储转换后的RGB24数据。通过调用转换函数`YUY2_to_RGB24`,将YUY2格式的字节流转换为RGB24格式。然后创建输出文件,并初始化AVFormatContext,设置输出格式和AVIOContext。接着创建AVStream,设置AVCodecContext,并写入文件头。最后,将转换后的RGB24数据写入文件,并写入文件尾,最后释放资源。 请注意,在使用这个函数之前,在项目属性中添加FFmpeg库和头文件路径,以及链接FFmpeg库。 希望这可以帮助到你!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值