/*****************************************************************************
* 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"
#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 open_file_yuv( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
{
yuv_input_t *h = malloc(sizeof(yuv_input_t));
+ if( !h )
+ return -1;
h->width = p_param->i_width;
h->height = p_param->i_height;
h->next_frame = 0;
yuv_input_t *h = handle;
if( !h || !h->fh )
return 0;
- return fclose(h->fh);
+ fclose( h->fh );
+ free( h );
+ return 0;
}
-
-/* avs/avi input file support under cygwin */
-
-#ifdef AVIS_INPUT
+/* YUV4MPEG2 raw 420 yuv file operation */
typedef struct {
- PAVISTREAM p_avi;
+ FILE *fh;
int width, height;
-} avis_input_t;
+ 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 gcd(int a, int b)
+int open_file_y4m( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
{
- int c;
+ int i, n, d;
+ char header[MAX_YUV4_HEADER+10];
+ char *tokstart, *tokend, *header_end;
+ y4m_input_t *h = malloc(sizeof(y4m_input_t));
+ if( !h )
+ return -1;
- while (1)
+ h->next_frame = 0;
+
+ 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++ )
{
- c = a % b;
- if (!c)
- return b;
- a = b;
- b = c;
+ 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;
+
+ /* 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 )
+{
+ y4m_input_t *h = handle;
+ int i_frame_total = 0;
+ uint64_t init_pos = ftell(h->fh);
+
+ if( !fseek( h->fh, 0, SEEK_END ) )
+ {
+ 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));
+ if( !h )
+ return -1;
AVISTREAMINFO info;
int i;
int (*p_close_infile)( hnd_t handle );
hnd_t p_handle;
x264_picture_t pic;
- pthread_t tid;
+ x264_pthread_t tid;
int next_frame;
int frame_total;
+ int in_progress;
struct thread_input_arg_t *next_args;
} thread_input_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 );
+ if( !h || x264_picture_alloc( &h->pic, X264_CSP_I420, p_param->i_width, p_param->i_height ) < 0 )
+ {
+ fprintf( stderr, "x264 [error]: malloc failed\n" );
+ return -1;
+ }
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));
+ if( !h->next_args )
+ return -1;
h->next_args->h = h;
h->next_args->status = 0;
h->frame_total = p_get_frame_total( h->p_handle );
return h->frame_total;
}
-void read_frame_thread_int( thread_input_arg_t *i )
+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 )
{
- pthread_join( h->tid, &stuff );
+ x264_pthread_join( h->tid, NULL );
ret |= h->next_args->status;
+ h->in_progress = 0;
}
if( h->next_frame == i_frame )
h->next_frame =
h->next_args->i_frame = i_frame+1;
h->next_args->pic = &h->pic;
- pthread_create( &h->tid, NULL, (void*)read_frame_thread_int, h->next_args );
+ if( x264_pthread_create( &h->tid, NULL, (void*)read_frame_thread_int, h->next_args ) )
+ return -1;
+ h->in_progress = 1;
}
else
h->next_frame = -1;
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;
}
} 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;
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;
p_mp4->p_config->profile_compatibility = p_nalu[6];
p_mp4->p_config->AVCLevelIndication = p_nalu[7];
p_slot = (GF_AVCConfigSlot *)malloc(sizeof(GF_AVCConfigSlot));
+ if( !p_slot )
+ return -1;
p_slot->size = i_size - 4;
p_slot->data = (char *)malloc(p_slot->size);
+ if( !p_slot->data )
+ return -1;
memcpy(p_slot->data, p_nalu + 4, i_size - 4);
gf_list_add(p_mp4->p_config->sequenceParameterSets, p_slot);
p_slot = NULL;
if (!p_mp4->b_pps)
{
p_slot = (GF_AVCConfigSlot *)malloc(sizeof(GF_AVCConfigSlot));
+ if( !p_slot )
+ return -1;
p_slot->size = i_size - 4;
p_slot->data = (char *)malloc(p_slot->size);
+ if( !p_slot->data )
+ return -1;
memcpy(p_slot->data, p_nalu + 4, i_size - 4);
gf_list_add(p_mp4->p_config->pictureParameterSets, p_slot);
p_slot = NULL;
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;
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;