[FFmpeg-devel] [PATCH 3/3] vc-1: Optimise parser (with special attention to ARM)

Ben Avison bavison at riscosopen.org
Wed Apr 23 02:32:45 CEST 2014


On Sun, 20 Apr 2014 04:22:25 +0100, Michael Niedermayer <michaelni at gmx.at> wrote:
> A parser can be fed with arbitrary pieces of data, for example
> a parser could receive only 1 byte at a time and never more
> above code looks like it needs more than 1 byte
[..]
> and make sure that cases where the start code is split over
> several calls to the parser work correctly

I did write the code with that in mind, however I was unable to find any
test clips that provoked these edge cases.

> also the unesc_buffer logic looks like it would fail if theres
> random data before a frame

That's not a problem - although it starts filling up the unesc_buffer
with any junk before the first startcode, prev_start_code is initialised
to 0 and so vc1_extract_header drops through the switch statement and
ignores it.

I wanted to be able to say with confidence that the code was known to
work in all cases, so I wrote the test program below. To the best of my
understanding, this calls vc1_parse() in the same manner as the rest of
the library does, and it allowed me to fuzz test different packet sizes
(and plenty of examples of start codes being split across buffer
boundaries). This did allow me to identify some subtle problems (when the
fast startcode search finds a match exactly one byte before the end of
the buffer, and specifically if the startcode that marks a frame boundary
is split across buffers). With the updated version of the patch that I
will be posting shortly, the stdout and stderr generated by this test
program are effectively identical, irrespective of the buffer size
chosen, from 1 byte upwards.

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include "libavcodec/avcodec.h"
#include "libavcodec/parser.h"
#include "libavcodec/vc1.h"
#include "libavutil/log.h"

#define BITSTREAM_SIZE 16384
#define MAX_STARTCODE_SEPARATION 100

extern AVCodecParser ff_vc1_parser;

int main(int argc, char *argv[])
{
      char priv_data[16384] = { 0 };
      AVCodecParserContext s = { .priv_data = priv_data };
      AVCodecContext avctx = { 0 };
      char bitstream[BITSTREAM_SIZE];
      char packet[BITSTREAM_SIZE] = { 0 };
      size_t packet_size;

      if (argc != 2)
      {
          fprintf(stderr, "Syntax: %s <packet size>\n", argv[0]);
          exit(EXIT_FAILURE);
      }
      packet_size = atoi(argv[1]);

      srand(0);
      for (size_t i = 0; i < BITSTREAM_SIZE; i++)
      {
          bitstream[i] = rand();
      }
      for (size_t i = rand() % MAX_STARTCODE_SEPARATION;
              i < BITSTREAM_SIZE - 4;
              i += 4 + (rand() % MAX_STARTCODE_SEPARATION))
      {
          bitstream[i+0] = 0;
          bitstream[i+1] = 0;
          bitstream[i+2] = 1;
          if (rand() & 1) {
              bitstream[i+3] = VC1_CODE_FRAME & 0xFF;
              printf("Frame header at %u\n", i);
          } else if (rand() & 1) {
              bitstream[i+3] = VC1_CODE_ENTRYPOINT & 0xFF;
              printf("Entry point header at %u\n", i);
          } else {
              bitstream[i+3] = VC1_CODE_SEQHDR & 0xFF;
              printf("Sequence header at %u\n", i);
          }
      }

      av_log_set_level(AV_LOG_DEBUG);

      ff_vc1_parser.parser_init(&s);
      for (size_t bitstream_index = 0;
              bitstream_index < BITSTREAM_SIZE;
              bitstream_index += packet_size)
      {
          size_t packet_max = BITSTREAM_SIZE - bitstream_index;
          if (packet_max > packet_size) packet_max = packet_size;
          /* Copy the data away to ensure the parser isn't reading
           * beyond the end of its supplied buffer */
          memcpy(packet, bitstream + bitstream_index, packet_max);
          for (size_t packet_index = 0; packet_index < packet_max; )
          {
              const uint8_t *outbuf;
              int outbuf_size;
              int delta = ff_vc1_parser.parser_parse(&s, &avctx,
                      &outbuf, &outbuf_size,
                      packet + packet_index, packet_max - packet_index);
              if (outbuf)
                  printf("Detected frame boundary at %u\n",
                          bitstream_index + packet_index + delta);
              if (delta == END_NOT_FOUND)
                  break;
              if (delta > 0)
                  packet_index += delta;
          }
      }
      /* Now the EOF packet */
      {
          const uint8_t *outbuf;
          int outbuf_size;
          ff_vc1_parser.parser_parse(&s, &avctx,
              &outbuf, &outbuf_size, packet /* crashes if null! */, 0);
          if (outbuf)
              printf("Detected frame boundary at EOF\n");
      }
      ff_vc1_parser.parser_close(&s);

      exit(EXIT_SUCCESS);
}

--


More information about the ffmpeg-devel mailing list