How to get metadata from various format files?

Started by trojannemo,

trojannemo

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

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

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:

-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

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

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#:

//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

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

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:

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

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

Quote from: Ian @ un4seenThe new option and its return value is defined like this:

#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

If you're writing in C# I can help you. It works beautifully for me.

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:

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

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

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

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

I thought it would work like with flac and opus so I used the same command:

--picture "path to image"

Should I have done something else?

Ian @ un4seen

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

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!