]> git.sesse.net Git - x264/commitdiff
Callback feature for low-latency per-slice output
authorFiona Glaser <fiona@x264.com>
Mon, 28 Jun 2010 22:02:33 +0000 (15:02 -0700)
committerFiona Glaser <fiona@x264.com>
Fri, 2 Jul 2010 03:38:26 +0000 (20:38 -0700)
Add a callback to allow the calling application to send slices immediately after being encoded.
Also add some extra information to the x264_nal_t structure to help inform such a calling application how the NAL units should be ordered.

Full documentation is in x264.h.

common/bitstream.c
common/bitstream.h
encoder/encoder.c
x264.h

index e094c261dfb6227574381b1e9076b013de7a72f1..23d3cb71745d45e7c0e0d677ad7e1228e8ceba0e 100644 (file)
@@ -44,7 +44,7 @@ uint8_t *x264_nal_escape_sse2( uint8_t *dst, uint8_t *src, uint8_t *end );
 /****************************************************************************
  * x264_nal_encode:
  ****************************************************************************/
-int x264_nal_encode( x264_t *h, uint8_t *dst, x264_nal_t *nal, int b_long_startcode )
+void x264_nal_encode( x264_t *h, uint8_t *dst, x264_nal_t *nal )
 {
     uint8_t *src = nal->p_payload;
     uint8_t *end = nal->p_payload + nal->i_payload;
@@ -52,7 +52,7 @@ int x264_nal_encode( x264_t *h, uint8_t *dst, x264_nal_t *nal, int b_long_startc
 
     if( h->param.b_annexb )
     {
-        if( b_long_startcode )
+        if( nal->b_long_startcode )
             *dst++ = 0x00;
         *dst++ = 0x00;
         *dst++ = 0x00;
@@ -77,7 +77,8 @@ int x264_nal_encode( x264_t *h, uint8_t *dst, x264_nal_t *nal, int b_long_startc
         orig_dst[3] = size>> 0;
     }
 
-    return size+4;
+    nal->i_payload = size+4;
+    nal->p_payload = orig_dst;
 }
 
 void x264_bitstream_init( int cpu, x264_bitstream_function_t *pf )
index f477a70266aaddf6730a47f4d3165b19af71424b..d10f3a2026ab72ad2a74f0b87cc6e434000d8988 100644 (file)
@@ -68,7 +68,6 @@ typedef struct
     uint8_t *(*nal_escape) ( uint8_t *dst, uint8_t *src, uint8_t *end );
 } x264_bitstream_function_t;
 
-int x264_nal_encode( x264_t *h, uint8_t *dst, x264_nal_t *nal, int b_long_startcode );
 void x264_bitstream_init( int cpu, x264_bitstream_function_t *pf );
 
 /* A larger level table size theoretically could help a bit at extremely
index 00b22d0ddd83dc391ca2687a3d1299c5fc6290e6..e5156337a88046720821633048050d4f7c341893 100644 (file)
@@ -427,6 +427,8 @@ static int x264_validate_parameters( x264_t *h )
     else
         h->param.b_sliced_threads = 0;
     h->i_thread_frames = h->param.b_sliced_threads ? 1 : h->param.i_threads;
+    if( h->i_thread_frames > 1 )
+        h->param.nalu_process = NULL;
 
     if( h->param.b_interlaced )
     {
@@ -1253,8 +1255,9 @@ static void x264_nal_start( x264_t *h, int i_type, int i_ref_idc )
 {
     x264_nal_t *nal = &h->out.nal[h->out.i_nal];
 
-    nal->i_ref_idc = i_ref_idc;
-    nal->i_type    = i_type;
+    nal->i_ref_idc        = i_ref_idc;
+    nal->i_type           = i_type;
+    nal->b_long_startcode = 1;
 
     nal->i_payload= 0;
     nal->p_payload= &h->out.p_bitstream[bs_pos( &h->out.bs ) / 8];
@@ -1280,6 +1283,8 @@ static int x264_nal_end( x264_t *h )
 {
     x264_nal_t *nal = &h->out.nal[h->out.i_nal];
     nal->i_payload = &h->out.p_bitstream[bs_pos( &h->out.bs ) / 8] - nal->p_payload;
+    if( h->param.nalu_process )
+        h->param.nalu_process( h, nal );
     h->out.i_nal++;
 
     return x264_nal_check_buffer( h );
@@ -1289,6 +1294,13 @@ static int x264_encoder_encapsulate_nals( x264_t *h, int start )
 {
     int nal_size = 0, previous_nal_size = 0;
 
+    if( h->param.nalu_process )
+    {
+        for( int i = start; i < h->out.i_nal; i++ )
+            nal_size += h->out.nal[i].i_payload;
+        return nal_size;
+    }
+
     for( int i = 0; i < start; i++ )
         previous_nal_size += h->out.nal[i].i_payload;
 
@@ -1311,11 +1323,9 @@ static int x264_encoder_encapsulate_nals( x264_t *h, int start )
 
     for( int i = start; i < h->out.i_nal; i++ )
     {
-        int long_startcode = !i || h->out.nal[i].i_type == NAL_SPS || h->out.nal[i].i_type == NAL_PPS;
-        int size = x264_nal_encode( h, nal_buffer, &h->out.nal[i], long_startcode );
-        h->out.nal[i].i_payload = size;
-        h->out.nal[i].p_payload = nal_buffer;
-        nal_buffer += size;
+        h->out.nal[i].b_long_startcode = !i || h->out.nal[i].i_type == NAL_SPS || h->out.nal[i].i_type == NAL_PPS;
+        x264_nal_encode( h, nal_buffer, &h->out.nal[i] );
+        nal_buffer += h->out.nal[i].i_payload;
     }
 
     x264_emms();
@@ -1805,6 +1815,7 @@ static int x264_slice_write( x264_t *h )
 
     /* Slice */
     x264_nal_start( h, h->i_nal_type, h->i_nal_ref_idc );
+    h->out.nal[h->out.i_nal].i_first_mb = h->sh.i_first_mb;
 
     /* Slice header */
     x264_macroblock_thread_init( h );
@@ -2020,6 +2031,7 @@ static int x264_slice_write( x264_t *h )
             i_mb_x = 0;
         }
     }
+    h->out.nal[h->out.i_nal].i_last_mb = h->sh.i_last_mb;
 
     if( h->param.b_cabac )
     {
diff --git a/x264.h b/x264.h
index 1138a8bafde49efc77e8adcbe4fc4256f6d1f82e..e1ae0840b24dd3675e218609dd20c78d980e55ac 100644 (file)
--- a/x264.h
+++ b/x264.h
 
 #include <stdarg.h>
 
-#define X264_BUILD 100
+#define X264_BUILD 101
 
 /* x264_t:
  *      opaque handler for encoder */
 typedef struct x264_t x264_t;
 
+/****************************************************************************
+ * NAL structure and functions
+ ****************************************************************************/
+
+enum nal_unit_type_e
+{
+    NAL_UNKNOWN     = 0,
+    NAL_SLICE       = 1,
+    NAL_SLICE_DPA   = 2,
+    NAL_SLICE_DPB   = 3,
+    NAL_SLICE_DPC   = 4,
+    NAL_SLICE_IDR   = 5,    /* ref_idc != 0 */
+    NAL_SEI         = 6,    /* ref_idc == 0 */
+    NAL_SPS         = 7,
+    NAL_PPS         = 8,
+    NAL_AUD         = 9,
+    NAL_FILLER      = 12,
+    /* ref_idc == 0 for 6,9,10,11,12 */
+};
+enum nal_priority_e
+{
+    NAL_PRIORITY_DISPOSABLE = 0,
+    NAL_PRIORITY_LOW        = 1,
+    NAL_PRIORITY_HIGH       = 2,
+    NAL_PRIORITY_HIGHEST    = 3,
+};
+
+/* The data within the payload is already NAL-encapsulated; the ref_idc and type
+ * are merely in the struct for easy access by the calling application.
+ * All data returned in an x264_nal_t, including the data in p_payload, is no longer
+ * valid after the next call to x264_encoder_encode.  Thus it must be used or copied
+ * before calling x264_encoder_encode or x264_encoder_headers again. */
+typedef struct
+{
+    int i_ref_idc;  /* nal_priority_e */
+    int i_type;     /* nal_unit_type_e */
+    int b_long_startcode;
+    int i_first_mb; /* If this NAL is a slice, the index of the first MB in the slice. */
+    int i_last_mb;  /* If this NAL is a slice, the index of the last MB in the slice. */
+
+    /* Size of payload in bytes. */
+    int     i_payload;
+    /* If param->b_annexb is set, Annex-B bytestream with startcode.
+     * Otherwise, startcode is replaced with a 4-byte size.
+     * This size is the size used in mp4/similar muxing; it is equal to i_payload-4 */
+    uint8_t *p_payload;
+} x264_nal_t;
+
 /****************************************************************************
  * Encoder parameters
  ****************************************************************************/
@@ -377,8 +425,41 @@ typedef struct x264_param_t
      * i.e. when an x264_param_t is passed to x264_t in an x264_picture_t or in zones.
      * Not used when x264_encoder_reconfig is called directly. */
     void (*param_free)( void* );
+
+    /* Optional low-level callback for low-latency encoding.  Called for each output NAL unit
+     * immediately after the NAL unit is finished encoding.  This allows the calling application
+     * to begin processing video data (e.g. by sending packets over a network) before the frame
+     * is done encoding.
+     *
+     * This callback MUST do the following in order to work correctly:
+     * 1) Have available an output buffer of at least size nal->i_payload*3/2 + 5 + 16.
+     * 2) Call x264_nal_encode( h, dst, nal ), where dst is the output buffer.
+     * After these steps, the content of nal is valid and can be used in the same way as if
+     * the NAL unit were output by x264_encoder_encode.
+     *
+     * This does not need to be synchronous with the encoding process: the data pointed to
+     * by nal (both before and after x264_nal_encode) will remain valid until the next
+     * x264_encoder_encode call.  The callback must be re-entrant.
+     *
+     * This callback does not work with frame-based threads; threads must be disabled
+     * or sliced-threads enabled.  This callback also does not work as one would expect
+     * with HRD -- since the buffering period SEI cannot be calculated until the frame
+     * is finished encoding, it will not be sent via this callback.
+     *
+     * Note also that the NALs are not necessarily returned in order when sliced threads is
+     * enabled.  Accordingly, the variable i_first_mb and i_last_mb are available in
+     * x264_nal_t to help the calling application reorder the slices if necessary.
+     *
+     * When this callback is enabled, x264_encoder_encode does not return valid NALs;
+     * the calling application is expected to acquire all output NALs through the callback.
+     *
+     * It is generally sensible to combine this callback with a use of slice-max-mbs or
+     * slice-max-size. */
+    void (*nalu_process) ( x264_t *h, x264_nal_t *nal );
 } x264_param_t;
 
+void x264_nal_encode( x264_t *h, uint8_t *dst, x264_nal_t *nal );
+
 /****************************************************************************
  * H.264 level restriction information
  ****************************************************************************/
@@ -585,51 +666,6 @@ int x264_picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_heigh
  *  x264_picture_alloc ONLY */
 void x264_picture_clean( x264_picture_t *pic );
 
-/****************************************************************************
- * NAL structure and functions
- ****************************************************************************/
-
-enum nal_unit_type_e
-{
-    NAL_UNKNOWN     = 0,
-    NAL_SLICE       = 1,
-    NAL_SLICE_DPA   = 2,
-    NAL_SLICE_DPB   = 3,
-    NAL_SLICE_DPC   = 4,
-    NAL_SLICE_IDR   = 5,    /* ref_idc != 0 */
-    NAL_SEI         = 6,    /* ref_idc == 0 */
-    NAL_SPS         = 7,
-    NAL_PPS         = 8,
-    NAL_AUD         = 9,
-    NAL_FILLER      = 12,
-    /* ref_idc == 0 for 6,9,10,11,12 */
-};
-enum nal_priority_e
-{
-    NAL_PRIORITY_DISPOSABLE = 0,
-    NAL_PRIORITY_LOW        = 1,
-    NAL_PRIORITY_HIGH       = 2,
-    NAL_PRIORITY_HIGHEST    = 3,
-};
-
-/* The data within the payload is already NAL-encapsulated; the ref_idc and type
- * are merely in the struct for easy access by the calling application.
- * All data returned in an x264_nal_t, including the data in p_payload, is no longer
- * valid after the next call to x264_encoder_encode.  Thus it must be used or copied
- * before calling x264_encoder_encode or x264_encoder_headers again. */
-typedef struct
-{
-    int i_ref_idc;  /* nal_priority_e */
-    int i_type;     /* nal_unit_type_e */
-
-    /* Size of payload in bytes. */
-    int     i_payload;
-    /* If param->b_annexb is set, Annex-B bytestream with 4-byte startcode.
-     * Otherwise, startcode is replaced with a 4-byte size.
-     * This size is the size used in mp4/similar muxing; it is equal to i_payload-4 */
-    uint8_t *p_payload;
-} x264_nal_t;
-
 /****************************************************************************
  * Encoder functions
  ****************************************************************************/