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



相关推荐
神仙别闹3 分钟前
基于C#实现的(WinForm)模拟操作系统文件管理系统
java·git·ffmpeg
汤姆和佩琦1 小时前
2024-12-25-sklearn学习(20)无监督学习-双聚类 料峭春风吹酒醒,微冷,山头斜照却相迎。
学习·聚类·sklearn
好学近乎知o1 小时前
正则表达式(学习Django过程中可能涉及的)
学习·正则表达式·django
雨中奔跑的小孩1 小时前
爬虫学习案例8
爬虫·学习
jieshenai1 小时前
使用 VSCode 学习与实践 LaTeX:从插件安装到排版技巧
ide·vscode·学习
灰太狼不爱写代码4 小时前
CUDA11.4版本的Pytorch下载
人工智能·pytorch·笔记·python·学习
eybk10 小时前
Pytorch+Mumu模拟器+萤石摄像头实现对小孩学习的监控
学习
6.9410 小时前
Scala学习记录 递归调用 练习
开发语言·学习·scala
守护者17011 小时前
JAVA学习-练习试用Java实现“使用Arrays.toString方法将数组转换为字符串并打印出来”
java·学习
学会沉淀。11 小时前
Docker学习
java·开发语言·学习