From: Steven Walters Date: Sat, 24 Oct 2009 00:23:50 +0000 (+0000) Subject: split muxers.c into one file per format X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a0df454b358000eb4f5485f8d09a2620fa6c32e5;p=libx264 split muxers.c into one file per format simplify internal muxer API --- diff --git a/Makefile b/Makefile index 9ba29f3d..3de0b616 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,23 @@ SRCS = common/mc.c common/predict.c common/pixel.c common/macroblock.c \ encoder/set.c encoder/macroblock.c encoder/cabac.c \ encoder/cavlc.c encoder/encoder.c encoder/lookahead.c -SRCCLI = x264.c matroska.c muxers.c +SRCCLI = x264.c input/yuv.c input/y4m.c output/raw.c \ + output/matroska.c output/matroska_ebml.c + +MUXERS := $(shell grep -E "(IN|OUT)PUT" config.h) + +# Optional muxer module sources +ifneq ($(findstring AVIS_INPUT, $(MUXERS)),) +SRCCLI += input/avis.c +endif + +ifneq ($(findstring HAVE_PTHREAD, $(CFLAGS)),) +SRCCLI += input/thread.c +endif + +ifneq ($(findstring MP4_OUTPUT, $(MUXERS)),) +SRCCLI += output/mp4.c +endif # Visualization sources ifeq ($(VIS),yes) diff --git a/input/avis.c b/input/avis.c new file mode 100644 index 00000000..2ca16340 --- /dev/null +++ b/input/avis.c @@ -0,0 +1,119 @@ +/***************************************************************************** + * avis.c: x264 avi/avs input module + ***************************************************************************** + * Copyright (C) 2003-2009 x264 project + * + * Authors: Laurent Aimar + * Loren Merritt + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#include "muxers.h" +#include +#include + +typedef struct +{ + PAVISTREAM p_avi; + int width, height; +} avis_hnd_t; + +static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ) +{ + avis_hnd_t *h = malloc( sizeof(avis_hnd_t) ); + if( !h ) + return -1; + AVISTREAMINFO info; + int i; + + *p_handle = h; + + AVIFileInit(); + if( AVIStreamOpenFromFile( &h->p_avi, psz_filename, streamtypeVIDEO, 0, OF_READ, NULL ) ) + { + AVIFileExit(); + return -1; + } + + if( AVIStreamInfo( h->p_avi, &info, sizeof(AVISTREAMINFO) ) ) + { + AVIStreamRelease( h->p_avi ); + AVIFileExit(); + return -1; + } + + // check input format + if( info.fccHandler != MAKEFOURCC('Y', 'V', '1', '2') ) + { + fprintf( stderr, "avis [error]: unsupported input format (%c%c%c%c)\n", + (char)(info.fccHandler & 0xff), (char)((info.fccHandler >> 8) & 0xff), + (char)((info.fccHandler >> 16) & 0xff), (char)((info.fccHandler >> 24)) ); + + 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; + p_param->i_fps_num = info.dwRate / i; + + fprintf( stderr, "avis [info]: %dx%d @ %.2f fps (%d frames)\n", + p_param->i_width, p_param->i_height, + (double)p_param->i_fps_num / (double)p_param->i_fps_den, + (int)info.dwLength ); + + return 0; +} + +static int get_frame_total( hnd_t handle ) +{ + avis_hnd_t *h = handle; + AVISTREAMINFO info; + + if( AVIStreamInfo( h->p_avi, &info, sizeof(AVISTREAMINFO) ) ) + return -1; + + return info.dwLength; +} + +static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame ) +{ + avis_hnd_t *h = handle; + + p_pic->img.i_csp = X264_CSP_YV12; + + 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; +} + +static int close_file( hnd_t handle ) +{ + avis_hnd_t *h = handle; + AVIStreamRelease( h->p_avi ); + AVIFileExit(); + free( h ); + return 0; +} + +cli_input_t avis_input = { open_file, get_frame_total, read_frame, close_file }; diff --git a/input/input.h b/input/input.h new file mode 100644 index 00000000..87d5386f --- /dev/null +++ b/input/input.h @@ -0,0 +1,40 @@ +/***************************************************************************** + * input.h: x264 file input modules + ***************************************************************************** + * Copyright (C) 2003-2009 x264 project + * + * Authors: Laurent Aimar + * Loren Merritt + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef X264_INPUT_H +#define X264_INPUT_H + +typedef struct +{ + int (*open_file)( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); + int (*get_frame_total)( hnd_t handle ); + int (*read_frame)( x264_picture_t *p_pic, hnd_t handle, int i_frame ); + int (*close_file)( hnd_t handle ); +} cli_input_t; + +extern cli_input_t yuv_input; +extern cli_input_t y4m_input; +extern cli_input_t avis_input; +extern cli_input_t thread_input; + +#endif diff --git a/input/thread.c b/input/thread.c new file mode 100644 index 00000000..a9330dcc --- /dev/null +++ b/input/thread.c @@ -0,0 +1,126 @@ +/***************************************************************************** + * thread.c: x264 threaded input module + ***************************************************************************** + * Copyright (C) 2003-2009 x264 project + * + * Authors: Laurent Aimar + * Loren Merritt + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#include "muxers.h" + +extern cli_input_t input; + +typedef struct +{ + cli_input_t input; + 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_hnd_t; + +typedef struct thread_input_arg_t +{ + thread_hnd_t *h; + x264_picture_t *pic; + int i_frame; + int status; +} thread_input_arg_t; + +static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ) +{ + thread_hnd_t *h = malloc( sizeof(thread_hnd_t) ); + 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->input = input; + 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 = input.get_frame_total( h->p_handle ); + + *p_handle = h; + return 0; +} + +static int get_frame_total( hnd_t handle ) +{ + thread_hnd_t *h = handle; + return h->frame_total; +} + +static void read_frame_thread_int( thread_input_arg_t *i ) +{ + i->status = i->h->input.read_frame( i->pic, i->h->p_handle, i->i_frame ); +} + +static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame ) +{ + thread_hnd_t *h = handle; + int ret = 0; + + if( h->next_frame >= 0 ) + { + x264_pthread_join( h->tid, NULL ); + 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->input.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; + 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; + + return ret; +} + +static int close_file( hnd_t handle ) +{ + thread_hnd_t *h = handle; + if( h->in_progress ) + x264_pthread_join( h->tid, NULL ); + h->input.close_file( h->p_handle ); + x264_picture_clean( &h->pic ); + free( h->next_args ); + free( h ); + return 0; +} + +cli_input_t thread_input = { open_file, get_frame_total, read_frame, close_file }; diff --git a/input/y4m.c b/input/y4m.c new file mode 100644 index 00000000..a04cd211 --- /dev/null +++ b/input/y4m.c @@ -0,0 +1,232 @@ +/***************************************************************************** + * y4m.c: x264 y4m input module + ***************************************************************************** + * Copyright (C) 2003-2009 x264 project + * + * Authors: Laurent Aimar + * Loren Merritt + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#include "muxers.h" + +typedef struct +{ + FILE *fh; + int width, height; + int next_frame; + int seq_header_len, frame_header_len; + int frame_size; +} y4m_hnd_t; + +#define Y4M_MAGIC "YUV4MPEG2" +#define MAX_YUV4_HEADER 80 +#define Y4M_FRAME_MAGIC "FRAME" +#define MAX_FRAME_HEADER 80 + +static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ) +{ + y4m_hnd_t *h = malloc( sizeof(y4m_hnd_t) ); + int i, n, d; + char header[MAX_YUV4_HEADER+10]; + char *tokstart, *tokend, *header_end; + if( !h ) + return -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++ ) + { + 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 = h; + return 0; +} + +/* Most common case: frame_header = "FRAME" */ +static int get_frame_total( hnd_t handle ) +{ + y4m_hnd_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; +} + +static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame ) +{ + y4m_hnd_t *h = handle; + int slen = strlen( Y4M_FRAME_MAGIC ); + int i = 0; + char header[16]; + + 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; +} + +static int close_file( hnd_t handle ) +{ + y4m_hnd_t *h = handle; + if( !h || !h->fh ) + return 0; + fclose( h->fh ); + free( h ); + return 0; +} + +cli_input_t y4m_input = { open_file, get_frame_total, read_frame, close_file }; diff --git a/input/yuv.c b/input/yuv.c new file mode 100644 index 00000000..98dc46c2 --- /dev/null +++ b/input/yuv.c @@ -0,0 +1,96 @@ +/***************************************************************************** + * yuv.c: x264 yuv input module + ***************************************************************************** + * Copyright (C) 2003-2009 x264 project + * + * Authors: Laurent Aimar + * Loren Merritt + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#include "muxers.h" + +typedef struct +{ + FILE *fh; + int width, height; + int next_frame; +} yuv_hnd_t; + +static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ) +{ + yuv_hnd_t *h = malloc( sizeof(yuv_hnd_t) ); + if( !h ) + return -1; + 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 = h; + return 0; +} + +static int get_frame_total( hnd_t handle ) +{ + yuv_hnd_t *h = handle; + int i_frame_total = 0; + + if( !fseek( h->fh, 0, SEEK_END ) ) + { + 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; +} + +static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame ) +{ + yuv_hnd_t *h = handle; + + 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, 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; +} + +static int close_file( hnd_t handle ) +{ + yuv_hnd_t *h = handle; + if( !h || !h->fh ) + return 0; + fclose( h->fh ); + free( h ); + return 0; +} + +cli_input_t yuv_input = { open_file, get_frame_total, read_frame, close_file }; diff --git a/muxers.c b/muxers.c deleted file mode 100644 index 1a8187d0..00000000 --- a/muxers.c +++ /dev/null @@ -1,1048 +0,0 @@ -/***************************************************************************** - * muxers.c: h264 file i/o plugins - ***************************************************************************** - * Copyright (C) 2003-2008 x264 project - * - * Authors: Laurent Aimar - * Loren Merritt - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. - *****************************************************************************/ - -#include "common/common.h" -#include "x264.h" -#include "matroska.h" -#include "muxers.h" -#include "config.h" - -#include - -#ifdef AVIS_INPUT -#include -#include -#endif - -#ifdef MP4_OUTPUT -#include -#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 ) -{ - 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; - - 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 ) -{ - yuv_input_t *h = handle; - int i_frame_total = 0; - - if( !fseek( h->fh, 0, SEEK_END ) ) - { - 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 ) -{ - yuv_input_t *h = handle; - - 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, 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_yuv(hnd_t handle) -{ - yuv_input_t *h = handle; - if( !h || !h->fh ) - return 0; - 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) ); - if( !h ) - return -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++ ) - { - 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( ifh) != '\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; - - *p_handle = (hnd_t)h; - - AVIFileInit(); - if( AVIStreamOpenFromFile( &h->p_avi, psz_filename, streamtypeVIDEO, 0, OF_READ, NULL ) ) - { - AVIFileExit(); - return -1; - } - - if( AVIStreamInfo( h->p_avi, &info, sizeof(AVISTREAMINFO) ) ) - { - AVIStreamRelease(h->p_avi); - AVIFileExit(); - return -1; - } - - // check input format - if( info.fccHandler != MAKEFOURCC('Y', 'V', '1', '2') ) - { - fprintf( stderr, "avis [error]: unsupported input format (%c%c%c%c)\n", - (char)(info.fccHandler & 0xff), (char)((info.fccHandler >> 8) & 0xff), - (char)((info.fccHandler >> 16) & 0xff), (char)((info.fccHandler >> 24)) ); - - 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; - p_param->i_fps_num = info.dwRate / i; - - fprintf( stderr, "avis [info]: %dx%d @ %.2f fps (%d frames)\n", - p_param->i_width, p_param->i_height, - (double)p_param->i_fps_num / (double)p_param->i_fps_den, - (int)info.dwLength ); - - return 0; -} - -int get_frame_total_avis( hnd_t handle ) -{ - avis_input_t *h = handle; - AVISTREAMINFO info; - - 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 ) -{ - avis_input_t *h = handle; - - p_pic->img.i_csp = X264_CSP_YV12; - - 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; -} - -int close_file_avis( hnd_t handle ) -{ - 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) ); - 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 ); - - *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; - int ret = 0; - - if( h->next_frame >= 0 ) - { - x264_pthread_join( h->tid, NULL ); - 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; - 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; - - 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 - - -int open_file_bsf( char *psz_filename, hnd_t *p_handle ) -{ - if( !(*p_handle = fopen(psz_filename, "w+b")) ) - return -1; - - return 0; -} - -int set_param_bsf( hnd_t handle, x264_param_t *p_param ) -{ - return 0; -} - -int write_nalu_bsf( hnd_t handle, uint8_t *p_nalu, int i_size ) -{ - if( fwrite( p_nalu, i_size, 1, (FILE*)handle ) > 0 ) - return i_size; - return -1; -} - -int set_eop_bsf( hnd_t handle, x264_picture_t *p_picture ) -{ - return 0; -} - -int close_file_bsf( hnd_t handle ) -{ - if( !handle || handle == stdout ) - return 0; - - return fclose( (FILE*)handle ); -} - -/* -- mp4 muxing support ------------------------------------------------- */ -#ifdef MP4_OUTPUT - -typedef struct -{ - GF_ISOFile *p_file; - GF_AVCConfig *p_config; - GF_ISOSample *p_sample; - int i_track; - uint32_t i_descidx; - int i_time_inc; - int i_time_res; - int i_numframe; - int i_init_delay; - uint8_t b_sps; - uint8_t b_pps; -} mp4_t; - - -static void recompute_bitrate_mp4(GF_ISOFile *p_file, int i_track) -{ - u32 i, count, di, timescale, time_wnd, rate; - u64 offset; - Double br; - GF_ESD *esd; - - esd = gf_isom_get_esd( p_file, i_track, 1 ); - if( !esd ) - return; - - esd->decoderConfig->avgBitrate = 0; - esd->decoderConfig->maxBitrate = 0; - rate = time_wnd = 0; - - timescale = gf_isom_get_media_timescale( p_file, i_track ); - count = gf_isom_get_sample_count( p_file, i_track ); - for( i = 0; i < count; i++ ) - { - GF_ISOSample *samp = gf_isom_get_sample_info( p_file, i_track, i+1, &di, &offset ); - - if( samp->dataLength>esd->decoderConfig->bufferSizeDB ) - esd->decoderConfig->bufferSizeDB = samp->dataLength; - - if( esd->decoderConfig->bufferSizeDB < samp->dataLength ) - esd->decoderConfig->bufferSizeDB = samp->dataLength; - - esd->decoderConfig->avgBitrate += samp->dataLength; - rate += samp->dataLength; - if( samp->DTS > time_wnd + timescale ) - { - if( rate > esd->decoderConfig->maxBitrate ) - esd->decoderConfig->maxBitrate = rate; - time_wnd = samp->DTS; - rate = 0; - } - - gf_isom_sample_del( &samp ); - } - - br = (Double)(s64)gf_isom_get_media_duration( p_file, i_track ); - br /= timescale; - esd->decoderConfig->avgBitrate = (u32)(esd->decoderConfig->avgBitrate / br); - /*move to bps*/ - esd->decoderConfig->avgBitrate *= 8; - esd->decoderConfig->maxBitrate *= 8; - - gf_isom_change_mpeg4_description( p_file, i_track, 1, esd ); - gf_odf_desc_del( (GF_Descriptor*)esd ); -} - - -int close_file_mp4( hnd_t handle ) -{ - mp4_t *p_mp4 = (mp4_t*)handle; - - if( !p_mp4 ) - return 0; - - if( p_mp4->p_config ) - gf_odf_avc_cfg_del( p_mp4->p_config ); - - if( p_mp4->p_sample ) - { - if( p_mp4->p_sample->data ) - free( p_mp4->p_sample->data ); - - gf_isom_sample_del( &p_mp4->p_sample ); - } - - if( p_mp4->p_file ) - { - recompute_bitrate_mp4( p_mp4->p_file, p_mp4->i_track ); - gf_isom_set_pl_indication( p_mp4->p_file, GF_ISOM_PL_VISUAL, 0x15 ); - gf_isom_set_storage_mode( p_mp4->p_file, GF_ISOM_STORE_FLAT ); - gf_isom_close( p_mp4->p_file ); - } - - free( p_mp4 ); - - return 0; -} - -int open_file_mp4( char *psz_filename, hnd_t *p_handle ) -{ - mp4_t *p_mp4; - - *p_handle = NULL; - - if( !(p_mp4 = malloc(sizeof(mp4_t))) ) - return -1; - - memset( p_mp4, 0, sizeof(mp4_t) ); - p_mp4->p_file = gf_isom_open( psz_filename, GF_ISOM_OPEN_WRITE, NULL ); - - if( !(p_mp4->p_sample = gf_isom_sample_new()) ) - { - close_file_mp4( p_mp4 ); - return -1; - } - - gf_isom_set_brand_info( p_mp4->p_file, GF_ISOM_BRAND_AVC1, 0 ); - - *p_handle = p_mp4; - - return 0; -} - - -int set_param_mp4( hnd_t handle, x264_param_t *p_param ) -{ - mp4_t *p_mp4 = (mp4_t*)handle; - - p_mp4->i_track = gf_isom_new_track( p_mp4->p_file, 0, GF_ISOM_MEDIA_VISUAL, - p_param->i_fps_num ); - - p_mp4->p_config = gf_odf_avc_cfg_new(); - gf_isom_avc_config_new( p_mp4->p_file, p_mp4->i_track, p_mp4->p_config, - NULL, NULL, &p_mp4->i_descidx ); - - gf_isom_set_track_enabled( p_mp4->p_file, p_mp4->i_track, 1 ); - - 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 = malloc( p_param->i_width * p_param->i_height * 3 / 2 ); - if( !p_mp4->p_sample->data ) - return -1; - - p_mp4->i_time_res = p_param->i_fps_num; - p_mp4->i_time_inc = p_param->i_fps_den; - p_mp4->i_init_delay = p_param->i_bframe ? (p_param->i_bframe_pyramid ? 2 : 1) : 0; - p_mp4->i_init_delay *= p_mp4->i_time_inc; - fprintf( stderr, "mp4 [info]: initial delay %d (scale %d)\n", - p_mp4->i_init_delay, p_mp4->i_time_res ); - - return 0; -} - - -int write_nalu_mp4( hnd_t handle, uint8_t *p_nalu, int i_size ) -{ - mp4_t *p_mp4 = (mp4_t*)handle; - GF_AVCConfigSlot *p_slot; - uint8_t type = p_nalu[4] & 0x1f; - int psize; - - switch( type ) - { - // sps - case 0x07: - if( !p_mp4->b_sps ) - { - p_mp4->p_config->configurationVersion = 1; - p_mp4->p_config->AVCProfileIndication = p_nalu[5]; - p_mp4->p_config->profile_compatibility = p_nalu[6]; - p_mp4->p_config->AVCLevelIndication = p_nalu[7]; - p_slot = malloc( sizeof(GF_AVCConfigSlot) ); - if( !p_slot ) - return -1; - p_slot->size = i_size - 4; - p_slot->data = 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; - p_mp4->b_sps = 1; - } - break; - - // pps - case 0x08: - if( !p_mp4->b_pps ) - { - p_slot = malloc( sizeof(GF_AVCConfigSlot) ); - if( !p_slot ) - return -1; - p_slot->size = i_size - 4; - p_slot->data = 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; - p_mp4->b_pps = 1; - if( p_mp4->b_sps ) - gf_isom_avc_config_update( p_mp4->p_file, p_mp4->i_track, 1, p_mp4->p_config ); - } - break; - - // slice, sei - case 0x1: - case 0x5: - case 0x6: - psize = i_size - 4 ; - memcpy( p_mp4->p_sample->data + p_mp4->p_sample->dataLength, p_nalu, i_size ); - p_mp4->p_sample->data[p_mp4->p_sample->dataLength + 0] = psize >> 24; - p_mp4->p_sample->data[p_mp4->p_sample->dataLength + 1] = psize >> 16; - p_mp4->p_sample->data[p_mp4->p_sample->dataLength + 2] = psize >> 8; - p_mp4->p_sample->data[p_mp4->p_sample->dataLength + 3] = psize >> 0; - p_mp4->p_sample->dataLength += i_size; - break; - } - - return i_size; -} - -int set_eop_mp4( hnd_t handle, x264_picture_t *p_picture ) -{ - mp4_t *p_mp4 = (mp4_t*)handle; - uint64_t dts = (uint64_t)p_mp4->i_numframe * p_mp4->i_time_inc; - uint64_t pts = (uint64_t)p_picture->i_pts; - int32_t offset = p_mp4->i_init_delay + pts - dts; - - p_mp4->p_sample->IsRAP = p_picture->i_type == X264_TYPE_IDR ? 1 : 0; - p_mp4->p_sample->DTS = dts; - p_mp4->p_sample->CTS_Offset = offset; - gf_isom_add_sample( p_mp4->p_file, p_mp4->i_track, p_mp4->i_descidx, p_mp4->p_sample ); - - p_mp4->p_sample->dataLength = 0; - p_mp4->i_numframe++; - - return 0; -} - -#endif - - -/* -- mkv muxing support ------------------------------------------------- */ -typedef struct -{ - mk_writer *w; - - uint8_t *sps, *pps; - int sps_len, pps_len; - - int width, height, d_width, d_height; - - int64_t frame_duration; - int fps_num; - - int b_header_written; - char b_writing_frame; -} mkv_t; - -static int write_header_mkv( mkv_t *p_mkv ) -{ - int ret; - uint8_t *avcC; - int avcC_len; - - if( !p_mkv->sps || !p_mkv->pps || - !p_mkv->width || !p_mkv->height || - !p_mkv->d_width || !p_mkv->d_height ) - return -1; - - avcC_len = 5 + 1 + 2 + p_mkv->sps_len + 1 + 2 + p_mkv->pps_len; - avcC = malloc( avcC_len ); - if( !avcC ) - return -1; - - avcC[0] = 1; - avcC[1] = p_mkv->sps[1]; - avcC[2] = p_mkv->sps[2]; - avcC[3] = p_mkv->sps[3]; - avcC[4] = 0xff; // nalu size length is four bytes - avcC[5] = 0xe1; // one sps - - avcC[6] = p_mkv->sps_len >> 8; - avcC[7] = p_mkv->sps_len; - - memcpy( avcC+8, p_mkv->sps, p_mkv->sps_len ); - - avcC[8+p_mkv->sps_len] = 1; // one pps - avcC[9+p_mkv->sps_len] = p_mkv->pps_len >> 8; - avcC[10+p_mkv->sps_len] = p_mkv->pps_len; - - memcpy( avcC+11+p_mkv->sps_len, p_mkv->pps, p_mkv->pps_len ); - - ret = mk_writeHeader( p_mkv->w, "x264", "V_MPEG4/ISO/AVC", - avcC, avcC_len, p_mkv->frame_duration, 50000, - p_mkv->width, p_mkv->height, - p_mkv->d_width, p_mkv->d_height ); - - free( avcC ); - - p_mkv->b_header_written = 1; - - return ret; -} - -int open_file_mkv( char *psz_filename, hnd_t *p_handle ) -{ - mkv_t *p_mkv; - - *p_handle = NULL; - - p_mkv = malloc( sizeof(*p_mkv) ); - if( !p_mkv ) - return -1; - - memset( p_mkv, 0, sizeof(*p_mkv) ); - - p_mkv->w = mk_create_writer( psz_filename ); - if( !p_mkv->w ) - { - free( p_mkv ); - return -1; - } - - *p_handle = p_mkv; - - return 0; -} - -int set_param_mkv( hnd_t handle, x264_param_t *p_param ) -{ - mkv_t *p_mkv = handle; - int64_t dw, dh; - - if( p_param->i_fps_num > 0 ) - { - p_mkv->frame_duration = (int64_t)p_param->i_fps_den * - (int64_t)1000000000 / p_param->i_fps_num; - p_mkv->fps_num = p_param->i_fps_num; - } - else - { - p_mkv->frame_duration = 0; - p_mkv->fps_num = 1; - } - - p_mkv->width = p_param->i_width; - p_mkv->height = p_param->i_height; - - if( p_param->vui.i_sar_width && p_param->vui.i_sar_height ) - { - dw = (int64_t)p_param->i_width * p_param->vui.i_sar_width; - dh = (int64_t)p_param->i_height * p_param->vui.i_sar_height; - } - else - { - dw = p_param->i_width; - dh = p_param->i_height; - } - - if( dw > 0 && dh > 0 ) - { - int64_t x = gcd( dw, dh ); - dw /= x; - dh /= x; - } - - p_mkv->d_width = (int)dw; - p_mkv->d_height = (int)dh; - - return 0; -} - -int write_nalu_mkv( hnd_t handle, uint8_t *p_nalu, int i_size ) -{ - mkv_t *p_mkv = handle; - uint8_t type = p_nalu[4] & 0x1f; - uint8_t dsize[4]; - int psize; - - switch( type ) - { - // sps - case 0x07: - if( !p_mkv->sps ) - { - p_mkv->sps = malloc( i_size - 4 ); - if( !p_mkv->sps ) - return -1; - p_mkv->sps_len = i_size - 4; - memcpy( p_mkv->sps, p_nalu + 4, i_size - 4 ); - } - break; - - // pps - case 0x08: - if( !p_mkv->pps ) - { - p_mkv->pps = malloc( i_size - 4 ); - if( !p_mkv->pps ) - return -1; - p_mkv->pps_len = i_size - 4; - memcpy( p_mkv->pps, p_nalu + 4, i_size - 4 ); - } - break; - - // slice, sei - case 0x1: - case 0x5: - case 0x6: - if( !p_mkv->b_writing_frame ) - { - if( mk_start_frame( p_mkv->w ) < 0 ) - return -1; - p_mkv->b_writing_frame = 1; - } - psize = i_size - 4 ; - dsize[0] = psize >> 24; - dsize[1] = psize >> 16; - dsize[2] = psize >> 8; - dsize[3] = psize; - if( mk_add_frame_data( p_mkv->w, dsize, 4 ) < 0 || - mk_add_frame_data( p_mkv->w, p_nalu + 4, i_size - 4 ) < 0 ) - return -1; - break; - - default: - break; - } - - if( !p_mkv->b_header_written && p_mkv->pps && p_mkv->sps && - write_header_mkv( p_mkv ) < 0 ) - return -1; - - return i_size; -} - -int set_eop_mkv( hnd_t handle, x264_picture_t *p_picture ) -{ - mkv_t *p_mkv = handle; - int64_t i_stamp = (int64_t)(p_picture->i_pts * 1e9 / p_mkv->fps_num); - - p_mkv->b_writing_frame = 0; - - return mk_set_frame_flags( p_mkv->w, i_stamp, p_picture->i_type == X264_TYPE_IDR ); -} - -int close_file_mkv( hnd_t handle ) -{ - mkv_t *p_mkv = handle; - int ret; - - if( p_mkv->sps ) - free( p_mkv->sps ); - if( p_mkv->pps ) - free( p_mkv->pps ); - - ret = mk_close( p_mkv->w ); - - free( p_mkv ); - - return ret; -} - diff --git a/muxers.h b/muxers.h index aaede1c0..44ea9662 100644 --- a/muxers.h +++ b/muxers.h @@ -1,7 +1,7 @@ /***************************************************************************** - * muxers.c: h264 file i/o plugins + * muxers.h: h264 file i/o modules ***************************************************************************** - * Copyright (C) 2003-2008 x264 project + * Copyright (C) 2003-2009 x264 project * * Authors: Laurent Aimar * Loren Merritt @@ -24,49 +24,24 @@ #ifndef X264_MUXERS_H #define X264_MUXERS_H -typedef void *hnd_t; - -int open_file_yuv( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); -int get_frame_total_yuv( hnd_t handle ); -int read_frame_yuv( x264_picture_t *p_pic, hnd_t handle, int i_frame ); -int close_file_yuv( hnd_t handle ); - -int open_file_y4m( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); -int get_frame_total_y4m( hnd_t handle ); -int read_frame_y4m( x264_picture_t *p_pic, hnd_t handle, int i_frame ); -int close_file_y4m( hnd_t handle ); - -int open_file_avis( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); -int get_frame_total_avis( hnd_t handle ); -int read_frame_avis( x264_picture_t *p_pic, hnd_t handle, int i_frame ); -int close_file_avis( hnd_t handle ); +#include "common/common.h" +#include "x264.h" -int open_file_thread( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); -int get_frame_total_thread( hnd_t handle ); -int read_frame_thread( x264_picture_t *p_pic, hnd_t handle, int i_frame ); -int close_file_thread( hnd_t handle ); - -int open_file_bsf( char *psz_filename, hnd_t *p_handle ); -int set_param_bsf( hnd_t handle, x264_param_t *p_param ); -int write_nalu_bsf( hnd_t handle, uint8_t *p_nal, int i_size ); -int set_eop_bsf( hnd_t handle, x264_picture_t *p_picture ); -int close_file_bsf( hnd_t handle ); - -int open_file_mp4( char *psz_filename, hnd_t *p_handle ); -int set_param_mp4( hnd_t handle, x264_param_t *p_param ); -int write_nalu_mp4( hnd_t handle, uint8_t *p_nal, int i_size ); -int set_eop_mp4( hnd_t handle, x264_picture_t *p_picture ); -int close_file_mp4( hnd_t handle ); - -int open_file_mkv( char *psz_filename, hnd_t *p_handle ); -int set_param_mkv( hnd_t handle, x264_param_t *p_param ); -int write_nalu_mkv( hnd_t handle, uint8_t *p_nal, int i_size ); -int set_eop_mkv( hnd_t handle, x264_picture_t *p_picture ); -int close_file_mkv( hnd_t handle ); +typedef void *hnd_t; -extern int (*p_open_infile)( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); -extern int (*p_get_frame_total)( hnd_t handle ); -extern int (*p_read_frame)( x264_picture_t *p_pic, hnd_t handle, int i_frame ); -extern int (*p_close_infile)( hnd_t handle ); +static inline int64_t gcd( int64_t a, int64_t b ) +{ + while( 1 ) + { + int64_t c = a % b; + if( !c ) + return b; + a = b; + b = c; + } +} + +#include "input/input.h" +#include "output/output.h" #endif diff --git a/output/matroska.c b/output/matroska.c new file mode 100644 index 00000000..e986a753 --- /dev/null +++ b/output/matroska.c @@ -0,0 +1,245 @@ +/***************************************************************************** + * matroska.c: x264 matroska output module + ***************************************************************************** + * Copyright (C) 2005 Mike Matsnev + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#include "muxers.h" +#include "matroska_ebml.h" + +typedef struct +{ + mk_writer *w; + + uint8_t *sps, *pps; + int sps_len, pps_len; + + int width, height, d_width, d_height; + + int64_t frame_duration; + int fps_num; + + int b_header_written; + char b_writing_frame; +} mkv_hnd_t; + +static int write_header( mkv_hnd_t *p_mkv ) +{ + int ret; + uint8_t *avcC; + int avcC_len; + + if( !p_mkv->sps || !p_mkv->pps || + !p_mkv->width || !p_mkv->height || + !p_mkv->d_width || !p_mkv->d_height ) + return -1; + + avcC_len = 5 + 1 + 2 + p_mkv->sps_len + 1 + 2 + p_mkv->pps_len; + avcC = malloc( avcC_len ); + if( !avcC ) + return -1; + + avcC[0] = 1; + avcC[1] = p_mkv->sps[1]; + avcC[2] = p_mkv->sps[2]; + avcC[3] = p_mkv->sps[3]; + avcC[4] = 0xff; // nalu size length is four bytes + avcC[5] = 0xe1; // one sps + + avcC[6] = p_mkv->sps_len >> 8; + avcC[7] = p_mkv->sps_len; + + memcpy( avcC+8, p_mkv->sps, p_mkv->sps_len ); + + avcC[8+p_mkv->sps_len] = 1; // one pps + avcC[9+p_mkv->sps_len] = p_mkv->pps_len >> 8; + avcC[10+p_mkv->sps_len] = p_mkv->pps_len; + + memcpy( avcC+11+p_mkv->sps_len, p_mkv->pps, p_mkv->pps_len ); + + ret = mk_writeHeader( p_mkv->w, "x264", "V_MPEG4/ISO/AVC", + avcC, avcC_len, p_mkv->frame_duration, 50000, + p_mkv->width, p_mkv->height, + p_mkv->d_width, p_mkv->d_height ); + + free( avcC ); + + p_mkv->b_header_written = 1; + + return ret; +} + +static int open_file( char *psz_filename, hnd_t *p_handle ) +{ + mkv_hnd_t *p_mkv; + + *p_handle = NULL; + + p_mkv = malloc( sizeof(*p_mkv) ); + if( !p_mkv ) + return -1; + + memset( p_mkv, 0, sizeof(*p_mkv) ); + + p_mkv->w = mk_create_writer( psz_filename ); + if( !p_mkv->w ) + { + free( p_mkv ); + return -1; + } + + *p_handle = p_mkv; + + return 0; +} + +static int set_param( hnd_t handle, x264_param_t *p_param ) +{ + mkv_hnd_t *p_mkv = handle; + int64_t dw, dh; + + if( p_param->i_fps_num > 0 ) + { + p_mkv->frame_duration = (int64_t)p_param->i_fps_den * + (int64_t)1000000000 / p_param->i_fps_num; + p_mkv->fps_num = p_param->i_fps_num; + } + else + { + p_mkv->frame_duration = 0; + p_mkv->fps_num = 1; + } + + p_mkv->width = p_param->i_width; + p_mkv->height = p_param->i_height; + + if( p_param->vui.i_sar_width && p_param->vui.i_sar_height ) + { + dw = (int64_t)p_param->i_width * p_param->vui.i_sar_width; + dh = (int64_t)p_param->i_height * p_param->vui.i_sar_height; + } + else + { + dw = p_param->i_width; + dh = p_param->i_height; + } + + if( dw > 0 && dh > 0 ) + { + int64_t x = gcd( dw, dh ); + dw /= x; + dh /= x; + } + + p_mkv->d_width = (int)dw; + p_mkv->d_height = (int)dh; + + return 0; +} + +static int write_nalu( hnd_t handle, uint8_t *p_nalu, int i_size ) +{ + mkv_hnd_t *p_mkv = handle; + uint8_t type = p_nalu[4] & 0x1f; + uint8_t dsize[4]; + int psize; + + switch( type ) + { + // sps + case 0x07: + if( !p_mkv->sps ) + { + p_mkv->sps = malloc( i_size - 4 ); + if( !p_mkv->sps ) + return -1; + p_mkv->sps_len = i_size - 4; + memcpy( p_mkv->sps, p_nalu + 4, i_size - 4 ); + } + break; + + // pps + case 0x08: + if( !p_mkv->pps ) + { + p_mkv->pps = malloc( i_size - 4 ); + if( !p_mkv->pps ) + return -1; + p_mkv->pps_len = i_size - 4; + memcpy( p_mkv->pps, p_nalu + 4, i_size - 4 ); + } + break; + + // slice, sei + case 0x1: + case 0x5: + case 0x6: + if( !p_mkv->b_writing_frame ) + { + if( mk_start_frame( p_mkv->w ) < 0 ) + return -1; + p_mkv->b_writing_frame = 1; + } + psize = i_size - 4; + dsize[0] = psize >> 24; + dsize[1] = psize >> 16; + dsize[2] = psize >> 8; + dsize[3] = psize; + if( mk_add_frame_data( p_mkv->w, dsize, 4 ) < 0 || + mk_add_frame_data( p_mkv->w, p_nalu + 4, i_size - 4 ) < 0 ) + return -1; + break; + + default: + break; + } + + if( !p_mkv->b_header_written && p_mkv->pps && p_mkv->sps && + write_header( p_mkv ) < 0 ) + return -1; + + return i_size; +} + +static int set_eop( hnd_t handle, x264_picture_t *p_picture ) +{ + mkv_hnd_t *p_mkv = handle; + int64_t i_stamp = (int64_t)(p_picture->i_pts * 1e9 / p_mkv->fps_num); + + p_mkv->b_writing_frame = 0; + + return mk_set_frame_flags( p_mkv->w, i_stamp, p_picture->i_type == X264_TYPE_IDR ); +} + +static int close_file( hnd_t handle ) +{ + mkv_hnd_t *p_mkv = handle; + int ret; + + if( p_mkv->sps ) + free( p_mkv->sps ); + if( p_mkv->pps ) + free( p_mkv->pps ); + + ret = mk_close( p_mkv->w ); + + free( p_mkv ); + + return ret; +} + +cli_output_t mkv_output = { open_file, set_param, write_nalu, set_eop, close_file }; diff --git a/matroska.c b/output/matroska_ebml.c similarity index 94% rename from matroska.c rename to output/matroska_ebml.c index 793420e6..4e6bf0c7 100644 --- a/matroska.c +++ b/output/matroska_ebml.c @@ -1,5 +1,5 @@ /***************************************************************************** - * matroska.c: + * matroska_ebml.c: ***************************************************************************** * Copyright (C) 2005 Mike Matsnev * @@ -21,20 +21,19 @@ #include #include #include "common/osdep.h" -#include "matroska.h" +#include "matroska_ebml.h" #define CLSIZE 1048576 #define CHECK(x)\ -do\ -{\ - if( (x) < 0 ) return -1;\ -}\ -while( 0 ) +do {\ + if( (x) < 0 )\ + return -1;\ +} while( 0 ) struct mk_context { struct mk_context *next, **prev, *parent; - struct mk_writer *owner; + mk_writer *owner; unsigned id; void *data; @@ -63,7 +62,7 @@ struct mk_writer static mk_context *mk_create_context( mk_writer *w, mk_context *parent, unsigned id ) { - mk_context *c; + mk_context *c; if( w->freelist ) { @@ -172,7 +171,7 @@ static int mk_flush_context_id( mk_context *c ) return 0; } -static int mk_flush_context_data(mk_context *c) +static int mk_flush_context_data( mk_context *c ) { if( !c->d_cur ) return 0; @@ -187,7 +186,7 @@ static int mk_flush_context_data(mk_context *c) return 0; } -static int mk_close_context(mk_context *c, unsigned *off) +static int mk_close_context( mk_context *c, unsigned *off ) { if( c->id ) { @@ -376,7 +375,7 @@ int mk_writeHeader( mk_writer *w, const char *writing_app, w->timescale = timescale; w->def_duration = default_frame_duration; - if( !(c = mk_create_context(w, w->root, 0x1a45dfa3)) ) // EBML + if( !(c = mk_create_context( w, w->root, 0x1a45dfa3 )) ) // EBML return -1; CHECK( mk_write_uint( c, 0x4286, 1 ) ); // EBMLVersion CHECK( mk_write_uint( c, 0x42f7, 1 ) ); // EBMLReadVersion @@ -392,7 +391,7 @@ int mk_writeHeader( mk_writer *w, const char *writing_app, CHECK( mk_flush_context_id( c ) ); CHECK( mk_close_context( c, 0 ) ); - if( !(c = mk_create_context(w, w->root, 0x1549a966)) ) // SegmentInfo + if( !(c = mk_create_context( w, w->root, 0x1549a966 )) ) // SegmentInfo return -1; CHECK( mk_write_string( c, 0x4d80, "Haali Matroska Writer b0" ) ); CHECK( mk_write_string( c, 0x5741, writing_app ) ); @@ -489,11 +488,11 @@ static int mk_flush_frame( mk_writer *w ) CHECK( mk_append_context_data( w->cluster, c_delta_flags, 3 ) ); if( w->frame ) { - CHECK( mk_append_context_data(w->cluster, w->frame->data, w->frame->d_cur)); + CHECK( mk_append_context_data( w->cluster, w->frame->data, w->frame->d_cur ) ); w->frame->d_cur = 0; } if( !w->keyframe ) - CHECK( mk_write_sint(w->cluster, 0xfb, ref ) ); // ReferenceBlock + CHECK( mk_write_sint( w->cluster, 0xfb, ref ) ); // ReferenceBlock w->in_frame = 0; w->prev_frame_tc_scaled = w->cluster_tc_scaled + delta; @@ -515,7 +514,7 @@ int mk_start_frame( mk_writer *w ) return 0; } -int mk_set_frame_flags( mk_writer *w,int64_t timestamp, int keyframe ) +int mk_set_frame_flags( mk_writer *w, int64_t timestamp, int keyframe ) { if( !w->in_frame ) return -1; @@ -538,7 +537,7 @@ int mk_add_frame_data( mk_writer *w, const void *data, unsigned size ) if( !(w->frame = mk_create_context( w, NULL, 0 )) ) return -1; - return mk_append_context_data( w->frame, data, size ); + return mk_append_context_data( w->frame, data, size ); } int mk_close( mk_writer *w ) @@ -550,7 +549,7 @@ int mk_close( mk_writer *w ) { fseek( w->fp, w->duration_ptr, SEEK_SET ); if( mk_write_float_raw( w->root, (float)((double)(w->max_frame_tc+w->def_duration) / w->timescale) ) < 0 || - mk_flush_context_data(w->root) < 0 ) + mk_flush_context_data( w->root ) < 0 ) ret = -1; } mk_destroy_contexts( w ); @@ -558,4 +557,3 @@ int mk_close( mk_writer *w ) free( w ); return ret; } - diff --git a/matroska.h b/output/matroska_ebml.h similarity index 83% rename from matroska.h rename to output/matroska_ebml.h index aa6c94cd..f8c641bc 100644 --- a/matroska.h +++ b/output/matroska_ebml.h @@ -1,5 +1,5 @@ /***************************************************************************** - * matroska.h: + * matroska_ebml.h: ***************************************************************************** * Copyright (C) 2005 Mike Matsnev * @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. *****************************************************************************/ -#ifndef X264_MATROSKA_H -#define X264_MATROSKA_H +#ifndef X264_MATROSKA_EBML_H +#define X264_MATROSKA_EBML_H typedef struct mk_writer mk_writer; @@ -33,9 +33,9 @@ int mk_writeHeader( mk_writer *w, const char *writing_app, unsigned width, unsigned height, unsigned d_width, unsigned d_height ); -int mk_start_frame( mk_writer *w ); -int mk_add_frame_data( mk_writer *w, const void *data, unsigned size ); -int mk_set_frame_flags( mk_writer *w, int64_t timestamp, int keyframe ); -int mk_close( mk_writer *w ); +int mk_start_frame( mk_writer *w ); +int mk_add_frame_data( mk_writer *w, const void *data, unsigned size ); +int mk_set_frame_flags( mk_writer *w, int64_t timestamp, int keyframe ); +int mk_close( mk_writer *w ); #endif diff --git a/output/mp4.c b/output/mp4.c new file mode 100644 index 00000000..c361b2be --- /dev/null +++ b/output/mp4.c @@ -0,0 +1,277 @@ +/***************************************************************************** + * mp4.c: x264 mp4 output module + ***************************************************************************** + * Copyright (C) 2003-2009 x264 project + * + * Authors: Laurent Aimar + * Loren Merritt + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#include "muxers.h" +#include + +typedef struct +{ + GF_ISOFile *p_file; + GF_AVCConfig *p_config; + GF_ISOSample *p_sample; + int i_track; + uint32_t i_descidx; + int i_time_inc; + int i_time_res; + int i_numframe; + int i_init_delay; + uint8_t b_sps; + uint8_t b_pps; +} mp4_hnd_t; + +static void recompute_bitrate_mp4( GF_ISOFile *p_file, int i_track ) +{ + u32 i, count, di, timescale, time_wnd, rate; + u64 offset; + Double br; + GF_ESD *esd; + + esd = gf_isom_get_esd( p_file, i_track, 1 ); + if( !esd ) + return; + + esd->decoderConfig->avgBitrate = 0; + esd->decoderConfig->maxBitrate = 0; + rate = time_wnd = 0; + + timescale = gf_isom_get_media_timescale( p_file, i_track ); + count = gf_isom_get_sample_count( p_file, i_track ); + for( i = 0; i < count; i++ ) + { + GF_ISOSample *samp = gf_isom_get_sample_info( p_file, i_track, i+1, &di, &offset ); + + if( samp->dataLength>esd->decoderConfig->bufferSizeDB ) + esd->decoderConfig->bufferSizeDB = samp->dataLength; + + if( esd->decoderConfig->bufferSizeDB < samp->dataLength ) + esd->decoderConfig->bufferSizeDB = samp->dataLength; + + esd->decoderConfig->avgBitrate += samp->dataLength; + rate += samp->dataLength; + if( samp->DTS > time_wnd + timescale ) + { + if( rate > esd->decoderConfig->maxBitrate ) + esd->decoderConfig->maxBitrate = rate; + time_wnd = samp->DTS; + rate = 0; + } + + gf_isom_sample_del( &samp ); + } + + br = (Double)(s64)gf_isom_get_media_duration( p_file, i_track ); + br /= timescale; + esd->decoderConfig->avgBitrate = (u32)(esd->decoderConfig->avgBitrate / br); + /*move to bps*/ + esd->decoderConfig->avgBitrate *= 8; + esd->decoderConfig->maxBitrate *= 8; + + gf_isom_change_mpeg4_description( p_file, i_track, 1, esd ); + gf_odf_desc_del( (GF_Descriptor*)esd ); +} + +static int close_file( hnd_t handle ) +{ + mp4_hnd_t *p_mp4 = handle; + + if( !p_mp4 ) + return 0; + + if( p_mp4->p_config ) + gf_odf_avc_cfg_del( p_mp4->p_config ); + + if( p_mp4->p_sample ) + { + if( p_mp4->p_sample->data ) + free( p_mp4->p_sample->data ); + + gf_isom_sample_del( &p_mp4->p_sample ); + } + + if( p_mp4->p_file ) + { + recompute_bitrate_mp4( p_mp4->p_file, p_mp4->i_track ); + gf_isom_set_pl_indication( p_mp4->p_file, GF_ISOM_PL_VISUAL, 0x15 ); + gf_isom_set_storage_mode( p_mp4->p_file, GF_ISOM_STORE_FLAT ); + gf_isom_close( p_mp4->p_file ); + } + + free( p_mp4 ); + + return 0; +} + +static int open_file( char *psz_filename, hnd_t *p_handle ) +{ + mp4_hnd_t *p_mp4; + + *p_handle = NULL; + + if( !(p_mp4 = malloc( sizeof(mp4_hnd_t) )) ) + return -1; + + memset( p_mp4, 0, sizeof(mp4_hnd_t) ); + p_mp4->p_file = gf_isom_open( psz_filename, GF_ISOM_OPEN_WRITE, NULL ); + + if( !(p_mp4->p_sample = gf_isom_sample_new()) ) + { + close_file( p_mp4 ); + return -1; + } + + gf_isom_set_brand_info( p_mp4->p_file, GF_ISOM_BRAND_AVC1, 0 ); + + *p_handle = p_mp4; + + return 0; +} + +static int set_param( hnd_t handle, x264_param_t *p_param ) +{ + mp4_hnd_t *p_mp4 = handle; + + p_mp4->i_track = gf_isom_new_track( p_mp4->p_file, 0, GF_ISOM_MEDIA_VISUAL, + p_param->i_fps_num ); + + p_mp4->p_config = gf_odf_avc_cfg_new(); + gf_isom_avc_config_new( p_mp4->p_file, p_mp4->i_track, p_mp4->p_config, + NULL, NULL, &p_mp4->i_descidx ); + + gf_isom_set_track_enabled( p_mp4->p_file, p_mp4->i_track, 1 ); + + 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 = malloc( p_param->i_width * p_param->i_height * 3 / 2 ); + if( !p_mp4->p_sample->data ) + return -1; + + p_mp4->i_time_res = p_param->i_fps_num; + p_mp4->i_time_inc = p_param->i_fps_den; + p_mp4->i_init_delay = p_param->i_bframe ? (p_param->i_bframe_pyramid ? 2 : 1) : 0; + p_mp4->i_init_delay *= p_mp4->i_time_inc; + fprintf( stderr, "mp4 [info]: initial delay %d (scale %d)\n", + p_mp4->i_init_delay, p_mp4->i_time_res ); + + return 0; +} + +static int write_nalu( hnd_t handle, uint8_t *p_nalu, int i_size ) +{ + mp4_hnd_t *p_mp4 = handle; + GF_AVCConfigSlot *p_slot; + uint8_t type = p_nalu[4] & 0x1f; + int psize; + + switch( type ) + { + // sps + case 0x07: + if( !p_mp4->b_sps ) + { + p_mp4->p_config->configurationVersion = 1; + p_mp4->p_config->AVCProfileIndication = p_nalu[5]; + p_mp4->p_config->profile_compatibility = p_nalu[6]; + p_mp4->p_config->AVCLevelIndication = p_nalu[7]; + p_slot = malloc( sizeof(GF_AVCConfigSlot) ); + if( !p_slot ) + return -1; + p_slot->size = i_size - 4; + p_slot->data = 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; + p_mp4->b_sps = 1; + } + break; + + // pps + case 0x08: + if( !p_mp4->b_pps ) + { + p_slot = malloc( sizeof(GF_AVCConfigSlot) ); + if( !p_slot ) + return -1; + p_slot->size = i_size - 4; + p_slot->data = 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; + p_mp4->b_pps = 1; + if( p_mp4->b_sps ) + gf_isom_avc_config_update( p_mp4->p_file, p_mp4->i_track, 1, p_mp4->p_config ); + } + break; + + // slice, sei + case 0x1: + case 0x5: + case 0x6: + psize = i_size - 4; + memcpy( p_mp4->p_sample->data + p_mp4->p_sample->dataLength, p_nalu, i_size ); + p_mp4->p_sample->data[p_mp4->p_sample->dataLength + 0] = psize >> 24; + p_mp4->p_sample->data[p_mp4->p_sample->dataLength + 1] = psize >> 16; + p_mp4->p_sample->data[p_mp4->p_sample->dataLength + 2] = psize >> 8; + p_mp4->p_sample->data[p_mp4->p_sample->dataLength + 3] = psize >> 0; + p_mp4->p_sample->dataLength += i_size; + break; + } + + return i_size; +} + +static int set_eop( hnd_t handle, x264_picture_t *p_picture ) +{ + mp4_hnd_t *p_mp4 = handle; + uint64_t dts = (uint64_t)p_mp4->i_numframe * p_mp4->i_time_inc; + uint64_t pts = (uint64_t)p_picture->i_pts; + int32_t offset = p_mp4->i_init_delay + pts - dts; + + p_mp4->p_sample->IsRAP = p_picture->i_type == X264_TYPE_IDR ? 1 : 0; + p_mp4->p_sample->DTS = dts; + p_mp4->p_sample->CTS_Offset = offset; + gf_isom_add_sample( p_mp4->p_file, p_mp4->i_track, p_mp4->i_descidx, p_mp4->p_sample ); + + p_mp4->p_sample->dataLength = 0; + p_mp4->i_numframe++; + + return 0; +} + +cli_output_t mp4_output = { open_file, set_param, write_nalu, set_eop, close_file }; diff --git a/output/output.h b/output/output.h new file mode 100644 index 00000000..5409f152 --- /dev/null +++ b/output/output.h @@ -0,0 +1,40 @@ +/***************************************************************************** + * output.h: x264 file output modules + ***************************************************************************** + * Copyright (C) 2003-2009 x264 project + * + * Authors: Laurent Aimar + * Loren Merritt + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef X264_OUTPUT_H +#define X264_OUTPUT_H + +typedef struct +{ + int (*open_file)( char *psz_filename, hnd_t *p_handle ); + int (*set_param)( hnd_t handle, x264_param_t *p_param ); + int (*write_nalu)( hnd_t handle, uint8_t *p_nal, int i_size ); + int (*set_eop)( hnd_t handle, x264_picture_t *p_picture ); + int (*close_file)( hnd_t handle ); +} cli_output_t; + +extern cli_output_t raw_output; +extern cli_output_t mkv_output; +extern cli_output_t mp4_output; + +#endif diff --git a/output/raw.c b/output/raw.c new file mode 100644 index 00000000..bc0b0daf --- /dev/null +++ b/output/raw.c @@ -0,0 +1,59 @@ +/***************************************************************************** + * raw.c: x264 raw bitstream output module + ***************************************************************************** + * Copyright (C) 2003-2009 x264 project + * + * Authors: Laurent Aimar + * Loren Merritt + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#include "muxers.h" + +static int open_file( char *psz_filename, hnd_t *p_handle ) +{ + if( !(*p_handle = fopen( psz_filename, "w+b" )) ) + return -1; + + return 0; +} + +static int set_param( hnd_t handle, x264_param_t *p_param ) +{ + return 0; +} + +static int write_nalu( hnd_t handle, uint8_t *p_nalu, int i_size ) +{ + if( fwrite( p_nalu, i_size, 1, (FILE*)handle ) > 0 ) + return i_size; + return -1; +} + +static int set_eop( hnd_t handle, x264_picture_t *p_picture ) +{ + return 0; +} + +static int close_file( hnd_t handle ) +{ + if( !handle || handle == stdout ) + return 0; + + return fclose( (FILE*)handle ); +} + +cli_output_t raw_output = { open_file, set_param, write_nalu, set_eop, close_file }; diff --git a/x264.c b/x264.c index fc4e44f1..4ffa7852 100644 --- a/x264.c +++ b/x264.c @@ -61,18 +61,9 @@ typedef struct { FILE *qpfile; } cli_opt_t; -/* input file operation function pointers */ -int (*p_open_infile)( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); -int (*p_get_frame_total)( hnd_t handle ); -int (*p_read_frame)( x264_picture_t *p_pic, hnd_t handle, int i_frame ); -int (*p_close_infile)( hnd_t handle ); - -/* output file operation function pointers */ -static int (*p_open_outfile)( char *psz_filename, hnd_t *p_handle ); -static int (*p_set_outfile_param)( hnd_t handle, x264_param_t *p_param ); -static int (*p_write_nalu)( hnd_t handle, uint8_t *p_nal, int i_size ); -static int (*p_set_eop)( hnd_t handle, x264_picture_t *p_picture ); -static int (*p_close_outfile)( hnd_t handle ); +/* i/o file operation function pointer structs */ +cli_input_t input; +static cli_output_t output; static void Help( x264_param_t *defaults, int longhelp ); static int Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ); @@ -550,18 +541,9 @@ static int Parse( int argc, char **argv, memset( opt, 0, sizeof(cli_opt_t) ); opt->b_progress = 1; - /* Default input file driver */ - p_open_infile = open_file_yuv; - p_get_frame_total = get_frame_total_yuv; - p_read_frame = read_frame_yuv; - p_close_infile = close_file_yuv; - - /* Default output file driver */ - p_open_outfile = open_file_bsf; - p_set_outfile_param = set_param_bsf; - p_write_nalu = write_nalu_bsf; - p_set_eop = set_eop_bsf; - p_close_outfile = close_file_bsf; + /* Default i/o modules */ + input = yuv_input; + output = raw_output; /* Presets are applied before all other options. */ for( optind = 0;; ) @@ -795,27 +777,17 @@ static int Parse( int argc, char **argv, if( !strncasecmp(optarg + strlen(optarg) - 4, ".mp4", 4) ) { #ifdef MP4_OUTPUT - p_open_outfile = open_file_mp4; - p_write_nalu = write_nalu_mp4; - p_set_outfile_param = set_param_mp4; - p_set_eop = set_eop_mp4; - p_close_outfile = close_file_mp4; + output = mp4_output; #else fprintf( stderr, "x264 [error]: not compiled with MP4 output support\n" ); return -1; #endif } else if( !strncasecmp(optarg + strlen(optarg) - 4, ".mkv", 4) ) - { - p_open_outfile = open_file_mkv; - p_write_nalu = write_nalu_mkv; - p_set_outfile_param = set_param_mkv; - p_set_eop = set_eop_mkv; - p_close_outfile = close_file_mkv; - } + output = mkv_output; if( !strcmp(optarg, "-") ) opt->hout = stdout; - else if( p_open_outfile( optarg, &opt->hout ) ) + else if( output.open_file( optarg, &opt->hout ) ) { fprintf( stderr, "x264 [error]: can't open output file `%s'\n", optarg ); return -1; @@ -1001,24 +973,16 @@ generic_option: if( b_avis ) { #ifdef AVIS_INPUT - p_open_infile = open_file_avis; - p_get_frame_total = get_frame_total_avis; - p_read_frame = read_frame_avis; - p_close_infile = close_file_avis; + input = avis_input; #else fprintf( stderr, "x264 [error]: not compiled with AVIS input support\n" ); return -1; #endif } if( b_y4m ) - { - p_open_infile = open_file_y4m; - p_get_frame_total = get_frame_total_y4m; - p_read_frame = read_frame_y4m; - p_close_infile = close_file_y4m; - } + input = y4m_input; - if( p_open_infile( psz_filename, &opt->hin, param ) ) + if( input.open_file( psz_filename, &opt->hin, param ) ) { fprintf( stderr, "x264 [error]: could not open input file '%s'\n", psz_filename ); return -1; @@ -1035,18 +999,13 @@ generic_option: if( b_thread_input || param->i_threads > 1 || (param->i_threads == X264_THREADS_AUTO && x264_cpu_num_processors() > 1) ) { - if( open_file_thread( NULL, &opt->hin, param ) ) + if( thread_input.open_file( NULL, &opt->hin, param ) ) { fprintf( stderr, "x264 [error]: threaded input failed\n" ); return -1; } else - { - p_open_infile = open_file_thread; - p_get_frame_total = get_frame_total_thread; - p_read_frame = read_frame_thread; - p_close_infile = close_file_thread; - } + input = thread_input; } #endif @@ -1129,13 +1088,13 @@ static int Encode_frame( x264_t *h, hnd_t hout, x264_picture_t *pic ) for( i = 0; i < i_nal; i++ ) { - i_nalu_size = p_write_nalu( hout, nal[i].p_payload, nal[i].i_payload ); + i_nalu_size = output.write_nalu( hout, nal[i].p_payload, nal[i].i_payload ); if( i_nalu_size < 0 ) return -1; i_file += i_nalu_size; } - if (i_nal) - p_set_eop( hout, &pic_out ); + if( i_nal ) + output.set_eop( hout, &pic_out ); return i_file; } @@ -1174,7 +1133,7 @@ static int Encode( x264_param_t *param, cli_opt_t *opt ) int i_update_interval; opt->b_progress &= param->i_log_level < X264_LOG_DEBUG; - i_frame_total = p_get_frame_total( opt->hin ); + i_frame_total = input.get_frame_total( opt->hin ); i_frame_total -= opt->i_seek; if( ( i_frame_total == 0 || param->i_frame_total < i_frame_total ) && param->i_frame_total > 0 ) @@ -1185,15 +1144,15 @@ static int Encode( x264_param_t *param, cli_opt_t *opt ) if( ( h = x264_encoder_open( param ) ) == NULL ) { fprintf( stderr, "x264 [error]: x264_encoder_open failed\n" ); - p_close_infile( opt->hin ); + input.close_file( opt->hin ); return -1; } - if( p_set_outfile_param( opt->hout, param ) ) + if( output.set_param( opt->hout, param ) ) { fprintf( stderr, "x264 [error]: can't set outfile param\n" ); - p_close_infile( opt->hin ); - p_close_outfile( opt->hout ); + input.close_file( opt->hin ); + output.close_file( opt->hout ); return -1; } @@ -1209,7 +1168,7 @@ static int Encode( x264_param_t *param, cli_opt_t *opt ) /* Encode frames */ for( i_frame = 0, i_file = 0, i_frame_output = 0; b_ctrl_c == 0 && (i_frame < i_frame_total || i_frame_total == 0); ) { - if( p_read_frame( &pic, opt->hin, i_frame + opt->i_seek ) ) + if( input.read_frame( &pic, opt->hin, i_frame + opt->i_seek ) ) break; pic.i_pts = (int64_t)i_frame * param->i_fps_den; @@ -1261,8 +1220,8 @@ static int Encode( x264_param_t *param, cli_opt_t *opt ) if( b_ctrl_c ) fprintf( stderr, "aborted at input frame %d, output frame %d\n", opt->i_seek + i_frame, i_frame_output ); - p_close_infile( opt->hin ); - p_close_outfile( opt->hout ); + input.close_file( opt->hin ); + output.close_file( opt->hout ); if( i_frame_output > 0 ) {