nopenpilot/tools/clib/FrameReader.cpp

177 lines
4.3 KiB
C++

#include "FrameReader.hpp"
#include <assert.h>
#include <unistd.h>
static int ffmpeg_lockmgr_cb(void **arg, enum AVLockOp op) {
pthread_mutex_t *mutex = (pthread_mutex_t *)*arg;
int err;
switch (op) {
case AV_LOCK_CREATE:
mutex = (pthread_mutex_t *)malloc(sizeof(*mutex));
if (!mutex)
return AVERROR(ENOMEM);
if ((err = pthread_mutex_init(mutex, NULL))) {
free(mutex);
return AVERROR(err);
}
*arg = mutex;
return 0;
case AV_LOCK_OBTAIN:
if ((err = pthread_mutex_lock(mutex)))
return AVERROR(err);
return 0;
case AV_LOCK_RELEASE:
if ((err = pthread_mutex_unlock(mutex)))
return AVERROR(err);
return 0;
case AV_LOCK_DESTROY:
if (mutex)
pthread_mutex_destroy(mutex);
free(mutex);
*arg = NULL;
return 0;
}
return 1;
}
FrameReader::FrameReader(const char *fn) {
int ret;
ret = av_lockmgr_register(ffmpeg_lockmgr_cb);
assert(ret >= 0);
avformat_network_init();
av_register_all();
snprintf(url, sizeof(url)-1, "http://data.comma.life/%s", fn);
t = new std::thread([&]() { this->loaderThread(); });
}
void FrameReader::loaderThread() {
int ret;
if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0) {
fprintf(stderr, "error loading %s\n", url);
valid = false;
return;
}
av_dump_format(pFormatCtx, 0, url, 0);
auto pCodecCtxOrig = pFormatCtx->streams[0]->codec;
auto pCodec = avcodec_find_decoder(pCodecCtxOrig->codec_id);
assert(pCodec != NULL);
pCodecCtx = avcodec_alloc_context3(pCodec);
ret = avcodec_copy_context(pCodecCtx, pCodecCtxOrig);
assert(ret == 0);
ret = avcodec_open2(pCodecCtx, pCodec, NULL);
assert(ret >= 0);
sws_ctx = sws_getContext(width, height, AV_PIX_FMT_YUV420P,
width, height, AV_PIX_FMT_BGR24,
SWS_BILINEAR, NULL, NULL, NULL);
assert(sws_ctx != NULL);
AVPacket *pkt = (AVPacket *)malloc(sizeof(AVPacket));
assert(pkt != NULL);
bool first = true;
while (av_read_frame(pFormatCtx, pkt)>=0) {
//printf("%d pkt %d %d\n", pkts.size(), pkt->size, pkt->pos);
if (first) {
AVFrame *pFrame = av_frame_alloc();
int frameFinished;
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, pkt);
first = false;
}
pkts.push_back(pkt);
pkt = (AVPacket *)malloc(sizeof(AVPacket));
assert(pkt != NULL);
}
free(pkt);
printf("framereader download done\n");
joined = true;
// cache
while (1) {
GOPCache(to_cache.get());
}
}
void FrameReader::GOPCache(int idx) {
AVFrame *pFrame;
int gop = idx - idx%15;
mcache.lock();
bool has_gop = cache.find(gop) != cache.end();
mcache.unlock();
if (!has_gop) {
//printf("caching %d\n", gop);
for (int i = gop; i < gop+15; i++) {
if (i >= pkts.size()) break;
//printf("decode %d\n", i);
int frameFinished;
pFrame = av_frame_alloc();
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, pkts[i]);
uint8_t *dat = toRGB(pFrame)->data[0];
mcache.lock();
cache.insert(std::make_pair(i, dat));
mcache.unlock();
}
}
}
AVFrame *FrameReader::toRGB(AVFrame *pFrame) {
AVFrame *pFrameRGB = av_frame_alloc();
int numBytes = avpicture_get_size(AV_PIX_FMT_BGR24, pFrame->width, pFrame->height);
uint8_t *buffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_BGR24, pFrame->width, pFrame->height);
sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
pFrame->linesize, 0, pFrame->height,
pFrameRGB->data, pFrameRGB->linesize);
return pFrameRGB;
}
uint8_t *FrameReader::get(int idx) {
if (!valid) return NULL;
waitForReady();
// TODO: one line?
uint8_t *dat = NULL;
// lookahead
to_cache.put(idx);
to_cache.put(idx+15);
mcache.lock();
auto it = cache.find(idx);
if (it != cache.end()) {
dat = it->second;
}
mcache.unlock();
if (dat == NULL) {
to_cache.put_front(idx);
// lookahead
while (dat == NULL) {
// wait for frame
usleep(50*1000);
// check for frame
mcache.lock();
auto it = cache.find(idx);
if (it != cache.end()) dat = it->second;
mcache.unlock();
if (dat == NULL) {
printf(".");
fflush(stdout);
}
}
}
return dat;
}