mirror of https://github.com/commaai/openpilot.git
177 lines
4.2 KiB
C++
177 lines
4.2 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,"%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;
|
|
}
|
|
|