{"id":527,"date":"2015-10-15T19:40:08","date_gmt":"2015-10-15T19:40:08","guid":{"rendered":"http:\/\/blogs.gentoo.org\/lu_zero\/?p=527"},"modified":"2015-10-15T19:41:58","modified_gmt":"2015-10-15T19:41:58","slug":"deprecating-avpicture","status":"publish","type":"post","link":"https:\/\/blogs.gentoo.org\/lu_zero\/2015\/10\/15\/deprecating-avpicture\/","title":{"rendered":"Deprecating AVPicture"},"content":{"rendered":"<p>In Libav we try to clean up the API and make it more regular, this is one of the possibly many articles I write about APIs, this time about deprecating some relic from the past and why we are doing it.<\/p>\n<h2>AVPicture<\/h2>\n<p>This struct used to store image data using <code>data<\/code> pointers and <code>linesizes<\/code>. It comes from the far past and it looks like this:<\/p>\n<div class=\"codehilite\">\n<pre><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">AVPicture<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kt\">uint8_t<\/span> <span class=\"o\">*<\/span><span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"n\">AV_NUM_DATA_POINTERS<\/span><span class=\"p\">];<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"n\">linesize<\/span><span class=\"p\">[<\/span><span class=\"n\">AV_NUM_DATA_POINTERS<\/span><span class=\"p\">];<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">AVPicture<\/span><span class=\"p\">;<\/span>\n<\/pre>\n<\/div>\n<p>Once the <strong>AVFrame<\/strong> was introduced it was made so it would <strong>alias<\/strong> to it and for some time the two structures were actually defined sharing the commond initial fields through a macro.<\/p>\n<p>The AVFrame then evolved to store both <strong>audio<\/strong> and <strong>image<\/strong> data, to use AVBuffer to not have to do needless copies and to provide more useful information (e.g. the actual data <strong>format<\/strong>), now it looks like:<\/p>\n<div class=\"codehilite\">\n<pre><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">AVFrame<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kt\">uint8_t<\/span> <span class=\"o\">*<\/span><span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"n\">AV_NUM_DATA_POINTERS<\/span><span class=\"p\">];<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"n\">linesize<\/span><span class=\"p\">[<\/span><span class=\"n\">AV_NUM_DATA_POINTERS<\/span><span class=\"p\">];<\/span>\n\n    <span class=\"kt\">uint8_t<\/span> <span class=\"o\">**<\/span><span class=\"n\">extended_data<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"kt\">int<\/span> <span class=\"n\">width<\/span><span class=\"p\">,<\/span> <span class=\"n\">height<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"kt\">int<\/span> <span class=\"n\">nb_samples<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"kt\">int<\/span> <span class=\"n\">format<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"kt\">int<\/span> <span class=\"n\">key_frame<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"k\">enum<\/span> <span class=\"n\">AVPictureType<\/span> <span class=\"n\">pict_type<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"n\">AVRational<\/span> <span class=\"n\">sample_aspect_ratio<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"kt\">int64_t<\/span> <span class=\"n\">pts<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"p\">...<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">AVFrame<\/span><span class=\"p\">;<\/span>\n<\/pre>\n<\/div>\n<p>The image-data manipulation functions moved to the <code>av_image<\/code> namespace and use directly <code>data<\/code> and <code>linesize<\/code> pointers, while the equivalent <code>avpicture<\/code> became a wrapper over them.<\/p>\n<div class=\"codehilite\">\n<pre><span class=\"kt\">int<\/span> <span class=\"nf\">avpicture_fill<\/span><span class=\"p\">(<\/span><span class=\"n\">AVPicture<\/span> <span class=\"o\">*<\/span><span class=\"n\">picture<\/span><span class=\"p\">,<\/span> <span class=\"kt\">uint8_t<\/span> <span class=\"o\">*<\/span><span class=\"n\">ptr<\/span><span class=\"p\">,<\/span>\n                   <span class=\"k\">enum<\/span> <span class=\"n\">AVPixelFormat<\/span> <span class=\"n\">pix_fmt<\/span><span class=\"p\">,<\/span> <span class=\"kt\">int<\/span> <span class=\"n\">width<\/span><span class=\"p\">,<\/span> <span class=\"kt\">int<\/span> <span class=\"n\">height<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">av_image_fill_arrays<\/span><span class=\"p\">(<\/span><span class=\"n\">picture<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">data<\/span><span class=\"p\">,<\/span> <span class=\"n\">picture<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">linesize<\/span><span class=\"p\">,<\/span>\n                                <span class=\"n\">ptr<\/span><span class=\"p\">,<\/span> <span class=\"n\">pix_fmt<\/span><span class=\"p\">,<\/span> <span class=\"n\">width<\/span><span class=\"p\">,<\/span> <span class=\"n\">height<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kt\">int<\/span> <span class=\"nf\">avpicture_layout<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span> <span class=\"n\">AVPicture<\/span><span class=\"o\">*<\/span> <span class=\"n\">src<\/span><span class=\"p\">,<\/span> <span class=\"k\">enum<\/span> <span class=\"n\">AVPixelFormat<\/span> <span class=\"n\">pix_fmt<\/span><span class=\"p\">,<\/span>\n                     <span class=\"kt\">int<\/span> <span class=\"n\">width<\/span><span class=\"p\">,<\/span> <span class=\"kt\">int<\/span> <span class=\"n\">height<\/span><span class=\"p\">,<\/span>\n                     <span class=\"kt\">unsigned<\/span> <span class=\"kt\">char<\/span> <span class=\"o\">*<\/span><span class=\"n\">dest<\/span><span class=\"p\">,<\/span> <span class=\"kt\">int<\/span> <span class=\"n\">dest_size<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">av_image_copy_to_buffer<\/span><span class=\"p\">(<\/span><span class=\"n\">dest<\/span><span class=\"p\">,<\/span> <span class=\"n\">dest_size<\/span><span class=\"p\">,<\/span>\n                                   <span class=\"n\">src<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">data<\/span><span class=\"p\">,<\/span> <span class=\"n\">src<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">linesize<\/span><span class=\"p\">,<\/span>\n                                   <span class=\"n\">pix_fmt<\/span><span class=\"p\">,<\/span> <span class=\"n\">width<\/span><span class=\"p\">,<\/span> <span class=\"n\">height<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"p\">...<\/span>\n<\/pre>\n<\/div>\n<p>It is also used in the subtitle abstraction:<\/p>\n<div class=\"codehilite\">\n<pre><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">AVSubtitleRect<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"n\">x<\/span><span class=\"p\">,<\/span> <span class=\"n\">y<\/span><span class=\"p\">,<\/span> <span class=\"n\">w<\/span><span class=\"p\">,<\/span> <span class=\"n\">h<\/span><span class=\"p\">;<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"n\">nb_colors<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"n\">AVPicture<\/span> <span class=\"n\">pict<\/span><span class=\"p\">;<\/span>\n    <span class=\"k\">enum<\/span> <span class=\"n\">AVSubtitleType<\/span> <span class=\"n\">type<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"kt\">char<\/span> <span class=\"o\">*<\/span><span class=\"n\">text<\/span><span class=\"p\">;<\/span>\n    <span class=\"kt\">char<\/span> <span class=\"o\">*<\/span><span class=\"n\">ass<\/span><span class=\"p\">;<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"n\">flags<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">AVSubtitleRect<\/span><span class=\"p\">;<\/span>\n<\/pre>\n<\/div>\n<p>And to crudely pass AVFrame from the decoder level to the muxer level, for certain rawvideo muxers by doing something such as:<\/p>\n<div class=\"codehilite\">\n<pre>    <span class=\"n\">pkt<\/span><span class=\"p\">.<\/span><span class=\"n\">data<\/span>   <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"kt\">uint8_t<\/span> <span class=\"o\">*<\/span><span class=\"p\">)<\/span><span class=\"n\">frame<\/span><span class=\"p\">;<\/span>\n    <span class=\"n\">pkt<\/span><span class=\"p\">.<\/span><span class=\"n\">size<\/span>   <span class=\"o\">=<\/span>  <span class=\"k\">sizeof<\/span><span class=\"p\">(<\/span><span class=\"n\">AVPicture<\/span><span class=\"p\">);<\/span>\n<\/pre>\n<\/div>\n<h2>AVPicture problems<\/h2>\n<p>In the codebase its remaining usage is dubious at best:<\/p>\n<h3>AVFrame as AVPicture<\/h3>\n<p>In some codecs the <code>AVFrame<\/code> produced or consumed are casted as <code>AVPicture<\/code> and passed to <code>avpicture<\/code> functions instead<br \/>\nof directly use the <code>av_image<\/code> functions.<\/p>\n<h3>AVSubtitleRect<\/h3>\n<p>For the subtitle codecs, accessing the Rect data requires a pointless indirection, usually something like:<\/p>\n<div class=\"codehilite\">\n<pre>    <span class=\"n\">wrap3<\/span> <span class=\"o\">=<\/span> <span class=\"n\">rect<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">pict<\/span><span class=\"p\">.<\/span><span class=\"n\">linesize<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">];<\/span>\n    <span class=\"n\">p<\/span> <span class=\"o\">=<\/span> <span class=\"n\">rect<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">pict<\/span><span class=\"p\">.<\/span><span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">];<\/span>\n    <span class=\"n\">pal<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"k\">const<\/span> <span class=\"kt\">uint32_t<\/span> <span class=\"o\">*<\/span><span class=\"p\">)<\/span><span class=\"n\">rect<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">pict<\/span><span class=\"p\">.<\/span><span class=\"n\">data<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">];<\/span>  <span class=\"cm\">\/* Now in YCrCb! *\/<\/span>\n<\/pre>\n<\/div>\n<h3>AVFMT_RAWPICTURE<\/h3>\n<p>Copying memory from a buffer to another when can be avoided is consider a major sin (&#8220;<code>memcpy<\/code> is murder&#8221;) since it is a costly operation in itself and usually it invalidates the cache if we are talking about large buffers.<\/p>\n<p>Certain muxers for rawvideo, try to spare a <code>memcpy<\/code> and thus avoid a &#8220;murder&#8221; by not copying the AVFrame data to the AVPacket.<\/p>\n<p>The idea in itself is simple enough, store the AVFrame pointer as if it would point a flat array, consider the data size as the AVPicture size and hope that the data pointed by the AVFrame remains valid while the AVPacket is consumed.<\/p>\n<p>Simple and <strong>faulty<\/strong>: with the <code>AVFrame<\/code> ref-counted API codecs may use a Pool of <code>AVFrame<\/code>s and <strong>reuse<\/strong> them.<br \/>\nIt can lead to surprising results because the buffer gets updated before the AVPacket is actually written.<br \/>\nIf the frame referenced changes dimensions or gets deallocated it could even lead to crashes.<\/p>\n<p>Definitely not a great idea.<\/p>\n<h2>Solutions<\/h2>\n<p>Vittorio, wm4 and I worked together to fix the problems. Radically.<\/p>\n<h4>AVFrame as AVPicture<\/h4>\n<p>The <code>av_image<\/code> functions are now used when needed.<br \/>\nSome pointless copies got replaced by <code>av_frame_ref<\/code>, leading to less memory usage and simpler code.<\/p>\n<p>No AVPictures are left in the video codecs.<\/p>\n<h4>AVSubtitle<\/h4>\n<p>The AVSubtitleRect is updated to have simple <code>data<\/code> and <code>linesize<\/code> fields and each codec is updated to keep the <code>AVPicture<\/code> and the new fields in sync during the deprecation window.<\/p>\n<p>The code is already a little easier to follow now.<\/p>\n<h4>AVFMT_RAWPICTURE<\/h4>\n<p>Just dropping the &#8220;feature&#8221; would be a problem since those muxers are widely used in <a href=\"https:\/\/wiki.libav.org\/FATE\">FATE<\/a> and the time the additional copy takes adds up to quite a lot. Your regression test must be as quick as possible.<\/p>\n<p>I wrote a safer wrapper pseudo-codec that leverages the fact that both <code>AVPacket<\/code> and <code>AVFrame<\/code> use a ref-counted system:<\/p>\n<ul>\n<li>The AVPacket takes the AVFrame and increases its ref-count by 1.<\/li>\n<li>The AVFrame is then stored in the <code>data<\/code> field and wrapped in a custom AVBuffer.<\/li>\n<li>That AVBuffer destructor callback <strong>unrefs<\/strong> the frame.<\/li>\n<\/ul>\n<p>This way the AVFrame data won&#8217;t change until the AVPacket gets destroyed.<\/p>\n<h2>Goodbye AVPicture<\/h2>\n<p>With the release 14 the <code>AVPicture<\/code> struct will be removed completely from Libav, people using it outside Libav should consider moving to use full AVFrame (and leverage the additional feature it provides) or the <code>av_image<\/code> functions directly.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Libav we try to clean up the API and make it more regular, this is one of the possibly many articles I write about APIs, this time about deprecating some relic from the past and why we are doing it. AVPicture This struct used to store image data using data pointers and linesizes. It &hellip; <a href=\"https:\/\/blogs.gentoo.org\/lu_zero\/2015\/10\/15\/deprecating-avpicture\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Deprecating AVPicture<\/span><\/a><\/p>\n","protected":false},"author":10,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[14],"tags":[19,23],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p1aGWH-8v","_links":{"self":[{"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/posts\/527"}],"collection":[{"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/users\/10"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/comments?post=527"}],"version-history":[{"count":2,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/posts\/527\/revisions"}],"predecessor-version":[{"id":529,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/posts\/527\/revisions\/529"}],"wp:attachment":[{"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/media?parent=527"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/categories?post=527"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/tags?post=527"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}