20

I want to convert my music from flac to ogg, and currently oggenc does that perfectly except for album art. Metaflac can output album art, however there seems to be no command line tool to embed album art into ogg. MP3Tag and EasyTag are able to do it, and there is a specification for it here which calls for the image to be base64 encoded. However so far I have been unsuccessful in being able to take an image file, converting it to base64 and embedding it into an ogg file.

If I take a base64 encoded image from an ogg file that already has the image embedded, I can easily embed it into another image using vorbiscomment:

vorbiscomment -l withimage.ogg > textfile
vorbiscomment -c textfile noimage.ogg

My problem is taking something like a jpeg and converting it to base64. Currently I have:

base64 --wrap=0 ./image.jpg

Which gives me the image file converted to base64, using vorbiscomment and following the tagging rules, I can embed that into an ogg file like so:

echo "METADATA_BLOCK_PICTURE=$(base64 --wrap=0 ./image.jpg)" > ./folder.txt
vorbiscomment -c textfile noimage.ogg

However this gives me an ogg whose image does not work properly. I noticed when comparing the base64 strings that all properly embedding pictures have a header line but all the base64 strings I generate are lacking this header. Further analysis of the header:

od -c header.txt
0000000  \0  \0  \0 003  \0  \0  \0  \n   i   m   a   g   e   /   j   p
0000020   e   g  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000040  \0  \0  \0  \0  \0  \0  \0  \0 035 332
0000052

Which follows the spec given above. Notice 003 corresponds to front cover and image/jpeg is the mime type.

So finally, my question is, how can I base64 encode a file and generate this header along with it for embedding into an ogg file?

1
  • 2
    FYI vorbiscomment doesn't work with OGG/Opus files, i.e. when the audio is encoded using the Opus codec rather than Vorbis.
    – pts
    Jan 28, 2021 at 0:37

6 Answers 6

8

Here's my solution for the /usr/bin/vorbiscomment: Argument list too long problem. I created a script and named it oggart. Just run it from the command line like this:

oggart /path/to/music_file.ogg /path/to/image_file

This tags your ogg file with METADATA_BLOCK_PICTURE field. Easytag uses the old way of doing this with COVERART field instead of METADATA_BLOCK_PICTURE. If you want Easytag compatibility then you can run the script like this:

oggart /path/to/music_file.ogg /path/to/image_file -e

Here's the script:

#!/bin/sh

FILE1="`basename \"$1\"`"
EXT1=${FILE1##*.}
EXTTYPE1=`echo $EXT1 | tr '[:upper:]' '[:lower:]'`

FILE2="`basename \"$2\"`"
EXT2=${FILE2##*.}
EXTTYPE2=`echo $EXT2 | tr '[:upper:]' '[:lower:]'`

OGG=""
if [ "$EXTTYPE1" = ogg ]; then
OGG="$1"
elif [ "$EXTTYPE2" = ogg ]; then
OGG="$2"
fi
if [ "$OGG" = "" ]; then
echo no ogg file selected
exit 0
fi

PIC=""
array=(jpeg jpg png)
for item in ${array[*]}
do
if [ "$item" = "$EXTTYPE1" ]; then
PIC="$1"
elif [ "$item" = "$EXTTYPE2" ]; then
PIC="$2"
fi
done
if [ "$PIC" = "" ]; then
echo no jpg or png file selected
exit 0
fi

if [ "$3" = -e ]; then
EASYTAG=Y
else
EASYTAG=N
fi

DESC=`basename "$PIC"`
APIC=`base64 --wrap=0 "$PIC"`
if [ "`which exiv2`" != "" ]; then
MIME=`exiv2 "$PIC" | grep 'MIME type ' | sed 's/: /|/' | cut -f 2 -d '|' | tail -n 1`
fi
if [ "$MIME" = "" ]; then
MIME="image/jpeg"
fi

vorbiscomment -l "$OGG" | grep -v '^COVERART=' | grep -v '^COVERARTDESCRIPTION=' | grep -v '^COVERARTMIME=' | grep -v 'METADATA_BLOCK_PICTURE=' > "$OGG".tags

if [ "$EASYTAG" = N ]; then
echo METADATA_BLOCK_PICTURE="$APIC" > "$OGG".tags2
else
echo COVERART="$APIC" > "$OGG".tags2
fi
vorbiscomment -w -R -c "$OGG".tags2 "$OGG"
vorbiscomment -a -R -t COVERARTDESCRIPTION="$DESC" "$OGG"
vorbiscomment -a -R -t COVERARTMIME="$MIME" "$OGG"
vorbiscomment -a -R -c "$OGG".tags "$OGG"

rm -f "$OGG".tags
rm -f "$OGG".tags2
6
  • 1
    The script posted funny here. You can download oggart.tar.gz @ murga-linux.com/puppy/viewtopic.php?mode=attach&id=44270
    – Jason
    Aug 4, 2011 at 16:49
  • I fixed the script formatting in the post.
    – Gareth
    Aug 4, 2011 at 16:53
  • 1
    If you get a "Syntax error: "(" unexpected" in Ubuntu it's probably something to do with the shell being used to run. I changed the first line to #!/bin/bash and it worked. Aug 23, 2012 at 11:41
  • 1
    this script is not working for me. As I can see it only uses base64 of the image but there needs to be a special header before it
    – Sergey
    Aug 24, 2013 at 18:01
  • @Jason Congratulations for your script. BTW, maybe you could migrate it to gist.github.com just in case, over the years, your murga-linux page goes down.
    – SebMa
    Mar 22, 2021 at 21:05
9

i've just written a script that export / import images from OGG/Vorbis files using vorbiscomment. It is part of a music library conversion tool.

The revelent script is in the 'mussync-tools-transfert_images' function of this tool:

https://github.com/biapy/howto.biapy.com/blob/master/various/mussync-tools

Basically, i've written a reader and a writer for the metadata_block_picture format.

The code is quite complex:

      OUTPUT_FILE="/path/to/my-ogg-file.ogg"
      IMAGE_PATH="/path/to/my-cover-art.jpg"
      IMAGE_MIME_TYPE="image/jpeg"
      # Export existing comments to file.
      local COMMENTS_PATH="$(command mktemp -t "tmp.XXXXXXXXXX")"
      command vorbiscomment --list --raw "${OUTPUT_FILE}" > "${COMMENTS_PATH}"

      # Remove existing images.
      command sed -i -e '/^metadata_block_picture/d' "${COMMENTS_PATH}"

      # Insert cover image from file.

      # metadata_block_picture format.
      # See: https://xiph.org/flac/format.html#metadata_block_picture

      local IMAGE_WITH_HEADER="$(command mktemp -t "tmp.XXXXXXXXXX")"
      local DESCRIPTION=""

      # Reset cache file.
      echo -n "" > "${IMAGE_WITH_HEADER}"

      # Picture type <32>.
      command printf "0: %.8x" 3 | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Mime type length <32>.
      command printf "0: %.8x" $(echo -n "${IMAGE_MIME_TYPE}" | command wc -c) \
                | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Mime type (n * 8)
      echo -n "${IMAGE_MIME_TYPE}" >> "${IMAGE_WITH_HEADER}"
      # Description length <32>.
      command printf "0: %.8x" $(echo -n "${DESCRIPTION}" | command wc -c) \
                | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Description (n * 8)
      echo -n "${DESCRIPTION}" >> "${IMAGE_WITH_HEADER}"
      # Picture with <32>.
      command printf "0: %.8x" 0 | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Picture height <32>.
      command printf "0: %.8x" 0 | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Picture color depth <32>.
      command printf "0: %.8x" 0 | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Picture color count <32>.
      command printf "0: %.8x" 0 | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Image file size <32>.
      command printf "0: %.8x" $(command wc -c "${IMAGE_PATH}" \
                | command cut --delimiter=' ' --fields=1) \
                | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Image file.
      command cat "${IMAGE_PATH}" >> "${IMAGE_WITH_HEADER}"

      echo "metadata_block_picture=$(command base64 --wrap=0 < "${IMAGE_WITH_HEADER}")" >> "${COMMENTS_PATH}"

      # Update vorbis file comments.
      command vorbiscomment --write --raw --commentfile "${COMMENTS_PATH}" "${OUTPUT_FILE}"

      # Delete cache file.
      command rm "${IMAGE_WITH_HEADER}"
      # Delete comments file.
      command rm "${COMMENTS_PATH}"
1
3

I'm not aware of anything that does it automatically by just pointing to the image.

However vorbiscomment can embed arbitrary tags, you just need to encode the image in base64 and then construct the tag in the correct format.

e.g vorbiscomment -a -t 'METADATA_BLOCK_PICTURE=...' file.ogg newfile.ogg

you'll have to hack these steps into a script of some kind for it to be useful.

6
  • This would be doable, but sadly if the picture is over 64kb then vorbiscomments returns "/usr/bin/vorbiscomment: Argument list too long ". Any idea how to get around this?
    – dmikalova
    Jul 29, 2010 at 6:31
  • what is your system, and what is the output of getconf ARG_MAX? Unfortunately there is no way around this limit without recompiling the kernel. Here on 64-bit 2.6.32-24, I have 2 MB.
    – sml
    Jul 29, 2010 at 17:14
  • I'm running arch linux 64-bit 2.6.34.1-1 and I have 2mb as well. Would it be possible to put in a marker e.g. vorbiscomment -a -t 'METADATA_BLOCK_PICTURE=marker' file.ogg newfile.ogg, then have something read the ogg file and replace the marker with the base64 image?
    – dmikalova
    Jul 29, 2010 at 17:16
  • Absolutely. If you see the tag format spec I linked, you could use vorbiscomment to insert a temporary (small) image, and then directly write to the file updating the last two parts of the tag - data length, and the data itself. Obviously you'll have to hack something together yourself, however.
    – sml
    Jul 30, 2010 at 4:00
  • I'm trying out mutagen, a low level python library for audio tagging and my preliminary looks seem like it can do what I need. I'll report back when I figure things out in it.
    – dmikalova
    Jul 30, 2010 at 5:07
2

ffmpeg can be used to workaround the "Argument list too long" problem others mentioned when trying to embed large image files.

ffmpeg -i in.ogg -i metadata.dat -map_metadata 1 -codec copy out.ogg

where metadata.dat is a text file composed as:

;FFMETADATA1
METADATA_BLOCK_PICTURE=<img_blob64>

where <img_blob64> is the base64 encoded blob as produced by the scripts provided by @biapy or @bob-smith

1

Like many landing on this post, I too, needed a way to get images into ogg files. @Biapy has it correct, the only way is to build the metadata_block_picture blob.

While extremely illuminating, Biapy's solution is complex only in the sense that it is hard to follow. Also, it is per-file, and I wanted a base64 blob I could use across all files in an album. So, building on Biapy's work, I wrote a script that only outputs a base64 blob. I'm worried about the width/height detection, but it hasn't let me down so far.

https://github.com/twopoint71/ogg-image-blobber

0

FFmpeg version 4.4 automatically supports embedding album art into Ogg containers with the Theora video codec (see "Ogg codecs" on Wikipedia for a list of supported codecs, although they may not all be supported by FFmpeg).

Here's an example converting an MP3 file with a video track containing album art to an Ogg file with Opus encoded audio and Theora encoded video:

$ ffprobe -hide_banner '01 - State of Grace.mp3' 
[mp3 @ 0x5594cbafe320] Estimating duration from bitrate, this may be inaccurate
Input #0, mp3, from '01 - State of Grace.mp3':
  Metadata:
    lyrics-eng      :  
    copyright       : š 2012 Big Machine Records, LLC.
    title           : State of Grace
    album_artist    : Taylor Swift
    album           : Red (Deluxe Version)
    date            : 2012
    track           : 01/22
    genre           : Country
    composer        : Taylor Swift
    disc            : 1/1
    comment         : Taylor Swift
  Duration: 00:04:55.81, start: 0.000000, bitrate: 321 kb/s
  Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 320 kb/s
  Stream #0:1: Video: mjpeg (Baseline), yuvj444p(pc, bt470bg/unknown/unknown), 600x600 [SAR 72:72 DAR 1:1], 90k tbr, 90k tbn, 90k tbc (attached pic)
    Metadata:
      title           : Cover
      comment         : Cover (front)
$ ffmpeg -hide_banner -i '01 - State of Grace.mp3' -c:a libopus -b:a 128000 -c:v libtheora -q:v 10 '01 - State of Grace.ogg'
[mp3 @ 0x55ebe6d3cc40] Estimating duration from bitrate, this may be inaccurate
Input #0, mp3, from '01 - State of Grace.mp3':
  Metadata:
    lyrics-eng      :  
    copyright       : š 2012 Big Machine Records, LLC.
    title           : State of Grace
    album_artist    : Taylor Swift
    album           : Red (Deluxe Version)
    date            : 2012
    track           : 01/22
    genre           : Country
    composer        : Taylor Swift
    disc            : 1/1
    comment         : Taylor Swift
  Duration: 00:04:55.81, start: 0.000000, bitrate: 321 kb/s
  Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 320 kb/s
  Stream #0:1: Video: mjpeg (Baseline), yuvj444p(pc, bt470bg/unknown/unknown), 600x600 [SAR 72:72 DAR 1:1], 90k tbr, 90k tbn, 90k tbc (attached pic)
    Metadata:
      title           : Cover
      comment         : Cover (front)
Stream mapping:
  Stream #0:1 -> #0:0 (mjpeg (native) -> theora (libtheora))
  Stream #0:0 -> #0:1 (mp3 (mp3float) -> opus (libopus))
Press [q] to stop, [?] for help
[swscaler @ 0x55ebe6db69e0] deprecated pixel format used, make sure you did set range correctly
[ogg @ 0x55ebe6d44c80] Frame rate very high for a muxer not efficiently supporting it.
Please consider specifying a lower framerate, a different muxer or -vsync 2
Output #0, ogg, to '01 - State of Grace.ogg':
  Metadata:
    lyrics-eng      :  
    copyright       : š 2012 Big Machine Records, LLC.
    title           : State of Grace
    album_artist    : Taylor Swift
    album           : Red (Deluxe Version)
    date            : 2012
    track           : 01/22
    genre           : Country
    composer        : Taylor Swift
    disc            : 1/1
    comment         : Taylor Swift
    encoder         : Lavf58.76.100
  Stream #0:0: Video: theora, yuv444p(tv, bt470bg/unknown/unknown, progressive), 600x600 [SAR 1:1 DAR 1:1], q=2-31, 200 kb/s, 90k fps, 90k tbn (attached pic)
    Metadata:
      title           : Cover
      DESCRIPTION     : Cover (front)
      encoder         : Lavc58.134.100 libtheora
      lyrics-eng      :  
      copyright       : š 2012 Big Machine Records, LLC.
      ALBUMARTIST     : Taylor Swift
      album           : Red (Deluxe Version)
      date            : 2012
      TRACKNUMBER     : 01/22
      genre           : Country
      composer        : Taylor Swift
      DISCNUMBER      : 1/1
  Stream #0:1: Audio: opus, 48000 Hz, stereo, flt, 128 kb/s
    Metadata:
      encoder         : Lavc58.134.100 libopus
      lyrics-eng      :  
      copyright       : š 2012 Big Machine Records, LLC.
      title           : State of Grace
      ALBUMARTIST     : Taylor Swift
      album           : Red (Deluxe Version)
      date            : 2012
      TRACKNUMBER     : 01/22
      genre           : Country
      composer        : Taylor Swift
      DISCNUMBER      : 1/1
      DESCRIPTION     : Taylor Swift
[mp3float @ 0x55ebe6d96360] Header missing time=00:04:31.63 bitrate=   0.1kbits/s speed=59.8x    64x    
Error while decoding stream #0:0: Invalid data found when processing input
frame=    1 fps=0.2 q=-0.0 Lsize=    4929kB time=00:04:55.79 bitrate= 136.5kbits/s speed=59.8x    
video:58kB audio:4830kB subtitle:0kB other streams:0kB global headers:3kB muxing overhead: 0.845459%
$ mpv '01 - State of Grace.ogg'
 (+) Video --vid=1 'Cover' (theora 600x600)
 (+) Audio --aid=1 'State of Grace' (opus 2ch 48000Hz)
AO: [alsa] 48000Hz stereo 2ch float
VO: [gpu] 600x600 yuv444p
(Paused) AV: -00:00:00 / 00:04:55 (0%)

Exiting... (Quit)
$ 

I wrote a more detailed answer about how to do this here: https://stackoverflow.com/a/70166081/17549713

1
  • Welcome to Super User! Please do not post the same answer to multiple questions. If the same information really answers both questions, then one question (usually the newer one) should be closed as a duplicate of the other. You can indicate this by voting to close it as a duplicate or, if you don't have enough reputation for that, raise a flag to indicate that it's a duplicate. Otherwise tailor your answer to this question and don't just paste the same answer in multiple places.
    – DavidPostill
    Nov 30, 2021 at 12:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .