mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 04:49:39 -10:00
150 lines
3.2 KiB
C++
150 lines
3.2 KiB
C++
|
|
#include "FFmpegContext.h"
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
namespace libs {
|
|
namespace ffmpeg {
|
|
|
|
FFmpegContext::FFmpegContext(CFILE* inFile) : m_ctx(nullptr), m_file(inFile) {
|
|
Assertion(inFile != nullptr, "Invalid file pointer passed!");
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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->m_ctx = avformat_alloc_context();
|
|
|
|
if (!instance->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, instance->m_file, cfileRead, nullptr, cfileSeek);
|
|
|
|
if (!ioContext) {
|
|
throw FFmpegException("Failed to allocate IO context!");
|
|
}
|
|
|
|
instance->m_ctx->pb = ioContext;
|
|
|
|
auto probe_ret = av_probe_input_buffer2(instance->m_ctx->pb, &instance->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);
|
|
}
|
|
|
|
instance->m_ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
|
|
|
|
auto ret = avformat_open_input(&instance->m_ctx, nullptr, instance->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(instance->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);
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
|
|
std::unique_ptr<FFmpegContext> FFmpegContext::createContext(const SCP_string& path, int dir_type) {
|
|
CFILE* file = cfopen(path.c_str(), "rb", CFILE_NORMAL, dir_type);
|
|
|
|
if (!file) {
|
|
throw FFmpegException("Failed to open file!");
|
|
}
|
|
|
|
return createContext(file);
|
|
}
|
|
|
|
}
|
|
}
|