如何使用 ov_open_callbacks 从流中打开 Ogg Vorbis 文件
How to use ov_open_callbacks to open an Ogg Vorbis file from a stream
打开 Ogg Vorbis 文件的标准方法是使用 ov_fopen
or ov_open
。但是,如果文件路径包含非 ASCII 字符,则这两个函数都不适用于 Windows。
在这种情况下,可以包装现有的 std::ifstream
(或不同的输入流)并通过 ov_open_callbacks
打开它。当我尝试这样做时,我发现 the documentation 关于所需包装函数的确切语义有点粗略。
如何包装现有流以便将其传递给 ov_open_callbacks
?
这是打开 .ogg 文件并打印出一些基本信息的工作示例。所有包装函数(除了 close
,在这种情况下不需要)都已实现,因此生成的 OggVorbis_File
结构是完全可搜索的。
#include <vorbis/vorbisfile.h>
#include <iostream>
#include <cassert>
#include <fstream>
#include <iomanip>
#include <vector>
size_t read(void* buffer, size_t elementSize, size_t elementCount, void* dataSource) {
assert(elementSize == 1);
std::ifstream& stream = *static_cast<std::ifstream*>(dataSource);
stream.read(static_cast<char*>(buffer), elementCount);
const std::streamsize bytesRead = stream.gcount();
stream.clear(); // In case we read past EOF
return static_cast<size_t>(bytesRead);
}
int seek(void* dataSource, ogg_int64_t offset, int origin) {
static const std::vector<std::ios_base::seekdir> seekDirections{
std::ios_base::beg, std::ios_base::cur, std::ios_base::end
};
std::ifstream& stream = *static_cast<std::ifstream*>(dataSource);
stream.seekg(offset, seekDirections.at(origin));
stream.clear(); // In case we seeked to EOF
return 0;
}
long tell(void* dataSource) {
std::ifstream& stream = *static_cast<std::ifstream*>(dataSource);
const auto position = stream.tellg();
assert(position >= 0);
return static_cast<long>(position);
}
int main() {
// Open file stream
std::ifstream stream;
stream.open("C:\path\to\file.ogg", std::ios::binary);
OggVorbis_File file;
const ov_callbacks callbacks{read, seek, nullptr, tell};
int result = ov_open_callbacks(&stream, &file, nullptr, 0, callbacks);
if (result < 0) {
std::cout << "Error opening file: " << result << std::endl;
return 0;
}
// Read file info
vorbis_info* vorbisInfo = ov_info(&file, -1);
std::cout << "File info: " << vorbisInfo->rate << "Hz, "
<< vorbisInfo->channels << " channels" << std::endl;
// Close file
ov_clear(&file);
}
打开 Ogg Vorbis 文件的标准方法是使用 ov_fopen
or ov_open
。但是,如果文件路径包含非 ASCII 字符,则这两个函数都不适用于 Windows。
在这种情况下,可以包装现有的 std::ifstream
(或不同的输入流)并通过 ov_open_callbacks
打开它。当我尝试这样做时,我发现 the documentation 关于所需包装函数的确切语义有点粗略。
如何包装现有流以便将其传递给 ov_open_callbacks
?
这是打开 .ogg 文件并打印出一些基本信息的工作示例。所有包装函数(除了 close
,在这种情况下不需要)都已实现,因此生成的 OggVorbis_File
结构是完全可搜索的。
#include <vorbis/vorbisfile.h>
#include <iostream>
#include <cassert>
#include <fstream>
#include <iomanip>
#include <vector>
size_t read(void* buffer, size_t elementSize, size_t elementCount, void* dataSource) {
assert(elementSize == 1);
std::ifstream& stream = *static_cast<std::ifstream*>(dataSource);
stream.read(static_cast<char*>(buffer), elementCount);
const std::streamsize bytesRead = stream.gcount();
stream.clear(); // In case we read past EOF
return static_cast<size_t>(bytesRead);
}
int seek(void* dataSource, ogg_int64_t offset, int origin) {
static const std::vector<std::ios_base::seekdir> seekDirections{
std::ios_base::beg, std::ios_base::cur, std::ios_base::end
};
std::ifstream& stream = *static_cast<std::ifstream*>(dataSource);
stream.seekg(offset, seekDirections.at(origin));
stream.clear(); // In case we seeked to EOF
return 0;
}
long tell(void* dataSource) {
std::ifstream& stream = *static_cast<std::ifstream*>(dataSource);
const auto position = stream.tellg();
assert(position >= 0);
return static_cast<long>(position);
}
int main() {
// Open file stream
std::ifstream stream;
stream.open("C:\path\to\file.ogg", std::ios::binary);
OggVorbis_File file;
const ov_callbacks callbacks{read, seek, nullptr, tell};
int result = ov_open_callbacks(&stream, &file, nullptr, 0, callbacks);
if (result < 0) {
std::cout << "Error opening file: " << result << std::endl;
return 0;
}
// Read file info
vorbis_info* vorbisInfo = ov_info(&file, -1);
std::cout << "File info: " << vorbisInfo->rate << "Hz, "
<< vorbisInfo->channels << " channels" << std::endl;
// Close file
ov_clear(&file);
}