mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
rvp8: per-macroblock info kept across key frames
A key frame freed the per-macroblock info array, and the next inter frame calloc'd it again, so every key frame of a stream playing cost a free and an allocation of mbw * mbh entries (8160 at 1080p) in the middle of playback. The video decoder now owns the array for its life and grows it only when a key frame brings more macroblocks. A key frame still detaches it from the decode state, and the first inter frame after one clears the entries it uses, which is what the calloc gave it, so the decode reads exactly what it did. A failed begin leaves the array with the decoder instead of freeing it. Output is bit-identical to before on every stream in the VP8 set (altref, error-resilient, partitions, odd sizes, 1080p, noisy), a keyframe-every-7 stream, one switching between three frame sizes mid-stream, and 120 corrupted streams; ASan/UBSan and LeakSanitizer clean; builds C89, as C++ and for aarch64. Heap allocations for the 4 s keyframe-every-7 clip: 23 -> 9.
This commit is contained in:
1 parent
76502adbb2
commit
5c6b2a3867
1 file changed
+28
-9
@@ -3795,6 +3795,14 @@ struct rvp8_video
|
||||
uint8_t *arena[4];
|
||||
uint8_t *ry[4], *ru[4], *rv[4];
|
||||
size_t arena_len[4];
|
||||
/* The per-macroblock info inter frames build their motion vectors
|
||||
* from, kept for the decoder's life and grown only when a key frame
|
||||
* brings more macroblocks. A key frame used to free it and the
|
||||
* next inter frame calloc it again, so every key frame cost a
|
||||
* free and an allocation mid-stream; now the first inter frame
|
||||
* after one clears it, which is what the calloc gave it. */
|
||||
rvp8_mbinfo *mbi;
|
||||
size_t mbi_cap; /* entries */
|
||||
unsigned refs[3];
|
||||
int w, h, mbw, mbh, ys, uvs;
|
||||
int have_refs;
|
||||
@@ -3811,7 +3819,9 @@ void rvp8_video_close(rvp8_video *v)
|
||||
if (!v) return;
|
||||
for (i = 0; i < 4; i++)
|
||||
free(v->arena[i]);
|
||||
free(v->s.mb_info);
|
||||
if (v->s.mb_info != v->mbi)
|
||||
free(v->s.mb_info);
|
||||
free(v->mbi);
|
||||
free(v);
|
||||
}
|
||||
|
||||
@@ -3824,7 +3834,6 @@ int rvp8_video_decode(rvp8_video *v, const uint8_t *data, size_t len)
|
||||
uint8_t sv_cprob[sizeof(v->s.cprob)], sv_mvc[sizeof(v->s.mvc)];
|
||||
uint8_t sv_ym[sizeof(v->s.ymode_prob)];
|
||||
uint8_t sv_uvm[sizeof(v->s.uvmode_prob)];
|
||||
void *mb_info;
|
||||
unsigned work, i;
|
||||
int kf, ret;
|
||||
if (len < 3) return -1;
|
||||
@@ -3840,12 +3849,26 @@ int rvp8_video_decode(rvp8_video *v, const uint8_t *data, size_t len)
|
||||
if (kf)
|
||||
{
|
||||
v->s.is_inter = 0;
|
||||
free(v->s.mb_info);
|
||||
v->s.mb_info = NULL;
|
||||
v->s.mb_info = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
v->s.is_inter = 1;
|
||||
if (!v->s.mb_info)
|
||||
{
|
||||
size_t n = (size_t)v->mbw * (size_t)v->mbh;
|
||||
if (n > v->mbi_cap)
|
||||
{
|
||||
free(v->mbi);
|
||||
v->mbi_cap = 0;
|
||||
if (!(v->mbi = (rvp8_mbinfo*)calloc(n, sizeof(rvp8_mbinfo))))
|
||||
return -1;
|
||||
v->mbi_cap = n;
|
||||
}
|
||||
else
|
||||
memset(v->mbi, 0, n * sizeof(rvp8_mbinfo));
|
||||
v->s.mb_info = v->mbi;
|
||||
}
|
||||
v->s.w = v->w; v->s.h = v->h;
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
@@ -3880,15 +3903,11 @@ int rvp8_video_decode(rvp8_video *v, const uint8_t *data, size_t len)
|
||||
}
|
||||
v->s.arena = v->arena[work];
|
||||
v->s.arena_len = v->arena_len[work];
|
||||
mb_info = v->s.mb_info;
|
||||
ret = rvp8_begin(data, len, &v->s);
|
||||
v->arena[work] = v->s.arena;
|
||||
v->arena_len[work] = v->s.arena_len;
|
||||
if (ret != 0)
|
||||
{
|
||||
if (!v->s.mb_info) free(mb_info);
|
||||
return -1;
|
||||
}
|
||||
return -1; /* the abort cleared s.mb_info; v->mbi keeps it */
|
||||
v->ry[work] = v->s.yb;
|
||||
v->ru[work] = v->s.ub;
|
||||
v->rv[work] = v->s.vb;
|
||||
|
||||
Reference in new issue
Block a user