+ fprintf( stderr, "encoded %d frames, %.2f fps, %.2f kb/s\n", i_frame, fps,
+ (double) i_file * 8 * param->i_fps_num / ( param->i_fps_den * i_frame * 1000 ) );
+ }
+
+ return 0;
+}
+
+/* raw 420 yuv file operation */
+static int open_file_yuv( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
+{
+ if ((*p_handle = fopen(psz_filename, "rb")) == NULL)
+ return -1;
+ return 0;
+}
+
+static int get_frame_total_yuv( hnd_t handle, int i_width, int i_height )
+{
+ FILE *f = (FILE *)handle;
+ int i_frame_total = 0;
+
+ if( !fseek( f, 0, SEEK_END ) )
+ {
+ int64_t i_size = ftell( f );
+ fseek( f, 0, SEEK_SET );
+ i_frame_total = (int)(i_size / ( i_width * i_height * 3 / 2 ));
+ }
+
+ return i_frame_total;
+}
+
+static int read_frame_yuv( x264_picture_t *p_pic, hnd_t handle, int i_frame, int i_width, int i_height )
+{
+ static int prev_frame = -1;
+ FILE *f = (FILE *)handle;
+
+ if( i_frame != prev_frame+1 )
+ if( fseek( f, i_frame * i_width * i_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 )
+ return -1;
+
+ prev_frame = i_frame;
+
+ return 0;
+}
+
+static int close_file_yuv(hnd_t handle)
+{
+ if (handle == NULL)
+ return 0;
+ return fclose((FILE *)handle);
+}
+
+
+/* avs/avi input file support under cygwin */
+
+#ifdef AVIS_INPUT
+
+static int gcd(int a, int b)
+{
+ int c;
+
+ while (1)
+ {
+ c = a % b;
+ if (!c)
+ return b;
+ a = b;
+ b = c;
+ }
+}
+
+static int open_file_avis( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
+{
+ AVISTREAMINFO info;
+ PAVISTREAM p_avi = NULL;
+ int i;
+
+ *p_handle = NULL;
+
+ AVIFileInit();
+ if( AVIStreamOpenFromFile( &p_avi, psz_filename, streamtypeVIDEO, 0, OF_READ, NULL ) )
+ {
+ AVIFileExit();
+ return -1;
+ }
+
+ if( AVIStreamInfo(p_avi, &info, sizeof(AVISTREAMINFO)) )
+ {
+ AVIStreamRelease(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(p_avi);
+ AVIFileExit();
+
+ return -1;
+ }
+
+ p_param->i_width = info.rcFrame.right - info.rcFrame.left;
+ 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 );
+
+ *p_handle = (hnd_t)p_avi;
+
+ return 0;
+}
+
+static int get_frame_total_avis( hnd_t handle, int i_width, int i_height )
+{
+ PAVISTREAM p_avi = (PAVISTREAM)handle;
+ AVISTREAMINFO info;
+
+ if( AVIStreamInfo(p_avi, &info, sizeof(AVISTREAMINFO)) )
+ return -1;
+
+ return info.dwLength;
+}
+
+static int read_frame_avis( x264_picture_t *p_pic, hnd_t handle, int i_frame, int i_width, int i_height )
+{
+ PAVISTREAM p_avi = (PAVISTREAM)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 ) )
+ return -1;
+
+ return 0;
+}
+
+static int close_file_avis( hnd_t handle )
+{
+ PAVISTREAM p_avi = (PAVISTREAM)handle;
+
+ AVIStreamRelease(p_avi);
+ AVIFileExit();
+
+ return 0;
+}
+
+#endif
+
+
+static int open_file_bsf( char *psz_filename, hnd_t *p_handle )
+{
+ if ((*p_handle = fopen(psz_filename, "w+b")) == NULL)
+ return -1;
+
+ return 0;
+}
+
+static int set_param_bsf( hnd_t handle, x264_param_t *p_param )
+{
+ return 0;
+}
+
+static 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;
+}
+
+static int set_eop_bsf( hnd_t handle, x264_picture_t *p_picture )
+{
+ return 0;
+}
+
+static int close_file_bsf( hnd_t handle )
+{
+ if ((handle == NULL) || (handle == stdout))
+ return 0;
+
+ return fclose((FILE *)handle);
+}
+
+/* -- mp4 muxing support ------------------------------------------------- */
+#ifdef MP4_OUTPUT
+
+typedef struct
+{
+ M4File *p_file;
+ AVCConfig *p_config;
+ M4Sample *p_sample;
+ int i_track;
+ int 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(M4File *p_file, int i_track)
+{
+ u32 i, count, di, timescale, time_wnd, rate;
+ u64 offset;
+ Double br;
+ ESDescriptor *esd;
+
+ esd = M4_GetStreamDescriptor(p_file, i_track, 1);
+ if (!esd) return;
+
+ esd->decoderConfig->avgBitrate = 0;
+ esd->decoderConfig->maxBitrate = 0;
+ rate = time_wnd = 0;
+
+ timescale = M4_GetMediaTimeScale(p_file, i_track);
+ count = M4_GetSampleCount(p_file, i_track);
+ for (i=0; i<count; i++) {
+ M4Sample *samp = M4_GetSampleInfo(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;
+ }
+
+ M4_DeleteSample(&samp);
+ }
+
+ br = (Double) (s64) M4_GetMediaDuration(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;
+
+ M4_ChangeStreamDescriptor(p_file, i_track, 1, esd);
+ OD_DeleteDescriptor((Descriptor **)&esd);
+}
+
+
+static int close_file_mp4( hnd_t handle )
+{
+ mp4_t *p_mp4 = (mp4_t *)handle;
+
+ if (p_mp4 == NULL)
+ return 0;
+
+ if (p_mp4->p_config)
+ AVC_DeleteConfig(p_mp4->p_config);
+
+ if (p_mp4->p_sample)
+ {
+ if (p_mp4->p_sample->data)
+ free(p_mp4->p_sample->data);
+
+ M4_DeleteSample(&p_mp4->p_sample);
+ }
+
+ if (p_mp4->p_file)
+ {
+ recompute_bitrate_mp4(p_mp4->p_file, p_mp4->i_track);
+ M4_SetMoviePLIndication(p_mp4->p_file, M4_PL_VISUAL, 0x15);
+ M4_SetStorageMode(p_mp4->p_file, M4_FLAT);
+ M4_MovieClose(p_mp4->p_file);
+ }
+
+ free(p_mp4);
+
+ return 0;
+}
+
+static int open_file_mp4( char *psz_filename, hnd_t *p_handle )
+{
+ mp4_t *p_mp4;
+
+ *p_handle = NULL;
+
+ if ((p_mp4 = (mp4_t *)malloc(sizeof(mp4_t))) == NULL)
+ return -1;
+
+ memset(p_mp4, 0, sizeof(mp4_t));
+ p_mp4->p_file = M4_MovieOpen(psz_filename, M4_OPEN_WRITE);
+
+ if ((p_mp4->p_sample = M4_NewSample()) == NULL)
+ {
+ close_file_mp4( p_mp4 );
+ return -1;
+ }
+
+ M4_SetMovieVersionInfo(p_mp4->p_file, H264_AVC_File, 0);
+
+ *p_handle = p_mp4;
+
+ return 0;
+}
+
+
+static int set_param_mp4( hnd_t handle, x264_param_t *p_param )
+{
+ mp4_t *p_mp4 = (mp4_t *)handle;
+
+ p_mp4->i_track = M4_NewTrack(p_mp4->p_file, 0, M4_VisualMediaType,
+ p_param->i_fps_num);
+
+ p_mp4->p_config = AVC_NewConfig();
+ M4_AVC_NewStreamConfig(p_mp4->p_file, p_mp4->i_track, p_mp4->p_config,
+ NULL, NULL, &p_mp4->i_descidx);
+
+ M4_SetVisualEntrySize(p_mp4->p_file, p_mp4->i_track, p_mp4->i_descidx,
+ p_param->i_width, p_param->i_height);
+
+ 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->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->b_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_mp4( hnd_t handle, uint8_t *p_nalu, int i_size )
+{
+ mp4_t *p_mp4 = (mp4_t *)handle;
+ 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 = (AVCConfigSlot *)malloc(sizeof(AVCConfigSlot));
+ p_slot->size = i_size - 4;
+ p_slot->data = (char *)malloc(p_slot->size);
+ memcpy(p_slot->data, p_nalu + 4, i_size - 4);
+ ChainAddEntry(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 = (AVCConfigSlot *)malloc(sizeof(AVCConfigSlot));
+ p_slot->size = i_size - 4;
+ p_slot->data = (char *)malloc(p_slot->size);
+ memcpy(p_slot->data, p_nalu + 4, i_size - 4);
+ ChainAddEntry(p_mp4->p_config->pictureParameterSets, p_slot);
+ p_slot = NULL;
+ p_mp4->b_pps = 1;
+ if (p_mp4->b_sps)
+ M4_AVC_UpdateStreamConfig(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) & 0xff;
+ p_mp4->p_sample->data[p_mp4->p_sample->dataLength + 1] = (psize >> 16) & 0xff;
+ p_mp4->p_sample->data[p_mp4->p_sample->dataLength + 2] = (psize >> 8) & 0xff;
+ p_mp4->p_sample->data[p_mp4->p_sample->dataLength + 3] = (psize >> 0) & 0xff;
+ p_mp4->p_sample->dataLength += i_size;
+ break;
+ }
+
+ return i_size;
+}
+
+static int set_eop_mp4( hnd_t handle, x264_picture_t *p_picture )
+{
+ mp4_t *p_mp4 = (mp4_t *)handle;
+ uint32_t dts = p_mp4->i_numframe * p_mp4->i_time_inc;
+ uint32_t pts = p_picture->i_pts;
+ int 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;
+ M4_AddSample(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 == NULL || p_mkv->pps == NULL ||
+ p_mkv->width == 0 || p_mkv->height == 0 ||
+ p_mkv->d_width == 0 || p_mkv->d_height == 0)
+ return -1;
+
+ avcC_len = 5 + 1 + 2 + p_mkv->sps_len + 1 + 2 + p_mkv->pps_len;
+ avcC = malloc(avcC_len);
+ if (avcC == NULL)
+ 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] = 0xfe; // nalu size length is three 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_mkv( char *psz_filename, hnd_t *p_handle )
+{
+ mkv_t *p_mkv;
+
+ *p_handle = NULL;
+
+ p_mkv = malloc(sizeof(*p_mkv));
+ if (p_mkv == NULL)
+ return -1;
+
+ memset(p_mkv, 0, sizeof(*p_mkv));
+
+ p_mkv->w = mk_createWriter(psz_filename);
+ if (p_mkv->w == NULL)
+ {
+ free(p_mkv);
+ return -1;