FFmpeg学习记录(二)—— ffmpeg多媒体文件处理

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是相同的,而视频可能是不同的,所以这里需要多加一步判断。



相关推荐
西岸行者12 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意12 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码12 天前
嵌入式学习路线
学习
毛小茛12 天前
计算机系统概论——校验码
学习
babe小鑫12 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms12 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下12 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。12 天前
2026.2.25监控学习
学习
im_AMBER12 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J12 天前
从“Hello World“ 开始 C++
c语言·c++·学习