前言
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