Skip to content

Instantly share code, notes, and snippets.

@xlphs
Last active June 24, 2022 18:12
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save xlphs/9895065 to your computer and use it in GitHub Desktop.
Save xlphs/9895065 to your computer and use it in GitHub Desktop.
MyIOContext
#include <cstdio>
#include <string>
class MyIOContext {
public:
std::string datafile;
AVIOContext *ioCtx;
uint8_t *buffer; // internal buffer for ffmpeg
int bufferSize;
FILE *fh;
public:
MyIOContext(const std::string &datafile);
~MyIOContext();
void initAVFormatContext(AVFormatContext *);
};
static int IOReadFunc(void *data, uint8_t *buf, int buf_size) {
MyIOContext *hctx = (MyIOContext*)data;
size_t len = fread(buf, 1, buf_size, hctx->fh);
if (len == 0) {
// Let FFmpeg know that we have reached EOF, or do something else
return AVERROR_EOF;
}
return (int)len;
}
// whence: SEEK_SET, SEEK_CUR, SEEK_END (like fseek) and AVSEEK_SIZE
static int64_t IOSeekFunc(void *data, int64_t pos, int whence) {
if (whence == AVSEEK_SIZE) {
// return the file size if you wish to
}
MyIOContext *hctx = (MyIOContext*)data;
int rs = fseek(hctx->fh, (long)pos, whence);
if (rs != 0) {
return -1;
}
long fpos = ftell(hctx->fh); // int64_t is usually long long
return (int64_t)fpos;
}
MyIOContext::MyIOContext(const std::string &s) {
datafile.assign(s);
// allocate buffer
bufferSize = 4096;
buffer = (uint8_t *)av_malloc(bufferSize); // see destructor for details
// open file
fh = fopen(datafile.c_str(), "rb");
if (!fh) {
fprintf(stderr, "MyIOContext: failed to open file %s\n",
datafile.c_str());
}
// allocate the AVIOContext
ioCtx = avio_alloc_context(
buffer, bufferSize, // internal buffer and its size
0, // write flag (1=true, 0=false)
(void*)this, // user data, will be passed to our callback functions
IOReadFunc,
0, // no writing
IOSeekFunc
);
}
MyIOContext::~MyIOContext() {
if (fh) fclose(fh);
av_free(buffer);
av_free(ioCtx);
}
void MyIOContext::initAVFormatContext(AVFormatContext *pCtx) {
pCtx->pb = ioCtx;
pCtx->flags |= AVFMT_FLAG_CUSTOM_IO;
// you can specify a format directly
//pCtx->iformat = av_find_input_format("h264");
// or read some of the file and let ffmpeg do the guessing
size_t len = fread(buffer, 1, bufferSize, fh);
if (len == 0) return;
fseek(fh, 0, SEEK_SET); // reset to beginning of file
AVProbeData probeData;
probeData.buf = buffer;
probeData.buf_size = bufferSize - 1;
probeData.filename = "";
pCtx->iformat = av_probe_input_format(&probeData, 1);
}
@JanKrcek-zz
Copy link

JanKrcek-zz commented Feb 12, 2021

Hello Marika, great job with this sample. Saved me a lot of time figuring out the interface for the callback functions.

If I may suggest an improvement, consider following changes:
line 49:

buffer = (uint8_t *)av_malloc(bufferSize + AVPROBE_PADDING_SIZE); // see destructor for details

line 90:

probeData.buf_size = bufferSize;
probeData.mime_type = "";

Setting the padding to 1 instead of the defined value in the library can cause invalid read outside of the buffer, which leads to segfault.
And apparently not setting the mime_type can cause trouble as well. Empty string should work fine, as avformat tries to match it against its known types, before using it.
Source:
https://ffmpeg.org/doxygen/2.8/structAVProbeData.html#a814cca49dda3f578ebb32d4b2f74368a
Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment