很好,没想到这么快就踩坑了。待会儿我会提到。

还是直接上代码来看,先说文件操作。

注意: 代码展示处我就不重复放include的了,如果有变动的话我会标出来

auto path = "D:\\music\\四季ノ唄.mp3";

AVIOContext* ctx;
int ret;

ret = avio_open(&ctx, path, AVIO_FLAG_READ);
if (ret < 0)
{
    av_log(nullptr, AV_LOG_ERROR, "Can't open %s: %s\n", path, av_err2str(ret));
    exit(EXIT_FAILURE);
}

ret = avio_close(ctx);
if (ret < 0)
{
    av_log(nullptr, AV_LOG_ERROR, "Can't close %s: %s\n", path, av_err2str(ret));
    exit(EXIT_FAILURE);
}

AVIOContext就是读取文件成功后所获得的信息。

avio_open用于打开文件,第一个参数就是用于接收结果的ctx,第二个是你要打开的文件,第三个是你打开的方式,AVIO_FLAG_READ是只读,同样还有只写和读写,具体的信息请参考ffmpeg doc(我有在log那篇提过哦)。

avio_close就是关闭你打开了的文件的内容,为了避免内存泄漏。

中间使用的av_log就是上一篇的内容就不多说了。

然后是目录的操作。

auto dir = "D:\\music";

AVIODirContext* dirCtx;
ret = avio_open_dir(&dirCtx, dir, nullptr);
if (ret < 0)
{
    av_log(nullptr, AV_LOG_ERROR, "Can't open %s: %s\n", dir, av_err2str(ret));
    exit(EXIT_FAILURE);
}

AVIODirContext是用来接受打开的目录的信息。

avio_open_dir用于打开目录,前两个参数与avio_open一样就不多说了,第三个参数目前也用不到,是一个option,这里直接传空就行了。

然后,如果你是在windows下运行这段代码的,那恭喜你,你或许会遇到和我一样的问题。

程序输出结果会如下所示

Can't open D:\music: Function not implemented

没错,它报错了。

debug中会发现返回值为-40,调用av_err2str将错误码转化为字符串得到的结果就是Function not implemented

这并不是因为你传入的dir有问题还是你用avio_open_dir的使用方式错了。

而是因为你在windows平台下使用了ffmpeg的avio_open_dir这个函数。

我在网上找了一番也没能找到这个问题的解答,于是自己在stackoverflow上问了一下,具体的话可以参考FFmpeg avio_open_dir returns -40 on Windows, even when directory exists

我简单解释一下就是avio_open_dir在打开本地目录时使用的file_open_dir,而file_open_dir是直接使用系统io去打开的,直接看7.1版本源码的话

static int file_open_dir(URLContext *h)
{
#if HAVE_LSTAT
    FileContext *c = h->priv_data;

    c->dir = opendir(h->filename);
    if (!c->dir)
        return AVERROR(errno);

    return 0;
#else
    return AVERROR(ENOSYS);
#endif /* HAVE_LSTAT */
}

可以发现file_open_dir使用了HAVE_LSTAT这个宏。

有它的时候就调用opendir来打开目录,这是个linux下的io。

而没有这个宏是就直接返回报错了。 没错,file_open_dir竟然没有windows侧的处理,我当时也很震惊啊!

所以avio_open_dir这个函数,很遗憾,你在windows下是没办法直接调用了。

那解决办法是什么嘞,除非你想自己修改源码编译后再使用,否则还是老老实实用其他api打开文件吧。

windows的api或者c的或cpp的filesystem,这里我就先使用cpp的了,毕竟最熟悉嘛。

至于使用其他api如何像avio_open_dir获得的AVIODirContext一样使用在ffmpeg中去,我也不造,嘛,毕竟也还在学习嘛,走一步是一步喽。之后有可以补充的话我还会写在后头的,就先不操心啦。

注意: OK,还有点没说,b站的那个课程用的avpriv_io_deleteavpriv_io_move在ffmpeg 7.1版本中是废弃的。不过这俩也只是对文件的基本操作同样可以用其他api替代就是了。


很好,我的错,我找到了新的打开文件的方法,参考在ffmpeg的doc的example,avio_reading.c

auto     filename   = "D:/music/四季ノ唄.mp3";
uint8_t* fileBuffer = nullptr;
size_t   fileSize   = 0;
auto ret = av_file_map(filename, &fileBuffer, &fileSize, 0, nullptr);
if (ret < 0)
{
    exit(-1);
}
av_file_unmap(fileBuffer, fileSize);

不用多说,av_file_map可以将文件映射到内存,打开到fileBuffer中,并获得文件大小fileSize,后面俩参数跟日志有关这里就直接设为空了。

av_file_unmap就是用于释放映射的文件数据,OK。

它这个方法好就好在可以直接用于windows平台,具体可以去看ffmpeg源码file.c中,有一部分是调用win32_open,然后直接将char转化为wchar再用win32 api去映射文件。OK。

注意: 目前在Windows下测试的,av_file_map所打开的文件仅为只读权限,无法写入。

Tags:

Categories:

Updated: