AVFormatContext协议层:理论与实战(一)

简介: AVFormatContext协议层:理论与实战(一)

前言

AVFormatContext 是一个贯穿始终的数据结构,很多函数都用到它作为参数,是输入输出相关信息的一个容器,本文讲解 AVFormatContext 的协议层,主要包括三大数据结构:AVIOContext, URLContext, URLProtocol


一、协议操作对象结构

首先放两张图,具体看一下相关数据结构的关系

更详细的看下图:

  • 协议(文件)操作的顶层结构是 AVIOContext,这个对象实现了带缓冲的读写操作;FFMPEG 的输入对象 AVFormat 的 pb 字段指向一个 AVIOContext
  • AVIOContext 的 opaque 实际指向一个 URLContext 对象, 这个对象封装了协议对象及协议操作对象, 其中 prot 指向具体的协议操作对象, priv_data 指向具体的协议对象。
  • URLProtocol 为协议操作对象,针对每种协议,会有一个这样的对象,每个协议操作对象和一个协议对象关联,比如,文件操作对象为 ff_file_protocol, 它关联的结构体是 FileContext

二、初始化 AVIOContext 函数调用关系

初始化 AVIOFormat 函数调用关系:

三、avio 实战 1:打开本地文件或网络直播流

本次实战的目的是熟悉使用 avformat 相关 API,分析输入文件的流数量

准备好本地视频素材,我将其放到了 QT 工程文件的 debug 目录下

1、示例源码

extern "C"
{
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavformat/avio.h>
    #include <libavutil/file.h>
};
int main(int argc, char* argv[])
{
    /av_register_all();
    avformat_network_init();
    printf("hello,ffmpeg\n");
    AVFormatContext* pFormatCtx = NULL;
    AVInputFormat *piFmt = NULL;
    printf("hello,avformat_alloc_context\n");
    // Allocate the AVFormatContext:
    pFormatCtx = avformat_alloc_context();
    printf("hello,avformat_open_input\n");
    //打开本地文件或网络直播流
    //rtsp://127.0.0.1:8554/rtsp1   ----> 使用 VLC 推流
    //./debug/test.mp4    ----> 使用本地文件
    if (avformat_open_input(&pFormatCtx, "./debug/test.mp4", piFmt, NULL) < 0) {
        printf("avformat open failed.\n");
        goto quit;
    }
    else {
        printf("open stream success!\n");
    }
    if (avformat_find_stream_info(pFormatCtx, NULL)<0)
    {
        printf("av_find_stream_info error \n");
        goto quit;
    }
    else {
        printf("av_find_stream_info success \n");
        printf("******nb_streams=%d\n",pFormatCtx->nb_streams);
    }
quit:
    avformat_free_context(pFormatCtx);
    avformat_close_input(&pFormatCtx);
    avformat_network_deinit();
    return 0;
}
  • avformat_network_init():初始化网络模块。它在使用网络相关功能之前被调用,以确保网络功能的正确运行。
  • avformat_alloc_context():用于分配和初始化 AVFormatContext 结构体,该结构体用于表示音视频格式上下文。
  • avformat_open_input():用于打开音视频文件或流并将其解析为 AVFormatContext 结构体。
  • avformat_find_stream_info():用于获取音视频文件或流的详细流信息并填充到 AVFormatContext 结构体中,如音视频流、时长、元数据等;

2、运行结果

出现程序异常结束的错误,如下图:

使用 debug 调试发现在 avformat_close_input() 出现了段错误:

①、解决方法 1

查了好多资料,看了好多帖子也无济于事,索性看了一下 ffmepg 官方提供的测试用例,如下:

avio_read_callback.c

/*
 * Copyright (c) 2014 Stefano Sabatini
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
/**
 * @file libavformat AVIOContext read callback API usage example
 * @example avio_read_callback.c
 *
 * Make libavformat demuxer access media content through a custom
 * AVIOContext read callback.
 */
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/file.h>
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)
        return AVERROR_EOF;
    printf("ptr:%p size:%zu\n", bd->ptr, bd->size);
    /* copy internal buffer data to buf */
    memcpy(buf, bd->ptr, buf_size);
    bd->ptr  += buf_size;
    bd->size -= buf_size;
    return buf_size;
}
int main(int argc, char *argv[])
{
    AVFormatContext *fmt_ctx = NULL;
    AVIOContext *avio_ctx = NULL;
    uint8_t *buffer = NULL, *avio_ctx_buffer = NULL;
    size_t buffer_size, avio_ctx_buffer_size = 4096;
    char *input_filename = NULL;
    int ret = 0;
    struct buffer_data bd = { 0 };
    if (argc != 2) {
        fprintf(stderr, "usage: %s input_file\n"
                "API example program to show how to read from a custom buffer "
                "accessed through AVIOContext.\n", argv[0]);
        return 1;
    }
    input_filename = argv[1];
    /* slurp file content into buffer */
    ret = av_file_map(input_filename, &buffer, &buffer_size, 0, NULL);
    if (ret < 0)
        goto end;
    /* fill opaque structure used by the AVIOContext read callback */
    bd.ptr  = buffer;
    bd.size = buffer_size;
    if (!(fmt_ctx = avformat_alloc_context())) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    avio_ctx_buffer = av_malloc(avio_ctx_buffer_size);
    if (!avio_ctx_buffer) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,
                                  0, &bd, &read_packet, NULL, NULL);
    if (!avio_ctx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    fmt_ctx->pb = avio_ctx;
    ret = avformat_open_input(&fmt_ctx, NULL, NULL, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not open input\n");
        goto end;
    }
    ret = avformat_find_stream_info(fmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not find stream information\n");
        goto end;
    }
    av_dump_format(fmt_ctx, 0, input_filename, 0);
end:
    avformat_close_input(&fmt_ctx);
    /* note: the internal buffer could have changed, and be != avio_ctx_buffer */
    if (avio_ctx)
        av_freep(&avio_ctx->buffer);
    avio_context_free(&avio_ctx);
    av_file_unmap(buffer, buffer_size);
    if (ret < 0) {
        fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
        return 1;
    }
    return 0;
}

AVFormatContext协议层:理论与实战(二)https://developer.aliyun.com/article/1473890

目录
相关文章
|
7月前
|
存储 缓存 编解码
AVFormatContext封装层:理论与实战(一)
AVFormatContext封装层:理论与实战(一)
102 1
|
7月前
|
编解码 算法 容器
AVFormatContext封装层:理论与实战(二)
AVFormatContext封装层:理论与实战(二)
60 0
|
XML 存储 JSON
【面试题精讲】序列化协议对应于 TCP/IP 4 层模型的哪一层?
【面试题精讲】序列化协议对应于 TCP/IP 4 层模型的哪一层?
|
7月前
|
编解码 内存技术
AVFormatContext编解码层:理论与实战(三)
AVFormatContext编解码层:理论与实战(三)
50 0
|
7月前
|
编解码 缓存 大数据
AVFormatContext编解码层:理论与实战(一)
AVFormatContext编解码层:理论与实战(一)
99 0
|
7月前
|
编解码
AVFormatContext编解码层:理论与实战(二)
AVFormatContext编解码层:理论与实战(二)
63 0
|
7月前
|
编解码 内存技术
AVFormatContext封装层:理论与实战(三)
AVFormatContext封装层:理论与实战(三)
40 1
AVFormatContext封装层:理论与实战(三)
|
7月前
|
测试技术
AVFormatContext协议层:理论与实战(三)
AVFormatContext协议层:理论与实战(三)
42 4
|
7月前
|
测试技术
AVFormatContext协议层:理论与实战(二)
AVFormatContext协议层:理论与实战(二)
51 1
|
7月前
|
网络协议 网络架构
【网络奇缘】- 计算机网络|分层结构|深入探索TCP/IP模型|5层参考模型
【网络奇缘】- 计算机网络|分层结构|深入探索TCP/IP模型|5层参考模型
81 0