读取本地文件(网络流)信息
#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);
}