Eyevinn / mp4ff

Library and tools for parsing and writing MP4 files including video, audio and subtitles. The focus is on fragmented files. Includes mp4ff-info, mp4ff-encrypt, mp4ff-decrypt and other tools.
MIT License
445 stars 81 forks source link

Too few bytes in ESDS box #331

Closed Diniboy1123 closed 5 months ago

Diniboy1123 commented 6 months ago

Hi,

I am trying to decode ESDS boxes and it seems to fail for this one:

[esds] size=12+24
[ESDescriptor] size=2+22
  es_id = 513
  stream_priority = 0
  [DecoderConfig] size=2+17
    stream_type = 5
    object_type = 64
    up_stream = 0
    buffer_size = 2048
    max_bitrate = 100000
    avg_bitrate = 100000
    DecoderSpecificInfo = 13 10

The mp4ff-info tool also simply says: 2024/02/26 02:05:27 decode box "moov": decode box trak: decode box mdia: decode box minf: decode box stbl: decode box stsd: decode box enca: decode box esds: too few bytes in esds box

If I edit the check in DecodeEsdsSR to:

if hdr.Size < 11+minimalEsDescrSize {
    return nil, fmt.Errorf("too few bytes in esds box")
}

I get:

Error creating decryption info: decode moov: decode box trak: decode box mdia: decode box minf: decode box stbl: decode box stsd: decode box enca: decode box esds: DecodeESDecriptor: got tag 0 instead of SLConfigDescrTag 6

So I tried to recreate the esds box myself:

func main() {
    esdsBox := mp4.EsdsBox{
        ESDescriptor: mp4.ESDescriptor{
            EsID: 0x01,
            DecConfigDescriptor: mp4.DecoderConfigDescriptor{
                ObjectType: 0x40, // Audio ISO/IEC 14496-3,
                StreamType: 0x15, // 0x5 << 2 + 0x01 (audioType + upstreamFlag + reserved)
                DecSpecificInfo: mp4.DecSpecificInfoDescriptor{
                    DecConfig: []byte{0x12, 0x10},
                },
            },
        },
    }
    outFile, _ := os.Create("esdsbox.bin")
    defer outFile.Close()
    esdsBox.Encode(outFile)
}

I noticed that even though I haven't specified an SLConfigDescriptor, it still wrote one into the file:

[esds] size=12+27
  [ESDescriptor] size=2+25
    es_id = 1
    stream_priority = 0
    [DecoderConfig] size=2+17
      stream_type = 5
      object_type = 64
      up_stream = 0
      buffer_size = 0
      max_bitrate = 0
      avg_bitrate = 0
      DecoderSpecificInfo = 12 10 
    [Descriptor:06] size=2+1

And that can successfully be parsed. So even if you consider this as not a bug and you say that Descriptor:06 is required, how do I fix the issue and how do I inject a SLConfigDescriptor to the encrypted chunk? I couldn't seem to easily access the enca blob...

Thanks

Diniboy1123 commented 6 months ago

It seems that it also fails with DecodeFile.

Edit: I got rid of SLConfigDescriptor manually by monkey patching all the code out and it worked. So maybe we could make that optional?

tobbee commented 6 months ago

@Diniboy1123 The descriptors are really a messy part of the MPEG-4 standard. As far as I read the 14496-1 standard, SLConfigDescriptor should always be present (ES descriptor text in section 7.2.6.5.1)

if (ODProfileLevelIndication==0x01) //no SL extension.
{
    SLConfigDescriptor slConfigDescr;
} else  { // SL extension is possible.
    SLConfigDescriptor slConfigDescr;
}

I haven't seen the ODProfileLevel being signaled (it should be in an ExtensionProfileLevel Descriptor with tag 0x13), but the standard says that there should be an SLConfigDescriptor present in both cases.

Since the descriptors don't follow the normal mp4 file format structure, it is easy to make a mistake when implementing them. There was a mistake in mp4ff before in this area, but I think it is correct now.

I haven't had problems with decoding mp4 audio before. Where did you get your file from?

Diniboy1123 commented 6 months ago

You are probably going to laugh, but I am no corpo entity trying to develop a streaming platform, but a desperate end user trying to access the content I am paying for.

A local broadcaster only provides applications for LG and Samsung smart TVs and I got an Android TV. So I noticed that they are mostly using smooth streaming and PlayReady. My TV supports PlayReady and it can play PR protected ISM manifests within Kodi just fine. However it wasn't the case with this provider ootb. First I noticed that the encrypted video chunks have wrong DataOffsets (my previous issue was about that), but I could easily fix that since that's just checked when the actual decryption would take place, not before. So I use mp4ff to reset that offset to 0 and that way my TV is able to play and decrypt the content the intended and mostly legal way. I wrote a proxy that repacks these fragments with fixed offsets and turns the media into a dash manifest. But I ran into this issue now on certain audio streams. The provider uses Amazon and some backend that calls itself pcdeve in headers.

I have a long blog post (unfortunately in hungarian) about my journey so far.

This is the reason why I am not too confident with sharing any of these fragments publicly, since I probably shouldn't redistribute those. I can send some in an email if necessary.

Either way if you are against supporting this edge case, I absolutely understand. In that case I will maintain a fork.

PS: I love the library so much. In fact I haven't encountered many so clearly written libs so far. It really is a huge help especially since it has many methods that take the heavy lifting of putting boxes together manually for basic needs. Amazing work!

tobbee commented 4 months ago

@Diniboy1123 I added support for handling some erroneous descriptors including your case in PR #350 that is now merged. I made a test case without an SLConfigDescriptor that should correspond to your content. Give it a try to see if it works for you?