Fraps: output repeat frames instead of discarding them
Signed-off-by: Javier Cabezas <jcabgz@gmail.com> Signed-off-by: Hendrik Leppkes <h.leppkes@gmail.com>
This commit is contained in:
parent
57d913f889
commit
b98d59b30e
|
@ -42,6 +42,7 @@
|
|||
#include "bswapdsp.h"
|
||||
#include "codec_internal.h"
|
||||
#include "thread.h"
|
||||
#include "threadframe.h"
|
||||
|
||||
#define FPS_TAG MKTAG('F', 'P', 'S', 'x')
|
||||
#define VLC_BITS 11
|
||||
|
@ -52,10 +53,15 @@
|
|||
typedef struct FrapsContext {
|
||||
AVCodecContext *avctx;
|
||||
BswapDSPContext bdsp;
|
||||
int cur_index, prev_index;
|
||||
int next_cur_index, next_prev_index;
|
||||
ThreadFrame frames[2];
|
||||
uint8_t *tmpbuf;
|
||||
int tmpbuf_size;
|
||||
} FrapsContext;
|
||||
|
||||
static av_cold int decode_end(AVCodecContext *avctx);
|
||||
|
||||
/**
|
||||
* initializes decoder
|
||||
* @param avctx codec context
|
||||
|
@ -64,12 +70,46 @@ typedef struct FrapsContext {
|
|||
static av_cold int decode_init(AVCodecContext *avctx)
|
||||
{
|
||||
FrapsContext * const s = avctx->priv_data;
|
||||
int i;
|
||||
|
||||
s->prev_index = 0;
|
||||
s->cur_index = 1;
|
||||
|
||||
s->avctx = avctx;
|
||||
s->tmpbuf = NULL;
|
||||
|
||||
ff_bswapdsp_init(&s->bdsp);
|
||||
|
||||
for (i = 0; i < FF_ARRAY_ELEMS(s->frames); i++) {
|
||||
s->frames[i].f = av_frame_alloc();
|
||||
if (!s->frames[i].f) {
|
||||
decode_end(avctx);
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_thread_context(AVCodecContext *avctx, const AVCodecContext *avctx_from)
|
||||
{
|
||||
FrapsContext *dst = avctx->priv_data, *src = avctx_from->priv_data;
|
||||
int i, ret;
|
||||
|
||||
if (avctx == avctx_from) return 0;
|
||||
|
||||
dst->cur_index = src->next_cur_index;
|
||||
dst->prev_index = src->next_prev_index;
|
||||
|
||||
for (i = 0; i < FF_ARRAY_ELEMS(dst->frames); i++) {
|
||||
ff_thread_release_ext_buffer(&dst->frames[i]);
|
||||
if (src->frames[i].f->data[0]) {
|
||||
ret = ff_thread_ref_frame(&dst->frames[i], &src->frames[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -132,18 +172,52 @@ static int fraps2_decode_plane(FrapsContext *s, uint8_t *dst, int stride, int w,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int decode_frame(AVCodecContext *avctx, AVFrame *f,
|
||||
static void frame_copy(FrapsContext *s,
|
||||
uint8_t *dst_data[3], const int dst_linesizes[3],
|
||||
uint8_t *src_data[3], const int src_linesizes[3],
|
||||
unsigned int version, int width, int height)
|
||||
{
|
||||
int i, k, h, bwidth;
|
||||
uint8_t *src, *dst;
|
||||
int planes = (version & 1) ? 1 : 3;
|
||||
|
||||
for (i = 0; i < planes; i++) {
|
||||
dst = dst_data[i];
|
||||
src = src_data[i];
|
||||
if (version & 1) {
|
||||
/* RGB data */
|
||||
h = height;
|
||||
bwidth = width * 3;
|
||||
} else {
|
||||
/* YUV 4:2:0 data */
|
||||
h = i ? height >> 1 : height;
|
||||
bwidth = i ? width >> 1 : width;
|
||||
}
|
||||
|
||||
ff_thread_await_progress(&s->frames[s->prev_index], i, 0);
|
||||
for (k = 0; k < h; k++) {
|
||||
memcpy(dst, src, bwidth);
|
||||
dst += dst_linesizes[i];
|
||||
src += src_linesizes[i];
|
||||
}
|
||||
ff_thread_report_progress(&s->frames[s->cur_index], i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int decode_frame(AVCodecContext *avctx, AVFrame *fout,
|
||||
int *got_frame, AVPacket *avpkt)
|
||||
{
|
||||
FrapsContext * const s = avctx->priv_data;
|
||||
const uint8_t *buf = avpkt->data;
|
||||
int buf_size = avpkt->size;
|
||||
ThreadFrame *frame, *prev_frame;
|
||||
AVFrame *f;
|
||||
uint32_t header;
|
||||
unsigned int version,header_size;
|
||||
const uint32_t *buf32;
|
||||
uint32_t *luma1,*luma2,*cb,*cr;
|
||||
uint32_t offs[4];
|
||||
int i, j, ret, is_chroma;
|
||||
int i, j, ret, is_chroma, is_Pframe;
|
||||
const int planes = 3;
|
||||
int is_pal;
|
||||
uint8_t *out;
|
||||
|
@ -153,6 +227,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f,
|
|||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
frame = &s->frames[s->cur_index];
|
||||
prev_frame = &s->frames[s->prev_index];
|
||||
f = frame->f;
|
||||
|
||||
header = AV_RL32(buf);
|
||||
version = header & 0xff;
|
||||
is_pal = buf[1] == 2 && version == 1;
|
||||
|
@ -179,22 +257,16 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f,
|
|||
if (version == 0) needed_size /= 2;
|
||||
needed_size += header_size;
|
||||
/* bit 31 means same as previous pic */
|
||||
if (header & (1U<<31)) {
|
||||
*got_frame = 0;
|
||||
return buf_size;
|
||||
}
|
||||
if (buf_size != needed_size) {
|
||||
is_Pframe = (header & (1U<<31)) ? 1 : 0;
|
||||
if (!is_Pframe && buf_size != needed_size) {
|
||||
av_log(avctx, AV_LOG_ERROR,
|
||||
"Invalid frame length %d (should be %d)\n",
|
||||
buf_size, needed_size);
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
} else {
|
||||
/* skip frame */
|
||||
if (buf_size == 8) {
|
||||
*got_frame = 0;
|
||||
return buf_size;
|
||||
}
|
||||
is_Pframe = buf_size == 8 ? 1 : 0;
|
||||
if (!is_Pframe) {
|
||||
if (AV_RL32(buf) != FPS_TAG || buf_size < planes*1024 + 24) {
|
||||
av_log(avctx, AV_LOG_ERROR, "error in data stream\n");
|
||||
return AVERROR_INVALIDDATA;
|
||||
|
@ -212,19 +284,43 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f,
|
|||
if (!s->tmpbuf)
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f->pict_type = AV_PICTURE_TYPE_I;
|
||||
f->flags |= AV_FRAME_FLAG_KEY;
|
||||
if (is_Pframe && !prev_frame->f->data[0]) {
|
||||
av_log(avctx, AV_LOG_ERROR, "decoding must start with keyframe\n");
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
ff_thread_release_ext_buffer(frame);
|
||||
|
||||
f->pict_type = is_Pframe ? AV_PICTURE_TYPE_P : AV_PICTURE_TYPE_I;
|
||||
f->flags |= is_Pframe ? 0 : AV_FRAME_FLAG_KEY;;
|
||||
|
||||
avctx->pix_fmt = version & 1 ? is_pal ? AV_PIX_FMT_PAL8 : AV_PIX_FMT_BGR24 : AV_PIX_FMT_YUVJ420P;
|
||||
avctx->color_range = version & 1 ? AVCOL_RANGE_UNSPECIFIED
|
||||
: AVCOL_RANGE_JPEG;
|
||||
avctx->colorspace = version & 1 ? AVCOL_SPC_UNSPECIFIED : AVCOL_SPC_BT709;
|
||||
|
||||
if ((ret = ff_thread_get_buffer(avctx, f, 0)) < 0)
|
||||
if ((ret = ff_thread_get_ext_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0)
|
||||
return ret;
|
||||
|
||||
s->next_prev_index = s->cur_index;
|
||||
s->next_cur_index = (s->cur_index - 1) & 1;
|
||||
|
||||
ff_thread_finish_setup(avctx);
|
||||
|
||||
/* Copy previous frame */
|
||||
if (is_Pframe) {
|
||||
frame_copy(s,
|
||||
frame->f->data,
|
||||
frame->f->linesize,
|
||||
prev_frame->f->data,
|
||||
prev_frame->f->linesize,
|
||||
version, avctx->width, avctx->height);
|
||||
goto end;
|
||||
}
|
||||
|
||||
switch (version) {
|
||||
case 0:
|
||||
default:
|
||||
|
@ -250,6 +346,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f,
|
|||
*cb++ = *buf32++;
|
||||
}
|
||||
}
|
||||
ff_thread_report_progress(frame, INT_MAX, 0);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
|
@ -272,6 +369,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f,
|
|||
&buf[y * avctx->width * 3],
|
||||
3 * avctx->width);
|
||||
}
|
||||
ff_thread_report_progress(frame, INT_MAX, 0);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
@ -288,8 +386,13 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f,
|
|||
buf + offs[i], offs[i + 1] - offs[i],
|
||||
is_chroma, 1)) < 0) {
|
||||
av_log(avctx, AV_LOG_ERROR, "Error decoding plane %i\n", i);
|
||||
return ret;
|
||||
}
|
||||
if (avctx->active_thread_type & FF_THREAD_FRAME) {
|
||||
ff_thread_report_progress(frame, INT_MAX, 0);
|
||||
break;
|
||||
} else
|
||||
return ret;
|
||||
} else
|
||||
ff_thread_report_progress(frame, i, 0);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
|
@ -300,7 +403,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f,
|
|||
-f->linesize[0], avctx->width, avctx->height,
|
||||
buf + offs[i], offs[i + 1] - offs[i], 0, 3)) < 0) {
|
||||
av_log(avctx, AV_LOG_ERROR, "Error decoding plane %i\n", i);
|
||||
return ret;
|
||||
if (avctx->active_thread_type & FF_THREAD_FRAME)
|
||||
break;
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
out = f->data[0];
|
||||
|
@ -314,11 +420,21 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f,
|
|||
}
|
||||
out += f->linesize[0] - 3*avctx->width;
|
||||
}
|
||||
ff_thread_report_progress(frame, INT_MAX, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
end:
|
||||
if ((ret = av_frame_ref(fout, frame->f)) < 0)
|
||||
return ret;
|
||||
*got_frame = 1;
|
||||
|
||||
s->prev_index = s->next_prev_index;
|
||||
s->cur_index = s->next_cur_index;
|
||||
|
||||
/* Only release frames that aren't used anymore */
|
||||
ff_thread_release_ext_buffer(&s->frames[s->cur_index]);
|
||||
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
|
@ -330,8 +446,16 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f,
|
|||
static av_cold int decode_end(AVCodecContext *avctx)
|
||||
{
|
||||
FrapsContext *s = avctx->priv_data;
|
||||
int i;
|
||||
|
||||
av_freep(&s->tmpbuf);
|
||||
|
||||
for (i = 0; i < FF_ARRAY_ELEMS(s->frames); i++) {
|
||||
if (s->frames[i].f)
|
||||
ff_thread_release_ext_buffer(&s->frames[i]);
|
||||
av_frame_free(&s->frames[i].f);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -345,4 +469,6 @@ const FFCodec ff_fraps_decoder = {
|
|||
.close = decode_end,
|
||||
FF_CODEC_DECODE_CB(decode_frame),
|
||||
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS,
|
||||
.caps_internal = FF_CODEC_CAP_ALLOCATE_PROGRESS,
|
||||
UPDATE_THREAD_CONTEXT(update_thread_context),
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue