{"id":723,"date":"2019-04-09T13:52:55","date_gmt":"2019-04-09T13:52:55","guid":{"rendered":"http:\/\/blogs.gentoo.org\/lu_zero\/?p=723"},"modified":"2019-04-09T13:53:07","modified_gmt":"2019-04-09T13:53:07","slug":"using-rav1e-from-your-code","status":"publish","type":"post","link":"https:\/\/blogs.gentoo.org\/lu_zero\/2019\/04\/09\/using-rav1e-from-your-code\/","title":{"rendered":"Using rav1e &#8211; from your code"},"content":{"rendered":"<h2>AV1, Rav1e, Crav1e, an intro<\/h2>\n<p><small>(this article is also available on my <a href=\"https:\/\/dev.to\/luzero\">dev.to<\/a> profile, I might use it more often since wordpress is pretty horrible at managing markdown.)<\/small><\/p>\n<blockquote><p>\n<a href=\"https:\/\/aomedia.org\/av1-features\/\">AV1<\/a> is a modern video codec brought to you by an alliance of <a href=\"https:\/\/aomedia.org\/membership\/members\/\">many<\/a> different bigger and smaller players in the multimedia field.<br \/>\nI&#8217;m part of the <a href=\"https:\/\/videolan.org\">VideoLan<\/a> organization and I spent quite a bit of time on this codec lately.<\/p><\/blockquote>\n<hr>\n<blockquote><p><a href=\"https:\/\/github.com\/xiph\/rav1e\/\">rav1e<\/a>: The safest and fastest AV1 encoder, built by many volunteers and Mozilla\/Xiph developers.<br \/>\nIt is written in <a href=\"https:\/\/rustlang.org\">rust<\/a> and strives to provide good speed, quality and stay maintainable.<\/p><\/blockquote>\n<hr>\n<blockquote><p><a href=\"https:\/\/github.com\/lu-zero\/crav1e\">crav1e<\/a>: A companion <a href=\"https:\/\/doc.rust-lang.org\/book\/crates-and-modules.html\">crate<\/a>, written by <a href=\"https:\/\/github.com\/lu-zero\">yours truly<\/a>, that provides a C-API, so the encoder can be used by C libraries and programs.<\/p><\/blockquote>\n<hr>\n<p>This article will just give a quick overview of the API available right now and it is mainly to help people start using it and hopefully report issues and problem.<\/p>\n<h2>Rav1e API<\/h2>\n<p>The current API is built around the following 4 structs and 1 enum:<\/p>\n<ul>\n<li><code>struct Frame<\/code>: The raw pixel data<\/li>\n<li><code>struct Packet<\/code>: The encoded bitstream<\/li>\n<li><code>struct Config<\/code>: The encoder configuration<\/li>\n<li><code>struct Context<\/code>: The encoder state<\/li>\n<\/ul>\n<hr>\n<ul>\n<li><code>enum EncoderStatus<\/code>: Fatal and non-fatal condition returned by the <code>Context<\/code>methods.<\/li>\n<\/ul>\n<h3>Config<\/h3>\n<p>The <code>Config<\/code> struct currently is simply constructed.<\/p>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"w\">    <\/span><span class=\"k\">struct<\/span> <span class=\"nc\">Config<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"><\/span>\n<span class=\"w\">        <\/span><span class=\"n\">enc<\/span>: <span class=\"nc\">EncoderConfig<\/span><span class=\"p\">,<\/span><span class=\"w\"><\/span>\n<span class=\"w\">        <\/span><span class=\"n\">threads<\/span>: <span class=\"kt\">usize<\/span><span class=\"p\">,<\/span><span class=\"w\"><\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span><span class=\"w\"><\/span>\n<\/pre>\n<\/div>\n<p>The <code>EncoderConfig<\/code> stores all the settings that have an impact to the actual bitstream while settings such as <code>threads<\/code> are kept outside.<\/p>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"w\">    <\/span><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"k\">mut<\/span><span class=\"w\"> <\/span><span class=\"n\">enc<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">EncoderConfig<\/span>::<span class=\"n\">with_speed_preset<\/span><span class=\"p\">(<\/span><span class=\"n\">speed<\/span><span class=\"p\">);<\/span><span class=\"w\"><\/span>\n<span class=\"w\">    <\/span><span class=\"n\">enc<\/span><span class=\"p\">.<\/span><span class=\"n\">width<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">w<\/span><span class=\"p\">;<\/span><span class=\"w\"><\/span>\n<span class=\"w\">    <\/span><span class=\"n\">enc<\/span><span class=\"p\">.<\/span><span class=\"n\">height<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">h<\/span><span class=\"p\">;<\/span><span class=\"w\"><\/span>\n<span class=\"w\">    <\/span><span class=\"n\">enc<\/span><span class=\"p\">.<\/span><span class=\"n\">bit_depth<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">8<\/span><span class=\"p\">;<\/span><span class=\"w\"><\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">cfg<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">Config<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span><span class=\"n\">enc<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">threads<\/span>: <span class=\"mi\">0<\/span><span class=\"w\"> <\/span><span class=\"p\">};<\/span><span class=\"w\"><\/span>\n<\/pre>\n<\/div>\n<blockquote><p><strong>NOTE<\/strong>: Some of the fields above may be shuffled around until the API is marked as stable.<\/p><\/blockquote>\n<h4>Methods<\/h4>\n<h5>new_context<\/h5>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"w\">    <\/span><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">cfg<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">Config<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span><span class=\"n\">enc<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">threads<\/span>: <span class=\"mi\">0<\/span><span class=\"w\"> <\/span><span class=\"p\">};<\/span><span class=\"w\"><\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">ctx<\/span>: <span class=\"nc\">Context<\/span><span class=\"o\">&lt;<\/span><span class=\"kt\">u8<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">cfg<\/span><span class=\"p\">.<\/span><span class=\"n\">new_context<\/span><span class=\"p\">();<\/span><span class=\"w\"><\/span>\n<\/pre>\n<\/div>\n<p>It produces a new encoding context. Where <code>bit_depth<\/code> is <em>8<\/em>, it is possible to use an optimized <code>u8<\/code> codepath, otherwise <code>u16<\/code> must be used.<\/p>\n<h3>Context<\/h3>\n<p>It is produced by <code>Config::new_context<\/code>, its implementation details are hidden.<\/p>\n<h4>Methods<\/h4>\n<p>The <code>Context<\/code> can be grouped into <strong>essential<\/strong>, <strong>optional<\/strong> and <strong>convenience<\/strong>.<\/p>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"w\">    <\/span><span class=\"c1\">\/\/ Essential API<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">pub<\/span><span class=\"w\"> <\/span><span class=\"k\">fn<\/span> <span class=\"nf\">send_frame<\/span><span class=\"o\">&lt;<\/span><span class=\"n\">F<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"k\">mut<\/span><span class=\"w\"> <\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">frame<\/span>: <span class=\"nc\">F<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span>-&gt; <span class=\"nb\">Result<\/span><span class=\"o\">&lt;<\/span><span class=\"p\">(),<\/span><span class=\"w\"> <\/span><span class=\"n\">EncoderStatus<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"><\/span>\n<span class=\"w\">      <\/span><span class=\"k\">where<\/span><span class=\"w\"> <\/span><span class=\"n\">F<\/span>: <span class=\"nb\">Into<\/span><span class=\"o\">&lt;<\/span><span class=\"nb\">Option<\/span><span class=\"o\">&lt;<\/span><span class=\"n\">Arc<\/span><span class=\"o\">&lt;<\/span><span class=\"n\">Frame<\/span><span class=\"o\">&lt;<\/span><span class=\"n\">T<\/span><span class=\"o\">&gt;&gt;&gt;&gt;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">T<\/span>: <span class=\"nc\">Pixel<\/span><span class=\"p\">;<\/span><span class=\"w\"><\/span>\n<span class=\"w\">    <\/span><span class=\"k\">pub<\/span><span class=\"w\"> <\/span><span class=\"k\">fn<\/span> <span class=\"nf\">receive_packet<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"k\">mut<\/span><span class=\"w\"> <\/span><span class=\"bp\">self<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span>-&gt; <span class=\"nb\">Result<\/span><span class=\"o\">&lt;<\/span><span class=\"n\">Packet<\/span><span class=\"o\">&lt;<\/span><span class=\"n\">T<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">EncoderStatus<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span><span class=\"w\"><\/span>\n<\/pre>\n<\/div>\n<p>The encoder works by processing each <code>Frame<\/code> fed through <code>send_frame<\/code> and producing each <code>Packet<\/code> that can be retrieved by <code>receive_packet<\/code>.<\/p>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"w\">    <\/span><span class=\"c1\">\/\/ Optional API<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">pub<\/span><span class=\"w\"> <\/span><span class=\"k\">fn<\/span> <span class=\"nf\">container_sequence_header<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"k\">mut<\/span><span class=\"w\"> <\/span><span class=\"bp\">self<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span>-&gt; <span class=\"nb\">Vec<\/span><span class=\"o\">&lt;<\/span><span class=\"kt\">u8<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span><span class=\"w\"><\/span>\n<span class=\"w\">    <\/span><span class=\"k\">pub<\/span><span class=\"w\"> <\/span><span class=\"k\">fn<\/span> <span class=\"nf\">get_first_pass_data<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"bp\">self<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span>-&gt; <span class=\"kp\">&amp;<\/span><span class=\"nc\">FirstPassData<\/span><span class=\"p\">;<\/span><span class=\"w\"><\/span>\n<\/pre>\n<\/div>\n<p>Depending on the container format, the <code>AV1<\/code>  Sequence Header could be stored in the extradata. <code>container_sequence_header<\/code> produces the data pre-formatted to be simply stored in <code>mkv<\/code> or  <code>mp4<\/code>.<\/p>\n<p><code>rav1e<\/code> supports multi-pass encoding and the encoding data from the first pass can be retrieved by calling <code>get_first_pass_data<\/code>.<\/p>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"w\">    <\/span><span class=\"c1\">\/\/ Convenience shortcuts<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">pub<\/span><span class=\"w\"> <\/span><span class=\"k\">fn<\/span> <span class=\"nf\">new_frame<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"bp\">self<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span>-&gt; <span class=\"nc\">Arc<\/span><span class=\"o\">&lt;<\/span><span class=\"n\">Frame<\/span><span class=\"o\">&lt;<\/span><span class=\"n\">T<\/span><span class=\"o\">&gt;&gt;<\/span><span class=\"p\">;<\/span><span class=\"w\"><\/span>\n<span class=\"w\">    <\/span><span class=\"k\">pub<\/span><span class=\"w\"> <\/span><span class=\"k\">fn<\/span> <span class=\"nf\">set_limit<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"k\">mut<\/span><span class=\"w\"> <\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">limit<\/span>: <span class=\"kt\">u64<\/span><span class=\"p\">);<\/span><span class=\"w\"><\/span>\n<span class=\"w\">    <\/span><span class=\"k\">pub<\/span><span class=\"w\"> <\/span><span class=\"k\">fn<\/span> <span class=\"nf\">flush<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"k\">mut<\/span><span class=\"w\"> <\/span><span class=\"bp\">self<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"><\/span>\n<\/pre>\n<\/div>\n<ul>\n<li><code>new_frame()<\/code> produces a frame according to the dimension and pixel format information in the Context.<\/li>\n<li><code>flush()<\/code> is functionally equivalent to call <code>send_frame(None)<\/code>.<\/li>\n<li><code>set_limit()<\/code>is functionally equivalent to call <code>flush()<\/code>once <code>limit<\/code> frames are sent to the encoder.<\/li>\n<\/ul>\n<h3>Workflow<\/h3>\n<p>The workflow is the following:<\/p>\n<ol>\n<li>Setup:\n<ul>\n<li>Prepare a <code>Config<\/code><\/li>\n<li>Call <code>new_context<\/code> from the <code>Config<\/code> to produce a <code>Context<\/code><\/li>\n<\/ul>\n<\/li>\n<li>Encode loop:\n<ul>\n<li>Pull each <code>Packet<\/code> using <code>receive_packet<\/code>.<\/li>\n<li>If <code>receive_packet<\/code> returns <code>EncoderStatus::NeedMoreData<\/code>\n<ul>\n<li>Feed each <code>Frame<\/code> to the <code>Context<\/code> using <code>send_frame<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>Complete the encoding\n<ul>\n<li>Issue a <code>flush()<\/code> to encode each pending <code>Frame<\/code> in a final <code>Packet<\/code>.<\/li>\n<li>Call <code>receive_packet<\/code> until <code>EncoderStatus::LimitReached<\/code> is returned.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h2>Crav1e API<\/h2>\n<p>The <a href=\"https:\/\/github.com\/lu-zero\/crav1e\">crav1e<\/a> API provides the same structures and features beside few key differences:<\/p>\n<ul>\n<li>The <code>Frame<\/code>, <code>Config<\/code>,  and <code>Context<\/code> structs are <em>opaque<\/em>.<\/li>\n<\/ul>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">RaConfig<\/span> <span class=\"n\">RaConfig<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">RaContext<\/span> <span class=\"n\">RaContext<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"n\">RaFrame<\/span> <span class=\"n\">RaFrame<\/span><span class=\"p\">;<\/span>\n<\/pre>\n<\/div>\n<ul>\n<li>The <code>Packet<\/code> struct exposed is much simpler than the <code>rav1e<\/code> original.<\/li>\n<\/ul>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"k\">typedef<\/span> <span class=\"k\">struct<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">const<\/span> <span class=\"kt\">uint8_t<\/span> <span class=\"o\">*<\/span><span class=\"n\">data<\/span><span class=\"p\">;<\/span>\n    <span class=\"kt\">size_t<\/span> <span class=\"n\">len<\/span><span class=\"p\">;<\/span>\n    <span class=\"kt\">uint64_t<\/span> <span class=\"n\">number<\/span><span class=\"p\">;<\/span>\n    <span class=\"n\">RaFrameType<\/span> <span class=\"n\">frame_type<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">RaPacket<\/span><span class=\"p\">;<\/span>\n<\/pre>\n<\/div>\n<ul>\n<li>The EncoderStatus includes a <code>Success<\/code> condition.<\/li>\n<\/ul>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"k\">typedef<\/span> <span class=\"k\">enum<\/span> <span class=\"p\">{<\/span>\n    <span class=\"n\">RA_ENCODER_STATUS_SUCCESS<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">RA_ENCODER_STATUS_NEED_MORE_DATA<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">RA_ENCODER_STATUS_ENOUGH_DATA<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">RA_ENCODER_STATUS_LIMIT_REACHED<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">RA_ENCODER_STATUS_FAILURE<\/span> <span class=\"o\">=<\/span> <span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span>\n<span class=\"p\">}<\/span> <span class=\"n\">RaEncoderStatus<\/span><span class=\"p\">;<\/span>\n<\/pre>\n<\/div>\n<h3>RaConfig<\/h3>\n<p>Since the configuration is <code>opaque<\/code> there are a number of functions to assemble it:<\/p>\n<ul>\n<li><code>rav1e_config_default<\/code> allocates a default configuration.<\/li>\n<li><code>rav1e_config_parse<\/code> and <code>rav1e_config_parse_int<\/code> set a specific <code>value<\/code> for a specific field selected by a <code>key<\/code> string.<\/li>\n<li><code>rav1e_config_set_${field}<\/code> are specialized setters for complex information such as the color description.<\/li>\n<\/ul>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"n\">RaConfig<\/span> <span class=\"o\">*<\/span><span class=\"nf\">rav1e_config_default<\/span><span class=\"p\">(<\/span><span class=\"kt\">void<\/span><span class=\"p\">);<\/span>\n\n<span class=\"cm\">\/**<\/span>\n<span class=\"cm\"> * Set a configuration parameter using its key and value as string.<\/span>\n<span class=\"cm\"> * Available keys and values<\/span>\n<span class=\"cm\"> * - \"quantizer\": 0-255, default 100<\/span>\n<span class=\"cm\"> * - \"speed\": 0-10, default 3<\/span>\n<span class=\"cm\"> * - \"tune\": \"psnr\"-\"psychovisual\", default \"psnr\"<\/span>\n<span class=\"cm\"> * Return a negative value on error or 0.<\/span>\n<span class=\"cm\"> *\/<\/span>\n<span class=\"kt\">int<\/span> <span class=\"nf\">rav1e_config_parse<\/span><span class=\"p\">(<\/span><span class=\"n\">RaConfig<\/span> <span class=\"o\">*<\/span><span class=\"n\">cfg<\/span><span class=\"p\">,<\/span> <span class=\"k\">const<\/span> <span class=\"kt\">char<\/span> <span class=\"o\">*<\/span><span class=\"n\">key<\/span><span class=\"p\">,<\/span> <span class=\"k\">const<\/span> <span class=\"kt\">char<\/span> <span class=\"o\">*<\/span><span class=\"n\">value<\/span><span class=\"p\">);<\/span>\n\n<span class=\"cm\">\/**<\/span>\n<span class=\"cm\"> * Set a configuration parameter using its key and value as integer.<\/span>\n<span class=\"cm\"> * Available keys and values are the same as rav1e_config_parse()<\/span>\n<span class=\"cm\"> * Return a negative value on error or 0.<\/span>\n<span class=\"cm\"> *\/<\/span>\n<span class=\"kt\">int<\/span> <span class=\"nf\">rav1e_config_parse_int<\/span><span class=\"p\">(<\/span><span class=\"n\">RaConfig<\/span> <span class=\"o\">*<\/span><span class=\"n\">cfg<\/span><span class=\"p\">,<\/span> <span class=\"k\">const<\/span> <span class=\"kt\">char<\/span> <span class=\"o\">*<\/span><span class=\"n\">key<\/span><span class=\"p\">,<\/span> <span class=\"kt\">int<\/span> <span class=\"n\">value<\/span><span class=\"p\">);<\/span>\n\n<span class=\"cm\">\/**<\/span>\n<span class=\"cm\"> * Set color properties of the stream.<\/span>\n<span class=\"cm\"> * Supported values are defined by the enum types<\/span>\n<span class=\"cm\"> * RaMatrixCoefficients, RaColorPrimaries, and RaTransferCharacteristics<\/span>\n<span class=\"cm\"> * respectively.<\/span>\n<span class=\"cm\"> * Return a negative value on error or 0.<\/span>\n<span class=\"cm\"> *\/<\/span>\n<span class=\"kt\">int<\/span> <span class=\"nf\">rav1e_config_set_color_description<\/span><span class=\"p\">(<\/span><span class=\"n\">RaConfig<\/span> <span class=\"o\">*<\/span><span class=\"n\">cfg<\/span><span class=\"p\">,<\/span>\n                                       <span class=\"n\">RaMatrixCoefficients<\/span> <span class=\"n\">matrix<\/span><span class=\"p\">,<\/span>\n                                       <span class=\"n\">RaColorPrimaries<\/span> <span class=\"n\">primaries<\/span><span class=\"p\">,<\/span>\n                                       <span class=\"n\">RaTransferCharacteristics<\/span> <span class=\"n\">transfer<\/span><span class=\"p\">);<\/span>\n\n<span class=\"cm\">\/**<\/span>\n<span class=\"cm\"> * Set the content light level information for HDR10 streams.<\/span>\n<span class=\"cm\"> * Return a negative value on error or 0.<\/span>\n<span class=\"cm\"> *\/<\/span>\n<span class=\"kt\">int<\/span> <span class=\"nf\">rav1e_config_set_content_light<\/span><span class=\"p\">(<\/span><span class=\"n\">RaConfig<\/span> <span class=\"o\">*<\/span><span class=\"n\">cfg<\/span><span class=\"p\">,<\/span>\n                                   <span class=\"kt\">uint16_t<\/span> <span class=\"n\">max_content_light_level<\/span><span class=\"p\">,<\/span>\n                                   <span class=\"kt\">uint16_t<\/span> <span class=\"n\">max_frame_average_light_level<\/span><span class=\"p\">);<\/span>\n\n<span class=\"cm\">\/**<\/span>\n<span class=\"cm\"> * Set the mastering display information for HDR10 streams.<\/span>\n<span class=\"cm\"> * primaries and white_point arguments are RaPoint, containing 0.16 fixed point values.<\/span>\n<span class=\"cm\"> * max_luminance is a 24.8 fixed point value.<\/span>\n<span class=\"cm\"> * min_luminance is a 18.14 fixed point value.<\/span>\n<span class=\"cm\"> * Returns a negative value on error or 0.<\/span>\n<span class=\"cm\"> *\/<\/span>\n<span class=\"kt\">int<\/span> <span class=\"nf\">rav1e_config_set_mastering_display<\/span><span class=\"p\">(<\/span><span class=\"n\">RaConfig<\/span> <span class=\"o\">*<\/span><span class=\"n\">cfg<\/span><span class=\"p\">,<\/span>\n                                       <span class=\"n\">RaPoint<\/span> <span class=\"n\">primaries<\/span><span class=\"p\">[<\/span><span class=\"mi\">3<\/span><span class=\"p\">],<\/span>\n                                       <span class=\"n\">RaPoint<\/span> <span class=\"n\">white_point<\/span><span class=\"p\">,<\/span>\n                                       <span class=\"kt\">uint32_t<\/span> <span class=\"n\">max_luminance<\/span><span class=\"p\">,<\/span>\n                                       <span class=\"kt\">uint32_t<\/span> <span class=\"n\">min_luminance<\/span><span class=\"p\">);<\/span>\n\n<span class=\"kt\">void<\/span> <span class=\"nf\">rav1e_config_unref<\/span><span class=\"p\">(<\/span><span class=\"n\">RaConfig<\/span> <span class=\"o\">*<\/span><span class=\"n\">cfg<\/span><span class=\"p\">);<\/span>\n<\/pre>\n<\/div>\n<p>The bare minimum setup code is the following:<\/p>\n<div class=\"codehilite\">\n<pre><span><\/span>    <span class=\"kt\">int<\/span> <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\n    <span class=\"n\">RaConfig<\/span> <span class=\"o\">*<\/span><span class=\"n\">rac<\/span> <span class=\"o\">=<\/span> <span class=\"n\">rav1e_config_default<\/span><span class=\"p\">();<\/span>\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"n\">rac<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"Unable to initialize<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">goto<\/span> <span class=\"n\">clean<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n\n    <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">rav1e_config_parse_int<\/span><span class=\"p\">(<\/span><span class=\"n\">rac<\/span><span class=\"p\">,<\/span> <span class=\"s\">\"width\"<\/span><span class=\"p\">,<\/span> <span class=\"mi\">64<\/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> <span class=\"p\">{<\/span>\n        <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"Unable to configure width<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">goto<\/span> <span class=\"n\">clean<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n\n    <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">rav1e_config_parse_int<\/span><span class=\"p\">(<\/span><span class=\"n\">rac<\/span><span class=\"p\">,<\/span> <span class=\"s\">\"height\"<\/span><span class=\"p\">,<\/span> <span class=\"mi\">96<\/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> <span class=\"p\">{<\/span>\n        <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"Unable to configure height<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">goto<\/span> <span class=\"n\">clean<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n\n    <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">rav1e_config_parse_int<\/span><span class=\"p\">(<\/span><span class=\"n\">rac<\/span><span class=\"p\">,<\/span> <span class=\"s\">\"speed\"<\/span><span class=\"p\">,<\/span> <span class=\"mi\">9<\/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> <span class=\"p\">{<\/span>\n        <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"Unable to configure speed<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">goto<\/span> <span class=\"n\">clean<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n<\/pre>\n<\/div>\n<h3>RaContext<\/h3>\n<p>As per the <code>rav1e<\/code> API, the context structure is produced from a configuration and the same <em>send-receive<\/em> model is used.<br \/>\nThe convenience methods aren&#8217;t exposed and the frame allocation function is actually essential.<\/p>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"c1\">\/\/ Essential API<\/span>\n<span class=\"n\">RaContext<\/span> <span class=\"o\">*<\/span><span class=\"nf\">rav1e_context_new<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span> <span class=\"n\">RaConfig<\/span> <span class=\"o\">*<\/span><span class=\"n\">cfg<\/span><span class=\"p\">);<\/span>\n<span class=\"kt\">void<\/span> <span class=\"nf\">rav1e_context_unref<\/span><span class=\"p\">(<\/span><span class=\"n\">RaContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">ctx<\/span><span class=\"p\">);<\/span>\n\n<span class=\"n\">RaEncoderStatus<\/span> <span class=\"nf\">rav1e_send_frame<\/span><span class=\"p\">(<\/span><span class=\"n\">RaContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">ctx<\/span><span class=\"p\">,<\/span> <span class=\"k\">const<\/span> <span class=\"n\">RaFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">RaEncoderStatus<\/span> <span class=\"nf\">rav1e_receive_packet<\/span><span class=\"p\">(<\/span><span class=\"n\">RaContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">ctx<\/span><span class=\"p\">,<\/span> <span class=\"n\">RaPacket<\/span> <span class=\"o\">**<\/span><span class=\"n\">pkt<\/span><span class=\"p\">);<\/span>\n<\/pre>\n<\/div>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"c1\">\/\/ Optional API<\/span>\n<span class=\"kt\">uint8_t<\/span> <span class=\"o\">*<\/span><span class=\"nf\">rav1e_container_sequence_header<\/span><span class=\"p\">(<\/span><span class=\"n\">RaContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">ctx<\/span><span class=\"p\">,<\/span> <span class=\"kt\">size_t<\/span> <span class=\"o\">*<\/span><span class=\"n\">buf_size<\/span><span class=\"p\">);<\/span>\n<span class=\"kt\">void<\/span> <span class=\"nf\">rav1e_container_sequence_header_unref<\/span><span class=\"p\">(<\/span><span class=\"kt\">uint8_t<\/span> <span class=\"o\">*<\/span><span class=\"n\">sequence<\/span><span class=\"p\">);<\/span>\n<\/pre>\n<\/div>\n<h3>RaFrame<\/h3>\n<p>Since the frame structure is opaque in C, we have the following functions to create, fill and dispose of the frames.<\/p>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"n\">RaFrame<\/span> <span class=\"o\">*<\/span><span class=\"nf\">rav1e_frame_new<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span> <span class=\"n\">RaContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">ctx<\/span><span class=\"p\">);<\/span>\n<span class=\"kt\">void<\/span> <span class=\"nf\">rav1e_frame_unref<\/span><span class=\"p\">(<\/span><span class=\"n\">RaFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame<\/span><span class=\"p\">);<\/span>\n\n<span class=\"cm\">\/**<\/span>\n<span class=\"cm\"> * Fill a frame plane<\/span>\n<span class=\"cm\"> * Currently the frame contains 3 planes, the first is luminance followed by<\/span>\n<span class=\"cm\"> * chrominance.<\/span>\n<span class=\"cm\"> * The data is copied and this function has to be called for each plane.<\/span>\n<span class=\"cm\"> * frame: A frame provided by rav1e_frame_new()<\/span>\n<span class=\"cm\"> * plane: The index of the plane starting from 0<\/span>\n<span class=\"cm\"> * data: The data to be copied<\/span>\n<span class=\"cm\"> * data_len: Lenght of the buffer<\/span>\n<span class=\"cm\"> * stride: Plane line in bytes, including padding<\/span>\n<span class=\"cm\"> * bytewidth: Number of bytes per component, either 1 or 2<\/span>\n<span class=\"cm\"> *\/<\/span>\n<span class=\"kt\">void<\/span> <span class=\"nf\">rav1e_frame_fill_plane<\/span><span class=\"p\">(<\/span><span class=\"n\">RaFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">frame<\/span><span class=\"p\">,<\/span>\n                            <span class=\"kt\">int<\/span> <span class=\"n\">plane<\/span><span class=\"p\">,<\/span>\n                            <span class=\"k\">const<\/span> <span class=\"kt\">uint8_t<\/span> <span class=\"o\">*<\/span><span class=\"n\">data<\/span><span class=\"p\">,<\/span>\n                            <span class=\"kt\">size_t<\/span> <span class=\"n\">data_len<\/span><span class=\"p\">,<\/span>\n                            <span class=\"kt\">ptrdiff_t<\/span> <span class=\"n\">stride<\/span><span class=\"p\">,<\/span>\n                            <span class=\"kt\">int<\/span> <span class=\"n\">bytewidth<\/span><span class=\"p\">);<\/span>\n<\/pre>\n<\/div>\n<h3>RaEncoderStatus<\/h3>\n<p>The encoder status enum is returned by the <code>rav1e_send_frame<\/code> and <code>rav1e_receive_packet<\/code> and it is possible already to arbitrarily query the context for its status.<\/p>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"n\">RaEncoderStatus<\/span> <span class=\"nf\">rav1e_last_status<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span> <span class=\"n\">RaContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">ctx<\/span><span class=\"p\">);<\/span>\n<\/pre>\n<\/div>\n<p>To simulate the rust <a href=\"https:\/\/doc.rust-lang.org\/std\/fmt\/trait.Debug.html\">Debug<\/a> functionality a <code>to_str<\/code> function is provided.<\/p>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"kt\">char<\/span> <span class=\"o\">*<\/span><span class=\"nf\">rav1e_status_to_str<\/span><span class=\"p\">(<\/span><span class=\"n\">RaEncoderStatus<\/span> <span class=\"n\">status<\/span><span class=\"p\">);<\/span>\n<\/pre>\n<\/div>\n<h3>Workflow<\/h3>\n<p>The <em>C<\/em> API workflow is similar to the <em>Rust<\/em> one, albeit a little more verbose due to the error checking.<\/p>\n<div class=\"codehilite\">\n<pre><span><\/span>    <span class=\"n\">RaContext<\/span> <span class=\"o\">*<\/span><span class=\"n\">rax<\/span> <span class=\"o\">=<\/span> <span class=\"n\">rav1e_context_new<\/span><span class=\"p\">(<\/span><span class=\"n\">rac<\/span><span class=\"p\">);<\/span>\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"n\">rax<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"Unable to allocate a new context<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">goto<\/span> <span class=\"n\">clean<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n<\/pre>\n<\/div>\n<div class=\"codehilite\">\n<pre><span><\/span>    <span class=\"n\">RaFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">f<\/span> <span class=\"o\">=<\/span> <span class=\"n\">rav1e_frame_new<\/span><span class=\"p\">(<\/span><span class=\"n\">rax<\/span><span class=\"p\">);<\/span>\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"n\">f<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"Unable to allocate a new frame<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">);<\/span>\n        <span class=\"k\">goto<\/span> <span class=\"n\">clean<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n<\/pre>\n<\/div>\n<div class=\"codehilite\">\n<pre><span><\/span><span class=\"k\">while<\/span> <span class=\"p\">(<\/span><span class=\"n\">keep_going<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">)){<\/span>\n     <span class=\"n\">RaPacket<\/span> <span class=\"o\">*<\/span><span class=\"n\">p<\/span><span class=\"p\">;<\/span>\n     <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">rav1e_receive_packet<\/span><span class=\"p\">(<\/span><span class=\"n\">rax<\/span><span class=\"p\">,<\/span> <span class=\"o\">&amp;<\/span><span class=\"n\">p<\/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> <span class=\"p\">{<\/span>\n         <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"Unable to receive packet %d<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">,<\/span> <span class=\"n\">i<\/span><span class=\"p\">);<\/span>\n         <span class=\"k\">goto<\/span> <span class=\"n\">clean<\/span><span class=\"p\">;<\/span>\n     <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">ret<\/span> <span class=\"o\">==<\/span> <span class=\"n\">RA_ENCODER_STATUS_SUCCESS<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n         <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"Packet %\"<\/span><span class=\"n\">PRIu64<\/span><span class=\"s\">\"<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">,<\/span> <span class=\"n\">p<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">number<\/span><span class=\"p\">);<\/span>\n         <span class=\"n\">do_something_with<\/span><span class=\"p\">(<\/span><span class=\"n\">p<\/span><span class=\"p\">);<\/span>\n         <span class=\"n\">rav1e_packet_unref<\/span><span class=\"p\">(<\/span><span class=\"n\">p<\/span><span class=\"p\">);<\/span>\n         <span class=\"n\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">;<\/span>\n     <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">ret<\/span> <span class=\"o\">==<\/span> <span class=\"n\">RA_ENCODER_STATUS_NEED_MORE_DATA<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n         <span class=\"n\">RaFrame<\/span> <span class=\"o\">*<\/span><span class=\"n\">f<\/span> <span class=\"o\">=<\/span> <span class=\"n\">get_frame_by_some_mean<\/span><span class=\"p\">(<\/span><span class=\"n\">rax<\/span><span class=\"p\">);<\/span>\n         <span class=\"n\">ret<\/span> <span class=\"o\">=<\/span> <span class=\"n\">rav1e_send_frame<\/span><span class=\"p\">(<\/span><span class=\"n\">rax<\/span><span class=\"p\">,<\/span> <span class=\"n\">f<\/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> <span class=\"p\">{<\/span>\n            <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"Unable to send frame %d<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">,<\/span> <span class=\"n\">i<\/span><span class=\"p\">);<\/span>\n            <span class=\"k\">goto<\/span> <span class=\"n\">clean<\/span><span class=\"p\">;<\/span>\n        <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"k\">if<\/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=\"c1\">\/\/ Cannot happen in normal conditions<\/span>\n            <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"Unable to append frame %d to the internal queue<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">,<\/span> <span class=\"n\">i<\/span><span class=\"p\">);<\/span>\n            <span class=\"n\">abort<\/span><span class=\"p\">();<\/span>\n        <span class=\"p\">}<\/span>\n     <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">ret<\/span> <span class=\"o\">==<\/span> <span class=\"n\">RA_ENCODER_STATUS_LIMIT_REACHED<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n         <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"Limit reached<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">);<\/span>\n         <span class=\"k\">break<\/span><span class=\"p\">;<\/span>\n     <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/pre>\n<\/div>\n<h2>In closing<\/h2>\n<p>This article was mainly a good excuse to try <a href=\"https:\/\/dev.to\">dev.to<\/a> and see write down some notes and clarify my ideas on what had been done API-wise so far and what I should change and improve.<\/p>\n<p>If you managed to read till here, your feedback is really welcome, please feel free to comment, try the software and open issues for <a href=\"https:\/\/github.com\/lu-zero\/crav1e\/issues\/new\">crav1e<\/a> and <a href=\"https:\/\/github.com\/xiph\/rav1e\/issues\/new\">rav1e<\/a>.<\/p>\n<h3>Coming next<\/h3>\n<ul>\n<li>Working <strong>crav1e<\/strong> got me to see what&#8217;s good and what is lacking in the <a href=\"https:\/\/blogs.gentoo.org\/lu_zero\/2018\/12\/30\/making-and-using-c-compatible-libraries-in-rust-present-and-future\/\">c-interoperability story<\/a> of <em>rust<\/em>, now that <a href=\"https:\/\/github.com\/rust-lang\/cargo\/pull\/6298\">this<\/a> landed I can start crafting and publishing better tools for it and maybe I&#8217;ll talk more about it here.<\/li>\n<li>Soon <strong>rav1e<\/strong> will get more threading-oriented features, some benchmarking experiments will happen soon.<\/li>\n<\/ul>\n<h3>Thanks<\/h3>\n<ul>\n<li>Special thanks to <a href=\"https:\/\/github.com\/dwbuiten\">Derek<\/a> and <a href=\"https:\/\/github.com\/kodabb\/\">Vittorio<\/a> spent lots of time integrating <code>crav1e<\/code> in larger software and gave precious feedback in what was missing and broken in the initial iterations.<\/li>\n<li>Thanks to <a href=\"http:\/\/github.com\/barrbrain\/\">David<\/a> for the review and editorial work.<\/li>\n<li>Also thanks to <a href=\"https:\/\/dev.to\/naufraghi\">Matteo<\/a> for introducing me to <a href=\"https:\/\/dev.to\">dev.to<\/a>.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>AV1, Rav1e, Crav1e, an intro (this article is also available on my dev.to profile, I might use it more often since wordpress is pretty horrible at managing markdown.) AV1 is a modern video codec brought to you by an alliance of many different bigger and smaller players in the multimedia field. I&#8217;m part of the &hellip; <a href=\"https:\/\/blogs.gentoo.org\/lu_zero\/2019\/04\/09\/using-rav1e-from-your-code\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Using rav1e &#8211; from your code<\/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":[1],"tags":[28,34,33,32,31],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p1aGWH-bF","_links":{"self":[{"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/posts\/723"}],"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=723"}],"version-history":[{"count":9,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/posts\/723\/revisions"}],"predecessor-version":[{"id":732,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/posts\/723\/revisions\/732"}],"wp:attachment":[{"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/media?parent=723"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/categories?post=723"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/lu_zero\/wp-json\/wp\/v2\/tags?post=723"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}