On 1/5/2017 3:07 PM, Soft Works wrote:
> The following three commits created a regression by writing initially
> invalid mkv headers:
> 
> 650e17d88b63b5aca6e0a43483e89e64b0f7d2dd avformat/matroskaenc: write a
> CRC32 element on Tags
> 3bcadf822711720ff0f8d14db71ae47cdf97e652 avformat/matroskaenc: write a
> CRC32 element on Info
> ee888cfbe777cd2916a3548c750e433ab8f8e6a5 avformat/matroskaenc: postpone
> writing the Tracks master
> 
> Symptoms:
> 
> - You can no longer playback a file that is still processed by ffmpeg,
> e.g. VLC fails playback
> - You can no longer stream a file to a client while if is still being
> processed
> - Various diagnosing tools show header errors or incomplete headers
> (e.g. ffprobe, mediainfo, mkvalidator)
> 
> Note: The symptoms do not apply to completed files or ffmpeg runs that
> were interrupted with 'q'
> 
> Cause:
> 
> The mentioned commits made changes in a way that some header elements
> are only partially written in
> mkv_write_header, leaving the header in an invalid state. Only in
> mkv_write_trailer, these elements
> are finished correctly, but that does only occur at the end of the
> process.
> 
> Regression:
> 
> Before these commits were applied, mkv headers have always been valid,
> even before completion of ffmpeg.
> This has worked reliably over many versions of ffmpeg, to it was an
> obvious regression.
> 
> Bugtracker:
> 
> This issue has been recorded as #5977 which is resolved by this patch
> 
> Patch:
> 
> The patch adds a new function 'end_ebml_master_crc32_preliminary' that
> preliminarily finishes the ebl
> element without destroying the buffer. The buffer can be used to update
> the ebml element later during
> mkv_write_trailer. But most important: mkv_write_header finishes with a
> valid mkv header again.
> ---
>  libavformat/matroskaenc.c | 28 +++++++++++++++++++++++++---
>  1 file changed, 25 insertions(+), 3 deletions(-)
> 
> diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
> index 827d755..27d83a6 100644
> --- a/libavformat/matroskaenc.c
> +++ b/libavformat/matroskaenc.c
> @@ -367,6 +367,28 @@ static void end_ebml_master_crc32(AVIOContext *pb, 
> AVIOContext **dyn_cp, Matrosk
>      *dyn_cp = NULL;
>  }
>  
> +/**
> +* Complete ebml master whithout destroying the buffer, allowing for later 
> updates
> +*/
> +static void end_ebml_master_crc32_preliminary(AVIOContext *pb, AVIOContext 
> **dyn_cp, MatroskaMuxContext *mkv,
> +    ebml_master master)
> +{
> +    uint8_t *buf, crc[4];
> +    int size, skip = 0;
> +
> +    if (pb->seekable) {
> +
> +        size = avio_get_dyn_buf(*dyn_cp, &buf);
> +        if (mkv->write_crc && mkv->mode != MODE_WEBM) {
> +            skip = 6; /* Skip reserved 6-byte long void element from the 
> dynamic buffer. */
> +            AV_WL32(crc, av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), 
> UINT32_MAX, buf + skip, size - skip) ^ UINT32_MAX);
> +            put_ebml_binary(pb, EBML_ID_CRC32, crc, sizeof(crc));
> +        }

IMO, no point calculating and writing a CRC element for this temporary state.
You can rename and simplify this function into something like

static void end_ebml_master_preliminary(AVIOContext *pb, AVIOContext **dyn_cp,
                                        ebml_master master)
{
    uint8_t *buf;
    int size = avio_get_dyn_buf(*dyn_cp, &buf);

    avio_write(pb, buf, size);
    end_ebml_master(pb, master);
}

> +        avio_write(pb, buf + skip, size - skip);
> +        end_ebml_master(pb, master);
> +    }
> +}
> +
>  static void put_xiph_size(AVIOContext *pb, int size)
>  {
>      ffio_fill(pb, 255, size / 255);
> @@ -1309,7 +1331,7 @@ static int mkv_write_tracks(AVFormatContext *s)
>      }
>  
>      if (pb->seekable && !mkv->is_live)
> -        put_ebml_void(pb, avio_tell(mkv->tracks_bc));
> +        end_ebml_master_crc32_preliminary(pb, &mkv->tracks_bc, mkv, 
> mkv->tracks_master);
>      else
>          end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, mkv->tracks_master);
>  
> @@ -1554,7 +1576,7 @@ static int mkv_write_tags(AVFormatContext *s)
>  
>      if (mkv->tags.pos) {
>          if (s->pb->seekable && !mkv->is_live)
> -            put_ebml_void(s->pb, avio_tell(mkv->tags_bc));
> +            end_ebml_master_crc32_preliminary(s->pb, &mkv->tags_bc, mkv, 
> mkv->tags);
>          else
>              end_ebml_master_crc32(s->pb, &mkv->tags_bc, mkv, mkv->tags);
>      }
> @@ -1811,7 +1833,7 @@ static int mkv_write_header(AVFormatContext *s)
>          }
>      }
>      if (s->pb->seekable && !mkv->is_live)
> -        put_ebml_void(s->pb, avio_tell(pb));
> +        end_ebml_master_crc32_preliminary(s->pb, &mkv->info_bc, mkv, 
> mkv->info);
>      else
>          end_ebml_master_crc32(s->pb, &mkv->info_bc, mkv, mkv->info);
>      pb = s->pb;

FATE passes so LGTM.

I'll leave the avio changes review to someone else.

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to