audio-metadata icon indicating copy to clipboard operation
audio-metadata copied to clipboard

Add MP4 (M4A) load(s) support

Open thebigmunch opened this issue 4 years ago • 11 comments

This has been sitting around locally for too long. Time to get some other people looking at/testing it, so it can finally get across the finish line.

For anyone willing to do some testing of MP4 support: pip install -U git+https://github.com/thebigmunch/audio-metadata@mp4.

I set a warning to emit when it runs into tags using data types I don't currently have explicitly handled. I'd love to get any files that emit this warning.

Any questions/issues/discussion, post here.

TODO

  • Implement any remaining tag types. Specifically the freeform ---- tags.
  • Handle multiple value tags.
  • Finish extension parsing code.
  • Reorganize/cleanup parsing code as necessary.
  • Expose any attributes deemed worthy of exposing on MP4StreamInfo et al.

thebigmunch avatar Feb 13 '20 14:02 thebigmunch

Hello! Found my issue got updated by this. Decided to test mp4 support. Seems to work for the most part. [Tested standard aac audio, not the weirded parts yet.]

image

Detects cover but doesn't detect other covers. [Again, those are placeholder images.]

image

Update 1:

Tested Apple AAC Encoding. Well it detects correctly but it doesn't detect HE-AAC, still displays it as AAC LC Referance Track
audio-metadata:
image ffprobe:
image

Ristellise avatar Mar 07 '20 09:03 Ristellise

Thanks for testing. I'm not sure everyone knows just how much I need people to test this stuff on their own files.

About the multiple tags, yeah, I hadn't gotten to supporting multiples for any of the MP4 tags yet. One of the things I need to finish up, though I had forgotten to list it.

And thanks especially for the HE-AAC track. So, HE-AAC is actually AAC LC with SBR extension (v1) or SBR and PS extensions (v2). But, I haven't finished the extension parsing yet to be able to modify the codec description. This appears to be a v1 file. Glad to have an example.

Hopefully I'll get motivation to work on the MP4 stuff again soon. Been banging out a lot of other stuff recently.

thebigmunch avatar Mar 07 '20 12:03 thebigmunch

Just pushed a new version that supports multiple covers.

thebigmunch avatar Mar 07 '20 13:03 thebigmunch

And, now it should set the codec description for your AAC-HE file. Plus, I found and fixed a bug along the way.

thebigmunch avatar Mar 07 '20 15:03 thebigmunch

Apple AAC Works: image

Also Doesn't detect Nero Encoded AAC for whatever reason... Test file if needed image

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'untitledneroaac.m4a':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: M4A mp42isom
    creation_time   : 2020-03-08T08:37:28.000000Z
    iTunSMPB        :  00000000 00000A40 000001C0 0000000000096000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    encoder         : Nero AAC codec / 1.5.4.0
  Duration: 00:00:12.86, start: 0.054667, bitrate: 96 kb/s
    Chapter #0:0: start 0.054667, end 12.918667
    Metadata:
      title           :
    Stream #0:0(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, mono, fltp, 93 kb/s (default)
    Metadata:
      creation_time   : 2020-03-08T08:37:28.000000Z
      handler_name    : Sound Media Handler

Tossed a few static gif images into appleaac.m4a for cover test.. and it crashed. File in Question

>>> metadata = audio_metadata.load('untitledappleaac.m4a')
ValueError: 0 is not a valid MP4CoverFormat

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Program Files\Python38\lib\site-packages\audio_metadata\api.py", line 125, in load
    return parser_cls.parse(data)
  File "C:\Program Files\Python38\lib\site-packages\audio_metadata\formats\mp4.py", line 671, in parse
    self.tags = MP4Tags.parse(data, ilst)
  File "C:\Users\joshw\AppData\Roaming\Python\Python38\site-packages\wrapt\wrappers.py", line 621, in __call__
    return self._self_wrapper(self.__wrapped__, instance, args,
  File "C:\Program Files\Python38\lib\site-packages\tbm_utils\decorators.py", line 41, in wrapper
    return wrapped(*args, **kwargs)
  File "C:\Program Files\Python38\lib\site-packages\audio_metadata\formats\mp4.py", line 219, in parse
    tag = MP4Tag.parse(data, child)
  File "C:\Users\joshw\AppData\Roaming\Python\Python38\site-packages\wrapt\wrappers.py", line 621, in __call__
    return self._self_wrapper(self.__wrapped__, instance, args,
  File "C:\Program Files\Python38\lib\site-packages\tbm_utils\decorators.py", line 41, in wrapper
    return wrapped(*args, **kwargs)
  File "C:\Program Files\Python38\lib\site-packages\audio_metadata\formats\mp4_tags.py", line 129, in parse
    return MP4CoverTag.parse(atom.read_data(data))
  File "C:\Users\joshw\AppData\Roaming\Python\Python38\site-packages\wrapt\wrappers.py", line 621, in __call__
    return self._self_wrapper(self.__wrapped__, instance, args,
  File "C:\Program Files\Python38\lib\site-packages\tbm_utils\decorators.py", line 41, in wrapper
    return wrapped(*args, **kwargs)
  File "C:\Program Files\Python38\lib\site-packages\audio_metadata\formats\mp4_tags.py", line 60, in parse
    covers.append(MP4Cover.parse(data))
  File "C:\Users\joshw\AppData\Roaming\Python\Python38\site-packages\wrapt\wrappers.py", line 621, in __call__
    return self._self_wrapper(self.__wrapped__, instance, args,
  File "C:\Program Files\Python38\lib\site-packages\tbm_utils\decorators.py", line 41, in wrapper
    return wrapped(*args, **kwargs)
  File "C:\Program Files\Python38\lib\site-packages\audio_metadata\formats\mp4_tags.py", line 24, in parse
    format_ = MP4CoverFormat(struct.unpack('>I', data.read(4))[0])
  File "C:\Program Files\Python38\lib\enum.py", line 304, in __call__
    return cls.__new__(cls, value)
  File "C:\Program Files\Python38\lib\enum.py", line 595, in __new__
    raise exc
  File "C:\Program Files\Python38\lib\enum.py", line 579, in __new__
    result = cls._missing_(value)
  File "C:\Program Files\Python38\lib\enum.py", line 608, in _missing_
    raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: 0 is not a valid MP4CoverFormat

Though... FFmpeg also says a Unknown Cover Type and just skips it entirely:

[mov,mp4,m4a,3gp,3g2,mj2 @ 0000024a2520c1c0] Unknown cover type: 0x0.
    Last message repeated 2 times
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'untitledappleaac.m4a':
  Metadata:
    major_brand     : M4A
    minor_version   : 0
    compatible_brands: M4A mp42isom
    creation_time   : 2020-03-07T09:13:17.000000Z
    encoder         : qaac 2.68, CoreAudioToolbox 7.9.8.3, AAC-HE Encoder, CVBR 16kbps, Quality 96
    iTunSMPB        :  00000000 00000840 000007C0 000000000004B000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    Encoding Params : vers
  Duration: 00:00:12.97, start: 0.044000, bitrate: 92 kb/s
    Stream #0:0(und): Audio: aac (HE-AAC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 15 kb/s (default)
    Metadata:
      creation_time   : 2020-03-07T09:13:17.000000Z

Ristellise avatar Mar 08 '20 08:03 Ristellise

Yeah, for the cover art, apparently it can be set as the IMPLICIT MP4 atom type. I don't know enough about MP4 to know if that's wrong or just nonsensical. Either way, I can just add an UNKNOWN cover format to handle that case. Taglib does the same. No reason to skip it.

Just pushed changes to support that and the Nero-encoded file. Though, it did remind me that I haven't added support for the freeform ---- tags yet.

Thanks again for the testing.

thebigmunch avatar Mar 08 '20 11:03 thebigmunch

Hi,

It seems like the bitrate is not being read correctly for some of my .m4a files. Any ideas as to why this could be happening? screenshot1 screenshot2

Nick-H-1234 avatar Aug 01 '20 14:08 Nick-H-1234

What's the current state of this issue? Thanks.

aw-was-here avatar Jun 01 '21 16:06 aw-was-here

For the code base I'm using this branch with, I'm not doing anything particularly fancy so it mostly works. Biggest issues I hit were:

  • m4v isn't in the list of supported file extentions
  • Freeform fields are definitely still broken in that they don't properly append

aw-was-here avatar Aug 09 '21 05:08 aw-was-here

It is working for m4a files recorded with quicktime media! thanks!!

ramcandrews avatar Jan 16 '22 07:01 ramcandrews

For anyone that cares, here's a fix for the freeform fields bug: https://github.com/whatsnowplaying/audio-metadata/commit/920b2bfde57a16292214afdf2660d43e572eafd5

aw-was-here avatar May 13 '23 05:05 aw-was-here