]> git.sesse.net Git - x264/commitdiff
Add support for arbitrary user SEIs
authorKieran Kunhya <kieran@kunhya.com>
Mon, 13 Sep 2010 14:09:06 +0000 (15:09 +0100)
committerFiona Glaser <fiona@x264.com>
Sun, 19 Sep 2010 01:18:19 +0000 (18:18 -0700)
This allows calling applications to insert SEIs that x264 doesn't know about while maintaining HRD/VBV accuracy.

common/frame.c
common/frame.h
encoder/encoder.c
encoder/set.c
encoder/set.h
x264.h

index 58e6788ab0713e0b243dfae0174dbfef55d22bdf..c28c7e38aa10136946a69e099d4fb8b8b82a2bee 100644 (file)
@@ -268,6 +268,7 @@ int x264_frame_copy_picture( x264_t *h, x264_frame_t *dst, x264_picture_t *src )
     dst->i_pts      = dst->i_reordered_pts = src->i_pts;
     dst->param      = src->param;
     dst->i_pic_struct = src->i_pic_struct;
+    dst->extra_sei  = src->extra_sei;
 
     uint8_t *pix[3];
     int stride[3];
index 258de90692d3467f133eae5c89652f779bb93039..16f7c1aa9dee83cf514bda2bace446fe467c01aa 100644 (file)
@@ -151,6 +151,9 @@ typedef struct x264_frame
 
     /* interactive encoder control */
     int     b_corrupt;
+
+    /* user sei */
+    x264_sei_t extra_sei;
 } x264_frame_t;
 
 /* synchronized frame list */
index 0bfe02a72690f6fd5b6189b4bda0102ba2691e30..a9326a95c01be3f8291a9563e567fb8fb1207c61 100644 (file)
@@ -2606,7 +2606,26 @@ int     x264_encoder_encode( x264_t *h,
         }
 
         /* buffering period sei is written in x264_encoder_frame_end */
+    }
 
+    /* write extra sei */
+    for( int i = 0; i < h->fenc->extra_sei.num_payloads; i++ )
+    {
+        x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
+        x264_sei_write( &h->out.bs, h->fenc->extra_sei.payloads[i].payload, h->fenc->extra_sei.payloads[i].payload_size,
+                        h->fenc->extra_sei.payloads[i].payload_type );
+        if( x264_nal_end( h ) )
+            return -1;
+        overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD - (h->param.b_annexb && h->out.i_nal-1);
+        if( h->fenc->extra_sei.sei_free && h->fenc->extra_sei.payloads[i].payload )
+            h->fenc->extra_sei.sei_free( h->fenc->extra_sei.payloads[i].payload );
+    }
+
+    if( h->fenc->extra_sei.sei_free && h->fenc->extra_sei.payloads )
+        h->fenc->extra_sei.sei_free( h->fenc->extra_sei.payloads );
+
+    if( h->fenc->b_keyframe )
+    {
         if( h->param.b_repeat_headers && h->fenc->i_frame == 0 )
         {
             /* identify ourself */
index e0c9a1ce661c3e33c5532cda2031a351621e7861..b0d2149b84919bc845237372636579f761b913a9 100644 (file)
@@ -76,27 +76,25 @@ static void scaling_list_write( bs_t *s, x264_pps_t *pps, int idx )
     }
 }
 
-static uint8_t *x264_sei_write_header( bs_t *s, int payload_type )
+void x264_sei_write( bs_t *s, uint8_t *payload, int payload_size, int payload_type )
 {
-    bs_write( s, 8, payload_type );
+    int i;
 
-    bs_flush( s );
-    uint8_t *p_start = s->p;
     bs_realign( s );
 
-    bs_write( s, 8, 0 );
-    return p_start;
-}
+    for( i = 0; i <= payload_type-255; i += 255 )
+        bs_write( s, 8, 255 );
+    bs_write( s, 8, payload_type-i );
 
-static void x264_sei_write( bs_t *s, uint8_t *p_start )
-{
-    bs_align_10( s );
-    bs_flush( s );
+    for( i = 0; i <= payload_size-255; i += 255 )
+        bs_write( s, 8, 255 );
+    bs_write( s, 8, payload_size-i );
 
-    p_start[0] = s->p - p_start - 1; // -1 for the length byte
-    bs_realign( s );
+    for( i = 0; i < payload_size; i++ )
+        bs_write(s, 8, payload[i] );
 
     bs_rbsp_trailing( s );
+    bs_flush( s );
 }
 
 void x264_sps_init( x264_sps_t *sps, int i_id, x264_param_t *param )
@@ -545,21 +543,26 @@ void x264_pps_write( bs_t *s, x264_pps_t *pps )
 
 void x264_sei_recovery_point_write( x264_t *h, bs_t *s, int recovery_frame_cnt )
 {
-    bs_realign( s );
-    uint8_t *p_start = x264_sei_write_header( s, SEI_RECOVERY_POINT );
+    bs_t q;
+    uint8_t tmp_buf[100];
+    bs_init( &q, tmp_buf, 100 );
 
-    bs_write_ue( s, recovery_frame_cnt ); // recovery_frame_cnt
-    bs_write( s, 1, 1 ); //exact_match_flag 1
-    bs_write( s, 1, 0 ); //broken_link_flag 0
-    bs_write( s, 2, 0 ); //changing_slice_group 0
+    bs_realign( &q );
+
+    bs_write_ue( &q, recovery_frame_cnt ); // recovery_frame_cnt
+    bs_write( &q, 1, 1 ); //exact_match_flag 1
+    bs_write( &q, 1, 0 ); //broken_link_flag 0
+    bs_write( &q, 2, 0 ); //changing_slice_group 0
+
+    bs_align_10( &q );
+    bs_flush( &q );
+
+    x264_sei_write( s, tmp_buf, bs_pos( &q ) / 8, SEI_RECOVERY_POINT );
 
-    x264_sei_write( s, p_start );
-    bs_flush( s );
 }
 
 int x264_sei_version_write( x264_t *h, bs_t *s )
 {
-    int i;
     // random ID number generated according to ISO-11578
     static const uint8_t uuid[16] =
     {
@@ -567,35 +570,23 @@ int x264_sei_version_write( x264_t *h, bs_t *s )
         0x96, 0x2c, 0xd8, 0x20, 0xd9, 0x23, 0xee, 0xef
     };
     char *opts = x264_param2string( &h->param, 0 );
-    char *version;
+    char *payload;
     int length;
 
     if( !opts )
         return -1;
-    CHECKED_MALLOC( version, 200 + strlen( opts ) );
+    CHECKED_MALLOC( payload, 200 + strlen( opts ) );
 
-    sprintf( version, "x264 - core %d%s - H.264/MPEG-4 AVC codec - "
+    memcpy( payload, uuid, 16 );
+    sprintf( payload+16, "x264 - core %d%s - H.264/MPEG-4 AVC codec - "
              "Copyleft 2003-2010 - http://www.videolan.org/x264.html - options: %s",
              X264_BUILD, X264_VERSION, opts );
-    length = strlen(version)+1+16;
+    length = strlen(payload)+1;
 
-    bs_realign( s );
-    bs_write( s, 8, SEI_USER_DATA_UNREGISTERED );
-    // payload_size
-    for( i = 0; i <= length-255; i += 255 )
-        bs_write( s, 8, 255 );
-    bs_write( s, 8, length-i );
-
-    for( int j = 0; j < 16; j++ )
-        bs_write( s, 8, uuid[j] );
-    for( int j = 0; j < length-16; j++ )
-        bs_write( s, 8, version[j] );
-
-    bs_rbsp_trailing( s );
-    bs_flush( s );
+    x264_sei_write( s, (uint8_t *)payload, length, SEI_USER_DATA_UNREGISTERED );
 
     x264_free( opts );
-    x264_free( version );
+    x264_free( payload );
     return 0;
 fail:
     x264_free( opts );
@@ -605,45 +596,54 @@ fail:
 void x264_sei_buffering_period_write( x264_t *h, bs_t *s )
 {
     x264_sps_t *sps = h->sps;
-    bs_realign( s );
-    uint8_t *p_start = x264_sei_write_header( s, SEI_BUFFERING_PERIOD );
+    bs_t q;
+    uint8_t tmp_buf[100];
+    bs_init( &q, tmp_buf, 100 );
 
-    bs_write_ue( s, sps->i_id );
+    bs_realign( &q );
+    bs_write_ue( &q, sps->i_id );
 
     if( sps->vui.b_nal_hrd_parameters_present )
     {
-        bs_write( s, sps->vui.hrd.i_initial_cpb_removal_delay_length, h->initial_cpb_removal_delay );
-        bs_write( s, sps->vui.hrd.i_initial_cpb_removal_delay_length, h->initial_cpb_removal_delay_offset );
+        bs_write( &q, sps->vui.hrd.i_initial_cpb_removal_delay_length, h->initial_cpb_removal_delay );
+        bs_write( &q, sps->vui.hrd.i_initial_cpb_removal_delay_length, h->initial_cpb_removal_delay_offset );
     }
 
-    x264_sei_write( s, p_start );
-    bs_flush( s );
+    bs_align_10( &q );
+    bs_flush( &q );
+
+    x264_sei_write( s, tmp_buf, bs_pos( &q ) / 8, SEI_BUFFERING_PERIOD );
 }
 
 void x264_sei_pic_timing_write( x264_t *h, bs_t *s )
 {
     x264_sps_t *sps = h->sps;
-    bs_realign( s );
-    uint8_t *p_start = x264_sei_write_header( s, SEI_PIC_TIMING );
+    bs_t q;
+    uint8_t tmp_buf[100];
+    bs_init( &q, tmp_buf, 100 );
+
+    bs_realign( &q );
 
     if( sps->vui.b_nal_hrd_parameters_present || sps->vui.b_vcl_hrd_parameters_present )
     {
-        bs_write( s, sps->vui.hrd.i_cpb_removal_delay_length, h->fenc->i_cpb_delay );
-        bs_write( s, sps->vui.hrd.i_dpb_output_delay_length, h->fenc->i_dpb_output_delay );
+        bs_write( &q, sps->vui.hrd.i_cpb_removal_delay_length, h->fenc->i_cpb_delay );
+        bs_write( &q, sps->vui.hrd.i_dpb_output_delay_length, h->fenc->i_dpb_output_delay );
     }
 
     if( sps->vui.b_pic_struct_present )
     {
-        bs_write( s, 4, h->fenc->i_pic_struct-1 ); // We use index 0 for "Auto"
+        bs_write( &q, 4, h->fenc->i_pic_struct-1 ); // We use index 0 for "Auto"
 
         // These clock timestamps are not standardised so we don't set them
         // They could be time of origin, capture or alternative ideal display
         for( int i = 0; i < num_clock_ts[h->fenc->i_pic_struct]; i++ )
-            bs_write1( s, 0 ); // clock_timestamp_flag
+            bs_write1( &q, 0 ); // clock_timestamp_flag
     }
 
-    x264_sei_write( s, p_start );
-    bs_flush( s );
+    bs_align_10( &q );
+    bs_flush( &q );
+
+    x264_sei_write( s, tmp_buf, bs_pos( &q ) / 8, SEI_PIC_TIMING );
 }
 
 void x264_filler_write( x264_t *h, bs_t *s, int filler )
index 2e26303b23ec4257d0e6a910e02b23f5ccdc1de7..6d70e962b7449d9a089886c42fb3053966ae3808 100644 (file)
@@ -36,6 +36,7 @@ int  x264_sei_version_write( x264_t *h, bs_t *s );
 int  x264_validate_levels( x264_t *h, int verbose );
 void x264_sei_buffering_period_write( x264_t *h, bs_t *s );
 void x264_sei_pic_timing_write( x264_t *h, bs_t *s );
+void x264_sei_write( bs_t *s, uint8_t *payload, int payload_size, int payload_type );
 void x264_filler_write( x264_t *h, bs_t *s, int filler );
 
 #endif
diff --git a/x264.h b/x264.h
index 288758b9b4503183b568c4e3dfdd51c553030fc3..cbf332e60410fec472ca03f63d08d85f90355d47 100644 (file)
--- a/x264.h
+++ b/x264.h
@@ -39,7 +39,7 @@
 
 #include <stdarg.h>
 
-#define X264_BUILD 104
+#define X264_BUILD 105
 
 /* x264_t:
  *      opaque handler for encoder */
@@ -586,6 +586,30 @@ typedef struct
     double dpb_output_time;
 } x264_hrd_t;
 
+/* Arbitrary user SEI:
+ * Payload size is in bytes and the payload pointer must be valid.
+ * Payload types and syntax can be found in Annex D of the H.264 Specification.
+ * SEI payload alignment bits as described in Annex D must be included at the
+ * end of the payload if needed.
+ * The payload should not be NAL-encapsulated.
+ * Payloads are written first in order of input, apart from in the case when HRD
+ * is enabled where payloads are written after the Buffering Period SEI. */
+
+typedef struct
+{
+    int payload_size;
+    int payload_type;
+    uint8_t *payload;
+} x264_sei_payload_t;
+
+typedef struct
+{
+    int num_payloads;
+    x264_sei_payload_t *payloads;
+    /* In: optional callback to free each payload AND x264_sei_payload_t when used. */
+    void (*sei_free)( void* );
+} x264_sei_t;
+
 typedef struct
 {
     int     i_csp;       /* Colorspace */
@@ -645,6 +669,8 @@ typedef struct
     x264_image_properties_t prop;
     /* Out: HRD timing information. Output only when i_nal_hrd is set. */
     x264_hrd_t hrd_timing;
+    /* In: arbitrary user SEI (e.g subtitles, AFDs) */
+    x264_sei_t extra_sei;
     /* private user data. libx264 doesn't touch this,
        not even copy it from input to output frames. */
     void *opaque;