|
|
|
@ -1,30 +1,10 @@ |
|
|
|
|
#include "selfdrive/ui/replay/framereader.h" |
|
|
|
|
|
|
|
|
|
#include <unistd.h> |
|
|
|
|
#include <cassert> |
|
|
|
|
#include <mutex> |
|
|
|
|
#include <sstream> |
|
|
|
|
|
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
int ffmpeg_lockmgr_cb(void **arg, enum AVLockOp op) { |
|
|
|
|
std::mutex *mutex = (std::mutex *)*arg; |
|
|
|
|
switch (op) { |
|
|
|
|
case AV_LOCK_CREATE: |
|
|
|
|
mutex = new std::mutex(); |
|
|
|
|
break; |
|
|
|
|
case AV_LOCK_OBTAIN: |
|
|
|
|
mutex->lock(); |
|
|
|
|
break; |
|
|
|
|
case AV_LOCK_RELEASE: |
|
|
|
|
mutex->unlock(); |
|
|
|
|
case AV_LOCK_DESTROY: |
|
|
|
|
delete mutex; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int readFunction(void *opaque, uint8_t *buf, int buf_size) { |
|
|
|
|
auto &iss = *reinterpret_cast<std::istringstream *>(opaque); |
|
|
|
|
iss.read(reinterpret_cast<char *>(buf), buf_size); |
|
|
|
@ -34,12 +14,6 @@ int readFunction(void *opaque, uint8_t *buf, int buf_size) { |
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
FrameReader::FrameReader(bool local_cache, int chunk_size, int retries) : FileReader(local_cache, chunk_size, retries) { |
|
|
|
|
static std::once_flag once_flag; |
|
|
|
|
std::call_once(once_flag, [] { |
|
|
|
|
av_lockmgr_register(ffmpeg_lockmgr_cb); |
|
|
|
|
av_register_all(); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
pFormatCtx_ = avformat_alloc_context(); |
|
|
|
|
av_frame_ = av_frame_alloc(); |
|
|
|
|
rgb_frame_ = av_frame_alloc(); |
|
|
|
@ -50,6 +24,7 @@ FrameReader::~FrameReader() { |
|
|
|
|
for (auto &f : frames_) { |
|
|
|
|
av_free_packet(&f.pkt); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (pCodecCtx_) avcodec_free_context(&pCodecCtx_); |
|
|
|
|
if (pFormatCtx_) avformat_close_input(&pFormatCtx_); |
|
|
|
|
if (av_frame_) av_frame_free(&av_frame_); |
|
|
|
@ -75,22 +50,22 @@ bool FrameReader::load(const std::string &url, std::atomic<bool> *abort) { |
|
|
|
|
pFormatCtx_->pb = avio_ctx_; |
|
|
|
|
|
|
|
|
|
pFormatCtx_->probesize = 10 * 1024 * 1024; // 10MB
|
|
|
|
|
int err = avformat_open_input(&pFormatCtx_, url.c_str(), NULL, NULL); |
|
|
|
|
if (err != 0) { |
|
|
|
|
int ret = avformat_open_input(&pFormatCtx_, url.c_str(), NULL, NULL); |
|
|
|
|
if (ret != 0) { |
|
|
|
|
char err_str[1024] = {0}; |
|
|
|
|
av_strerror(err, err_str, std::size(err_str)); |
|
|
|
|
av_strerror(ret, err_str, std::size(err_str)); |
|
|
|
|
printf("Error loading video - %s - %s\n", err_str, 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; |
|
|
|
|
auto pCodec = avcodec_find_decoder(pCodecCtxOrig->codec_id); |
|
|
|
|
AVStream *video = pFormatCtx_->streams[0]; |
|
|
|
|
auto pCodec = avcodec_find_decoder(video->codec->codec_id); |
|
|
|
|
if (!pCodec) return false; |
|
|
|
|
|
|
|
|
|
pCodecCtx_ = avcodec_alloc_context3(pCodec); |
|
|
|
|
int ret = avcodec_copy_context(pCodecCtx_, pCodecCtxOrig); |
|
|
|
|
ret = avcodec_parameters_to_context(pCodecCtx_, video->codecpar); |
|
|
|
|
if (ret != 0) return false; |
|
|
|
|
|
|
|
|
|
// pCodecCtx_->thread_count = 0;
|
|
|
|
@ -98,14 +73,14 @@ bool FrameReader::load(const std::string &url, std::atomic<bool> *abort) { |
|
|
|
|
ret = avcodec_open2(pCodecCtx_, pCodec, NULL); |
|
|
|
|
if (ret < 0) return false; |
|
|
|
|
|
|
|
|
|
width = (pCodecCtxOrig->width + 3) & ~3; |
|
|
|
|
height = pCodecCtxOrig->height; |
|
|
|
|
rgb_sws_ctx_ = sws_getContext(pCodecCtxOrig->width, pCodecCtxOrig->height, AV_PIX_FMT_YUV420P, |
|
|
|
|
width = (pCodecCtx_->width + 3) & ~3; |
|
|
|
|
height = pCodecCtx_->height; |
|
|
|
|
rgb_sws_ctx_ = sws_getContext(pCodecCtx_->width, pCodecCtx_->height, AV_PIX_FMT_YUV420P, |
|
|
|
|
width, height, AV_PIX_FMT_BGR24, |
|
|
|
|
SWS_FAST_BILINEAR, NULL, NULL, NULL); |
|
|
|
|
if (!rgb_sws_ctx_) return false; |
|
|
|
|
|
|
|
|
|
yuv_sws_ctx_ = sws_getContext(pCodecCtxOrig->width, pCodecCtxOrig->height, AV_PIX_FMT_YUV420P, |
|
|
|
|
yuv_sws_ctx_ = sws_getContext(pCodecCtx_->width, pCodecCtx_->height, AV_PIX_FMT_YUV420P, |
|
|
|
|
width, height, AV_PIX_FMT_YUV420P, |
|
|
|
|
SWS_FAST_BILINEAR, NULL, NULL, NULL); |
|
|
|
|
if (!yuv_sws_ctx_) return false; |
|
|
|
@ -113,10 +88,10 @@ bool FrameReader::load(const std::string &url, std::atomic<bool> *abort) { |
|
|
|
|
frames_.reserve(60 * 20); // 20fps, one minute
|
|
|
|
|
while (!(abort && *abort)) { |
|
|
|
|
Frame &frame = frames_.emplace_back(); |
|
|
|
|
err = av_read_frame(pFormatCtx_, &frame.pkt); |
|
|
|
|
if (err < 0) { |
|
|
|
|
ret = av_read_frame(pFormatCtx_, &frame.pkt); |
|
|
|
|
if (ret < 0) { |
|
|
|
|
frames_.pop_back(); |
|
|
|
|
valid_ = (err == AVERROR_EOF); |
|
|
|
|
valid_ = (ret == AVERROR_EOF); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
// some stream seems to contian no keyframes
|
|
|
|
@ -150,17 +125,27 @@ bool FrameReader::decode(int idx, uint8_t *rgb, uint8_t *yuv) { |
|
|
|
|
for (int i = from_idx; i <= idx; ++i) { |
|
|
|
|
Frame &frame = frames_[i]; |
|
|
|
|
if ((!frame.decoded || i == idx) && !frame.failed) { |
|
|
|
|
avcodec_decode_video2(pCodecCtx_, av_frame_, &frame.decoded, &(frame.pkt)); |
|
|
|
|
frame.decoded = decodeFrame(&frame.pkt); |
|
|
|
|
frame.failed = !frame.decoded; |
|
|
|
|
if (frame.decoded && i == idx) { |
|
|
|
|
return decodeFrame(av_frame_, rgb, yuv); |
|
|
|
|
return copyBuffers(av_frame_, rgb, yuv); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool FrameReader::decodeFrame(AVFrame *f, uint8_t *rgb, uint8_t *yuv) { |
|
|
|
|
bool FrameReader::decodeFrame(AVPacket *pkt) { |
|
|
|
|
int ret = avcodec_send_packet(pCodecCtx_, pkt); |
|
|
|
|
if (ret < 0) { |
|
|
|
|
printf("Error sending a packet for decoding\n"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
ret = avcodec_receive_frame(pCodecCtx_, av_frame_); |
|
|
|
|
return ret == 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool FrameReader::copyBuffers(AVFrame *f, uint8_t *rgb, uint8_t *yuv) { |
|
|
|
|
// images is going to be written to output buffers, no alignment (align = 1)
|
|
|
|
|
if (yuv) { |
|
|
|
|
av_image_fill_arrays(yuv_frame_->data, yuv_frame_->linesize, yuv, AV_PIX_FMT_YUV420P, width, height, 1); |
|
|
|
|