]> git.sesse.net Git - x264/blobdiff - muxers.c
Yet more CABAC and CAVLC optimizations
[x264] / muxers.c
index 0efd67fd371e619769ccd9a43698e33469092d40..d62be5cd8ec7b522bcb5acc5fd62ac0585af7baf 100644 (file)
--- a/muxers.c
+++ b/muxers.c
@@ -1,7 +1,10 @@
 /*****************************************************************************
  * muxers.c: h264 file i/o plugins
  *****************************************************************************
- * Copyright (C) 2003-2006 x264 project
+ * Copyright (C) 2003-2008 x264 project
+ *
+ * Authors: Laurent Aimar <fenrir@via.ecp.fr>
+ *          Loren Merritt <lorenm@u.washington.edu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
  *****************************************************************************/
 
-#define _LARGEFILE_SOURCE
-#define _FILE_OFFSET_BITS 64
-
-#include <stdio.h>
-#include <string.h>
-
 #include "common/common.h"
 #include "x264.h"
 #include "matroska.h"
@@ -33,6 +30,8 @@
 #include "config.h"
 #endif
 
+#include <sys/types.h>
+
 #ifdef AVIS_INPUT
 #include <windows.h>
 #include <vfw.h>
 #include <gpac/isomedia.h>
 #endif
 
+static int64_t gcd( int64_t a, int64_t b )
+{
+    while (1)
+    {
+        int64_t c = a % b;
+        if( !c )
+            return b;
+        a = b;
+        b = c;
+    }
+}
+
+typedef struct {
+    FILE *fh;
+    int width, height;
+    int next_frame;
+} yuv_input_t;
+
 /* raw 420 yuv file operation */
 int open_file_yuv( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
 {
-    if ((*p_handle = fopen(psz_filename, "rb")) == NULL)
+    yuv_input_t *h = malloc(sizeof(yuv_input_t));
+    h->width = p_param->i_width;
+    h->height = p_param->i_height;
+    h->next_frame = 0;
+
+    if( !strcmp(psz_filename, "-") )
+        h->fh = stdin;
+    else
+        h->fh = fopen(psz_filename, "rb");
+    if( h->fh == NULL )
         return -1;
+
+    *p_handle = (hnd_t)h;
     return 0;
 }
 
-int get_frame_total_yuv( hnd_t handle, int i_width, int i_height )
+int get_frame_total_yuv( hnd_t handle )
 {
-    FILE *f = (FILE *)handle;
+    yuv_input_t *h = handle;
     int i_frame_total = 0;
 
-    if( !fseek( f, 0, SEEK_END ) )
+    if( !fseek( h->fh, 0, SEEK_END ) )
     {
-        uint64_t i_size = ftell( f );
-        fseek( f, 0, SEEK_SET );
-        i_frame_total = (int)(i_size / ( i_width * i_height * 3 / 2 ));
+        uint64_t i_size = ftell( h->fh );
+        fseek( h->fh, 0, SEEK_SET );
+        i_frame_total = (int)(i_size / ( h->width * h->height * 3 / 2 ));
     }
 
     return i_frame_total;
 }
 
-int read_frame_yuv( x264_picture_t *p_pic, hnd_t handle, int i_frame, int i_width, int i_height )
+int read_frame_yuv( x264_picture_t *p_pic, hnd_t handle, int i_frame )
 {
-    static int prev_frame = -1;
-    FILE *f = (FILE *)handle;
+    yuv_input_t *h = handle;
 
-    if( i_frame != prev_frame+1 )
-        if( fseek( f, (uint64_t)i_frame * i_width * i_height * 3 / 2, SEEK_SET ) )
+    if( i_frame != h->next_frame )
+        if( fseek( h->fh, (uint64_t)i_frame * h->width * h->height * 3 / 2, SEEK_SET ) )
             return -1;
 
-    if( fread( p_pic->img.plane[0], 1, i_width * i_height, f ) <= 0
-            || fread( p_pic->img.plane[1], 1, i_width * i_height / 4, f ) <= 0
-            || fread( p_pic->img.plane[2], 1, i_width * i_height / 4, f ) <= 0 )
+    if( fread( p_pic->img.plane[0], 1, h->width * h->height, h->fh ) <= 0
+            || fread( p_pic->img.plane[1], 1, h->width * h->height / 4, h->fh ) <= 0
+            || fread( p_pic->img.plane[2], 1, h->width * h->height / 4, h->fh ) <= 0 )
         return -1;
 
-    prev_frame = i_frame;
+    h->next_frame = i_frame+1;
 
     return 0;
 }
 
 int close_file_yuv(hnd_t handle)
 {
-    if (handle == NULL)
+    yuv_input_t *h = handle;
+    if( !h || !h->fh )
         return 0;
-    return fclose((FILE *)handle);
+    fclose( h->fh );
+    free( h );
+    return 0;
 }
 
+/* YUV4MPEG2 raw 420 yuv file operation */
+typedef struct {
+    FILE *fh;
+    int width, height;
+    int next_frame;
+    int seq_header_len, frame_header_len;
+    int frame_size;
+} y4m_input_t;
+
+#define Y4M_MAGIC "YUV4MPEG2"
+#define MAX_YUV4_HEADER 80
+#define Y4M_FRAME_MAGIC "FRAME"
+#define MAX_FRAME_HEADER 80
+
+int open_file_y4m( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
+{
+    int  i, n, d;
+    char header[MAX_YUV4_HEADER+10];
+    char *tokstart, *tokend, *header_end;
+    y4m_input_t *h = malloc(sizeof(y4m_input_t));
 
-/* avs/avi input file support under cygwin */
+    h->next_frame = 0;
 
-#ifdef AVIS_INPUT
+    if( !strcmp(psz_filename, "-") )
+        h->fh = stdin;
+    else
+        h->fh = fopen(psz_filename, "rb");
+    if( h->fh == NULL )
+        return -1;
+
+    h->frame_header_len = strlen(Y4M_FRAME_MAGIC)+1;
+
+    /* Read header */
+    for( i=0; i<MAX_YUV4_HEADER; i++ )
+    {
+        header[i] = fgetc(h->fh);
+        if( header[i] == '\n' )
+        {
+            /* Add a space after last option. Makes parsing "444" vs
+               "444alpha" easier. */
+            header[i+1] = 0x20;
+            header[i+2] = 0;
+            break;
+        }
+    }
+    if( i == MAX_YUV4_HEADER || strncmp(header, Y4M_MAGIC, strlen(Y4M_MAGIC)) )
+        return -1;
 
-int gcd(int a, int b)
+    /* Scan properties */
+    header_end = &header[i+1]; /* Include space */
+    h->seq_header_len = i+1;
+    for( tokstart = &header[strlen(Y4M_MAGIC)+1]; tokstart < header_end; tokstart++ )
+    {
+        if(*tokstart==0x20) continue;
+        switch(*tokstart++)
+        {
+        case 'W': /* Width. Required. */
+            h->width = p_param->i_width = strtol(tokstart, &tokend, 10);
+            tokstart=tokend;
+            break;
+        case 'H': /* Height. Required. */
+            h->height = p_param->i_height = strtol(tokstart, &tokend, 10);
+            tokstart=tokend;
+            break;
+        case 'C': /* Color space */
+            if( strncmp("420", tokstart, 3) )
+            {
+                fprintf(stderr, "Colorspace unhandled\n");
+                return -1;
+            }
+            tokstart = strchr(tokstart, 0x20);
+            break;
+        case 'I': /* Interlace type */
+            switch(*tokstart++)
+            {
+            case 'p': break;
+            case '?':
+            case 't':
+            case 'b':
+            case 'm':
+            default:
+                fprintf(stderr, "Warning, this sequence might be interlaced\n");
+            }
+            break;
+        case 'F': /* Frame rate - 0:0 if unknown */
+            if( sscanf(tokstart, "%d:%d", &n, &d) == 2 && n && d )
+            {
+                x264_reduce_fraction( &n, &d );
+                p_param->i_fps_num = n;
+                p_param->i_fps_den = d;
+            }
+            tokstart = strchr(tokstart, 0x20);
+            break;
+        case 'A': /* Pixel aspect - 0:0 if unknown */
+            /* Don't override the aspect ratio if sar has been explicitly set on the commandline. */
+            if( sscanf(tokstart, "%d:%d", &n, &d) == 2 && n && d && !p_param->vui.i_sar_width && !p_param->vui.i_sar_height )
+            {
+                x264_reduce_fraction( &n, &d );
+                p_param->vui.i_sar_width = n;
+                p_param->vui.i_sar_height = d;
+            }
+            tokstart = strchr(tokstart, 0x20);
+            break;
+        case 'X': /* Vendor extensions */
+            if( !strncmp("YSCSS=",tokstart,6) )
+            {
+                /* Older nonstandard pixel format representation */
+                tokstart += 6;
+                if( strncmp("420JPEG",tokstart,7) &&
+                    strncmp("420MPEG2",tokstart,8) &&
+                    strncmp("420PALDV",tokstart,8) )
+                {
+                    fprintf(stderr, "Unsupported extended colorspace\n");
+                    return -1;
+                }
+            }
+            tokstart = strchr(tokstart, 0x20);
+            break;
+        }
+    }
+
+    fprintf(stderr, "yuv4mpeg: %ix%i@%i/%ifps, %i:%i\n",
+            h->width, h->height, p_param->i_fps_num, p_param->i_fps_den,
+            p_param->vui.i_sar_width, p_param->vui.i_sar_height);
+
+    *p_handle = (hnd_t)h;
+    return 0;
+}
+
+/* Most common case: frame_header = "FRAME" */
+int get_frame_total_y4m( hnd_t handle )
 {
-    int c;
+    y4m_input_t *h             = handle;
+    int          i_frame_total = 0;
+    uint64_t     init_pos      = ftell(h->fh);
 
-    while (1)
+    if( !fseek( h->fh, 0, SEEK_END ) )
     {
-        c = a % b;
-        if (!c)
-            return b;
-        a = b;
-        b = c;
+        uint64_t i_size = ftell( h->fh );
+        fseek( h->fh, init_pos, SEEK_SET );
+        i_frame_total = (int)((i_size - h->seq_header_len) /
+                              (3*(h->width*h->height)/2+h->frame_header_len));
     }
+
+    return i_frame_total;
 }
 
+int read_frame_y4m( x264_picture_t *p_pic, hnd_t handle, int i_frame )
+{
+    int          slen = strlen(Y4M_FRAME_MAGIC);
+    int          i    = 0;
+    char         header[16];
+    y4m_input_t *h    = handle;
+
+    if( i_frame != h->next_frame )
+    {
+        if (fseek(h->fh, (uint64_t)i_frame*(3*(h->width*h->height)/2+h->frame_header_len)
+                  + h->seq_header_len, SEEK_SET))
+            return -1;
+    }
+
+    /* Read frame header - without terminating '\n' */
+    if (fread(header, 1, slen, h->fh) != slen)
+        return -1;
+
+    header[slen] = 0;
+    if (strncmp(header, Y4M_FRAME_MAGIC, slen))
+    {
+        fprintf(stderr, "Bad header magic (%"PRIx32" <=> %s)\n",
+                *((uint32_t*)header), header);
+        return -1;
+    }
+
+    /* Skip most of it */
+    while (i<MAX_FRAME_HEADER && fgetc(h->fh) != '\n')
+        i++;
+    if (i == MAX_FRAME_HEADER)
+    {
+        fprintf(stderr, "Bad frame header!\n");
+        return -1;
+    }
+    h->frame_header_len = i+slen+1;
+
+    if( fread(p_pic->img.plane[0], 1, h->width*h->height, h->fh) <= 0
+        || fread(p_pic->img.plane[1], 1, h->width * h->height / 4, h->fh) <= 0
+        || fread(p_pic->img.plane[2], 1, h->width * h->height / 4, h->fh) <= 0)
+        return -1;
+
+    h->next_frame = i_frame+1;
+
+    return 0;
+}
+
+int close_file_y4m(hnd_t handle)
+{
+    y4m_input_t *h = handle;
+    if( !h || !h->fh )
+        return 0;
+    fclose( h->fh );
+    free( h );
+    return 0;
+}
+
+/* avs/avi input file support under cygwin */
+
+#ifdef AVIS_INPUT
+typedef struct {
+    PAVISTREAM p_avi;
+    int width, height;
+} avis_input_t;
+
 int open_file_avis( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
 {
+    avis_input_t *h = malloc(sizeof(avis_input_t));
     AVISTREAMINFO info;
-    PAVISTREAM p_avi = NULL;
     int i;
 
-    *p_handle = NULL;
+    *p_handle = (hnd_t)h;
 
     AVIFileInit();
-    if( AVIStreamOpenFromFile( &p_avi, psz_filename, streamtypeVIDEO, 0, OF_READ, NULL ) )
+    if( AVIStreamOpenFromFile( &h->p_avi, psz_filename, streamtypeVIDEO, 0, OF_READ, NULL ) )
     {
         AVIFileExit();
         return -1;
     }
 
-    if( AVIStreamInfo(p_avi, &info, sizeof(AVISTREAMINFO)) )
+    if( AVIStreamInfo(h->p_avi, &info, sizeof(AVISTREAMINFO)) )
     {
-        AVIStreamRelease(p_avi);
+        AVIStreamRelease(h->p_avi);
         AVIFileExit();
         return -1;
     }
@@ -139,13 +361,15 @@ int open_file_avis( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
             (char)(info.fccHandler & 0xff), (char)((info.fccHandler >> 8) & 0xff),
             (char)((info.fccHandler >> 16) & 0xff), (char)((info.fccHandler >> 24)) );
 
-        AVIStreamRelease(p_avi);
+        AVIStreamRelease(h->p_avi);
         AVIFileExit();
 
         return -1;
     }
 
+    h->width =
     p_param->i_width = info.rcFrame.right - info.rcFrame.left;
+    h->height =
     p_param->i_height = info.rcFrame.bottom - info.rcFrame.top;
     i = gcd(info.dwRate, info.dwScale);
     p_param->i_fps_den = info.dwScale / i;
@@ -156,29 +380,27 @@ int open_file_avis( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
         (double)p_param->i_fps_num / (double)p_param->i_fps_den,
         (int)info.dwLength );
 
-    *p_handle = (hnd_t)p_avi;
-
     return 0;
 }
 
-int get_frame_total_avis( hnd_t handle, int i_width, int i_height )
+int get_frame_total_avis( hnd_t handle )
 {
-    PAVISTREAM p_avi = (PAVISTREAM)handle;
+    avis_input_t *h = handle;
     AVISTREAMINFO info;
 
-    if( AVIStreamInfo(p_avi, &info, sizeof(AVISTREAMINFO)) )
+    if( AVIStreamInfo(h->p_avi, &info, sizeof(AVISTREAMINFO)) )
         return -1;
 
     return info.dwLength;
 }
 
-int read_frame_avis( x264_picture_t *p_pic, hnd_t handle, int i_frame, int i_width, int i_height )
+int read_frame_avis( x264_picture_t *p_pic, hnd_t handle, int i_frame )
 {
-    PAVISTREAM p_avi = (PAVISTREAM)handle;
+    avis_input_t *h = handle;
 
     p_pic->img.i_csp = X264_CSP_YV12;
 
-    if( AVIStreamRead(p_avi, i_frame, 1, p_pic->img.plane[0], i_width * i_height * 3 / 2, NULL, NULL ) )
+    if( AVIStreamRead(h->p_avi, i_frame, 1, p_pic->img.plane[0], h->width * h->height * 3 / 2, NULL, NULL ) )
         return -1;
 
     return 0;
@@ -186,14 +408,111 @@ int read_frame_avis( x264_picture_t *p_pic, hnd_t handle, int i_frame, int i_wid
 
 int close_file_avis( hnd_t handle )
 {
-    PAVISTREAM p_avi = (PAVISTREAM)handle;
-
-    AVIStreamRelease(p_avi);
+    avis_input_t *h = handle;
+    AVIStreamRelease(h->p_avi);
     AVIFileExit();
+    free(h);
+    return 0;
+}
+#endif
 
+
+#ifdef HAVE_PTHREAD
+typedef struct {
+    int (*p_read_frame)( x264_picture_t *p_pic, hnd_t handle, int i_frame );
+    int (*p_close_infile)( hnd_t handle );
+    hnd_t p_handle;
+    x264_picture_t pic;
+    x264_pthread_t tid;
+    int next_frame;
+    int frame_total;
+    int in_progress;
+    struct thread_input_arg_t *next_args;
+} thread_input_t;
+
+typedef struct thread_input_arg_t {
+    thread_input_t *h;
+    x264_picture_t *pic;
+    int i_frame;
+    int status;
+} thread_input_arg_t;
+
+int open_file_thread( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
+{
+    thread_input_t *h = malloc(sizeof(thread_input_t));
+    x264_picture_alloc( &h->pic, X264_CSP_I420, p_param->i_width, p_param->i_height );
+    h->p_read_frame = p_read_frame;
+    h->p_close_infile = p_close_infile;
+    h->p_handle = *p_handle;
+    h->in_progress = 0;
+    h->next_frame = -1;
+    h->next_args = malloc(sizeof(thread_input_arg_t));
+    h->next_args->h = h;
+    h->next_args->status = 0;
+    h->frame_total = p_get_frame_total( h->p_handle );
+
+    *p_handle = (hnd_t)h;
     return 0;
 }
 
+int get_frame_total_thread( hnd_t handle )
+{
+    thread_input_t *h = handle;
+    return h->frame_total;
+}
+
+static void read_frame_thread_int( thread_input_arg_t *i )
+{
+    i->status = i->h->p_read_frame( i->pic, i->h->p_handle, i->i_frame );
+}
+
+int read_frame_thread( x264_picture_t *p_pic, hnd_t handle, int i_frame )
+{
+    thread_input_t *h = handle;
+    UNUSED void *stuff;
+    int ret = 0;
+
+    if( h->next_frame >= 0 )
+    {
+        x264_pthread_join( h->tid, &stuff );
+        ret |= h->next_args->status;
+        h->in_progress = 0;
+    }
+
+    if( h->next_frame == i_frame )
+    {
+        XCHG( x264_picture_t, *p_pic, h->pic );
+    }
+    else
+    {
+        ret |= h->p_read_frame( p_pic, h->p_handle, i_frame );
+    }
+
+    if( !h->frame_total || i_frame+1 < h->frame_total )
+    {
+        h->next_frame =
+        h->next_args->i_frame = i_frame+1;
+        h->next_args->pic = &h->pic;
+        x264_pthread_create( &h->tid, NULL, (void*)read_frame_thread_int, h->next_args );
+        h->in_progress = 1;
+    }
+    else
+        h->next_frame = -1;
+
+    return ret;
+}
+
+int close_file_thread( hnd_t handle )
+{
+    thread_input_t *h = handle;
+    if( h->in_progress )
+        x264_pthread_join( h->tid, NULL );
+    h->p_close_infile( h->p_handle );
+    x264_picture_clean( &h->pic );
+    free( h->next_args );
+    free( h );
+    return 0;
+}
 #endif
 
 
@@ -239,7 +558,7 @@ typedef struct
     GF_AVCConfig *p_config;
     GF_ISOSample *p_sample;
     int i_track;
-    int i_descidx;
+    uint32_t i_descidx;
     int i_time_inc;
     int i_time_res;
     int i_numframe;
@@ -249,7 +568,7 @@ typedef struct
 } mp4_t;
 
 
-void recompute_bitrate_mp4(GF_ISOFile *p_file, int i_track)
+static void recompute_bitrate_mp4(GF_ISOFile *p_file, int i_track)
 {
     u32 i, count, di, timescale, time_wnd, rate;
     u64 offset;
@@ -367,6 +686,18 @@ int set_param_mp4( hnd_t handle, x264_param_t *p_param )
     gf_isom_set_visual_info(p_mp4->p_file, p_mp4->i_track, p_mp4->i_descidx,
         p_param->i_width, p_param->i_height);
 
+    if( p_param->vui.i_sar_width && p_param->vui.i_sar_height )
+    {
+        uint64_t dw = p_param->i_width << 16;
+        uint64_t dh = p_param->i_height << 16;
+        double sar = (double)p_param->vui.i_sar_width / p_param->vui.i_sar_height;
+        if( sar > 1.0 )
+            dw *= sar ;
+        else
+            dh /= sar;
+        gf_isom_set_track_layout_info( p_mp4->p_file, p_mp4->i_track, dw, dh, 0, 0, 0 );
+    }
+
     p_mp4->p_sample->data = (char *)malloc(p_param->i_width * p_param->i_height * 3 / 2);
     if (p_mp4->p_sample->data == NULL)
         return -1;
@@ -480,7 +811,7 @@ typedef struct
     char      b_writing_frame;
 } mkv_t;
 
-int write_header_mkv( mkv_t *p_mkv )
+static int write_header_mkv( mkv_t *p_mkv )
 {
     int       ret;
     uint8_t   *avcC;
@@ -583,19 +914,9 @@ int set_param_mkv( hnd_t handle, x264_param_t *p_param )
 
     if( dw > 0 && dh > 0 )
     {
-        int64_t a = dw, b = dh;
-
-        for (;;)
-        {
-            int64_t c = a % b;
-            if( c == 0 )
-              break;
-            a = b;
-            b = c;
-        }
-
-        dw /= b;
-        dh /= b;
+        int64_t x = gcd( dw, dh );
+        dw /= x;
+        dh /= x;
     }
 
     p_mkv->d_width = (int)dw;