Author Topic: How to get metadata from various format files?  (Read 1013 times)

trojannemo

  • Posts: 143
Re: How to get metadata from various format files?
« Reply #25 on: 1 Aug '24 - 12:27 »
The first solution sounds easy enough. The second one not so much because I couldn't make heads or tails of the structure, which is why I ended up resulting to Tags Library in the first place to extract those files. Then I moved on to adding them to Ogg files but that was secondary.

Maybe I'm missing something, but if you're aware of how it's done and it seems rather trivial, why isn't it in BassEnc_Ogg.BASS_Encode_OGG_StartFile? For Opus, MP3 and Flac I pass an argument with their respective BassEnc calls and voila, the album art is there in the output file. Would love that to be the case with BassEnc_Ogg. Or is it already and it's not documented on Bernd's website?

Ian @ un4seen

  • Administrator
  • Posts: 26177
Re: How to get metadata from various format files?
« Reply #26 on: 1 Aug '24 - 13:16 »
BASSenc_OGG basically supports OGGENC's options, so that it can be swapped-in without having to change existing option settings. OGGENC doesn't include an album art option, but the "-c" option can be used to set either of the tags mentioned above after base64-encoding the data yourself, eg. "-c COVERART=<base64-data>".

trojannemo

  • Posts: 143
Re: How to get metadata from various format files?
« Reply #27 on: 2 Aug '24 - 01:10 »
So -c COVERART is not working. The Base64 string ends up in the OGG file, but neither Windows' Media Player, VLC or MP3 Tag see the album art. And yes, if you copy/paste the Base64 string into the online decoder the image shows up just fine. This is the entire argument string being passed to Bass's ogg encoder:

Code: [Select]
-q 5 -t "Azul" -a "Cristian Castro" -G "Pop-Rock" -d "2001" -l "Azul" -N "7" -c "COMMENT=Made by Nemo" -c "COVERART=base64stringherebutitstoolongfortheforums"

Everything is working as intended except the album art part.

This is a snippet of the file in HxD:



Any ideas?

trojannemo

  • Posts: 143
Re: How to get metadata from various format files?
« Reply #28 on: 2 Aug '24 - 06:05 »
The simple method didn't work but the hard method did :-)

I could have sworn I had everything right but it kept on not working. Until I was chatting with my good friend ChatGPT about it and it casually mentioned that int values had to be stored as big endian. As soon as I accounted for that, the album art was recognized by VLC and by MP3 Tag. Windows Media Player doesn't but it doesn't surprise me that it's limited.

Thanks for the help.

This is where I am now:

Text metadata and album art working
FLAC TO MP3
FLAC TO OGG
FLAC TO OPUS
MP3 to FLAC
MP3 TO OGG
MP3 TO OPUS
M4A TO FLAC
M4A TO MP3
M4A TO OGG
M4A TO OPUS
OPUS TO FLAC
OPUS TO MP3
OPUS TO OGG
OGG TO FLAC
OGG TO MP3
OGG TO OPUS

Text metadata working (album art not yet)
?????

Nothing working yet
Any metadata to and from WAV

trojannemo

  • Posts: 143
Re: How to get metadata from various format files?
« Reply #29 on: 2 Aug '24 - 12:47 »
I wanted to share the working C# code since I know i've looked for sample code in the past and it was helpful when I found it. In case anyone else searches on how to add album art to ogg file in C#:

Code: [Select]
//art is a string path to the album art image

var artBytes = CreateFLACPictureBlock(art);
var art64 = Convert.ToBase64String(artBytes);
var argument_for_BASS = " -c \"METADATA_BLOCK_PICTURE=" + art64 + "\"";

private static byte[] CreateFLACPictureBlock(string art)
{
    int Width;
    int Height;
    int ColorDepth = 24;
    const int NoOfColors = 0;           
    string MIME;           
    const string Desc = "Front Cover";           

    using (var fs = File.Open(art, FileMode.Open))
    {
        using (var BitmapStream = new MemoryStream())
        {
            fs.CopyTo(BitmapStream);
            BitmapStream.Position = 0;

            Image img = Image.FromStream(BitmapStream);
            Width = img.Width;
            Height = img.Height;

            if (img.RawFormat.Equals(ImageFormat.Jpeg))
            {
                MIME = "image/jpeg";
                ColorDepth = 24;
            }
            else if (img.RawFormat.Equals(ImageFormat.Png))
            {
                MIME = "image/png";
                ColorDepth = Image.GetPixelFormatSize(img.PixelFormat);
            }
            else
            {
                MIME = "image/";
                ColorDepth = Image.GetPixelFormatSize(img.PixelFormat);
            }
        }
    }

    using (MemoryStream ms = new MemoryStream())
    {
        BinaryWriter writer = new BinaryWriter(ms);

        // Write the APIC value
        writer.Write(ConvertToBigEndianBytes(3));

        // Write the MIME type
        if (!string.IsNullOrEmpty(MIME))
        {
            byte[] mimeBytes = Encoding.ASCII.GetBytes(MIME);
            writer.Write(ConvertToBigEndianBytes(mimeBytes.Length));
            writer.Write(mimeBytes);
        }
        else
        {
            writer.Write(0);
        }

        // Write the Description
        if (!string.IsNullOrEmpty(Desc))
        {
            byte[] descBytes = Encoding.UTF8.GetBytes(Desc);
            writer.Write(ConvertToBigEndianBytes(descBytes.Length));
            writer.Write(descBytes);
        }
        else
        {
            writer.Write(0);
        }

        // Write the dimensions and color depth
        writer.Write(ConvertToBigEndianBytes(Width));
        writer.Write(ConvertToBigEndianBytes(Height));
        writer.Write(ConvertToBigEndianBytes(ColorDepth));
        writer.Write(ConvertToBigEndianBytes(NoOfColors));

        // Write the picture data
        var bytes = File.ReadAllBytes(art);
        if (bytes != null && bytes.Length > 0)
        {
            writer.Write(ConvertToBigEndianBytes(bytes.Length));
            writer.Write(bytes);
        }
        else
        {
            writer.Write(0);
        }

        return ms.ToArray();
    }
}

private static byte[] ConvertToBigEndianBytes(int value)
{
    var bytes = BitConverter.GetBytes(value);
    if (BitConverter.IsLittleEndian)
    {
        Array.Reverse(bytes);
    }
    return bytes;
}
[code]       

Ian @ un4seen

  • Administrator
  • Posts: 26177
Re: How to get metadata from various format files?
« Reply #30 on: 2 Aug '24 - 16:26 »
Good to see you got it working. I've never checked how widely supported either of the 2 tags are, but METADATA_BLOCK_PICTURE is the one officially recommended by Xiph, so it makes sense that's more commonly supported these days. The original OGG spec didn't include anything about embedded images, so I think what happened is that some software introduced the COVERART tag for that, and then FLAC introduced METADATA_BLOCK_PICTURE and that was later adopted by OGG.

As the official FLAC and OPUS encoders both have a "--picture" option, supported by BASSenc_FLAC and BASSenc_OPUS, I think it'd make sense for that option to be available for Ogg Vorbis too, seeing as they all use METADATA_BLOCK_PICTURE. I'll look into adding it to BASSenc_OGG.

trojannemo

  • Posts: 143
Re: How to get metadata from various format files?
« Reply #31 on: 3 Aug '24 - 00:50 »
That would be great, as it would simplify the code I shared above into one argument for the encoder.

Using this newfound knowledge, I figured out how to reverse it and extract the album art data from Opus/OGG/FLAC files without needing the Tags Library add-on. So that's gone. Here's the code in case it might be useful to someone:

Code: [Select]
private static bool ConvertBase64DataToImage(string base64Data, string album_art)
{
    var decoded = Convert.FromBase64String(base64Data);

    using (MemoryStream ms = new MemoryStream(decoded))
    {
        BinaryReader reader = new BinaryReader(ms);

        // Read the APIC value
        int apic = ReadInt32BigEndian(reader);

        // Read the MIME type
        int mimeLength = ReadInt32BigEndian(reader);
        string mime = mimeLength > 0 ? Encoding.ASCII.GetString(reader.ReadBytes(mimeLength)) : null;

        // Read the Description
        int descLength = ReadInt32BigEndian(reader);
        string desc = descLength > 0 ? Encoding.UTF8.GetString(reader.ReadBytes(descLength)) : null;

        // Read the dimensions and color depth
        int width = ReadInt32BigEndian(reader);
        int height = ReadInt32BigEndian(reader);
        int colorDepth = ReadInt32BigEndian(reader);
        int noOfColors = ReadInt32BigEndian(reader);

        // Read the picture data
        int pictureDataLength = ReadInt32BigEndian(reader);
        byte[] pictureData = reader.ReadBytes(pictureDataLength);

        // Save the picture data to a file
        File.WriteAllBytes(album_art, pictureData);
    }
    return File.Exists(album_art);
}

private static int ReadInt32BigEndian(BinaryReader reader)
{
    byte[] bytes = reader.ReadBytes(4);
    if (BitConverter.IsLittleEndian)
    {
        Array.Reverse(bytes);
    }
    return BitConverter.ToInt32(bytes, 0);
}

and the whole thing is called by

Code: [Select]
IntPtr oggTags = Bass.BASS_ChannelGetTags(BassStream, BASSTag.BASS_TAG_OGG);
string[] tags = Utils.IntPtrToArrayNullTermUtf8(oggTags);
if (tags != null)
{
    foreach (string tag in tags)
    {
         //read and process other tags here
         else if (tag.Contains("METADATA_BLOCK_PICTURE="))
         {
             savedAlbumArt = ConvertBase64DataToImage(tag.Replace("METADATA_BLOCK_PICTURE=", ""), art);
         }
    }
}

v0lt

  • Posts: 4
Re: How to get metadata from various format files?
« Reply #32 on: 3 Aug '24 - 05:44 »
The new option and its return value is defined like this:

Code: [Select]
#define BASS_TAG_MP4_PICTURE 0x1400 // + index #, picture : TAG_BINARY structure

typedef struct {
const void *data;
DWORD length;
} TAG_BINARY;
Cool! When are you planning this update?

trojannemo

  • Posts: 143
Re: How to get metadata from various format files?
« Reply #33 on: 3 Aug '24 - 07:04 »
If you're writing in C# I can help you. It works beautifully for me.

Code: [Select]
const int BASS_TAG_MP4_PICTURE = 0x1400; // + index #, picture : TAG_BINARY structure

[DllImport("bass.dll", EntryPoint = "BASS_ChannelGetTags", CharSet = CharSet.Auto)]
public static extern IntPtr BASS_ChannelGetTags(int handle, int tags);

[StructLayout(LayoutKind.Sequential)]
public struct TAG_BINARY
{
    public IntPtr data;
    public int length;
}

once you have this set up, the actual use is trivial:

Code: [Select]
var tagPtr = BASS_ChannelGetTags(BassStream, BASS_TAG_MP4_PICTURE);
if (tagPtr != IntPtr.Zero)
{
    TAG_BINARY tag = (TAG_BINARY)Marshal.PtrToStructure(tagPtr, typeof(TAG_BINARY));
    byte[] imageData = new byte[tag.length];
    Marshal.Copy(tag.data, imageData, 0, tag.length);
    File.WriteAllBytes(art, imageData);
}

Ian @ un4seen

  • Administrator
  • Posts: 26177
Re: How to get metadata from various format files?
« Reply #34 on: 5 Aug '24 - 13:04 »
Here's a BASSenc_OGG update that includes a "--picture" option:

   www.un4seen.com/stuff/bassenc_ogg.zip

It should work the same as the FLAC/OPUSENC option of the same name. Please report any problems.

trojannemo

  • Posts: 143
Re: How to get metadata from various format files?
« Reply #35 on: 6 Aug '24 - 01:22 »
It worked in the sense that it didn't give an error, and the album at showed up in VLC. However, it does not show up in MP3Tag, which i've been using to check metadata as I go along. My code results in an image that shows up both in VLC and MP3Tag. Neither your image nor my image show up in Media Player.

Looking at the resulting ogg file in the HxD editor, mine has metadata_block_picture and the base64encoded string. Yours just says picture and that's it? No base64 string. Somehow it works in VLC though.

Ian @ un4seen

  • Administrator
  • Posts: 26177
Re: How to get metadata from various format files?
« Reply #36 on: 6 Aug '24 - 14:23 »
That's strange. I am seeing a "METADATA_BLOCK_PICTURE=<base64data>" tag being written here, and it is visible in MP3TAG. Are you using the "SPECIFICATION" form of the "--picture" option or just an image filename? If the former, please post that setting. Please also upload/link an affected image file to check here.

   ftp.un4seen.com/incoming

trojannemo

  • Posts: 143
Re: How to get metadata from various format files?
« Reply #37 on: 6 Aug '24 - 14:49 »
I thought it would work like with flac and opus so I used the same command:

Code: [Select]
--picture "path to image"

Should I have done something else?

Ian @ un4seen

  • Administrator
  • Posts: 26177
Re: How to get metadata from various format files?
« Reply #38 on: 6 Aug '24 - 15:10 »
No, that should be fine. The "--picture" option does also have an extended "SPECIFICATION" form (see the FLAC or OPUSENC docs for details) that can be used instead of just a filename. Anyway, if it's still not working, please provide an affected image file to check here.

trojannemo

  • Posts: 143
Re: How to get metadata from various format files?
« Reply #39 on: 7 Aug '24 - 03:18 »
Nevermind, it works. I missed a space before the --picture argument. VLC and MP3Tag show it. Media Player doesn't but it also doesn't with other formats.
Great job!