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



相关推荐
Slow菜鸟7 小时前
AI学习篇(五) | awesome-design-md 使用说明
人工智能·学习
狐狐生风8 小时前
LangChain 向量存储:Chroma、FAISS
人工智能·python·学习·langchain·faiss·agentai
狐狐生风8 小时前
LangChain RAG 基础
人工智能·python·学习·langchain·rag·agentai
努力努力再努力FFF10 小时前
医生对AI辅助诊断感兴趣,作为临床人员该怎么了解和学习?
人工智能·学习
sakiko_12 小时前
UIKit学习笔记5-使用UITableView制作聊天页面
笔记·学习·swift·uikit
Alice-YUE13 小时前
【js高频八股】防抖与节流
开发语言·前端·javascript·笔记·学习·ecmascript
北山有鸟13 小时前
修改源码法和插件法
嵌入式硬件·学习
richxu2025100114 小时前
嵌入式学习之路->stm32篇->(14)通用定时器(上)
stm32·单片机·嵌入式硬件·学习
qeen8714 小时前
【数据结构】建堆的时间复杂度讨论与TOP-K问题
c语言·数据结构·c++·学习·
lizhihai_9915 小时前
股市学习心得-六张分时保命图
大数据·人工智能·学习