{"id":520,"date":"2015-10-02T14:39:31","date_gmt":"2015-10-02T14:39:31","guid":{"rendered":"http:\/\/blogs.gentoo.org\/lu_zero\/?p=520"},"modified":"2015-10-02T14:41:43","modified_gmt":"2015-10-02T14:41:43","slug":"decoupling-an-api-part-ii","status":"publish","type":"post","link":"https:\/\/blogs.gentoo.org\/lu_zero\/2015\/10\/02\/decoupling-an-api-part-ii\/","title":{"rendered":"Decoupling an API &#8211; Part II"},"content":{"rendered":"<p>During the <a href=\"http:\/\/vdd.videolan.org\/\">VDD<\/a> we had lots of discussions and I enjoyed reviewing the initial <a href=\"http:\/\/codecs.multimedia.cx\/?cat=25\">NihAV<\/a> implementation. Kostya already wrote <a href=\"http:\/\/codecs.multimedia.cx\/?p=1005\">some more<\/a> about the decoupled API that I described at high level <a href=\"https:\/\/blogs.gentoo.org\/lu_zero\/2015\/03\/23\/decoupling-an-api\">here<\/a>.<\/p>\n<p>This article is about some possible implementation details, at least another will follow.<\/p>\n<p>The new API requires some additional data structures, mainly something to keep the data that is being consumed\/produced, additional implementation-callbacks in AVCodec and possibly a mean to skip the queuing up completely.<\/p>\n<h2>Data Structures<\/h2>\n<h3>AVPacketQueue and AVFrameQueue<\/h3>\n<p>In the <a href=\"\">previous<\/a> post I considered as given some kind of Queue.<\/p>\n<p>Ideally the API for it could be really simple:<\/p>\n<div class=\"codehilite\">\n<pre><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">AVPacketQueue<\/span><span class=\"p\">;<\/span>\n\n<span class=\"n\">AVPacketQueue<\/span> <span class=\"o\">*<\/span><span class=\"nf\">av_packet_queue_alloc<\/span><span class=\"p\">(<\/span><span class=\"kt\">int<\/span> <span class=\"n\">size<\/span><span class=\"p\">);<\/span>\n<span class=\"kt\">int<\/span> <span class=\"nf\">av_packet_queue_put<\/span><span class=\"p\">(<\/span><span class=\"n\">AVPacketQueue<\/span> <span class=\"o\">*<\/span><span class=\"n\">q<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n<span class=\"kt\">int<\/span> <span class=\"nf\">av_packet_queue_get<\/span><span class=\"p\">(<\/span><span class=\"n\">AVPacketQueue<\/span> <span class=\"o\">*<\/span><span class=\"n\">q<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n<span class=\"kt\">int<\/span> <span class=\"nf\">av_packet_queue_size<\/span><span class=\"p\">(<\/span><span class=\"n\">AVPacketQueue<\/span> <span class=\"o\">*<\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n<span class=\"kt\">void<\/span> <span class=\"nf\">av_packet_queue_free<\/span><span class=\"p\">(<\/span><span class=\"n\">AVPacketQueue<\/span> <span class=\"o\">**<\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n<\/pre>\n<\/div>\n<div class=\"codehilite\">\n<pre><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">AVFrameQueue<\/span><span class=\"p\">;<\/span>\n\n<span class=\"n\">AVFrameQueue<\/span> <span class=\"o\">*<\/span><span class=\"nf\">av_frame_queue_alloc<\/span><span class=\"p\">(<\/span><span class=\"kt\">int<\/span> <span class=\"n\">size<\/span><span class=\"p\">);<\/span>\n<span class=\"kt\">int<\/span> <span class=\"nf\">av_frame_queue_put<\/span><span class=\"p\">(<\/span><span class=\"n\">AVFrameQueue<\/span> <span class=\"o\">*<\/span><span class=\"n\">q<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n<span class=\"kt\">int<\/span> <span class=\"nf\">av_frame_queue_get<\/span><span class=\"p\">(<\/span><span class=\"n\">AVFrameQueue<\/span> <span class=\"o\">*<\/span><span class=\"n\">q<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n<span class=\"kt\">int<\/span> <span class=\"nf\">av_frame_queue_size<\/span><span class=\"p\">(<\/span><span class=\"n\">AVFrameQueue<\/span> <span class=\"o\">*<\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n<span class=\"kt\">void<\/span> <span class=\"nf\">av_frame_queue_free<\/span><span class=\"p\">(<\/span><span class=\"n\">AVFrameQueue<\/span> <span class=\"o\">**<\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n<\/pre>\n<\/div>\n<p>Internally it leverages the <strong>ref-counted<\/strong> API (<code>av_packet_move_ref<\/code> and <code>av_frame_move_ref<\/code>) and any data structure that could fit the queue-usage. It will be used in a multi-thread scenario so a form of Lock has to be fit into it.<\/p>\n<p>We have already something specific for AVPlay, using a simple Linked List and a FIFO for some other components that have a near-constant maximum number of items (e.g. avconv, NVENC, QSV).<\/p>\n<p>Possibly also a Tree could be used to implement something such as <code>av_packet_queue_insert_by_pts<\/code> and have some forms of reordering happen on the fly. I&#8217;m not a fan of it, but I&#8217;m sure someone will come up with the idea..<\/p>\n<p>The Queues are part of <code>AVCodecContext<\/code>.<\/p>\n<div class=\"codehilite\">\n<pre><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">AVCodecContext<\/span> <span class=\"p\">{<\/span>\n    <span class=\"c1\">\/\/ ...<\/span>\n\n    <span class=\"n\">AVPacketQueue<\/span> <span class=\"o\">*<\/span><span class=\"n\">packet_queue<\/span><span class=\"p\">;<\/span>\n    <span class=\"n\">AVFrameQueue<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame_queue<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"c1\">\/\/ ...<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">AVCodecContext<\/span><span class=\"p\">;<\/span>\n<\/pre>\n<\/div>\n<h2>Implementation Callbacks<\/h2>\n<p>In Libav the <code>AVCodec<\/code> struct describes some specific codec features (such as the supported framerates) and hold the actual codec implementation through callbacks such as <code>init<\/code>, <code>decode<\/code>\/<code>encode2<\/code>, <code>flush<\/code> and <code>close<\/code>.<br \/>\nThe new model obviously requires additional callbacks.<\/p>\n<p>Once the data is in a queue it is ready to be processed, the actual decoding or encoding can happen in multiple places, for example:<\/p>\n<ul>\n<li>\nIn <code>avcodec_*_push<\/code> or <code>avcodec_*_pull<\/code>, once there is enough data. In that case the remaining functions are glorified proxies for the matching queue function.\n<\/li>\n<li>\nsomewhere else such as a separate thread that is started on <code>avcodec_open<\/code> or the first <code>avcodec_decode_push<\/code> and is eventually stopped once the context related to it is freed by <code>avcodec_close<\/code>. This is what happens under the hood when you have certain hardware acceleration.\n<\/li>\n<\/ul>\n<h3>Common<\/h3>\n<div class=\"codehilite\">\n<pre><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">AVCodec<\/span> <span class=\"p\">{<\/span>\n    <span class=\"c1\">\/\/ ... previous fields<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">need_data<\/span><span class=\"p\">)(<\/span><span class=\"n\">AVCodecContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">avctx<\/span><span class=\"p\">);<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">has_data<\/span><span class=\"p\">)(<\/span><span class=\"n\">AVCodecContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">avctx<\/span><span class=\"p\">);<\/span>\n    <span class=\"c1\">\/\/ ...<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">AVCodec<\/span><span class=\"p\">;<\/span>\n<\/pre>\n<\/div>\n<p>Those are used by both the <strong>encoder<\/strong> and <strong>decoder<\/strong>, some implementations such as QSV have functions that can be used to probe the internal state in this regard.<\/p>\n<h3>Decoding<\/h3>\n<div class=\"codehilite\">\n<pre><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">AVCodec<\/span> <span class=\"p\">{<\/span>\n    <span class=\"c1\">\/\/ ... previous fields<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">decode_push<\/span><span class=\"p\">)(<\/span><span class=\"n\">AVCodecContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">avctx<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">packet<\/span><span class=\"p\">);<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">decode_pull<\/span><span class=\"p\">)(<\/span><span class=\"n\">AVCodecContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">avctx<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n    <span class=\"c1\">\/\/ ...<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">AVCodec<\/span><span class=\"p\">;<\/span>\n<\/pre>\n<\/div>\n<p>Those two functions can take a portion of the work the current <code>decode<\/code> function does, for example:<br \/>\n&#8211; the initial parsing and dispatch to a worker thread can happen in the <code>_push<\/code>.<br \/>\n&#8211; reordering and blocking until there is data to output can happen on <code>_pull<\/code>.<\/p>\n<p>Assuming the reordering does not happen outside the pull callback in some generic code.<\/p>\n<h3>Encoding<\/h3>\n<div class=\"codehilite\">\n<pre><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">AVCodec<\/span> <span class=\"p\">{<\/span>\n    <span class=\"c1\">\/\/ ... previous fields<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">encode_push<\/span><span class=\"p\">)(<\/span><span class=\"n\">AVCodecContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">avctx<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">encode_pull<\/span><span class=\"p\">)(<\/span><span class=\"n\">AVCodecContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">avctx<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">packet<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">AVCodec<\/span><span class=\"p\">;<\/span>\n<\/pre>\n<\/div>\n<p>As per the Decoding callbacks, <code>encode2<\/code> workload is split. the <code>_push<\/code> function might just keep queuing up until there are enough frames to complete the initial the analysis, while, for single thread encoding, the rest of the work happens at the <code>_pull<\/code>.<\/p>\n<h2>Yielding data directly<\/h2>\n<p>So far the API mainly keeps some queue filled and let some magic happen under the hood, let see some usage examples first:<\/p>\n<h3>Simple Usage<\/h3>\n<p>Let&#8217;s expand the last example from the previous post:  register callbacks to pull\/push the data and have some simple loops.<\/p>\n<h4>Decoding<\/h4>\n<div class=\"codehilite\">\n<pre><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">DecodeCallback<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">pull_packet<\/span><span class=\"p\">)(<\/span><span class=\"kt\">void<\/span> <span class=\"o\">*<\/span><span class=\"n\">priv<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">push_frame<\/span><span class=\"p\">)(<\/span><span class=\"kt\">void<\/span> <span class=\"o\">*<\/span><span class=\"n\">priv<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n    <span class=\"kt\">void<\/span> <span class=\"o\">*<\/span><span class=\"n\">priv_data_pull<\/span><span class=\"p\">,<\/span> <span class=\"o\">*<\/span><span class=\"n\">priv_data_push<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">DecodeCallback<\/span><span class=\"p\">;<\/span>\n<\/pre>\n<\/div>\n<p>Two pointers since you pull from a demuxer+parser and you push to a splitter+muxer.<\/p>\n<div class=\"codehilite\">\n<pre><span class=\"kt\">int<\/span> <span class=\"nf\">decode_loop<\/span><span class=\"p\">(<\/span><span class=\"n\">AVCodecContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">avctx<\/span><span class=\"p\">,<\/span> <span class=\"n\">DecodeCallback<\/span> <span class=\"o\">*<\/span><span class=\"n\">cb<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n    <span class=\"n\">AVPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">pkt<\/span>  <span class=\"o\">=<\/span> <span class=\"n\">av_packet_alloc<\/span><span class=\"p\">();<\/span>\n    <span class=\"n\">AVFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame<\/span> <span class=\"o\">=<\/span> <span class=\"n\">av_frame_alloc<\/span><span class=\"p\">();<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"n\">ret<\/span><span class=\"p\">;<\/span>\n    <span class=\"k\">while<\/span> <span class=\"p\">((<\/span><span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">avcodec_decode_need_data<\/span><span class=\"p\">(<\/span><span class=\"n\">avctx<\/span><span class=\"p\">))<\/span> <span class=\"o\">&gt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">cb<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">pull_packet<\/span><span class=\"p\">(<\/span><span class=\"n\">cb<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">priv_data_pull<\/span><span class=\"p\">,<\/span> <span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">ret<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">goto<\/span> <span class=\"n\">end<\/span><span class=\"p\">;<\/span>\n        <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">avcodec_decode_push<\/span><span class=\"p\">(<\/span><span class=\"n\">avctx<\/span><span class=\"p\">,<\/span> <span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">ret<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">goto<\/span> <span class=\"n\">end<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n    <span class=\"k\">while<\/span> <span class=\"p\">((<\/span><span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">avcodec_decode_have_data<\/span><span class=\"p\">(<\/span><span class=\"n\">avctx<\/span><span class=\"p\">))<\/span> <span class=\"o\">&gt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">avcodec_decode_pull<\/span><span class=\"p\">(<\/span><span class=\"n\">avctx<\/span><span class=\"p\">,<\/span> <span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">ret<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">goto<\/span> <span class=\"n\">end<\/span><span class=\"p\">;<\/span>\n        <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">cb<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">push_frame<\/span><span class=\"p\">(<\/span><span class=\"n\">cb<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">priv_data_push<\/span><span class=\"p\">,<\/span> <span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">ret<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">goto<\/span> <span class=\"n\">end<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n\n<span class=\"nl\">end:<\/span>\n    <span class=\"n\">av_frame_free<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n    <span class=\"n\">av_packet_free<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">ret<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/pre>\n<\/div>\n<h4>Encoding<\/h4>\n<p>For encoding something quite similar can be done:<\/p>\n<div class=\"codehilite\">\n<pre><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">EncodeCallback<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">pull_frame<\/span><span class=\"p\">)(<\/span><span class=\"kt\">void<\/span> <span class=\"o\">*<\/span><span class=\"n\">priv<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">push_packet<\/span><span class=\"p\">)(<\/span><span class=\"kt\">void<\/span> <span class=\"o\">*<\/span><span class=\"n\">priv<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">packet<\/span><span class=\"p\">);<\/span>\n    <span class=\"kt\">void<\/span> <span class=\"o\">*<\/span><span class=\"n\">priv_data_push<\/span><span class=\"p\">,<\/span> <span class=\"o\">*<\/span><span class=\"n\">priv_data_pull<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">EncodeCallback<\/span><span class=\"p\">;<\/span>\n<\/pre>\n<\/div>\n<p>The loop is exactly the same beside the data types swapped.<\/p>\n<div class=\"codehilite\">\n<pre><span class=\"kt\">int<\/span> <span class=\"nf\">encode_loop<\/span><span class=\"p\">(<\/span><span class=\"n\">AVCodecContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">avctx<\/span><span class=\"p\">,<\/span> <span class=\"n\">EncodeCallback<\/span> <span class=\"o\">*<\/span><span class=\"n\">cb<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n    <span class=\"n\">AVPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">pkt<\/span>  <span class=\"o\">=<\/span> <span class=\"n\">av_packet_alloc<\/span><span class=\"p\">();<\/span>\n    <span class=\"n\">AVFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame<\/span> <span class=\"o\">=<\/span> <span class=\"n\">av_frame_alloc<\/span><span class=\"p\">();<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"n\">ret<\/span><span class=\"p\">;<\/span>\n    <span class=\"k\">while<\/span> <span class=\"p\">((<\/span><span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">avcodec_encode_need_data<\/span><span class=\"p\">(<\/span><span class=\"n\">avctx<\/span><span class=\"p\">))<\/span> <span class=\"o\">&gt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">cb<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">pull_frame<\/span><span class=\"p\">(<\/span><span class=\"n\">cb<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">priv_data_pull<\/span><span class=\"p\">,<\/span> <span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">ret<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">goto<\/span> <span class=\"n\">end<\/span><span class=\"p\">;<\/span>\n        <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">avcodec_encode_push<\/span><span class=\"p\">(<\/span><span class=\"n\">avctx<\/span><span class=\"p\">,<\/span> <span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">ret<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">goto<\/span> <span class=\"n\">end<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n    <span class=\"k\">while<\/span> <span class=\"p\">((<\/span><span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">avcodec_encode_have_data<\/span><span class=\"p\">(<\/span><span class=\"n\">avctx<\/span><span class=\"p\">))<\/span> <span class=\"o\">&gt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">avcodec_encode_pull<\/span><span class=\"p\">(<\/span><span class=\"n\">avctx<\/span><span class=\"p\">,<\/span> <span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">ret<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">goto<\/span> <span class=\"n\">end<\/span><span class=\"p\">;<\/span>\n        <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">cb<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">push_packet<\/span><span class=\"p\">(<\/span><span class=\"n\">cb<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">priv_data_push<\/span><span class=\"p\">,<\/span> <span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">ret<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">goto<\/span> <span class=\"n\">end<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n\n<span class=\"nl\">end:<\/span>\n    <span class=\"n\">av_frame_free<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n    <span class=\"n\">av_packet_free<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">ret<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/pre>\n<\/div>\n<h4>Transcoding<\/h4>\n<p>Transcoding, the naive way, could be something such as<\/p>\n<div class=\"codehilite\">\n<pre><span class=\"kt\">int<\/span> <span class=\"nf\">transcode<\/span><span class=\"p\">(<\/span><span class=\"n\">AVFormatContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">mux<\/span><span class=\"p\">,<\/span>\n              <span class=\"n\">AVFormatContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">dem<\/span><span class=\"p\">,<\/span>\n              <span class=\"n\">AVCodecContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">enc<\/span><span class=\"p\">,<\/span>\n              <span class=\"n\">AVCodecContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">dec<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n    <span class=\"n\">DecodeCallbacks<\/span> <span class=\"n\">dcb<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n        <span class=\"n\">get_packet<\/span><span class=\"p\">,<\/span>\n        <span class=\"n\">av_frame_queue_put<\/span><span class=\"p\">,<\/span>\n        <span class=\"n\">dem<\/span><span class=\"p\">,<\/span> <span class=\"n\">enc<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">frame_queue<\/span> <span class=\"p\">};<\/span>\n    <span class=\"n\">EncodeCallbacks<\/span> <span class=\"n\">ecb<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n        <span class=\"n\">av_frame_queue_get<\/span><span class=\"p\">,<\/span>\n        <span class=\"n\">push_packet<\/span><span class=\"p\">,<\/span>\n        <span class=\"n\">enc<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">frame_queue<\/span><span class=\"p\">,<\/span> <span class=\"n\">mux<\/span> <span class=\"p\">};<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"k\">while<\/span> <span class=\"p\">(<\/span><span class=\"n\">ret<\/span> <span class=\"o\">&gt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"k\">if<\/span> <span class=\"p\">((<\/span><span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">decode_loop<\/span><span class=\"p\">(<\/span><span class=\"n\">dec<\/span><span class=\"p\">,<\/span> <span class=\"o\">&amp;<\/span><span class=\"n\">dcb<\/span><span class=\"p\">))<\/span> <span class=\"o\">&gt;<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n            <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">encode_loop<\/span><span class=\"p\">(<\/span><span class=\"n\">enc<\/span><span class=\"p\">,<\/span> <span class=\"o\">&amp;<\/span><span class=\"n\">ecb<\/span><span class=\"p\">);<\/span>\n    <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/pre>\n<\/div>\n<p>One loop feeds the other throught the queue. <code>get_packet<\/code> and <code>push_packet<\/code> are muxing and demuxing functions, they might end up being other two Queue functions once the AVFormat layer gets a similar overhaul.<\/p>\n<h3>Advanced usage<\/h3>\n<p>From the examples above you would notice that in some situation you would possibly do better,<br \/>\nall the loops pull data from a queue push it immediately to another:<\/p>\n<ul>\n<li>why not feeding right queue immediately once you have the data ready?<\/li>\n<li>why not doing some processing before feeding the decoded data to the encoder, such as conver the pixel format?<\/li>\n<\/ul>\n<p>Here some additional structures and functions to enable advanced users:<\/p>\n<div class=\"codehilite\">\n<pre><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">AVFrameCallback<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">yield<\/span><span class=\"p\">)(<\/span><span class=\"kt\">void<\/span> <span class=\"o\">*<\/span><span class=\"n\">priv<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n    <span class=\"kt\">void<\/span> <span class=\"o\">*<\/span><span class=\"n\">priv_data<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">AVFrameCallback<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">AVPacketCallback<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">yield<\/span><span class=\"p\">)(<\/span><span class=\"kt\">void<\/span> <span class=\"o\">*<\/span><span class=\"n\">priv<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n    <span class=\"kt\">void<\/span> <span class=\"o\">*<\/span><span class=\"n\">priv_data<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">AVPacketCallback<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">AVCodecContext<\/span> <span class=\"p\">{<\/span>\n<span class=\"c1\">\/\/ ...<\/span>\n\n<span class=\"n\">AVFrameCallback<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame_cb<\/span><span class=\"p\">;<\/span>\n<span class=\"n\">AVPacketCallback<\/span> <span class=\"o\">*<\/span><span class=\"n\">packet_cb<\/span><span class=\"p\">;<\/span>\n\n<span class=\"c1\">\/\/ ...<\/span>\n\n<span class=\"p\">}<\/span> <span class=\"n\">AVCodecContext<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kt\">int<\/span> <span class=\"nf\">av_frame_yield<\/span><span class=\"p\">(<\/span><span class=\"n\">AVFrameCallback<\/span> <span class=\"o\">*<\/span><span class=\"n\">cb<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">cb<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">yield<\/span><span class=\"p\">(<\/span><span class=\"n\">cb<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">priv_data<\/span><span class=\"p\">,<\/span> <span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kt\">int<\/span> <span class=\"nf\">av_packet_yield<\/span><span class=\"p\">(<\/span><span class=\"n\">AVPacketCallback<\/span> <span class=\"o\">*<\/span><span class=\"n\">cb<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">packet<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">cb<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">yield<\/span><span class=\"p\">(<\/span><span class=\"n\">cb<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">priv_data<\/span><span class=\"p\">,<\/span> <span class=\"n\">packet<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/pre>\n<\/div>\n<p>Instead of using directly the Queue API, would be possible to use <code>yield<\/code> functions and give the user a mean to override them.<\/p>\n<p>Some API sugar could be something along the lines of this:<\/p>\n<div class=\"codehilite\">\n<pre><span class=\"kt\">int<\/span> <span class=\"nf\">avcodec_decode_yield<\/span><span class=\"p\">(<\/span><span class=\"n\">AVCodecContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">avctx<\/span><span class=\"p\">,<\/span> <span class=\"n\">AVFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n    <span class=\"kt\">int<\/span> <span class=\"n\">ret<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">avctx<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">frame_cb<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">av_frame_yield<\/span><span class=\"p\">(<\/span><span class=\"n\">avctx<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">frame_cb<\/span><span class=\"p\">,<\/span> <span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n    <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"p\">{<\/span>\n        <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">av_frame_queue_put<\/span><span class=\"p\">(<\/span><span class=\"n\">avctx<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">frame_queue<\/span><span class=\"p\">,<\/span> <span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n    <span class=\"p\">}<\/span>\n\n    <span class=\"k\">return<\/span> <span class=\"n\">ret<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/pre>\n<\/div>\n<p>Whenever a frame (or a packet) is ready it could be passed immediately to another function, depending on your threading model and cpu it might be much more efficient skipping some <em>enqueuing+dequeuing<\/em> steps such as feeding directly some user-queue that uses different datatypes.<\/p>\n<p>This approach might work well even internally to insert bitstream reformatters after the encoding or before the decoding.<\/p>\n<h4>Open problems<\/h4>\n<p>The callback system is quite powerful but you have at least a couple of issues to take care of:<br \/>\n&#8211; Error reporting: when something goes wrong how to notify what broke?<br \/>\n&#8211; Error recovery: how much the user have to undo to fallback properly?<\/p>\n<p>Probably this part should be kept for later, since there is already a huge amount of work.<\/p>\n<h2>What&#8217;s next<\/h2>\n<h3>Muxing and demuxing<\/h3>\n<p>Ideally the container format layer should receive the same kind of overhaul, I&#8217;m not even halfway documenting what should<br \/>\nchange, but from this blog post you might guess the kind of changes. <strong>Spoiler:<\/strong> The I\/O layer gets spun in a separate library.<\/p>\n<h3>Proof of Concept<\/h3>\n<p><em>Soon<\/em>^W<em>Not so late<\/em> I&#8217;ll complete a POC out of this and possibly hack <code>avplay<\/code> so that either it uses <code>QSV<\/code> or <code>videotoolbox<\/code> as test-case (depending on which operating system I&#8217;m playing with when I start), probably I&#8217;ll see which are the limitations in this approach soon.<\/p>\n<div class=\"alert alert-success\">\nIf you like the ideas posted above or you want to discuss them more, you can join the Libav <a href=\"irc:\/\/irc.freenode.net\/libav-devel\">irc channel<\/a> or <a href=\"https:\/\/lists.libav.org\/mailman\/listinfo\/libav-devel\/\">mailing list<\/a> to discuss and help.\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>During the VDD we had lots of discussions and I enjoyed reviewing the initial NihAV implementation. Kostya already wrote some more about the decoupled API that I described at high level here. This article is about some possible implementation details, at least another will follow. The new API requires some additional data structures, mainly something &hellip; <a href=\"https:\/\/blogs.gentoo.org\/lu_zero\/2015\/10\/02\/decoupling-an-api-part-ii\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Decoupling an API &#8211; Part II<\/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],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p1aGWH-8o","_links":{"self":[{"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/posts\/520"}],"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=520"}],"version-history":[{"count":5,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/posts\/520\/revisions"}],"predecessor-version":[{"id":525,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/posts\/520\/revisions\/525"}],"wp:attachment":[{"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/media?parent=520"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/categories?post=520"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/tags?post=520"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}