libavformat
:实现流协议,容器格式及IO访问,下面代码使用vim编写,提高记忆.
1 删除文件和文件的重用名
#include
int main(int argc, char *argv[]){
int ret = 0;
//删除路径可以是个url
ret = avpriv_io_delete("./test.txt");
if(ret <0){
av_log(NULL,AV_LOG_ERROR,"文件删除失败\n");
return -1;
}
// AV_LOG_ERROR //级别最高
//AV_LOG_WARNING
//AV_LOG_INFO
//AV_LOG_DEBUG
av_log(NULL,AV_LOG_DEBUG,"文件删除成功\n");
// 重用名
int removeRet;
removeRet = avpriv_io_move("1.txt","2.txt");
if(removeRet < 0){
av_log(NULL,AV_LOG_INFO,"1.txt文件重用名失败 \n");
}else{
av_log(NULL,AV_LOG_INFO,"1.txt文件改成了 2.txt\n");
}
return 0;
}
在mac下 使用clang
对上述代码进行编译.
clang -g -o ffmpeg_file DelectmoveFFmpeg.c `pkg-config --libs libavformat`
2 文件目录的操作
#include
//logo
#include
#include
int main(int argc, char *argv[]){
printf("ffmpeg 文件目录的操作");
// 获取上下文
AVIODirContext *ctx = NULL;
AVIODirEntry *entry = NULL;
av_log_set_level(AV_LOG_INFO);
// 打开目录
int isSucceed = avio_open_dir(&ctx,"./",NULL);
if(isSucceed < 0 ){
av_log(NULL,AV_LOG_ERROR,"目录打开失败");
return -1;
}
// 读取每一项
while(1){
isSucceed = avio_read_dir(ctx,&entry);
if(isSucceed < 0){
av_log(NULL,AV_LOG_ERROR,"不能读取目录:%s\n",av_err2str(isSucceed));
//防止内存泄漏
goto __fail;
// return -1;
}
if(entry == NULL){
break;
}
av_log(NULL,AV_LOG_INFO,"%12"PRId64" %s\n",entry->size,entry->name);
// 释放entry
avio_free_directory_entry(&entry);
}
// 释放
__fail:
avio_close_dir(&ctx);
return 0;
}
clang编译:
clang -g -o testcatalogue FFmpegCatalogueHandle.c `pkg-config --libs libavformat,libavutil`
运行结果:
./testcatalogue
6148 .DS_Store
50320 testcatalogue
96 testcatalogue.dSYM
738 FFmpegCatalogueHandle.c
3 FFmpeg 打印音视频的信息
#include "libavutil/log.h"
#include "libavformat/avformat.h"
int main(int avgr ,char *argv[]){
AVFormatContext *ctx = NULL;
av_log_set_level(AV_LOG_INFO);
// 在ffmepg4.0以上 该方法已经废弃,不用了。
// av_register_all();
int ret = avformat_open_input(&ctx,"./scale.mp4",NULL,NULL);
if(ret >= 0){
// 音视频信息
av_dump_format(ctx,0,"./scale.mp4",0);
}else{
av_log(NULL,AV_LOG_ERROR,"不能打开文件:%s\n",av_err2str(ret));
}
avformat_close_input(&ctx);
return 0;
}
编译 clang -g -o testmeta VideoAudioMeta.c `pkg-config --libs libavformat,libavutil
需要怎么改进程序,解除这个警告呢??? 解决方法如下:
// 在ffmepg4.0以上 该方法已经废弃,不用了。
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
av_register_all();
#endif
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
avcodec_register_all();
#endif
4 抽取音频流
#include "libavutil/log.h"
#include "libavformat/avformat.h"
int main(int argc, char *argv[]){
char *src = NULL;
char *dst = NULL;
AVFormatContext *ctx = NULL;
av_log_set_level(AV_LOG_INFO);
// 在ffmepg4.0以上 该方法已经废弃,不用了。
// #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
// av_register_all();
//#endif
// #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
// avcodec_register_all();
//#endif
// 读取参数
if(argc < 3){
av_log(NULL,AV_LOG_ERROR,"传入的参数不对");
return -1;
}
// 赋值
src = argv[1];
dst = argv[2];
if(!src || !dst){
av_log(NULL,AV_LOG_ERROR,"参数有误");
return -1;
}
int ret = avformat_open_input(&ctx,src,NULL,NULL);
if(ret >= 0){
av_dump_format(ctx,0,src,0);
}else{
av_log(NULL,AV_LOG_ERROR,"不能打开文件%s\n",av_err2str(ret));
}
// 打开写入文件 二进制文件 aac
FILE *dst_fd = fopen(dst,"wb");
if(!dst_fd){
av_log(NULL,AV_LOG_ERROR,"文件打开失败\n");
avformat_close_input(&ctx);
return -1;
}
AVPacket pkt;
int audio_index;
int len;
// 获取流
ret = av_find_best_stream(ctx,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
if(ret < 0){
av_log(NULL,AV_LOG_ERROR,"音频流获取失败\n");
avformat_close_input(&ctx);
return -1;
}
audio_index = ret;
av_init_packet(&pkt);
// 获得一帧音频的压缩数据
while(av_read_frame(ctx,&pkt) >=0){
if(pkt.stream_index == audio_index){
// 写入流 是压缩后的流 我们需要最好的流写进去
len = fwrite(pkt.data,1,pkt.size,dst_fd);
if(len != pkt.size){
av_log(NULL,AV_LOG_WARNING,"警告,写入数据出现未知的问题\n");
}
}
//回收
av_packet_unref(&pkt);
}
avformat_close_input(&ctx);
if(dst_fd){
fclose(dst_fd);
}
return 0;
}
编译完成后运行 ,需要传两个参数才可以运行: scale.mp4 ,getaudio.aac
./testAudioMeta scale.mp4 getaudio.aac
注意:这里的aac文件无法播放 .如果需要播放,似乎需要修改程序. 比如使用ffplay播放,会报错:
ffplay getaudio.aac
5 抽取视频流
#include
#include
#include
#ifndef AV_WEB32
# define AV_WB32(p,val) do { \
uint32_t d = (val); \
((uint8_t *)(p))[3] = (d); \
((uint8_t *)(p))[2] = (d)>>8; \
((uint8_t *)(p))[1] = (d)>>16; \
((uint8_t *)(p))[0] = (d)>>24; \
}while(0)
#endif
#ifndef AV_RB16
# define AV_RB16(x) \
((((const uint8_t *)(x))[0] << 8) | \
((const uint8_t *)(x))[1])
#endif
static int alloc_and_copy(AVPacket *out,const uint8_t *sps_pps,uint32_t sps_pps_size,const uint8_t *in ,uint32_t in_size){
uint32_t offset = out ->size;
uint8_t nal_header_size = offset ? 3: 4;
int err ;
// 扩容av_grow_packet
if((err = av_grow_packet(out,sps_pps_size + in_size + nal_header_size)) <0){
return err;
}
// 先copy sps和pps
if(sps_pps){
memcpy(out->data + offset,sps_pps,sps_pps_size);
}
// 在拷贝特征码 startcode
memcpy(out->data + sps_pps_size + nal_header_size +
offset,in,in_size);
if(!offset){
// 0001 没有sps和pps
AV_WB32(out->data + sps_pps_size,1);
}else{
// sps pps 后面三位设置成 001
(out->data + offset + sps_pps_size)[0] = 0;
(out->data + offset + sps_pps_size)[1] = 0;
(out->data + offset + sps_pps_size)[2] = 1;
}
// for(int i = 0;i < nal_header_size;i++){
// (out->data+offset + sps_pps_size)[i] = i == nal_header_size - 1?1:0;
// }
// memcpy(out->data + sps_pps_size + nal_header_size +
// offset,in,in_size);
return 0;
}
// 读取sps/pps的数据,并拷贝
int h264_extraData_to_Annexb(const uint8_t *codec_Data,const int codecDataSize,AVPacket *out_extraData,int padding){
// sps/pps的数据长度
uint16_t unitSize;
//所有sps/pps 加上其特征码后总长度
uint64_t totalSize = 0;
for(int i = 0; i < codecDataSize;i++){
printf("%02x ",*(codec_Data + i));
}
uint8_t *out = NULL;
uint8_t unit_nb;
uint8_t sps_done = 0;
uint8_t sps_seen = 0;
uint8_t pps_seen = 0;
// sps数据偏移
uint8_t sps_offset = 0;
//pps数据偏移
uint8_t pps_offset = 0;
//过滤掉前4个字节
const uint8_t *extraData = codec_Data + 4 ;
static const uint8_t nalu_header[4] = {0,0,0,1};
// 2位
int length_size = (*extraData++ & 0x3) + 1;
sps_offset = pps_offset = -1;
// sps和pps的个数
unit_nb = *extraData++ & 0x1f;
if(!unit_nb){
goto pps;
}else{
sps_offset = 0;
sps_seen = 1;
}
while(unit_nb--){
int err;
// unitSize = (extraData[0] << 8) | extraData[1];
unitSize = AV_RB16(extraData);
totalSize += unitSize + 4;
if(totalSize > INT_MAX - padding){
av_log(NULL,AV_LOG_ERROR,"输入数据流太大,视频流损坏或者非法的MP4/AVCC 比特流\n");
av_free(out);
return AVERROR(EINVAL);
}
if(extraData + 2 + unitSize > codecDataSize + codec_Data ){
av_log(NULL,AV_LOG_ERROR,"数据异常\n");
av_free(out);
return AVERROR(EINVAL);
}
// av_log(NULL,AV_LOG_ERROR,"是否执行\n");
if((err = av_reallocp(&out,totalSize + padding)) < 0)
return err;
//拷贝nalu_header
memcpy(out + totalSize - unitSize - 4,nalu_header,4 );
// 拷贝spspps
memcpy(out + totalSize - unitSize ,extraData + 2,unitSize);
extraData += 2 + unitSize;
pps:
if(!unit_nb && !sps_done++){
unit_nb = *extraData++;
if(unit_nb){
pps_offset = totalSize;
pps_seen = 1;
}
}
}
if(out){
memset(out + totalSize,0,padding);
}
if(!sps_seen){
av_log(NULL,AV_LOG_WARNING,"警告没有sps数据");
}
if(!pps_seen){
av_log(NULL,AV_LOG_WARNING,"警告没有pps数据");
}
out_extraData->data = out;
out_extraData->size = totalSize;
return length_size;
}
//为包数据添加始码startcode ,并将获取的sps/pps等信息写入文件
int h264_videotoannexb(AVFormatContext *ctx, AVPacket *in,FILE *dst_fd){
AVPacket *out = NULL;
AVPacket spspps_pkt;
int len;
uint8_t unit_type;
int32_t nal_size;
uint32_t cumul_size = 0;
const uint8_t *buf;
const uint8_t *buf_end;
int buf_size;
int ret = 0,i;
out = av_packet_alloc();
buf = in->data;
buf_size = in->size;
//缓冲区的最后位置
buf_end = in->data + in->size;
do{
ret = AVERROR(EINVAL);
// 数据没有内容
if(buf + 4 > buf_end)
goto fail;
//取出packet前面4个字节和32位的高位地址和32位地位地址进行交换,得到nal_size大小
for(nal_size = 0,i = 0; i < 4; i++)
nal_size = (nal_size << 8) | buf[i];
// 向后移4个字节
buf += 4;
// 第一个字节的后五位,就是单元类型
unit_type = *buf & 0x1f;
if(nal_size > buf_end - buf || nal_size < 0)
goto fail;
//获取sps/pps
if(unit_type == 5){
h264_extraData_to_Annexb(ctx ->streams[in->stream_index] ->codec ->extradata,ctx ->streams[in->stream_index]->codec->extradata_size,&spspps_pkt,AV_INPUT_BUFFER_PADDING_SIZE);
//新增特征吗 startcode
if((ret=alloc_and_copy(out,spspps_pkt.data,spspps_pkt.size,buf,nal_size)) <0 ){
goto fail;
}
}else{
if((ret = alloc_and_copy(out,NULL,0,buf,nal_size)) < 0)
goto fail;
}
len = fwrite(out ->data,1,out->size,dst_fd);
if(len != out -> size){
av_log(NULL,AV_LOG_DEBUG,"警告,写入文件长度不等于包大小 %d, %d\n",len, out->size);
}
fflush(dst_fd);
buf += nal_size;
cumul_size += nal_size + 4;
}while(cumul_size < buf_size);
// len = fwrite(out ->data,1,out->size,dst_fd);
// if(len != out -> size){
// av_log(NULL,AV_LOG_DEBUG,"警告,写入文件长度不等于包大小 %d, %d\n",len,out->size);
//}
// fflush(dst_fd);
fail:
av_packet_free(&out);
return ret;
}
int main(int argc,char *argv[]){
// 音视频文件
char *src = NULL;
// 输出文件
char *dst = NULL;
av_log_set_level(AV_LOG_INFO);
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
av_register_all();
#endif
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
avcodec_register_all();
#endif
// 必须传入音频文件和视频流输出文件
if(argc <3){
av_log(NULL,AV_LOG_ERROR,"参数传入数量错误!\n");
return -1;
}
src = argv[1];
dst = argv[2];
if(!src || !dst){
av_log(NULL,AV_LOG_ERROR,"传入参数有误,请检查\n");
return -1;
}
AVFormatContext *ctx = NULL;
// 错误描述
char *errbuf;
int ret = avformat_open_input(&ctx,src,NULL,NULL);
FILE *dst_fd = fopen(dst,"wb");
if(!dst_fd){
av_log(NULL,AV_LOG_DEBUG,"不能打开文件%s\n",dst);
return -1;
}
if(ret < 0){
av_strerror(ret,errbuf,1024);
av_log(NULL,AV_LOG_DEBUG,"不能打开资源文件:%s,%d(%s)\n",src,ret,errbuf);
return -1;
}
// 打开资源文件,获取最好的流
ret = avformat_find_stream_info(ctx,NULL);
if(ret < 0){
av_strerror(ret,errbuf,1024);
av_log(NULL,AV_LOG_DEBUG,"不能打开资源文件:%s,%d(%s)\n",src,ret,errbuf);
return -1;
}
av_dump_format(ctx,0,src,0);
//初始化packet
AVPacket pkt ;
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
// 视频流的index
int video_stream_index = av_find_best_stream(ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
if(video_stream_index < 0){
av_log(NULL,AV_LOG_DEBUG,"不能找到%s流,输入到文件%s\n",av_get_media_type_string(AVMEDIA_TYPE_VIDEO),src);
return AVERROR(EINVAL);
}
//取出流中数据包 并处理
while(av_read_frame(ctx,&pkt) >= 0){
if(pkt.stream_index == video_stream_index){
h264_videotoannexb(ctx,&pkt,dst_fd);
}
av_packet_unref(&pkt);
}
if(dst_fd){
fclose(dst_fd);
}
avformat_close_input(&ctx);
return 0;
}
6 mp4转化为flv
#include
#include
#include
//#include
//log_packet' is invalid in C99 [-Werror,-Wimplicit-function-declaration] 函
// 数没有被定义,不能调用隐式函数
//*** error for object 0x1: pointer being freed was not allocated 野指针
static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag);
int main(int argc,char *argv[]){
av_log_set_level(AV_LOG_INFO);
// 在ffmepg4.0以上 该方法已经废弃,不用了。
// #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
// av_register_all();
//#endif
// #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
// avcodec_register_all();
// #endif
AVOutputFormat *ofmt = NULL;
AVFormatContext *ifmt_ctx = NULL;
AVFormatContext *ofmt_ctx = NULL;
AVPacket pkt;
const char *in_fileName, *out_fileName;
if(argc < 3){
av_log(NULL,AV_LOG_ERROR,"入参错误");
return -1;
}
in_fileName = argv[1];
out_fileName = argv[2];
int ret;
// 打开多媒体文件
if((ret = avformat_open_input(&ifmt_ctx,in_fileName,0,0)) < 0){
av_log(NULL,AV_LOG_ERROR,"多媒体文件打开失败\n");
goto fail;
}
if((ret = avformat_find_stream_info(ifmt_ctx,0)) < 0){
av_log(NULL,AV_LOG_ERROR,"多媒体文件无法获取流信息\n");
goto fail;
}
// dump 信息
av_dump_format(ifmt_ctx,0,in_fileName,0);
// 创建输出上下文
avformat_alloc_output_context2(&ofmt_ctx,NULL,NULL,out_fileName);
if(!ofmt_ctx){
av_log(NULL,AV_LOG_ERROR,"创建输出上下文失败\n");
ret = AVERROR_UNKNOWN;
goto fail;
}
int stream_index = 0;
int *stream_mapping = NULL;
int stream_mapping_size = 0;
stream_mapping_size = ifmt_ctx -> nb_streams;
stream_mapping = av_mallocz_array(stream_mapping_size,sizeof(*stream_mapping));
if(!stream_mapping){
ret = AVERROR(ENOMEM);
goto fail;
}
ofmt = ofmt_ctx -> oformat;
for(int i = 0; i < ifmt_ctx ->nb_streams; i++){
// copy输入音频流
AVStream *out_stream;
AVStream *in_stream = ifmt_ctx -> streams[i];
AVCodecParameters *in_codecpar = in_stream->codecpar;
if(in_codecpar -> codec_type != AVMEDIA_TYPE_AUDIO && in_codecpar ->codec_type != AVMEDIA_TYPE_VIDEO && in_codecpar ->codec_type != AVMEDIA_TYPE_SUBTITLE){
stream_mapping[i] = -1;
continue;
}
stream_mapping[i] = stream_index++;
out_stream = avformat_new_stream(ofmt_ctx,NULL);
if(!out_stream){
av_log(NULL,AV_LOG_ERROR,"错误的输出流\n");
ret = AVERROR_UNKNOWN;
goto fail;
}
ret = avcodec_parameters_copy(out_stream -> codecpar,in_codecpar );
if(ret < 0){
av_log(NULL,AV_LOG_ERROR,"编码器参数错误\n");
goto fail;
}
out_stream->codecpar->codec_tag = 0;
}
av_dump_format(ofmt_ctx,0,out_fileName,1);
if(!(ofmt -> flags & AVFMT_NOFILE)){
ret = avio_open(&ofmt_ctx->pb,out_fileName,AVIO_FLAG_WRITE);
if(ret < 0){
av_log(NULL,AV_LOG_ERROR,"'%s' 文件不能打开\n",out_fileName);
goto fail;
}
}
// 写入haeder
ret = avformat_write_header(ofmt_ctx,NULL);
if(ret < 0){
av_log(NULL,AV_LOG_ERROR,"写入head失败\n");
goto fail;
}
while(1){
AVStream *in_stream, *out_stream;
ret = av_read_frame(ifmt_ctx,&pkt);
if(ret < 0)
break;
in_stream = ifmt_ctx ->streams[pkt.stream_index];
if(pkt.stream_index >= stream_mapping_size || stream_mapping[pkt.stream_index] < 0 ){
av_packet_unref(&pkt);
continue;
}
pkt.stream_index = stream_mapping[pkt.stream_index];
out_stream = ofmt_ctx ->streams[pkt.stream_index];
log_packet(ifmt_ctx, &pkt, "in");
// 刻度转化 转化时间基
pkt.pts = av_rescale_q_rnd(pkt.pts,in_stream->time_base,out_stream->time_base,AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
pkt.dts = av_rescale_q_rnd(pkt.dts,in_stream->time_base,out_stream->time_base,AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
pkt.duration = av_rescale_q(pkt.duration,in_stream->time_base,out_stream->time_base);
pkt.pos = -1;
log_packet(ofmt_ctx, &pkt, "out");
ret = av_interleaved_write_frame(ofmt_ctx,&pkt);
if(ret < 0){
av_log(NULL,AV_LOG_ERROR,"error muxing packet\n");
break;
}
av_packet_unref(&pkt);
}
av_write_trailer(ofmt_ctx);
fail:
avformat_close_input(&ifmt_ctx);
if(ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
av_freep(&stream_mapping);
if(ret < 0 && ret != AVERROR_EOF){
av_log(NULL,AV_LOG_ERROR,"文件解析出现异常:%s\n",av_err2str(ret));
return -1;
}
return 0;
}
static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag)
{
AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
tag,
av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
pkt->stream_index);
}
编译clang -g -o HandelFormat VideoHandelFormatCharge.c `pkg-config --libs libavformat libavutil libavcodec