[Ffmpeg-devel] LucasArts SANM (Smush v2) decoder

Michael Niedermayer michaelni
Wed Feb 28 00:47:09 CET 2007


Hi

On Tue, Feb 27, 2007 at 01:23:36AM -0500, Cyril Zorin wrote:
> Ok, here's the new patch. I've fixed pretty much everything except as  
> noted below. Let me know if I missed anything.
> 

[...]

> 
> >>+static edge_t which_edge(int x, int y, int edge_size)
> >>+{
> >>+    edge_t edge;
> >>+    const int edge_max = edge_size - 1;
> >>+
> >>+    if (!y)
> >>+    {
> >>+        edge = bottom_edge;
> >>+    }
> >>+    else if (edge_max == y)
> >>+    {
> >>+        edge = top_edge;
> >>+    }
> >>+    else if (!x)
> >>+    {
> >>+        edge = left_edge;
> >>+    }
> >>+    else if (edge_max == x)
> >>+    {
> >>+        edge = right_edge;
> >>+    }
> >>+    else
> >>+    {
> >>+        edge = no_edge;
> >>+    }
> >>+
> >>+    return edge;
> >>+}
> >
> >if this function needs more 16byte then replace it by a table as this
> >can be done with 2 simple 8 byte tables indexed by i>>1 where
> >x= pxvector[i], y = pyvector[i];
> 
> Can you explain this a little more? I'm not sure how the tables are  
> constructed.

ok, first the x,y argument is always from glyph4/8_x/y, they have just 16
entries each so you can simply hardcode all awnsers with 2 16 entry tables
one for glyph4_x/yand one for glyph8
when you do you will IIRC notice that entry 2i and 2i+1 are always the
same so you can cut the table size in half ...


> 
> >>+    {
> >>+        dir = dir_right;
> >>+    }
> >>+
> >>+    return dir;
> >>+}
> >
> >
> >also the meaning of top_edge, bottom_edge and dir_down and dir_up  
> >doesnt
> >match top_edge + top_edge -> dir_down but its really toward that  
> >edge not
> >away if i understood the code correctly
> 
> It's trying to decide which way to fill the glyph given a line with  
> two points v0 and v1. It will fill the inside of the smaller region  
> of the glyph enclosed by the line.
> 
> >also this can be done with a 25byte table so if thats smaller  
> >please use
> >a table, the code above is totally unreadable anyway
> 
> Can you explain how to convert this to a lookup table?

well te function has 2 values as input each has 5 possible values so
you can build a lut[5][5] which contains all the awnsers for all the
possible inputs


[...]
> >>+static void fill_db(uint16_t* pbuf, int buf_size, uint16_t color)
> >>+{
> >>+    while (buf_size--)
> >>+    {
> >>+        *pbuf++ = color;
> >>+    }
> >>+}
> >
> >code duplication see msmpeg4_memsetw()
> 
> That function is static within msmpeg4.c. Maybe we should move it to  
> some utility header file?

yes thats what i was thinking, create a new file like memset.c memset.h
and add the 16bit memset into, other developers could if needed add 32bit 
and float memset equivalents to it later ...


> 
> >>+static void rotate_bufs(sanm_ctx_t* pctx, int rotate_code)
> >>+{
> >>+    if (1 == rotate_code)
> >>+    {
> >>+        swap(&pctx->pdb0, &pctx->pdb2);
> >>+    }
> >>+    else if (2 == rotate_code)
> >>+    {
> >>+        swap(&pctx->pdb1, &pctx->pdb2);
> >>+        swap(&pctx->pdb2, &pctx->pdb0);
> >>+    }
> >>+}
> >
> >if(2 == rotate_code)
> >    swap(&pctx->pdb1, &pctx->pdb2);
> >swap(&pctx->pdb2, &pctx->pdb0);
> 
> There is no guarantee that the rotate code is is always 1 or 2, so I  
> like to make it more explicit by pointing out both cases. We don't  
> know what to do if the rotate code is anything other than 1 or 2, so  
> I think it's better to leave it like this until we have the  
> aforementioned guarantee.

indeed, but then you should add a check where the value is read,
something like:

if(rotate != 1 && rotate != 2){
    av_log(context, AV_LOG_ERROR, "unknown rotate code, please contact developers and provide this file to them!\n");
}


[...]
> 
> >>+static void copy_block(uint16_t* pdest, uint16_t* psrc, int  
> >>block_size, int pitch)
> >>+{
> >>+    int y;
> >>+    for (y = 0; y != block_size; ++y, pdest += pitch, psrc += pitch)
> >>+    {
> >>+        memcpy(pdest, psrc, block_size * sizeof(pdest[0]));
> >>+    }
> >>+}
> >
> >code duplication see DSPContext.put_pixels_tab
> 
> The smush block sizes are 8x8, 4x4, and 2x2. Is this supported by  
> put_pixels_tab?

hmm i do think so 8x8 is [1][0] 4x4 [2][0] and 2x2 [3][0]
if any of them is NULL then just forget my comment and use your code


[...]
> >>+static void copy_output(sanm_ctx_t* pctx, frame_header_t* pheader)
> >>+{
> >>+    uint8_t* poutput = pctx->output.data[0];
> >>+    const uint8_t* pinput = (uint8_t*) pctx->pdb0;
> >>+
> >>+    int height = pctx->height;
> >>+    long inpitch = pctx->pitch * sizeof(pctx->pdb0[0]);
> >>+    long outpitch = pctx->output.linesize[0];
> >>+    while (height--)
> >>+    {
> >>+        memcpy(poutput, pinput, inpitch);
> >>+        pinput += inpitch;
> >>+        poutput += outpitch;
> >>+    }
> >
> >this is unacceptable, either use the buffers provided by get_buffer()
> >or return your buffer
> 
> I call "get_buffer" in decode_frame -- is that enough?

the idea is to not memcpy the frame but either return your internal
buffer and if possible use get_buffer() to allocate it, but note
get_buffer() does not gurantee that linesize == width*pixel_size
linesize can be larger, if this is a problem then dont use get_buffer()


> 
> >>+static int dispatch_codec(sanm_ctx_t* pctx, const uint8_t* pinput)
> >>+{
> >
> >why not remove this function and place the code into decode_frame?
> 
> I like giving each function one job to do. One function deal with  
> buffers/contexts/etc., another to figure out how to decode.

ok


[...]
> >>+        case MKBETAG('B', 'l', '1', '6'):
> >>+            if (size != av_get_packet(pbuf, ppacket, size))
> >>+            {
> >>+                return AVERROR_IO;
> >
> >memleak at EOF?
> 
> Sorry I don't see it. How particularly?

well if there is less then size bytes left av_get_packet will read less
you return failure so the user application would assume that nothing
has been allocated and wont free the smaller packet


> 
> >>+            ppacket->stream_index = pctx->vinfo.stream_index;
> >>+            ppacket->pts = pctx->vinfo.pts;
> >>+            ++pctx->vinfo.pts;
> >
> >do NOT set pts unless there is a pts in the packet (pts++ indicates  
> >that
> >you dont have a pts)
> 
> Hmm, my mistake. Can you explain what pts is actually used for? I  
> more or less copied this from another decoder and forgot to ask what  
> it was for =)

pts = presentation time stamp, its simply the time when the packet
after decoding it should be shown to the user, the libavformat
core will add +1 for you if you do nothing, at least it should
if everything like frame_rate if set correctly


> 
> >>+            done = 1;
> >>+            break;
> >
> >instead of the done=1; break stuff everywhere why not return 0; ?
> 
> Because the audio info chunk contains a bunch of headers that are  
> skipped, and I need to make sure that the stream seeks to the right  
> place once we got the info we're interested in. So instead of copying  
> the url_fseek call twice in each case label, I think it's cleaner to  
> give it an exit condition and then do the necessary "cleanup" (i.e.  
> url_fseek) right before return.

hmm there are 2 switch() with these done = 1 one could simply be changed
to return 0;

the other could do 

case MKBETAG('W', 'a', 'v', 'e'):
    painfo->freq = get_le32(pbuf);
    painfo->nchannels = get_le32(pbuf);
default:
    url_seek ...
    return something

case MKBETAG('B', 'l', '1', '6'):
    url_fseek(pbuf, size, SEEK_CUR);
    break;

}

-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

Democracy is the form of government in which you can choose your dictator
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20070228/2caf8c6a/attachment.pgp>



More information about the ffmpeg-devel mailing list