FFmpeg【SDK02】关于AVIO的一些使用

简介: 在C++中使用FFmpeg库处理本地文件和网络流,包括使用AVFormatContext打开和解析文件,自定义AVIO进行读取和定位,以及处理自定义数据源获取视频文件信息。

读取本地文件(网络流)信息

#include <iostream>
#include <stdio.h>


extern "C"
{
#include <libavformat\avformat.h>
#include <libavutil\avutil.h>
#include <libavutil\log.h>
#include <libavformat\avio.h>
#include <libavutil\file.h>
#include <libavcodec\avcodec.h>
#include <libavutil\error.h>
}

#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")

// 1. 读取本地文件 和 网络流(rtsp)
void readLoadHostFile()
{
    // av_register_all();        // ffmpeg4.0 版本后被抛弃
    avformat_network_init();    // 使用网络流时需要添加

    AVFormatContext* pAVFmtCtx = NULL;
    AVInputFormat*   pAVInputFmt = NULL;

    pAVFmtCtx = avformat_alloc_context();        // 申请AVFormatContext内存

    // 打开文件流或网络流
    // No1. 本地文件流        input_5s.mp4
    // No2. VLC推rtsp流        rtsp://:8554/rtsp1    rtsp://localhost:8554/rtsp1
    if (avformat_open_input(&pAVFmtCtx, "input_5s.mp4", pAVInputFmt, NULL))
    {
        av_log(pAVFmtCtx, AV_LOG_WARNING, "open file/networkStream faild!\n");
        avformat_free_context(pAVFmtCtx);        // 释放AVFormatContext内存
        avformat_network_deinit();
        return;
    }
    av_log(pAVFmtCtx, AV_LOG_INFO, "open file/networkStream success!\n");

    // 查找流信息
    if (avformat_find_stream_info(pAVFmtCtx, NULL) < 0)
    {
        av_log(pAVFmtCtx, AV_LOG_WARNING, "find stream info faild!\n");    
        avformat_close_input(&pAVFmtCtx); 
        avformat_free_context(pAVFmtCtx);        
        avformat_network_deinit();
        return;
    }
    av_log(pAVFmtCtx, AV_LOG_INFO, "find stream info success!\n");

    // 打印流的个数
    av_log(pAVFmtCtx, AV_LOG_INFO, "nbstream: %d\n", pAVFmtCtx->nb_streams);

    avformat_close_input(&pAVFmtCtx);            // 先关闭再释放
    avformat_free_context(pAVFmtCtx);        
    avformat_network_deinit();
}

使AVIO获取视频文件信息

// 2. 自定义AVIO
// 读回调(返回读取的字节数)
int read_callback(void *opaque, uint8_t *buf, int buf_size)
{
    FILE* fp = (FILE*)opaque;
    size_t size = fread(buf, sizeof(uint8_t), buf_size, fp);
    printf("Read Bytes: %d\n", size);
    return (int)size;
}

// 返回seek到的位置
int64_t seek_callback(void *opaque, int64_t offset, int whence)
{
    FILE *fp = (FILE*)opaque;
    if (whence == AVSEEK_SIZE) 
    {
        return -1;
    }
    fseek(fp, offset, whence);
    return ftell(fp);
}

void selfDefAVIO()
{
    AVFormatContext* pAVFmtCtx = NULL;
    AVInputFormat*   pAVInputFmt = NULL;

    pAVFmtCtx = avformat_alloc_context();

    FILE* fp = fopen("input_5s.mp4", "rb");
    int nBuffSize = 1024;
    // unsigned char* buffer = (unsigned char*)malloc(nBuffSize);    // 此处使用malloc会中断
    uint8_t* buffer = (uint8_t*)av_malloc(nBuffSize);

    AVIOContext* pAVIOCtx = avio_alloc_context(buffer, 
                                               nBuffSize, 
                                               0,
                                               fp,                // 文件指针
                                               read_callback,    // 读回调
                                               0,                // 写回调
                                               seek_callback);    // 定位回调

    if (NULL == pAVIOCtx)
    {
        av_log(NULL, AV_LOG_WARNING, "AVIOContext alloc faild!\n");
        av_free(pAVIOCtx);
        return;
    }

    pAVFmtCtx->pb = pAVIOCtx;                    // 将AVIOContext绑定到AVFormatContext
    pAVFmtCtx->flags = AVFMT_FLAG_CUSTOM_IO;    // 告诉ffmpeg自己创建AVIOContext(不需要它再创建)

    // 打开流
    if (avformat_open_input(&pAVFmtCtx, "", pAVInputFmt, NULL) < 0)
    {
        av_log(pAVFmtCtx, AV_LOG_WARNING, "open file stream faild!\n");
        av_free(pAVIOCtx);
        avformat_free_context(pAVFmtCtx);        // 释放AVFormatContext内存
        return;
    }
    av_log(pAVFmtCtx, AV_LOG_INFO, "open file stream success!\n");
    // 进一步读取流信息(为 pFormatCtx->streams 填充上正确的信息)
    if (avformat_find_stream_info(pAVFmtCtx, NULL) < 0)
    {
        av_log(pAVFmtCtx, AV_LOG_WARNING, "find stream info faild!\n");
        avformat_close_input(&pAVFmtCtx);
        av_free(pAVIOCtx);
        avformat_free_context(pAVFmtCtx);    
        return;
    }
    av_log(pAVFmtCtx, AV_LOG_INFO, "find stream info success!\n");
    // 打印流的个数
    av_log(pAVFmtCtx, AV_LOG_INFO, " nbstream: %d\n", pAVFmtCtx->nb_streams);

    avformat_close_input(&pAVFmtCtx);
    av_free(pAVIOCtx);
    avformat_free_context(pAVFmtCtx);
}

使用AVIO和自定义数据类型获取视频文件信息

// 3. 自定义数据来源(可以是文件,内存,网络流)
struct buffer_data    // 自定义缓冲区
{
    uint8_t *ptr;
    size_t size;    // size left in the buffer
};

//读取数据(回调函数)
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
    struct buffer_data *bd = (struct buffer_data*)opaque;
    buf_size = FFMIN(buf_size, bd->size);

    if (!buf_size)            // 0
        return AVERROR_EOF;
    printf("ptr:%p size:%d\n", bd->ptr, bd->size);

    /// 灵活应用[内存buf]:读取的是内存,例如:加密播放器,这里解密
    memcpy(buf, bd->ptr, buf_size);
    bd->ptr += buf_size;
    bd->size -= buf_size;

    return buf_size;
}

void selfDefDataSrc()
{
    struct buffer_data bd = { 0 };
    // 1. 将文件映射到内存上
    int nRet = av_file_map("input_5s.mp4", &bd.ptr, &bd.size, 0, NULL);
    if (nRet < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "map file to memory faild!\n");
        return;
    }
    av_log(NULL, AV_LOG_INFO, "map file to memory success!\n");

    // 2. 创建AVFormatContext对象
    AVFormatContext* pAVFmtCtx = NULL;
    pAVFmtCtx = avformat_alloc_context();
    if (NULL == pAVFmtCtx)
    {
        av_log(NULL, AV_LOG_ERROR, "create AVFormatContext faild!\n");
        av_file_unmap(bd.ptr, bd.size);    // 内存映射文件:解绑定
        return;
    }
    av_log(NULL, AV_LOG_INFO, "create AVFormatContext success!\n");

    // 3. 为avio buffer分配内存
    uint8_t* avio_ctx_buffer = NULL;
    size_t avio_ctx_buffer_size = 4096;                // 回调每次读取 4096 bytes
    avio_ctx_buffer = (uint8_t*)av_malloc(avio_ctx_buffer_size);
    if (!avio_ctx_buffer) 
    {
        av_log(NULL, AV_LOG_ERROR, "av_malloc avio_ctx_buffer faild!\n");
        avformat_free_context(pAVFmtCtx);
        av_file_unmap(bd.ptr, bd.size);
        return;
    }
    av_log(NULL, AV_LOG_INFO, "av_malloc avio_ctx_buffer success!\n");

    AVIOContext* avio_ctx = avio_alloc_context(
        avio_ctx_buffer, 
        avio_ctx_buffer_size,
        0,
        &bd,                    // 从bd里面读数据
        &read_packet,            
        NULL,
        NULL);
    if (!avio_ctx) 
    {
        av_log(NULL, AV_LOG_ERROR, "avio_alloc_context AVIOContext faild!\n");
        av_freep(avio_ctx_buffer);
        avformat_free_context(pAVFmtCtx);
        av_file_unmap(bd.ptr, bd.size);
        return;
    }
    pAVFmtCtx->pb = avio_ctx;
    av_log(NULL, AV_LOG_INFO, "avio_alloc_context AVIOContext success!\n");

    nRet = avformat_open_input(&pAVFmtCtx, NULL, NULL, NULL);
    if (nRet)
    {
        av_log(NULL, AV_LOG_ERROR, "avformat_open_input faild!\n");
        avio_context_free(&avio_ctx);
        av_freep(avio_ctx_buffer);
        avformat_free_context(pAVFmtCtx);
        av_file_unmap(bd.ptr, bd.size);
        return;
    }
    av_log(NULL, AV_LOG_INFO, "avformat_open_input success!\n");

    // 查找流信息
    nRet = avformat_find_stream_info(pAVFmtCtx, NULL);
    if (nRet < 0) 
    {
        av_log(NULL, AV_LOG_ERROR, "avformat_find_stream_info faild!\n");
        avformat_close_input(&pAVFmtCtx);
        avio_context_free(&avio_ctx);
        av_freep(avio_ctx_buffer);
        avformat_free_context(pAVFmtCtx);
        av_file_unmap(bd.ptr, bd.size);
        return;
    }
    av_log(NULL, AV_LOG_INFO, "avformat_find_stream_info success!\n");
    printf(" 》》》nb_streams=%d\n", pAVFmtCtx->nb_streams);

    av_dump_format(pAVFmtCtx, 0, "input_5s.mp4", 0);

    // 关闭释放资源
    avformat_close_input(&pAVFmtCtx);
    if (avio_ctx)
        av_freep(&avio_ctx->buffer);
    avio_context_free(&avio_ctx);
    av_file_unmap(bd.ptr, bd.size);
}
相关文章
|
7月前
|
开发工具
使用FFmpeg4.3.1的SDK官方开发包编译ffmpeg.c(三)
使用FFmpeg4.3.1的SDK官方开发包编译ffmpeg.c(三)
102 0
|
2月前
FFmpeg【SDK01】日志和字典的使用
FFmpeg中日志功能的使用方法,包括日志级别的设置和AVDictionary的基本操作,同时展示了字符串解析函数如av_parse_video_size、av_parse_video_rate和av_parse_time的应用。
45 2
|
7月前
|
编解码 IDE 开发工具
使用FFmpeg4.3.1的SDK官方开发包编译ffmpeg.c(一)
使用FFmpeg4.3.1的SDK官方开发包编译ffmpeg.c(一)
64 1
|
7月前
|
开发工具
使用FFmpeg4.3.1的SDK官方开发包编译ffmpeg.c(二)
使用FFmpeg4.3.1的SDK官方开发包编译ffmpeg.c(二)
84 0
|
编解码 Ubuntu IDE
基于Ubuntu交叉编译X264, FFmpeg Windows SDK详细教程
基于Ubuntu交叉编译X264, FFmpeg Windows SDK详细教程
249 0
|
编解码 Ubuntu Linux
基于Ubuntu交叉编译FFmpeg Windows SDK
写在前面   FFmpeg是一个开源且跨平台的音视频解决方案,集采集、转码、流式化为一身,项目的libavcodec编解码模块和libavformat媒体格式模块,支持非常非常丰富的编解码格式和容器封装格式,是做媒体相关开发工作必须要掌握和借鉴的一个项目。
1976 0
|
开发工具 数据安全/隐私保护
【FFMpeg视频开发与应用基础】七、 调用FFMpeg SDK实现视频水印
《FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK》视频教程已经在“CSDN学院”上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK Github工程代码地址:FFmpeg_Tutorial 视频的水印通常指附加在原始视频上的可见或者不可见的,与原始视频无直接关联的标识。
1501 0
|
编解码 缓存 开发工具
【FFMpeg视频开发与应用基础】八、 调用FFMpeg SDK实现视频缩放
《FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK》视频教程已经在“CSDN学院”上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK Github工程代码地址:FFmpeg_Tutorial 视频缩放是视频开发中一项最基本的功能。
1792 0