#include "stream_info.hpp"
#include <vector>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/file.h>
}
namespace libcamlite {
struct buffer_data {
uint8_t *ptr;
size_t size; size_t size_total;
};
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
struct buffer_data *bd = (struct buffer_data *)opaque;
buf_size = FFMIN(buf_size, bd->size);
if (!buf_size){
return AVERROR_EOF;
}
memcpy(buf, bd->ptr, buf_size);
bd->ptr += buf_size;
bd->size -= buf_size;
return buf_size;
}
struct StreamInfo::Impl {
friend StreamInfo;
AVFormatContext *fmt_ctx = NULL;
AVIOContext *avio_ctx = NULL;
uint8_t *avio_ctx_buffer = NULL;
size_t avio_ctx_buffer_size = 4096;
struct buffer_data bd;
std::vector<uint8_t> discoveryBuf;
const unsigned int discoveryMaxBytes = 1024*100;
const unsigned int discoveryMinBytes = 8096;
virtual ~Impl(){
if (fmt_ctx)
avformat_close_input(&fmt_ctx);
if (avio_ctx_buffer)
av_freep(&avio_ctx_buffer);
if (avio_ctx)
avio_context_free(&avio_ctx);
}
AVFormatContext* init(){
if (fmt_ctx != NULL){
return fmt_ctx;
}
if (!(fmt_ctx = avformat_alloc_context())) {
fprintf(stderr, "Failed to make avformat\n");
return NULL;
}
avio_ctx_buffer = (uint8_t*)av_malloc(avio_ctx_buffer_size);
if (!avio_ctx_buffer) {
fprintf(stderr, "Failed to make avio_ctx buf\n");
avformat_close_input(&fmt_ctx);
fmt_ctx = NULL;
return NULL;
}
avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,
0, &bd, &read_packet, NULL, NULL);
if (!avio_ctx) {
fprintf(stderr, "Failed to make avio_ctx\n");
avformat_close_input(&fmt_ctx);
av_freep(&avio_ctx_buffer);
fmt_ctx = NULL;
return NULL;
}
fmt_ctx->pb = avio_ctx;
int ret = avformat_open_input(&fmt_ctx, NULL, NULL, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open input\n");
avformat_close_input(&fmt_ctx);
avio_ctx_buffer = NULL;
fmt_ctx = NULL;
avio_ctx = NULL;
}
return fmt_ctx;
}
AVStream* analyze(uint8_t* mem, size_t size){
if(discoveryBuf.size() < discoveryMaxBytes){
for(int i = 0; i < size; i++){
discoveryBuf.push_back(mem[i]);
}
}
if (discoveryBuf.size() < discoveryMinBytes){
return NULL;
}
bd.ptr = discoveryBuf.data();
bd.size = discoveryBuf.size();
bd.size_total = discoveryBuf.size();
if (!init()){
return NULL;
}
int ret = avformat_find_stream_info(fmt_ctx, NULL);
if (ret < 0) {
fprintf(stderr, "Could not find stream information\n");
return NULL;
}
discoveryBuf.clear();
return fmt_ctx->streams[0];
}
};
StreamInfo::StreamInfo():impl(std::make_shared<StreamInfo::Impl>()){}
AVStream* StreamInfo::analyze(uint8_t* mem, size_t size){
return impl->analyze(mem, size);
}
}