openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
4.4 KiB

#include "selfdrive/ui/replay/framereader.h"
#include <unistd.h>
#include <cassert>
#include <mutex>
#include "libyuv.h"
5 years ago
static int ffmpeg_lockmgr_cb(void **arg, enum AVLockOp op) {
std::mutex *mutex = (std::mutex *)*arg;
5 years ago
switch (op) {
case AV_LOCK_CREATE:
mutex = new std::mutex();
break;
5 years ago
case AV_LOCK_OBTAIN:
mutex->lock();
break;
5 years ago
case AV_LOCK_RELEASE:
mutex->unlock();
5 years ago
case AV_LOCK_DESTROY:
delete mutex;
break;
5 years ago
}
return 0;
5 years ago
}
struct AVInitializer {
AVInitializer() {
int ret = av_lockmgr_register(ffmpeg_lockmgr_cb);
assert(ret >= 0);
av_register_all();
avformat_network_init();
}
~AVInitializer() { avformat_network_deinit(); }
};
FrameReader::FrameReader() {
static AVInitializer av_initializer;
5 years ago
}
FrameReader::~FrameReader() {
for (auto &f : frames_) {
av_free_packet(&f.pkt);
}
if (pCodecCtx_) {
avcodec_close(pCodecCtx_);
avcodec_free_context(&pCodecCtx_);
5 years ago
}
if (pFormatCtx_) {
avformat_close_input(&pFormatCtx_);
}
if (av_frame_) {
av_frame_free(&av_frame_);
}
}
5 years ago
bool FrameReader::load(const std::string &url) {
pFormatCtx_ = avformat_alloc_context();
pFormatCtx_->probesize = 10 * 1024 * 1024; // 10MB
if (avformat_open_input(&pFormatCtx_, url.c_str(), NULL, NULL) != 0) {
printf("error loading %s\n", url.c_str());
return false;
}
avformat_find_stream_info(pFormatCtx_, NULL);
// av_dump_format(pFormatCtx_, 0, url.c_str(), 0);
auto pCodecCtxOrig = pFormatCtx_->streams[0]->codec;
5 years ago
auto pCodec = avcodec_find_decoder(pCodecCtxOrig->codec_id);
if (!pCodec) return false;
5 years ago
pCodecCtx_ = avcodec_alloc_context3(pCodec);
int ret = avcodec_copy_context(pCodecCtx_, pCodecCtxOrig);
if (ret != 0) return false;
5 years ago
pCodecCtx_->thread_count = 0;
pCodecCtx_->thread_type = FF_THREAD_FRAME;
ret = avcodec_open2(pCodecCtx_, pCodec, NULL);
if (ret < 0) return false;
5 years ago
av_frame_ = av_frame_alloc();
width = pCodecCtxOrig->width;
height = pCodecCtxOrig->height;
frames_.reserve(60 * 20); // 20fps, one minute
while (true) {
Frame &frame = frames_.emplace_back();
int err = av_read_frame(pFormatCtx_, &frame.pkt);
if (err < 0) {
frames_.pop_back();
valid_ = (err == AVERROR_EOF);
break;
5 years ago
}
// some stream seems to contian no keyframes
key_frames_count_ += frame.pkt.flags & AV_PKT_FLAG_KEY;
}
return valid_;
}
bool FrameReader::get(int idx, uint8_t *rgb, uint8_t *yuv) {
assert(rgb != nullptr);
if (!valid_ || idx < 0 || idx >= frames_.size()) {
return false;
}
return decode(idx, rgb, yuv);
5 years ago
}
bool FrameReader::decode(int idx, uint8_t *rgb, uint8_t *yuv) {
auto get_keyframe = [=](int idx) {
for (int i = idx; i >= 0 && key_frames_count_ > 1; --i) {
if (frames_[i].pkt.flags & AV_PKT_FLAG_KEY) return i;
}
return idx;
};
5 years ago
int from_idx = idx;
if (idx > 0 && !frames_[idx].decoded && !frames_[idx - 1].decoded) {
// find the previous keyframe
from_idx = get_keyframe(idx);
5 years ago
}
for (int i = from_idx; i <= idx; ++i) {
Frame &frame = frames_[i];
if ((!frame.decoded || i == idx) && !frame.failed) {
while (true) {
int ret = avcodec_decode_video2(pCodecCtx_, av_frame_, &frame.decoded, &(frame.pkt));
if (ret > 0 && !frame.decoded) {
// decode thread is still receiving the initial packets
usleep(0);
} else {
break;
}
}
frame.failed = !frame.decoded;
if (frame.decoded && i == idx) {
return decodeFrame(av_frame_, rgb, yuv);
}
}
}
return false;
}
bool FrameReader::decodeFrame(AVFrame *f, uint8_t *rgb, uint8_t *yuv) {
uint8_t *y = yuv;
if (!yuv) {
if (yuv_buf_.empty()) {
yuv_buf_.resize(getYUVSize());
}
y = yuv_buf_.data();
5 years ago
}
int i, j, k;
for (i = 0; i < f->height; i++) {
memcpy(y + f->width * i, f->data[0] + f->linesize[0] * i, f->width);
}
for (j = 0; j < f->height / 2; j++) {
memcpy(y + f->width * i + f->width / 2 * j, f->data[1] + f->linesize[1] * j, f->width / 2);
}
for (k = 0; k < f->height / 2; k++) {
memcpy(y + f->width * i + f->width / 2 * j + f->width / 2 * k, f->data[2] + f->linesize[2] * k, f->width / 2);
}
uint8_t *u = y + f->width * f->height;
uint8_t *v = u + (f->width / 2) * (f->height / 2);
libyuv::I420ToRGB24(y, f->width, u, f->width / 2, v, f->width / 2, rgb, f->width * 3, f->width, f->height);
return true;
5 years ago
}