thebigmunch / audio-metadata

A library for reading and, in the future, writing audio metadata. https://audio-metadata.readthedocs.io/
https://forum.thebigmunch.me
MIT License
57 stars 11 forks source link

Add MP4 (M4A) load(s) support #14

Open thebigmunch opened 4 years ago

thebigmunch commented 4 years ago

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

Ristellise commented 4 years ago

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

thebigmunch commented 4 years ago

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 commented 4 years ago

Just pushed a new version that supports multiple covers.

thebigmunch commented 4 years ago

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

Ristellise commented 4 years ago

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
thebigmunch commented 4 years ago

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.

Nick-H-1234 commented 4 years ago

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

aw-was-here commented 3 years ago

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

aw-was-here commented 3 years ago

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:

ramcandrews commented 2 years ago

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

aw-was-here commented 1 year ago

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