Opened 2 months ago

Closed 2 months ago

Last modified 2 months ago

#7847 closed defect (invalid)

Mkv customIO wrong cluster

Reported by: lunatichai Owned by:
Priority: normal Component: avformat
Version: git-master Keywords: mkv
Cc: Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description

Summary of the bug: when transcode mkv->webm with custom write with "avio_alloc_context" the result file is different from default avio.
I found that the difference is in MATROSKA_ID_CLUSTER. In customIO bytes: 1F 43 B6 75 01 FF FF FF FF FF FF FF E7. In default avio (for example): 1F 43 B6 75 01 00 00 00 00 06 32 D8 E7.

My simple code to create io:

    uint8_t *avio_ctx_buffer = nullptr;
    const size_t avio_ctx_buffer_size = 4096;
    avio_ctx_buffer = (uint8_t*)av_malloc(avio_ctx_buffer_size);
    if (!avio_ctx_buffer) return;
    bufferData = new BufferData();
    avio_ctx = avio_alloc_context(avio_ctx_buffer, 
                                  avio_ctx_buffer_size,
                                  1, bufferData, NULL,
                                  &write_packet_to_buffer, NULL);

Attachments (2)

converter.cpp (31.8 KB) - added by lunatichai 2 months ago.
converter.h (4.0 KB) - added by lunatichai 2 months ago.

Download all attachments as: .zip

Change History (11)

comment:1 Changed 2 months ago by mkver

  • Keywords cluster removed

That's strange: Your hex stuff says that the cluster has been written as unknown-length cluster (usually every element has its length at the beginning, but it is also legal to use an "unknown length field" (like 0x01FFFFFFFFFFFFFFFF) which is designed for streaming. But actually the Matroska muxer tries to avoid this: Both in the codepath for seekable output and for non-seekable output no unknown-length cluster should be written.
More exactly, the non-seekable codepath (that's the one that should be used for your sample as you did not specify a seek function) works like this: The whole cluster (including the Cluster ID and the size field) is written into a temporary buffer. The size of the buffer is updated in the (seekable) buffer before it is actually output to your IO context.
Meanwhile, the seekable codepath currently looks like this: The Cluster ID is written and an unknown length field for the cluster (the 1F 43 B6 75 01 FF FF FF FF FF FF FF). Then the cluster is written into a temporary buffer. When it is decided to close the current cluster, the temporary buffer is written to the output IO, a seek to the length field is performed and the length field is overwritten with the real length.

What you describe shouldn't happen in the case that the customIO isn't seekable. Did you change the seekable flag somewhere? The output is exactly what I expect when the AVIOContext isn't seekable, but claims to be.

comment:2 Changed 2 months ago by lunatichai

I haven't changed anywhere the seekable flag (and other flags too). So avio_ctx->seekable=0.

comment:3 Changed 2 months ago by mkver

Then I'd like to see all of your code, not only the snippet above.

Changed 2 months ago by lunatichai

Changed 2 months ago by lunatichai

comment:4 Changed 2 months ago by lunatichai

Attach all codes with ffmpeg. But after the rebuild ffmpeg it only got worse and all too much bytes difference. Difference in codes only in comment\uncomment function createBuffer(). And not working seeking in windows media player.

Last edited 2 months ago by lunatichai (previous) (diff)

comment:5 follow-up: Changed 2 months ago by mkver

  1. What does worse mean? Do you measure the badness by how much output differs from the ordinary output with vanilla IO?
  2. If so, then you are using the wrong measure: The Matroska/WebM muxer's output is supposed to be different for seekable and non-seekable output:
    a) In the seekable case, clusters are by default big, wherease the non-seekable mode uses very small clusters (can be overridden with the cluster_size_limit and cluster_time_limit options).
    b) Also, there won't be cues (the index used for seeking) in the non-seekable-mode (cues can be at the beginning of the output file (in front of the clusters) or at the end. In the first case, one would need to seek to the beginning to write them, which is impossible in non-seekable mode. In the second case, the position of the cues is being put into the SeekHead? at the beginning of the file, so that the cues can be found. Writing said seekhead would require a seek.
    c) And there are some more minor differences: If your output format weren't WebM, but Matroska, then there would be CRC-32 elements written for every level 1 element by default in the seekable mode, but not the non-seekable mode. (These elements are invalid for WebM and aren't written at all for WebM, so that this difference can't occur in your usecase.) Furthermore, in the seekable mode, the duration and a few other elements are updated at the end based upon the real duration, in the non-seekable mode this can't happen.
  3. Are there currently unknown-sized clusters in the output? The only thing that should be unknown-sized right now is the "segment" (i.e. you should have 0x18 53 80 67 01 FF FF FF FF FF FF FF somewhere at the beginning). If so, then everything is as expected.

comment:6 Changed 2 months ago by cehoyos

  • Keywords avio_alloc_context removed

comment:7 Changed 2 months ago by cehoyos

Isn't the whole issue reproducible with ffmpeg if you use pipe output?

comment:8 in reply to: ↑ 5 Changed 2 months ago by lunatichai

  • Resolution set to fixed
  • Status changed from new to closed

Replying to mkver:

  1. What does worse mean? Do you measure the badness by how much output differs from the ordinary output with vanilla IO?
  2. If so, then you are using the wrong measure: The Matroska/WebM muxer's output is supposed to be different for seekable and non-seekable output:
    a) In the seekable case, clusters are by default big, wherease the non-seekable mode uses very small clusters (can be overridden with the cluster_size_limit and cluster_time_limit options).
    b) Also, there won't be cues (the index used for seeking) in the non-seekable-mode (cues can be at the beginning of the output file (in front of the clusters) or at the end. In the first case, one would need to seek to the beginning to write them, which is impossible in non-seekable mode. In the second case, the position of the cues is being put into the SeekHead? at the beginning of the file, so that the cues can be found. Writing said seekhead would require a seek.
    c) And there are some more minor differences: If your output format weren't WebM, but Matroska, then there would be CRC-32 elements written for every level 1 element by default in the seekable mode, but not the non-seekable mode. (These elements are invalid for WebM and aren't written at all for WebM, so that this difference can't occur in your usecase.) Furthermore, in the seekable mode, the duration and a few other elements are updated at the end based upon the real duration, in the non-seekable mode this can't happen.
  3. Are there currently unknown-sized clusters in the output? The only thing that should be unknown-sized right now is the "segment" (i.e. you should have 0x18 53 80 67 01 FF FF FF FF FF FF FF somewhere at the beginning). If so, then everything is as expected.

Thank you very much for explaining! Now i understand how it works and it's not bug. So it can be closed.

comment:9 Changed 2 months ago by cehoyos

  • Resolution changed from fixed to invalid
Note: See TracTickets for help on using tickets.