[FFmpeg-devel] [PATCH 2/9] avcodec/sanm: better frame size detection for old codecs
Manuel Lauss
manuel.lauss at gmail.com
Tue Mar 11 17:03:34 EET 2025
The size of the video frame (FOBJ) of the old codecs (ANIMv0/1/2) can
very reliably be determined:
- ANIMv0/1 (=Rebel Assault 1) uses a 384x242 internal buffer for
everything. The codec parameters only describe the size and offset
of the specific FOBJ on that buffer.
- ANIMv2 titles usually use one of the fullscreen codecs (37/47/48)
as first FOBJ, and their dimensions can generally be trusted.
- RA2 uses 424x260 as internal buffer, use that if encountered.
- ignore sizes smaller than 2x2 or larger than 800x600 as those
don't exist in LucasArts titles.
- some videos have an initial fobj with either 1x1 or -1x-1
pixels in size, ignore them with a warning (Full Throttle
and the Rebel Assault 2 xxRETRY.SAN videos). Ignoring them
does not impact the final image.
Once known/valid dimensions have been discovered, use them and
don't change them for subsequent FOBJs, rather clamp the large frame
to the determined dimensions (The Dig's sq1.san).
Tested with RA1, RA2, Full Throttle, Dig, Outlaws, SotE and MotS
videos.
Signed-off-by: Manuel Lauss <manuel.lauss at gmail.com>
---
libavcodec/sanm.c | 59 ++++++++++++++++++++++++++++++++++++-----------
1 file changed, 45 insertions(+), 14 deletions(-)
diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index cbf78f33b9..ca345bc61a 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -264,7 +264,7 @@ typedef struct SANMVideoContext {
AVCodecContext *avctx;
GetByteContext gb;
- int version, subversion;
+ int version, subversion, have_dimensions;
uint32_t pal[PALETTE_SIZE];
int16_t delta_pal[PALETTE_DELTA];
@@ -1241,21 +1241,52 @@ static int process_frame_obj(SANMVideoContext *ctx)
uint16_t w = bytestream2_get_le16u(&ctx->gb);
uint16_t h = bytestream2_get_le16u(&ctx->gb);
- if (!w || !h) {
- av_log(ctx->avctx, AV_LOG_ERROR, "Dimensions are invalid.\n");
- return AVERROR_INVALIDDATA;
+ if (w < 2 || h < 2 || w > 800 || h > 600) {
+ av_log(ctx->avctx, AV_LOG_WARNING, "ignoring invalid fobj dimensions.\n");
+ return 0;
}
- if (ctx->width < left + w || ctx->height < top + h) {
- int ret = ff_set_dimensions(ctx->avctx, FFMAX(left + w, ctx->width),
- FFMAX(top + h, ctx->height));
- if (ret < 0)
- return ret;
- init_sizes(ctx, FFMAX(left + w, ctx->width),
- FFMAX(top + h, ctx->height));
- if (init_buffers(ctx)) {
- av_log(ctx->avctx, AV_LOG_ERROR, "Error resizing buffers.\n");
- return AVERROR(ENOMEM);
+ if (!ctx->have_dimensions) {
+ int xres, yres;
+ if (ctx->subversion < 2) {
+ /* Rebel Assault 1: 384x242 internal size */
+ xres = 384;
+ yres = 242;
+ ctx->have_dimensions = 1;
+ } else if (codec == 37 || codec == 47 || codec == 48) {
+ /* these codecs work on full frames, trust their dimensions */
+ xres = w;
+ yres = h;
+ ctx->have_dimensions = 1;
+ } else {
+ /* Rebel Assault 2: 424x260 internal size */
+ if (((left + w) == 424) && ((top + h) == 260))
+ ctx->have_dimensions = 1;
+
+ xres = FFMAX(left + w, ctx->width);
+ yres = FFMAX(top + h, ctx->height);
+ }
+
+ if (ctx->width < xres || ctx->height < yres) {
+ int ret = ff_set_dimensions(ctx->avctx, xres, yres);
+ if (ret < 0)
+ return ret;
+ init_sizes(ctx, xres, yres);
+ if (init_buffers(ctx)) {
+ av_log(ctx->avctx, AV_LOG_ERROR, "Error resizing buffers.\n");
+ return AVERROR(ENOMEM);
+ }
+ }
+ } else {
+ if ((left + w > ctx->width) || (top + h > ctx->height)) {
+ /* correct unexpected overly large frames.
+ * this happens for instance with The Dig's sq1.san video: it has
+ * a few (all black) 640x480 frames halfway in.
+ */
+ left = top = 0;
+ w = ctx->width;
+ h = ctx->height;
+ av_log(ctx->avctx, AV_LOG_WARNING, "resizing too large fobj.\n");
}
}
bytestream2_skip(&ctx->gb, 4);
--
2.48.1
More information about the ffmpeg-devel
mailing list