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.

190 lines
5.2 KiB

#include "selfdrive/ui/replay/framereader.h"
#include <cassert>
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
}
class AVInitializer {
public:
AVInitializer() {
int ret = av_lockmgr_register(ffmpeg_lockmgr_cb);
assert(ret >= 0);
av_register_all();
avformat_network_init();
}
5 years ago
~AVInitializer() { avformat_network_deinit(); }
};
FrameReader::FrameReader() {
static AVInitializer av_initializer;
5 years ago
}
FrameReader::~FrameReader() {
// wait until thread is finished.
exit_ = true;
cv_decode_.notify_all();
cv_frame_.notify_all();
if (decode_thread_.joinable()) {
decode_thread_.join();
}
// free all.
for (auto &f : frames_) {
av_free_packet(&f.pkt);
}
if (frmRgb_) {
av_frame_free(&frmRgb_);
}
if (pCodecCtx_) {
avcodec_close(pCodecCtx_);
avcodec_free_context(&pCodecCtx_);
5 years ago
}
if (pFormatCtx_) {
avformat_close_input(&pFormatCtx_);
}
if (sws_ctx_) {
sws_freeContext(sws_ctx_);
}
}
5 years ago
bool FrameReader::load(const std::string &url) {
pFormatCtx_ = avformat_alloc_context();
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
ret = avcodec_open2(pCodecCtx_, pCodec, NULL);
if (ret < 0) return false;
5 years ago
width = pCodecCtxOrig->width;
height = pCodecCtxOrig->height;
sws_ctx_ = sws_getContext(width, height, AV_PIX_FMT_YUV420P,
width, height, AV_PIX_FMT_BGR24,
SWS_BILINEAR, NULL, NULL, NULL);
if (!sws_ctx_) return false;
frmRgb_ = av_frame_alloc();
if (!frmRgb_) return false;
5 years ago
frames_.reserve(60 * 20); // 20fps, one minute
do {
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
}
} while (!exit_);
if (valid_) {
decode_thread_ = std::thread(&FrameReader::decodeThread, this);
}
return valid_;
}
std::optional<std::pair<uint8_t *, uint8_t *>> FrameReader::get(int idx) {
if (!valid_ || idx < 0 || idx >= frames_.size()) {
return std::nullopt;
}
std::unique_lock lk(mutex_);
decode_idx_ = idx;
cv_decode_.notify_one();
cv_frame_.wait(lk, [=] { return exit_ || frames_[idx].rgb_data || frames_[idx].failed; });
if (!frames_[idx].rgb_data) {
return std::nullopt;
}
return std::make_pair(frames_[idx].rgb_data.get(), frames_[idx].yuv_data.get());
5 years ago
}
void FrameReader::decodeThread() {
int idx = 0;
while (!exit_) {
const int from = std::max(idx - 15, 0);
const int to = std::min(idx + 20, (int)frames_.size());
for (int i = 0; i < frames_.size() && !exit_; ++i) {
Frame &frame = frames_[i];
if (i >= from && i < to) {
if (frame.rgb_data || frame.failed) continue;
auto [rgb_data, yuv_data] = decodeFrame(&frame.pkt);
std::unique_lock lk(mutex_);
frame.rgb_data.reset(rgb_data);
frame.yuv_data.reset(yuv_data);
frame.failed = !rgb_data;
cv_frame_.notify_all();
} else {
frame.rgb_data.reset(nullptr);
frame.yuv_data.reset(nullptr);
frame.failed = false;
}
}
5 years ago
// sleep & wait
std::unique_lock lk(mutex_);
cv_decode_.wait(lk, [=] { return exit_ || decode_idx_ != -1; });
idx = decode_idx_;
decode_idx_ = -1;
5 years ago
}
}
std::pair<uint8_t *, uint8_t *> FrameReader::decodeFrame(AVPacket *pkt) {
uint8_t *rgb_data = nullptr, *yuv_data = nullptr;
int gotFrame = 0;
AVFrame *f = av_frame_alloc();
avcodec_decode_video2(pCodecCtx_, f, &gotFrame, pkt);
if (gotFrame) {
rgb_data = new uint8_t[getRGBSize()];
yuv_data = new uint8_t[getYUVSize()];
int i, j, k;
for (i = 0; i < f->height; i++) {
memcpy(yuv_data + f->width * i, f->data[0] + f->linesize[0] * i, f->width);
}
for (j = 0; j < f->height / 2; j++) {
memcpy(yuv_data + 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(yuv_data + f->width * i + f->width / 2 * j + f->width / 2 * k, f->data[2] + f->linesize[2] * k, f->width / 2);
}
5 years ago
int ret = avpicture_fill((AVPicture *)frmRgb_, rgb_data, AV_PIX_FMT_BGR24, f->width, f->height);
assert(ret > 0);
if (sws_scale(sws_ctx_, (const uint8_t **)f->data, f->linesize, 0, f->height, frmRgb_->data, frmRgb_->linesize) <= 0) {
delete[] rgb_data;
delete[] yuv_data;
rgb_data = yuv_data = nullptr;
}
5 years ago
}
av_frame_free(&f);
return {rgb_data, yuv_data};
5 years ago
}