Files
fs2open.github.com/code/libs/ffmpeg/FFmpegContext.cpp
T
Taylor Richards 552f9990d9 remove support for memory mapped files (#6748)
FSO doesn't appear to use this any longer. And even when it was enabled the
requirements made it largely useless anyway. Removing to clean up cfile a
bit and so we don't have to keep maintaining it.
2025-06-05 17:53:13 -04:00

221 lines
5.6 KiB
C++

#include "FFmpegContext.h"
#include <cstring>
namespace {
const size_t AVIO_BUFFER_SIZE = 8192;
int cfileRead(void* ptr, uint8_t* buf, int buf_size) {
auto cfile = reinterpret_cast<CFILE*>(ptr);
auto numRead = cfread(buf, 1, buf_size, cfile);
if (numRead == 0) {
// Read failed
return -1;
}
return numRead;
}
int64_t cfileSeek(void* ptr, int64_t offset, int whence) {
auto cfile = reinterpret_cast<CFILE*>(ptr);
int op = CF_SEEK_SET;
switch (whence) {
case SEEK_SET:
op = CF_SEEK_SET;
break;
case SEEK_CUR:
op = CF_SEEK_CUR;
break;
case SEEK_END:
op = CF_SEEK_END;
break;
case AVSEEK_SIZE:
return cfilelength(cfile);
}
auto intOff = static_cast<int>(offset);
if ((int64_t)intOff != offset) {
// Overflow!!!
return -1;
}
auto ret = cfseek(cfile, intOff, op);
if (ret != 0) {
// Error
return -1;
}
// cfseek returns the offset in the archive file (who thought that would be a good idea?)
return cftell(cfile);
}
int memfile_read(void* opaque, uint8_t* buf, int buf_size) {
auto memsound = reinterpret_cast<libs::ffmpeg::MemFileCursor*>(opaque);
auto vec_buf_size = static_cast<SCP_vector<uint8_t>::size_type>(buf_size);
if ((int)vec_buf_size != buf_size) {
// Overflow!!!
return -1;
}
vec_buf_size = std::min(vec_buf_size, memsound->snddata.size() - memsound->cursor_pos);
if (vec_buf_size > 0) {
std::memcpy(buf, memsound->snddata.data() + memsound->cursor_pos, vec_buf_size);
memsound->cursor_pos += vec_buf_size;
return static_cast<int>(vec_buf_size);
} else {
return -1;
}
}
int64_t memfile_seek(void* opaque, int64_t offset, int whence) {
auto memsound = reinterpret_cast<libs::ffmpeg::MemFileCursor*>(opaque);
if (whence == AVSEEK_SIZE) {
return memsound->snddata.size();
}
int64_t cursor_pos = memsound->cursor_pos;
switch (whence) {
case SEEK_SET:
cursor_pos = offset;
break;
case SEEK_CUR:
cursor_pos += offset;
break;
case SEEK_END:
cursor_pos = memsound->snddata.size() + offset;
break;
}
memsound->cursor_pos = std::min(static_cast<SCP_vector<uint8_t>::size_type>(cursor_pos), memsound->snddata.size());
return static_cast<int64_t>(memsound->cursor_pos);
}
}//namespace
namespace libs {
namespace ffmpeg {
MemFileCursor::MemFileCursor(const uint8_t* data, size_t snd_len) : snddata(data, data + snd_len), cursor_pos(0) {
}
MemFileCursor::MemFileCursor() : snddata() , cursor_pos(0) {}
FFmpegContext::FFmpegContext(CFILE* inFile) : m_ctx(nullptr), m_file(inFile), m_memsound() {
Assertion(inFile != nullptr, "Invalid file pointer passed!");
}
FFmpegContext::FFmpegContext(const uint8_t* snddata, size_t snd_len) : m_ctx(nullptr), m_file(nullptr), m_memsound(snddata, snd_len) {
}
FFmpegContext::~FFmpegContext() {
if (m_ctx) {
if (m_ctx->pb) {
if (m_ctx->pb->buffer) {
av_free(m_ctx->pb->buffer);
}
av_free(m_ctx->pb);
m_ctx->pb = nullptr;
}
avformat_close_input(&m_ctx);
m_ctx = nullptr;
}
if (m_file) {
cfclose(m_file);
m_file = nullptr;
}
}
void FFmpegContext::prepare(void *opaque,
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
int64_t (*seek)(void *opaque, int64_t offset, int whence)) {
m_ctx = avformat_alloc_context();
if (!m_ctx) {
throw FFmpegException("Failed to allocate context!");
}
auto avioBuffer = reinterpret_cast<uint8_t*>(av_malloc(AVIO_BUFFER_SIZE));
if (!avioBuffer) {
throw FFmpegException("Failed to allocate IO buffer!");
}
auto ioContext =
avio_alloc_context(avioBuffer, AVIO_BUFFER_SIZE, 0, opaque, read_packet, nullptr, seek);
if (!ioContext) {
throw FFmpegException("Failed to allocate IO context!");
}
m_ctx->pb = ioContext;
auto probe_ret = av_probe_input_buffer2(m_ctx->pb, &m_ctx->iformat, nullptr, nullptr, 0, 0);
if (probe_ret < 0) {
char errorStr[1024];
av_strerror(probe_ret, errorStr, 1024);
throw FFmpegException(SCP_string("Could not open movie file! Error: ") + errorStr);
}
m_ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
auto ret = avformat_open_input(&m_ctx, nullptr, m_ctx->iformat, nullptr);
if (ret < 0) {
char errorStr[1024];
av_strerror(ret, errorStr, 1024);
throw FFmpegException(SCP_string("Could not open movie file! Error: ") + errorStr);
}
ret = avformat_find_stream_info(m_ctx, nullptr);
if (ret < 0) {
char errorStr[1024];
av_strerror(ret, errorStr, 1024);
throw FFmpegException(SCP_string("Failed to get stream information! Error: ") + errorStr);
}
}
std::unique_ptr<FFmpegContext> FFmpegContext::createContext(CFILE* mediaFile) {
Assertion(mediaFile != nullptr, "File pointer must be valid!");
std::unique_ptr<FFmpegContext> instance(new FFmpegContext(mediaFile));
instance->prepare(instance->m_file, cfileRead, cfileSeek);
return instance;
}
std::unique_ptr<FFmpegContext> FFmpegContext::createContextMem(const uint8_t* snddata, size_t snd_len) {
std::unique_ptr<FFmpegContext> instance(new FFmpegContext(snddata, snd_len));
instance->prepare(&(instance->m_memsound), memfile_read, memfile_seek);
return instance;
}
std::unique_ptr<FFmpegContext> FFmpegContext::createContext(const SCP_string& path, int dir_type) {
CFILE* file = cfopen(path.c_str(), "rb", dir_type);
if (!file) {
throw FFmpegException("Failed to open file!");
}
return createContext(file);
}
}
}