Web lists-archives.com

[MPlayer-dev-eng] Variable frame rate for mencoder




Hi all

I had a need to generate variable frame rate avi files and timecode stamps for 
muxing into other containers (specifically .mkv) and hacked in a -makevfr 
option into mencoder. There are only a handful of really awful unmaintained 
windows apps that manage these currently.

What this does is it generates an avi output file without any null frames, 
which is pretty much unusable by itself, but it also generates a timecodes 
version2 file (timecodes.txt). The resulting avi file and timecodes file can 
then be muxed into a true variable frame rate container such as mkv using 
mkvtoolnix.

So what this is useful for, is the various pseudo variable frame rate videos 
out there such as wmv (which imitates 1000fps) and the so called 120fps ntsc 
video captures. These are usually a blend of various combinations of 
24000/1001, 30000/1001 and 60000/1001 videos. No fixed frame rate ever 
creates smooth video from them.

To use this, you must use nosound as the resultant file is meaningless without 
it.

Any frame rate input should be able to be used, and the resultant output 
*should* always have audio video sync.

Eg:

mencoder -nosound -ovc lavc -mc 0 -makevfr -o temporary_vfr.avi source.wmv

the resultant temporary_vfr.avi file then needs to be muxed with mkvtoolnix 
specifying the timecodes for that video stream and the audio needs to be 
added separately.

mplayer -vo null -ao pcm:fast:file=audio.wav source.wmv
faac audio.wav

mkvmerge -o vfr.mkv --timecodes 0:timecodes.txt  temporary_vfr.avi audio.aac

This should generate a real variable frame rate in a container that supports 
it without losing audio sync from any input.

The use of -ofps can be used to set the maximum frame rate, but is usually not 
required. I'd actually suggest using it for wmv because at a precision of 
1000fps it actually isn't any meaningful multiple of the real frame rates. 
The best is actually that 120 number used by avi, so -ofps 120000/1001

Generally speaking, those who already know about making variable frame rate 
anime will already know what to do.


I realise mencoder has lots of issues and is considered broken at the moment, 
but there are many of us who depend on it as it fills a niche nothing else 
does.

I also realise that I have not studied the code style of mplayer/mencoder 
extensively and have probably committed numerous sins in the way I've hacked 
this up. Thus I don't expect the code to be committed. However, I offer it as 
a demonstration piece and of interest since it has been incredibly useful to 
me, dealing with variable frame rate mkv, mp4, 120fps avi, wmv.

Cheers, and thanks for mplayer.

Patch attached.

-- 
-ck
---
 cfg-mencoder.h     |    3 +++
 libmpdemux/muxer.c |   24 +++++++++++++++++++++++-
 mencoder.c         |   23 +++++++++++++++++++++++
 3 files changed, 49 insertions(+), 1 deletion(-)

Index: MPlayer-1.0rc2/libmpdemux/muxer.c
===================================================================
--- MPlayer-1.0rc2.orig/libmpdemux/muxer.c	2008-02-18 11:03:55.221531553 +1100
+++ MPlayer-1.0rc2/libmpdemux/muxer.c	2008-02-18 11:04:09.222335021 +1100
@@ -18,6 +18,9 @@
 #include "help_mp.h"
 #include "stheader.h"
 
+extern short makevfr;
+extern FILE* timecodes_file;
+
 muxer_t *muxer_new_muxer(int type,stream_t *stream){
     muxer_t* muxer=calloc(1,sizeof(muxer_t));
     if(!muxer)
@@ -58,8 +61,27 @@
  * (a) have at least one frame from each stream
  * (b) run out of memory */
 void muxer_write_chunk(muxer_stream_t *s, size_t len, unsigned int flags, double dts, double pts) {
-    if(dts == MP_NOPTS_VALUE) dts= s->timer;
     if(pts == MP_NOPTS_VALUE) pts= s->timer; // this is wrong
+    if(dts == MP_NOPTS_VALUE) {
+	if (makevfr) {
+		/* In variable frame rate video, we just update the time but don't output a frame */
+		if(s->h.dwSampleSize){
+		// CBR
+		s->h.dwLength+=len/s->h.dwSampleSize;
+		if(len%s->h.dwSampleSize) mp_msg(MSGT_MUXER, MSGL_WARN, MSGTR_WarningLenIsntDivisible);
+		} else {
+		// VBR
+		s->h.dwLength++;
+		}
+		dts= s->timer;
+		s->timer=(double)s->h.dwLength*s->h.dwScale/s->h.dwRate;
+		return;
+	}
+	dts= s->timer;
+}
+
+	if (makevfr)
+		fprintf(timecodes_file, "%f\n",(s->timer * 1000));
 
     if (s->muxer->muxbuf_skip_buffer) {
       s->muxer->cont_write_chunk(s, len, flags, dts, pts);
Index: MPlayer-1.0rc2/cfg-mencoder.h
===================================================================
--- MPlayer-1.0rc2.orig/cfg-mencoder.h	2008-02-18 11:03:55.221531553 +1100
+++ MPlayer-1.0rc2/cfg-mencoder.h	2008-02-18 11:04:09.222335021 +1100
@@ -205,6 +205,9 @@
 	{"noskiplimit", &skip_limit, CONF_TYPE_FLAG, 0, 0, -1, NULL},
 	{"noskip", &skip_limit, CONF_TYPE_FLAG, 0, 0, 0, NULL},
 
+	{"makevfr", &makevfr, CONF_TYPE_FLAG, 0, 0, 1, NULL},
+	{"timecodesfile", &timecodesfile, CONF_TYPE_STRING, CONF_GLOBAL, 0, 0, NULL},
+
 	{"audio-density", &audio_density, CONF_TYPE_INT, CONF_RANGE|CONF_GLOBAL, 1, 50, NULL},
 	{"audio-preload", &audio_preload, CONF_TYPE_FLOAT, CONF_RANGE|CONF_GLOBAL, 0, 2, NULL},
 	{"audio-delay",   &audio_delay_fix, CONF_TYPE_FLOAT, CONF_GLOBAL, 0, 0, NULL},
Index: MPlayer-1.0rc2/mencoder.c
===================================================================
--- MPlayer-1.0rc2.orig/mencoder.c	2008-02-18 11:03:55.221531553 +1100
+++ MPlayer-1.0rc2/mencoder.c	2008-02-18 11:04:09.222335021 +1100
@@ -181,6 +181,7 @@
 int force_audiofmttag=-1;
 
 char* passtmpfile="divx2pass.log";
+char* timecodesfile="timecodes.txt";
 
 static int play_n_frames=-1;
 static int play_n_frames_mf=-1;
@@ -296,6 +297,7 @@
     \return 1 for success, 0 for failure, 2 for EOF.
 */
 static int edl_seek(edl_record_ptr next_edl_record, demuxer_t* demuxer, demux_stream_t *d_audio, muxer_stream_t* mux_a, s_frame_data * frame_data, int framecopy);
+short makevfr; ///Generate no null frame variable frame rate and timecode file.
 
 #include "cfg-mencoder.h"
 
@@ -372,6 +374,17 @@
 
 extern void print_wave_header(WAVEFORMATEX *h, int verbose_level);
 
+FILE *timecodes_file;
+
+static int init_timecodes_output(void)
+{
+	timecodes_file = fopen(timecodesfile, "w");
+	if(timecodes_file == NULL)
+		return 1;
+	fprintf(timecodes_file, "# timecode format v2\n");
+	return 0;
+}
+
 int main(int argc,char* argv[]){
 
 stream_t* stream=NULL;
@@ -745,6 +758,12 @@
   mp_msg(MSGT_MENCODER, MSGL_FATAL, MSGTR_CannotInitializeMuxer);
   mencoder_exit(1,NULL);
 }
+
+if (makevfr) {
+	if (init_timecodes_output())
+		mp_msg(MSGT_MENCODER,MSGL_ERR,"Failed to open timecodes file: filename=%s\n", timecodesfile);
+}
+
 #if 0
 //disabled: it horrybly distorts filtered sound
 if(out_file_format == MUXER_TYPE_MPEG) audio_preload = 0;
@@ -1519,6 +1538,8 @@
 	if(sh_video){ uninit_video(sh_video);sh_video=NULL; }
 	if(demuxer) free_demuxer(demuxer);
 	if(stream) free_stream(stream); // kill cache thread
+	if (makevfr)
+		fclose(timecodes_file);
 
 	at_eof = 0;
 
@@ -1573,6 +1594,8 @@
 if(sh_video){ uninit_video(sh_video);sh_video=NULL; }
 if(demuxer) free_demuxer(demuxer);
 if(stream) free_stream(stream); // kill cache thread
+if (makevfr)
+	fclose(timecodes_file);
 
 return interrupted;
 }
_______________________________________________
MPlayer-dev-eng mailing list
MPlayer-dev-eng@xxxxxxxxxxxx
https://lists.mplayerhq.hu/mailman/listinfo/mplayer-dev-eng