[Ffmpeg-devel] Re: Bethsoft VID demuxer and decoder

Michael Niedermayer michaelni
Tue Mar 27 23:13:37 CEST 2007


Hi

On Tue, Mar 27, 2007 at 11:07:26AM -0700, Nicholas T wrote:
[...]

> >trailing whitespace
> already fixed, please see previous message.

theres still trailing whitespace in the patch


> 
> >you missunderstand, no input valid or invalid may cause anything to be
> writen
> >to unallocated memory
> >
> >as one example why this is so bad lets assume the buffer is on the stack
> >each time a function is called the return address is put on the stack then
> >the code jumps to the code of the function and it reserves some space for
> >its local variables like our example buffer on the stack, now if we write
> >over the end of the buffer we may overwrite this return address.
> >but its not overwritten with any random value but rather with exactly what
> 
> >is stored in the file, lets assume that the new address will point into
> >some packet read from the file and that packet contains executable code
> >simply watching a video would turn your poor computer into someone elses
> >computer ;)
> >
> >also asserts are inappropriate to check input validity, an assert() simply
> >aborts the whole application if its not true. just think of printf()
> aborting
> >your editor (without it being able to safe your work) due to a failed
> assert()
> >similarely a video decoder may not abort the parent application because
> some
> >video is full of random damaged or even intentionally set bytes
> >
> >also asserts() are only enabled if debuging is so they are not appropriate
> 
> >to check for overflows as they simply arent always enabled ...
> I know why buffer overflows are bad...but what am I supposed to use instead
> of asserts?

return -1


> 
> >Vid is not correct as there are also audio packets
> The file format is called "VID", so I renamed this to "BVID", hopefully that
> will clear some things up

ok


[...]

> 
> >uneeded cast, also theres no sense in storing the rle decoded picture
> >we only need its length in the demuxer
> cast removed. what's the point of reading all of the file to get the length,
> then calling create packet, which
> will read it all over again?

its not the job of the demuxer to decode the video frames
the demuxer doesnt has access to AVCodecContext.get/release_buffer() which
means it cant use direct rendering
decoding in the demuxer breaks stream copy
if the video is stored in another container which does store the packet
size then a decoder seperate from a demuxer is needed
...


[...]

> +/** write data to a frame with extra bytes on the end. */
> +static void write_to_frame(uint8_t * frame, uint8_t * buffer,
> +                           int length, int x, int width, uint8_t * next_line)
> +{
> +    if(x + length > width)
> +    {
> +        // copy any bytes between the width and current position
> +        memcpy(frame, buffer, width - x);
> +        // copy on the new line anything that is left
> +        memcpy(next_line, &buffer[width - x], length - (width - x));
> +    }
> +    else { memcpy(frame, buffer, length); }
> +}
> +
> +/** get the next frame pointer */
> +static void get_next_frame_pointer(uint8_t ** line_start, int length,
> +                                        int * x, int width, uint8_t * next_line)
> +{
> +    if(*x + length > width) { *x = length - (width - *x); *line_start = next_line; }
> +    else { *x += length; }       // didn't go to the next line, only increment x
> +}

both these functions fail if a single run is over 3 lines

also x + length occurs 5 times in the 2 functions, it likely would be faster
not to redo this and hope the compiler would remove it
thats also one reason why i dont like to hide such trivial code in functions
it hides such trivial optimization opertunities


[...]
> +    else if(block_type == VIDEO_OFFSET_DIFFERENCE_FRAME_BLOCK)
> +    {
> +        line_start += vid->frame.linesize[0] * AV_RL16(buf);
> +        buf += 2;

bytestream_get_le16(&buf)


[...]
> +            if(block_type == VIDEO_FULL_FRAME_BLOCK)
> +            {
> +                assert(line_start + x + rle_num_bytes + (vid->linesize[0] - avctx->width) < frame_end);
> +                memset(scratch, buf++[0], rle_num_bytes);
> +                write_to_frame(&line_start[xoffset], scratch, rle_num_bytes,
> +                                xoffset, avctx->width, line_start + vid->frame.linesize[0]);

it should be more efficient to do the memsets directly into the destination
buffer


[...]
> +/** clean up resources */
> +static int bethsoftvid_decode_end(AVCodecContext *avctx)
> +{
> +    av_log(avctx, AV_LOG_VERBOSE, "[bethsoftvid video decoder] closing\n");
> +    return 0;
> +}

the function does nothing and is thus unneeded


[...]
> +    struct {
> +        int nframes;
> +        int frame_width;
> +        int frame_height;

> +        int npixels; // width * height

comment is not doxygen compatible


[...]
> +    struct {
> +        /** video presentation time stamp.
> +         * delay = 16 milliseconds * (global_delay + per_frame_delay) */
> +        /// video presentation time stamp

duplicate comment

[...]
> +    do
> +    {
> +        rle_num_bytes = get_byte(pb);
> +        vidbuf++[0] = rle_num_bytes;
> +
> +        // special codes for RLE: is an rle sequence (first case), is a plain sequence, 0 for stop
> +        if(rle_num_bytes > 0x80)
> +        {
> +            if(block_type == VIDEO_FULL_FRAME_BLOCK) { vidbuf++[0] = (uint8_t)get_byte(pb); }

the cast is unneeded


> +            bytes_copied += rle_num_bytes - 0x80;
> +        }
> +        else if(rle_num_bytes != 0)
> +        {
> +            if(get_buffer(pb, vidbuf, rle_num_bytes) != rle_num_bytes)
> +            { return AVERROR_IO; }
> +            vidbuf += rle_num_bytes;
> +            bytes_copied += rle_num_bytes;
> +        }
> +        if(bytes_copied == vid->header.npixels)
> +        {
> +            // may contain a 0 byte even if read all pixels
> +            if(get_byte(pb)) { url_fseek(pb, url_ftell(pb) - 1, SEEK_SET); }
> +            vidbuf++[0] = 0;        // set a zero so the decoder doesn't have to mess with this

the demuxer should not modify the packet beyond the absolute neccesary


> +            break;
> +        }
> +        assert(bytes_copied < vid->header.npixels);
> +    } while(rle_num_bytes);
> +
> +    // copy packet data, and set some other parameters
> +    vidbuf_nbytes = vidbuf - vidbuf_start; // doesn't undercount, increments to next byte above
> +    if(av_new_packet(pkt, vidbuf_nbytes)) { return AVERROR_NOMEM; }

calling av_new_packet(pkt) after setting pkt->pts of course cannot work


[...]
> +    static int is_finished;                         // if terminating block is reached, don't need to read junk afterwards

non constant static variables break thread safety


[...]
> +        case FIRST_AUDIO_BLOCK: av_log(s, AV_LOG_VERBOSE, "first audio block.\n"); exit(0);
> +        case AUDIO_BLOCK: av_log(s, AV_LOG_VERBOSE, "audio block.\n"); exit(0);

calling exit() in a demuxer is not ok


[...]
> +static int vid_read_close(AVFormatContext *s)
> +{
> +    av_log(s, AV_LOG_VERBOSE, "[bethsoftvid demuxer] closing\n");
> +    return 0;
> +}

this function does nothing so its unneeded

[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

it is not once nor twice but times without number that the same ideas make
their appearance in the world. -- Aristotle
-------------- 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/20070327/66bf3bbf/attachment.pgp>



More information about the ffmpeg-devel mailing list