examples/decode_video: use a parser for splitting the input
authorAnton Khirnov <anton@khirnov.net>
Wed, 19 Oct 2016 19:56:22 +0000 (21:56 +0200)
committerAnton Khirnov <anton@khirnov.net>
Wed, 23 Nov 2016 12:10:28 +0000 (13:10 +0100)
Do not rely on the decoder handling this, as it's not guaranteed to
work.

doc/examples/decode_video.c

index 43819ec..4c1068b 100644 (file)
@@ -50,16 +50,45 @@ static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize,
     fclose(f);
 }
 
+static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,
+                   const char *filename)
+{
+    char buf[1024];
+    int ret, got_picture;
+
+    while (pkt->size > 0) {
+        ret = avcodec_decode_video2(dec_ctx, frame, &got_picture, pkt);
+        if (ret < 0) {
+            fprintf(stderr, "Error while decoding frame %d\n", dec_ctx->frame_number);
+            exit(1);
+        }
+        if (got_picture) {
+            printf("saving frame %3d\n", dec_ctx->frame_number);
+            fflush(stdout);
+
+            /* the picture is allocated by the decoder. no need to
+               free it */
+            snprintf(buf, sizeof(buf), filename, dec_ctx->frame_number);
+            pgm_save(frame->data[0], frame->linesize[0],
+                     frame->width, frame->height, buf);
+        }
+        pkt->size -= ret;
+        pkt->data += ret;
+    }
+}
+
 int main(int argc, char **argv)
 {
     const char *filename, *outfilename;
     const AVCodec *codec;
+    AVCodecParserContext *parser;
     AVCodecContext *c= NULL;
-    int frame, got_picture, len;
     FILE *f;
     AVFrame *picture;
     uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
-    char buf[1024];
+    uint8_t *data;
+    size_t   data_size;
+    int ret;
     AVPacket avpkt;
 
     if (argc <= 2) {
@@ -83,12 +112,15 @@ int main(int argc, char **argv)
         exit(1);
     }
 
+    parser = av_parser_init(codec->id);
+    if (!parser) {
+        fprintf(stderr, "parser not found\n");
+        exit(1);
+    }
+
     c = avcodec_alloc_context3(codec);
     picture = av_frame_alloc();
 
-    if (codec->capabilities & AV_CODEC_CAP_TRUNCATED)
-        c->flags |= AV_CODEC_FLAG_TRUNCATED; // we do not send complete frames
-
     /* For some codecs, such as msmpeg4 and mpeg4, width and height
        MUST be initialized there because this information is not
        available in the bitstream. */
@@ -105,70 +137,37 @@ int main(int argc, char **argv)
         exit(1);
     }
 
-    frame = 0;
-    for(;;) {
-        avpkt.size = fread(inbuf, 1, INBUF_SIZE, f);
-        if (avpkt.size == 0)
+    while (!feof(f)) {
+        /* read raw data from the input file */
+        data_size = fread(inbuf, 1, INBUF_SIZE, f);
+        if (!data_size)
             break;
 
-        /* NOTE1: some codecs are stream based (mpegvideo, mpegaudio)
-           and this is the only method to use them because you cannot
-           know the compressed data size before analysing it.
-
-           BUT some other codecs (msmpeg4, mpeg4) are inherently frame
-           based, so you must call them with all the data for one
-           frame exactly. You must also initialize 'width' and
-           'height' before initializing them. */
-
-        /* NOTE2: some codecs allow the raw parameters (frame size,
-           sample rate) to be changed at any frame. We handle this, so
-           you should also take care of it */
-
-        /* here, we use a stream based decoder (mpeg1video), so we
-           feed decoder and see if it could decode a frame */
-        avpkt.data = inbuf;
-        while (avpkt.size > 0) {
-            len = avcodec_decode_video2(c, picture, &got_picture, &avpkt);
-            if (len < 0) {
-                fprintf(stderr, "Error while decoding frame %d\n", frame);
+        /* use the parser to split the data into frames */
+        data = inbuf;
+        while (data_size > 0) {
+            ret = av_parser_parse2(parser, c, &avpkt.data, &avpkt.size,
+                                   data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
+            if (ret < 0) {
+                fprintf(stderr, "Error while parsing\n");
                 exit(1);
             }
-            if (got_picture) {
-                printf("saving frame %3d\n", frame);
-                fflush(stdout);
-
-                /* the picture is allocated by the decoder. no need to
-                   free it */
-                snprintf(buf, sizeof(buf), outfilename, frame);
-                pgm_save(picture->data[0], picture->linesize[0],
-                         c->width, c->height, buf);
-                frame++;
-            }
-            avpkt.size -= len;
-            avpkt.data += len;
+            data      += ret;
+            data_size -= ret;
+
+            if (avpkt.size)
+                decode(c, picture, &avpkt, outfilename);
         }
     }
 
-    /* Some codecs, such as MPEG, transmit the I- and P-frame with a
-       latency of one frame. You must do the following to have a
-       chance to get the last frame of the video. */
+    /* flush the decoder */
     avpkt.data = NULL;
     avpkt.size = 0;
-    len = avcodec_decode_video2(c, picture, &got_picture, &avpkt);
-    if (got_picture) {
-        printf("saving last frame %3d\n", frame);
-        fflush(stdout);
-
-        /* the picture is allocated by the decoder. no need to
-           free it */
-        snprintf(buf, sizeof(buf), outfilename, frame);
-        pgm_save(picture->data[0], picture->linesize[0],
-                 c->width, c->height, buf);
-        frame++;
-    }
+    decode(c, picture, &avpkt, outfilename);
 
     fclose(f);
 
+    av_parser_close(parser);
     avcodec_free_context(&c);
     av_frame_free(&picture);