1.日志系统
常用的日志级别:
- AV_LOG_ERROR
- AV_LOG_WARNING
- AV_LOG_INFO
- AV_LOG_DEBUG
c
#include <stdio.h>
#include <libavutil/log.h>
int main(int argc, char *argv[])
{
av_log_set_level(AV_LOG_DEBUG);
av_log(NULL, AV_LOG_DEBUG, "hello world!\n");
return 0;
}
2.文件的删除与重命名
文件删除与重命名的API:
- avpriv_io_delete()
- avpriv_io_move()
c
int ret;
// 重命名
ret = avpriv_io_move("111.txt", "222.txt");
if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to rename\n");
return -1;
}
av_log(NULL, AV_LOG_INFO, "Success to renmae\n");
// 删除
ret = avpriv_io_delete("./test.txt");
if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to delete\n");
return -1;
}
av_log(NULL, AV_LOG_INFO, "Success to delete\n");
3.目录操作
操作目录重要函数:
- avio_open_dir()
- avio_read_dir()
- avio_close_dir()
操作目录重要结构体:
- AVIODirContext
- AVIODirEntry
c
avdevice_register_all();
int ret;
AVIODirContext *ctx = NULL;
AVIODirEntry *entry = NULL;
av_log_set_level(AV_LOG_INFO);
ret = avio_open_dir(&ctx, "./", NULL);
if(ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "Can't open dir: %s\n", av_err2str(ret));
return -1;
}
while(1) {
avio_read_dir(ctx, &entry);
if(ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "Can't read dir: %s\n", av_err2str(ret));
goto __fail;
}
if(!entry) {
break;
}
av_log(NULL, AV_LOG_INFO,"%-9s %12"PRId64" %s\n", entry->size, entry->name);
avio_free_directory_entry(&entry);
}
__fail:
avio_close_dir(&ctx);
4.抽取音频数据
- 1.处理一些参数
- 2.打开多媒体文件
- 3.从多媒体文件中找到音频流
- 4.打开目的文件的上下文
- 5.为目的文件,创建一个新的音频流
- 6.设置输出音频参数
- 7.写多媒体文件头到目的文件
- 8...从源多媒体文件中读音频数据到目的文件中
- 9.写多媒体文件尾到文件中
- 10.将申请的资源释放掉
步骤比较多,一点一点来实现:
c
// 1.处理一些参数
if(argc < 3) {
av_log(NULL, AV_LOG_INFO, "argument must be 3");
exit(-1);
}
src = argv[1];
dst = argv[2];
// 2.打开多媒体文件
ret = avformat_open_input(&pFmtCtx, src, NULL, NULL);
if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "%s\n", av_err2str(ret));
exit(-1);
}
c
// 3.从多媒体文件中找到音频流
idx = av_find_best_stream(pFmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if(idx < 0) {
av_log(pFmtCtx, AV_LOG_ERROR, "Does not include audio\n");
goto _ERROR;
}
// 4.打开目的文件的上下文
oFmtCtx = avformat_alloc_context();
if(!oFmtCtx) {
av_log(NULL, AV_LOG_ERROR, "No Memory\n");
goto _ERROR;
}
outFmt = av_guess_format(NULL, dst, NULL);
oFmtCtx->oformat = outFmt;
// 5.为目的文件,创建一个新的音频流
outStream = av_format_new_stream(oFmtCtx, NULL);
// 6.设置输出音频参数
inStream = pFmtCtx->stream[idx];
avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);
outStream->codecpar->codec_tag = 0;
c
// 7.写多媒体文件头到目的文件
ret = avformat_write_header(oFmtCtx, NULL);
if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "%s\n", av_err2str(ret));
goto _ERROR;
}
// 8.聪源多媒体文件中读到音频数据到目的文件中
while(av_read_frame(pFmtCtx, &pkt) >= 0) {
if(pkt.stream_index == idx) {
pkt.pts = av_rescale_q_rnd(pkt.pts, inStream->time_base, outStream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
pkt.dts = pkt.dts;
pkt.duration = av_rescale_q(pkt.duration, inStream->time_base, outStream->time_base);
pkt.stream_index = 0;
pkt.pos = -1;
av_interleaved_write_frame(oFmtCtx, &pkt);
av_packet_unref(&pkt);
}
}
// 9.写多媒体文件尾到文件中
av_write_trailer(oFmtCtx);
// 10.将申请的资源释放掉
_ERROR:
if(pFmtCtx) {
avformat_close_input(&pFmtCtx);
pFmtCtx = NULL;
}
if(oFmtCtx->pb) {
avio_close(oFmtCtx->pb);
}
if(oFmtCtx) {
avformat_free_context(oFmtCtx);
oFmtCtx = NULL;
}
6.抽取视频数据
与处理音频流程相同,简单修改即可
c
// 3.从多媒体文件中找到视频流
idx = av_find_best_stream(pFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if(idx < 0) {
av_log(pFmtCtx, AV_LOG_ERROR, "Does not include VIDEO\n");
goto _ERROR;
}
... ...
// 8.从源多媒体文件中读到视频数据到目的文件中
while(av_read_frame(pFmtCtx, &pkt) >= 0) {
if(pkt.stream_index == idx) {
pkt.pts = av_rescale_q_rnd(pkt.pts, inStream->time_base, outStream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
pkt.dts = av_rescale_q_rnd(pkt.dts, inStream->time_base, outStream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
// pkt.dts = pkt.dts;
pkt.duration = av_rescale_q(pkt.duration, inStream->time_base, outStream->time_base);
pkt.stream_index = 0;
pkt.pos = -1;
av_interleaved_write_frame(oFmtCtx, &pkt);
av_packet_unref(&pkt);
}
}
6.多媒体格式转封装
7.视频裁剪
程序是在上面的程序基础上修改的,这里就把核心修改地方贴出来。
有两个注意点:(对应下面的修改2和修改3)
第一个是音视频时间的问题。
针对音频:从第5秒钟截取,那他就是第5秒。针对视频:因为有IPB 帧,如果刚好第5秒是P帧或者B帧,没有了I帧,那么就不能解码成功,播放的时候就会花屏。
解决:告诉ffmpeg如果不是I帧,就要向前或者向后找最近的I帧
第二个是时间的问题。
由于是裁剪,所以需要计算相对时间。 使用后面的时间减去第一个时间基的时间,这样子才能保证时间的正确性。音频的dts和pts是相同的,而视频可能是不同的,所以这里需要多加一步判断。