1
0
Fork 0

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:
Javier Cabezas 2013-05-25 18:51:29 +02:00 committed by Hendrik Leppkes
parent 57d913f889
commit b98d59b30e
Signed by: hendrik
GPG Key ID: 846079A4B0A7C1B5
1 changed files with 144 additions and 18 deletions

View File

@ -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),
};