{"id":2028,"date":"2009-12-21T20:34:55","date_gmt":"2009-12-22T04:34:55","guid":{"rendered":"http:\/\/multimedia.cx\/eggs\/?p=2028"},"modified":"2010-07-16T07:21:42","modified_gmt":"2010-07-16T14:21:42","slug":"supplying-ffmpeg-with-metadata","status":"publish","type":"post","link":"https:\/\/multimedia.cx\/eggs\/supplying-ffmpeg-with-metadata\/","title":{"rendered":"Supplying FFmpeg With Metadata"},"content":{"rendered":"<p><strong>UPDATE, 2010-06-17: This information is now maintained via the <a href=\"http:\/\/wiki.multimedia.cx\/index.php?title=FFmpeg_Metadata\">FFmpeg Metadata page on the MultimediaWiki<\/a>.<\/strong><\/p>\n<p>While creating <a href=\"http:\/\/multimedia.cx\/eggs\/archivists-burden\/\">my automated game archiving solution<\/a>, I wanted to automatically tag ALAC\/M4A files with appropriate metadata while encoding using <a href=\"http:\/\/ffmpeg.org\/\">FFmpeg<\/a>. Then I realized I didn&#8217;t know how to do that. I do remember that FFmpeg recently supplanted a series of specific metadata command line options (like -title and -album) with a highly generalized metadata framework that is accessed by the option &#8216;-metadata &lt;key&gt;=&lt;value&gt;&#8217;. <a href=\"http:\/\/ffmpeg.org\/ffmpeg-doc.html#SEC8\">The official documentation<\/a> provides this lonely (and, I came to realize, useless NO-OP; more on why later) example of the option&#8217;s usage:<\/p>\n<pre>\r\n    `-metadata key=value'\r\n\r\n      Set a metadata key\/value pair. For example, \r\n      for setting the title in the output file:\r\n\r\n      ffmpeg -i in.avi -metadata title=\"my title\" out.flv\r\n<\/pre>\n<p>So this option allows you to specify absolutely any key\/value metadata pair you can imagine. However, a specific muxer won&#8217;t necessarily recognize it. How can I know the specific keys that, e.g., the MOV\/MP4 muxer honors? 2 methods spring to mind: Trial and error (worked out well for me at first) and reading the code (which was made a good deal easier when I already knew a few of the keys that worked).<\/p>\n<p>I think it would be a good idea to document the specific metadata keys that each muxer in FFmpeg recognizes. This blog post will lead by example. This data comes from SVN revision 20910, current as of 2009-12-21.<\/p>\n<p><strong>QuickTime\/MOV\/MP4\/M4A\/et al.<\/strong><\/p>\n<p>I have constructed the following table of the metadata keys that libavformat\/movenc.c honors. The low-level identifier column lists the atom name that the format uses to encode the data on disc, which is not interesting to most readers. For the interested but uninitiated, the notation, e.g., &#8216;\\251nam&#8217; indicates a 4-byte code consisting of the byte A9 in hexadecimal (or 251 in octal) followed by the ASCII characters &#8216;n&#8217;, &#8216;a&#8217;, and &#8216;m&#8217;.<\/p>\n<p><!--more--><\/p>\n<table border=\"1\" cellpadding=\"5\">\n<tr>\n<th>Key<\/th>\n<th>iTunes field<\/th>\n<th>Low-level identifier<\/th>\n<\/tr>\n<tr>\n<td>&#8220;title&#8221;<\/td>\n<td>Name<\/td>\n<td>&#8216;\\251nam&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;author&#8221;<\/td>\n<td>Artist<\/td>\n<td>&#8216;\\251ART&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;composer&#8221;<\/td>\n<td>Composer<\/td>\n<td>&#8216;\\251wrt&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;album&#8221;<\/td>\n<td>Album<\/td>\n<td>&#8216;\\251alb&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;year&#8221;<\/td>\n<td>Year<\/td>\n<td>&#8216;\\251day&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;track&#8221;<\/td>\n<td>Track Number<\/td>\n<td>&#8216;trkn&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;comment&#8221;<\/td>\n<td>Comments<\/td>\n<td>&#8216;\\251cmt&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;genre&#8221;<\/td>\n<td>Genre<\/td>\n<td>&#8216;\\251gen&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;copyright&#8221;<\/td>\n<td>??<\/td>\n<td>&#8216;\\251cpy&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;description&#8221;<\/td>\n<td>Description<\/td>\n<td>&#8216;desc&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;synopsis&#8221;<\/td>\n<td>Information dialog when selecting &#8220;Show Description&#8221; in context menu<\/td>\n<td>&#8216;ldes&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;show&#8221;<\/td>\n<td>Show<\/td>\n<td>&#8216;tvsh&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;episode_id&#8221;<\/td>\n<td>Episode ID<\/td>\n<td>&#8216;tven&#8217;<\/td>\n<\/tr>\n<tr>\n<td>&#8220;network&#8221;<\/td>\n<td>??<\/td>\n<td>&#8216;tvnn&#8217;<\/td>\n<\/tr>\n<\/table>\n<p>Further, the MOV muxer encodes libavformat version string into the &#8216;\\251too&#8217; field. Here&#8217;s an example that ties together all of the presently supported metadata options, even a few video options that don&#8217;t make sense for an audio file:<\/p>\n<pre>\r\n  ffmpeg -i track05.wav \\\r\n    -metadata title=\"Track #5\" \\\r\n    -metadata author=\"Unknown Artist\" \\\r\n    -metadata composer=\"Composer Unknown\" \\\r\n    -metadata album=\"Tracer Video Game Soundtrack\" \\\r\n    -metadata year=\"1996\" \\\r\n    -metadata track=\"5\" \\\r\n    -metadata comment=\"This is redbook CD audio track #5 from the Windows 95 game \\\"Tracer\\\"\" \\\r\n    -metadata genre=\"Game Soundtrack\" \\\r\n    -metadata copyright=\"Copyright 1996 Future Endeavors, Inc.\" \\\r\n    -metadata description=\"Nifty techno background tune for a futuristic video game\" \\\r\n    -metadata synopsis=\"Hey, is this thing on? This is where the 'synopsis' field shows up.\" \\\r\n    -metadata show=\"Tracer\" \\\r\n    -metadata episode_id=\"102\" \\\r\n    -metadata network=\"Some network\" \\\r\n    -acodec alac \\\r\n    -y track05.m4a\r\n<\/pre>\n<p>The lavf version shows up in the Summary tab of the informational dialog:<\/p>\n<p><center><br \/>\n<img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2009\/12\/itunes-encoded-with-lavf.png\" alt=\"iTunes files encoded with FFmpeg&#039;s libavformat\" title=\"iTunes files encoded with FFmpeg&#039;s libavformat\" width=\"166\" height=\"60\" class=\"aligncenter size-full wp-image-2034\" \/><br \/>\n<\/center><\/p>\n<p>This is the Info tab of the same dialog:<\/p>\n<p><center><br \/>\n<img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2009\/12\/itunes-metadata-1.png\" alt=\"iTunes Info dialog\" title=\"iTunes Info dialog\" width=\"660\" height=\"614\" class=\"aligncenter size-full wp-image-2035\" srcset=\"https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2009\/12\/itunes-metadata-1.png 660w, https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2009\/12\/itunes-metadata-1-300x279.png 300w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><br \/>\n<\/center><\/p>\n<p>Here is the video tab:<\/p>\n<p><center><br \/>\n<img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2009\/12\/itunes-metadata-2.png\" alt=\"iTunes video metadata\" title=\"iTunes video metadata\" width=\"660\" height=\"614\" class=\"aligncenter size-full wp-image-2036\" srcset=\"https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2009\/12\/itunes-metadata-2.png 660w, https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2009\/12\/itunes-metadata-2-300x279.png 300w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><br \/>\n<\/center><\/p>\n<p>And this is where the &#8220;synopsis&#8221; key shows up (selecting &#8220;Show Description&#8221; from the context menu):<\/p>\n<p><center><br \/>\n<img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2009\/12\/itunes-metadata-synopsis.png\" alt=\"iTunes description dialog\" title=\"iTunes description dialog\" width=\"364\" height=\"292\" class=\"aligncenter size-full wp-image-2044\" srcset=\"https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2009\/12\/itunes-metadata-synopsis.png 364w, https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2009\/12\/itunes-metadata-synopsis-300x240.png 300w\" sizes=\"auto, (max-width: 364px) 100vw, 364px\" \/><br \/>\n<\/center><\/p>\n<p>I don&#8217;t know where the &#8220;copyright&#8221; or &#8220;network&#8221; strings appear in various iTunes dialogs. But the strings are definitely encoded in the file.<\/p>\n<p><strong>ASF\/WMV\/WMA<\/strong><\/p>\n<p>FFmpeg&#8217;s ASF muxer honors the following metadata keys:<\/p>\n<ul>\n<li>&#8220;title&#8221;<\/li>\n<li>&#8220;author&#8221;<\/li>\n<li>&#8220;copyright&#8221;<\/li>\n<li>&#8220;comment&#8221;<\/li>\n<\/ul>\n<p><strong>AVI<\/strong><\/p>\n<p>FFmpeg&#8217;s AVI muxer honors the following metadata keys and maps them to these FourCCs in the file header:<\/p>\n<ul>\n<li>&#8220;Title&#8221; -&gt; &#8220;INAM&#8221;<\/li>\n<li>&#8220;Artist&#8221; or &#8220;Author&#8221; -&gt; &#8220;IART&#8221;<\/li>\n<li>&#8220;Copyright&#8221; -&gt; &#8220;ICOP&#8221;<\/li>\n<li>&#8220;Comment&#8221; -&gt; &#8220;ICMT&#8221;<\/li>\n<li>&#8220;Album&#8221; -&gt; &#8220;IPRD&#8221;<\/li>\n<li>&#8220;Genre&#8221; -&gt; &#8220;IGNR&#8221;<\/li>\n<li>&#8220;Track&#8221; -&gt; &#8220;IPRT&#8221;<\/li>\n<\/ul>\n<p>Further, if the bit-exact flag is not explicitly specified (using &#8216;-flags bitexact&#8217;, the muxer will also encode the libavformat version string into the &#8220;ISFT&#8221; field.<\/p>\n<p><strong>Matroska<\/strong><\/p>\n<p>Per my reading of libavformat\/matroskaenc.c, the muxer only supports 3 keys. This surprises me&#8211; I always thought MKV was supposed to be a container format to end all container formats; maybe FFmpeg&#8217;s muxer just doesn&#8217;t support many of the tags available; or perhaps the format embeds some other metadata format. Whatever the case, these are the only metadata keys that the muxer supports:<\/p>\n<ul>\n<li>&#8220;title&#8221;<\/li>\n<li>&#8220;description&#8221;<\/li>\n<li>&#8220;language&#8221;<\/li>\n<\/ul>\n<p><strong>MP3<\/strong><\/p>\n<p>According to libavformat\/mp3.c, the following metadata keys are supported:<\/p>\n<ul>\n<li>&#8220;title&#8221;<\/li>\n<li>&#8220;author&#8221;<\/li>\n<li>&#8220;album&#8221;<\/li>\n<li>&#8220;year&#8221;<\/li>\n<li>&#8220;comment&#8221;<\/li>\n<li>&#8220;track&#8221;<\/li>\n<li>&#8220;genre&#8221;<\/li>\n<\/ul>\n<p><strong>MPEG Transport Streams<\/strong><\/p>\n<p>The libavformat\/mpegtsenc.c file indicates that the muxer honors the metadata keys &#8220;title&#8221; and &#8220;language&#8221;.<\/p>\n<p><strong>NUT<\/strong><\/p>\n<p>FFmpeg&#8217;s NUT muxer honors the following metadata keys:<\/p>\n<ul>\n<li>&#8220;title&#8221;<\/li>\n<li>&#8220;author&#8221;<\/li>\n<li>&#8220;copyright&#8221;<\/li>\n<\/ul>\n<p><strong>Realmedia<\/strong><\/p>\n<p>FFmpeg&#8217;s Realmedia muxer encodes a &#8220;CONT&#8221; chunk by concatenating certain metadata values specified on the command line. These are the recognized metadata keys:<\/p>\n<ul>\n<li>&#8220;title&#8221;<\/li>\n<li>&#8220;author&#8221;<\/li>\n<li>&#8220;copyright&#8221;<\/li>\n<li>&#8220;comment&#8221;<\/li>\n<\/ul>\n<p>Example:<\/p>\n<pre>\r\n  ffmpeg -i track05.wav \\\r\n    -metadata title=\"This is the title\" \\\r\n    -metadata author=\"Made by Me\" \\\r\n    -metadata copyright=\"Copyright 2009 Me\" \r\n    -metadata comment=\"An exercise in Realmedia metadata\" \\\r\n    -y track05.rm\r\n<\/pre>\n<p>This is what the start of the file looks like in a hex editor:<\/p>\n<pre>\r\n0040   00 01 00 03  43 4F 4E 54  00 00 00 5F  00 00 00 11  ....CONT..._....\r\n0050   54 68 69 73  20 69 73 20  74 68 65 20  74 69 74 6C  This is the titl\r\n0060   65 00 0A 4D  61 64 65 20  62 79 20 4D  65 00 11 43  e..Made by Me..C\r\n0070   6F 70 79 72  69 67 68 74  20 32 30 30  39 20 4D 65  opyright 2009 Me\r\n0080   00 21 41 6E  20 65 78 65  72 63 69 73  65 20 69 6E  .!An exercise in\r\n0090   20 52 65 61  6C 6D 65 64  69 61 20 6D  65 74 61 64   Realmedia metad\r\n00A0   61 74 61 4D  44 50 52 00  00 00 9B 00  00 00 00 00  ataMDPR.........\r\n<\/pre>\n<p><strong>Odds &#8216;n Ends<\/strong><\/p>\n<p>The SDP muxer honors the &#8220;title&#8221; metadata key. <\/p>\n<p>The SoX native format muxer honors &#8220;comment&#8221;.<\/p>\n<p><strong>Observations and Conclusions<\/strong><\/p>\n<p>The craziest part I notice is that the example provided in the official documentation showed how to set metadata for a FLV file. However, the FLV muxer does not honor any metadata keys. This could be that FLV metadata is highly free-form and wholly dependent on what the client player SWF cares about. <a href=\"http:\/\/www.adobe.com\/devnet\/flv\/\">The official FLV spec<\/a> describes the onMetaData tag which allows a FLV to encode metadata which will be presented to an ActionScript program via Netstream.onMetaData. Really, though, this just means that this should be the easiest format for metadata support; just iterate through each key\/value pair and write it to an onMetaData tag.<\/p>\n<p>I was slightly disturbed by the case inconsistency seen between a few of the muxers. A few of them (like AVI) specify &#8220;Title&#8221; while most specify &#8220;title&#8221;. However, digging in libavformat\/metadata.c reveals that the default behavior is to treat the metadata keys as case-insensitive unless a certain flag is specified, which is never specified.<\/p>\n<p>For completeness, it looks like we should add some other fields as well. For the MP4 muxer: &#8216;\\141ART&#8217; (Album Artist, vs. Artist which is &#8216;\\251ART&#8217;), &#8216;\\251grp&#8217; (Grouping), &#8216;\\251lyr&#8217; (Lyrics), &#8216;tvsn&#8217; (Season Number) and &#8216;tves&#8217; (Episode Number), and perhaps any other minor options, if the mood strikes. If you see some omissions with metadata types for other formats, you may want to chime in. I also notice that Ogg and FLAC are not hooked up to the metadata API while both formats support such information.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Explicitly documenting FFmpeg&#8217;s metadata facilities<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-2028","post","type-post","status-publish","format-standard","hentry","category-open-source-multimedia"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/posts\/2028","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/comments?post=2028"}],"version-history":[{"count":18,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/posts\/2028\/revisions"}],"predecessor-version":[{"id":2607,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/posts\/2028\/revisions\/2607"}],"wp:attachment":[{"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/media?parent=2028"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/categories?post=2028"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/tags?post=2028"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}