diff --git a/DOCS/mplayer.1 b/DOCS/mplayer.1 index f54aa8080d..1fad4ade32 100644 --- a/DOCS/mplayer.1 +++ b/DOCS/mplayer.1 @@ -206,6 +206,19 @@ Prints some statistics on CPU usage and dropped frames at the end. Use in combination with \-nosound and \-vo null for benchmarking only the video codec. .TP +.B \-edl +Enables edit decision list actions during playback. Video will be +skipped over and audio will be muted and unmuted according to the +entries in the given file. See DOCS/edl.html for details on how to use +this. +.TP +.B \-edlout +Creates a new file and writes edit decision list records to that +file. During playback, when the user hits 'i', an entry to skip over +the last two seconds of playback will be written to the file. This +provides a starting point from which the user can fine-tune EDL +entries later. See DOCS/edl.html for details. +.TP .B \-framedrop (also see \-hardframedrop) Skips displaying some frames to maintain A/\:V sync on slow systems. Decoding of B frames is also skipped and video filters are not used. diff --git a/cfg-common.h b/cfg-common.h index 731434784e..d4f5e910f0 100644 --- a/cfg-common.h +++ b/cfg-common.h @@ -227,6 +227,11 @@ extern int demuxer_type, audio_demuxer_type, sub_demuxer_type; #include "libmpdemux/tv.h" +#ifdef USE_EDL +extern char* edl_filename; +extern char* edl_output_filename; +#endif + #ifdef USE_TV struct config tvopts_conf[]={ {"on", &tv_param_on, CONF_TYPE_FLAG, 0, 0, 1, NULL}, diff --git a/cfg-mplayer.h b/cfg-mplayer.h index 7fd9c672d4..859dc48955 100644 --- a/cfg-mplayer.h +++ b/cfg-mplayer.h @@ -203,6 +203,13 @@ static config_t mplayer_opts[]={ CONF_TYPE_PRINT, 0, 0, 0, NULL}, {"noalsa", "Option -noalsa has been removed, new audio code doesn't need it! Remove it from your config file!\n", CONF_TYPE_PRINT, 0, 0, 0, NULL}, +#ifdef USE_EDL + {"edl", &edl_filename, CONF_TYPE_STRING, 0, 0, 0, NULL}, + {"edlout", &edl_output_filename, CONF_TYPE_STRING, 0, 0, 0, NULL}, +#else + {"edl", "MPlayer was compiled without EDL support\n", CONF_TYPE_PRINT, 0, 0, 0, NULL}, + {"edlout", "MPlayer was compiled without EDL support\n", CONF_TYPE_PRINT, 0, 0, 0, NULL}, +#endif #ifdef HAVE_X11 {"display", &mDisplayName, CONF_TYPE_STRING, 0, 0, 0, NULL}, diff --git a/configure b/configure index 925fc1d819..e3820480a5 100755 --- a/configure +++ b/configure @@ -140,6 +140,7 @@ Optional features: --disable-tv disable TV Interface (tv/dvb grabbers) [enable] --disable-tv-v4l disable Video4Linux TV Interface support [autodetect] --disable-tv-bsdbt848 disable BSD BT848 Interface support [autodetect] + --disable-edl disable EDL (edit decision list) support [enable] --disable-rtc disable RTC (/dev/rtc) on Linux [autodetect] --disable-streaming disable network streaming support (support for: http/mms/rtp) [enable] @@ -1003,6 +1004,7 @@ _select=yes _tv=yes _tv_v4l=auto _tv_bsdbt848=auto +_edl=yes _streaming=yes _vidix=auto _joystick=no @@ -1153,6 +1155,8 @@ for ac_option do --disable-alsa) _alsa=no ;; --enable-tv) _tv=yes ;; --disable-tv) _tv=no ;; + --enable-edl) _edl=yes ;; + --disable-edl) _edl=no ;; --enable-tv-bsdbt848) _tv_bsdbt848=yes ;; --disable-tv-bsdbt848) _tv_bsdbt848=no ;; --enable-tv-v4l) _tv_v4l=yes ;; @@ -4210,6 +4214,16 @@ else fi echores "$_tv" +echocheck "EDL support" +if test "$_edl" = yes ; then + _def_edl='#define USE_EDL' + _inputmodules="edl $_inputmodules" +else + _noinputmodules="edl $_noinputmodules" + _def_edl='#undef USE_EDL' +fi +echores "$_edl" + echocheck "*BSD BrookTree 848 TV interface" if test "$_tv_bsdbt848" = auto ; then _tv_bsdbt848=no @@ -5027,6 +5041,9 @@ $_def_nas /* Enable TV Interface support */ $_def_tv +/* Enable EDL support */ +$_def_edl + /* Enable Video 4 Linux TV interface support */ $_def_tv_v4l diff --git a/edl.h b/edl.h new file mode 100644 index 0000000000..f6817ffa15 --- /dev/null +++ b/edl.h @@ -0,0 +1,25 @@ +// EDL version 0.5 +// Author: Michael Halcrow + +#ifndef EDLH +#define EDLH + +#define EDL_SKIP 0 +#define EDL_MUTE 1 + +#define MAX_EDL_ENTRIES 1000 + +struct edl_record { + float start_sec; + long start_frame; + float stop_sec; + long stop_frame; + float length_sec; + long length_frame; + short action; + struct edl_record* next; +}; + +typedef struct edl_record* edl_record_ptr; + +#endif diff --git a/input/input.c b/input/input.c index 3cce4ebf33..3b580bd06b 100644 --- a/input/input.c +++ b/input/input.c @@ -46,6 +46,9 @@ static mp_cmd_t mp_cmds[] = { { MP_CMD_SEEK, "seek", 1, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } }, +#ifdef USE_EDL + { MP_CMD_EDL_MARK, "edl_mark", 0, { {-1,{0}} } }, +#endif { MP_CMD_AUDIO_DELAY, "audio_delay", 1, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } }, { MP_CMD_QUIT, "quit", 0, { {-1,{0}} } }, { MP_CMD_PAUSE, "pause", 0, { {-1,{0}} } }, @@ -246,6 +249,9 @@ static mp_cmd_bind_t def_cmd_binds[] = { { { 't', 0 }, "sub_pos +1" }, { { 'v', 0 }, "sub_visibility" }, { { 'j', 0 }, "vobsub_lang" }, +#ifdef USE_EDL + { { 'i', 0 }, "edl_mark" }, +#endif #ifdef USE_TV { { 'h', 0 }, "tv_step_channel 1" }, { { 'k', 0 }, "tv_step_channel -1" }, diff --git a/input/input.h b/input/input.h index 95b606bc6e..b55aaa40cd 100644 --- a/input/input.h +++ b/input/input.h @@ -37,6 +37,10 @@ #define MP_CMD_GET_TIME_LENGTH 34 #define MP_CMD_GET_PERCENT_POS 35 #define MP_CMD_SUB_STEP 36 +//#define MP_CMD_TV_SET_CHANNEL 37 +#ifdef USE_EDL +#define MP_CMD_EDL_MARK 38 +#endif #define MP_CMD_GUI_EVENTS 5000 #define MP_CMD_GUI_LOADFILE 5001 diff --git a/mplayer.c b/mplayer.c index 2e8a02d5a8..5a2364a0c0 100644 --- a/mplayer.c +++ b/mplayer.c @@ -56,6 +56,10 @@ #include #endif +#ifdef USE_EDL +#include "edl.h" +#endif + #include "spudec.h" #include "vobsub.h" @@ -298,6 +302,16 @@ static char* menu_root = "main"; static int nortc; #endif +#ifdef USE_EDL +struct edl_record edl_records[ MAX_EDL_ENTRIES ]; +int num_edl_records = 0; +FILE* edl_fd = NULL; +edl_record_ptr next_edl_record = NULL; +static char* edl_filename = NULL; +static char* edl_output_filename = NULL; +short edl_decision = 0; +#endif + static unsigned int inited_flags=0; #define INITED_VO 1 #define INITED_AO 2 @@ -725,6 +739,102 @@ if(!parse_codec_cfg(get_path("codecs.conf"))){ exit(0); } +#ifdef USE_EDL + { + FILE* fd; + char line[ 100 ]; + float start, stop, duration; + int action; + int next_edl_array_index = 0; + int lineCount = 0; + next_edl_record = edl_records; + if( edl_filename ) { + if( ( fd = fopen( edl_filename, "r" ) ) == NULL ) { + printf( "Error opening EDL file [%s]!\n", edl_filename ); + next_edl_record->next = NULL; + } else { + while( fgets( line, 99, fd ) != NULL ) { + lineCount++; + if( ( sscanf( line, "%f %f %d", &start, &stop, &action ) ) == 0 ) { + printf( "Invalid EDL line: [%s]\n", line ); + } else { + if( next_edl_array_index > 0 ) { + edl_records[ next_edl_array_index-1 ].next = &edl_records[ next_edl_array_index ]; + if( start <= edl_records[ next_edl_array_index-1 ].stop_sec ) { + printf( "Invalid EDL line [%d]: [%s]\n", lineCount, line ); + printf( "Last stop position was [%f]; next start is [%f]. Entries must be in chronological order and cannot overlap. Discarding EDL entry.\n", edl_records[ next_edl_array_index-1 ].stop_sec, start ); + continue; + } + } + if( stop <= start ) { + printf( "Invalid EDL line [%d]: [%s]\n", lineCount, line ); + printf( "Stop time must follow start time. Discarding EDL entry.\n" ); + continue; + } + edl_records[ next_edl_array_index ].action = action; + if( action == EDL_MUTE ) { + edl_records[ next_edl_array_index ].length_sec = 0; + edl_records[ next_edl_array_index ].start_sec = start; + edl_records[ next_edl_array_index ].stop_sec = start; + next_edl_array_index++; + if( next_edl_array_index >= MAX_EDL_ENTRIES-1 ) { + break; + } + edl_records[ next_edl_array_index-1 ].next = &edl_records[ next_edl_array_index ]; + edl_records[ next_edl_array_index ].action = EDL_MUTE; + edl_records[ next_edl_array_index ].length_sec = 0; + edl_records[ next_edl_array_index ].start_sec = stop; + edl_records[ next_edl_array_index ].stop_sec = stop; + } else { + edl_records[ next_edl_array_index ].length_sec = stop - start; + edl_records[ next_edl_array_index ].start_sec = start; + edl_records[ next_edl_array_index ].stop_sec = stop; + } + next_edl_array_index++; + if( next_edl_array_index >= MAX_EDL_ENTRIES-1 ) { + break; + } + } + } + if( next_edl_array_index > 0 ) { + edl_records[ next_edl_array_index-1 ].next = &edl_records[ next_edl_array_index ]; + } + edl_records[ next_edl_array_index ].start_sec = -1; + edl_records[ next_edl_array_index ].next = NULL; + num_edl_records = ( next_edl_array_index ); + } + fclose( fd ); + } else { + next_edl_record->next = NULL; + } + if( edl_output_filename ) { + if( edl_filename ) { + printf( "Sorry; EDL mode and EDL output mode are mutually exclusive! Disabling all EDL functions.\n" ); + edl_output_filename = NULL; + edl_filename = NULL; + next_edl_record->next = NULL; + } else { + if( ( edl_fd = fopen( edl_output_filename, "w" ) ) == NULL ) { + printf( "Error opening file [%s] for writing!\n" ); + edl_output_filename = NULL; + next_edl_record->next = NULL; + } + } + } +#ifdef DEBUG_EDL + { + printf( "EDL Records:\n" ); + if( next_edl_record->next != NULL ) { + while( next_edl_record->next != NULL ) { + printf( "EDL: start [%f], stop [%f], action [%d]\n", next_edl_record->start_sec, next_edl_record->stop_sec, next_edl_record->action ); + next_edl_record = next_edl_record->next; + } + next_edl_record = edl_records; + } + } +#endif + } +#endif if(!filename && !vcd_track && !dvd_title && !dvd_nav && !tv_param_on){ if(!use_gui){ @@ -1955,6 +2065,32 @@ if (stream->type==STREAMTYPE_DVDNAV && dvd_nav_still) dvdnav_stream_sleeping((dvdnav_priv_t*)stream->priv); #endif +//================= EDL ========================================= + +#ifdef USE_EDL + if( next_edl_record->next ) { // Are we (still?) doing EDL? + if( d_video->pts >= next_edl_record->start_sec ) { + if( next_edl_record->action == EDL_SKIP ) { + osd_function = OSD_FFW; + abs_seek_pos = 0; + rel_seek_secs = next_edl_record->length_sec; +#ifdef DEBUG_EDL + printf( "\nEDL_SKIP: start [%f], stop [%f], length [%f]\n", next_edl_record->start_sec, next_edl_record->stop_sec, next_edl_record->length_sec ); +#endif + edl_decision = 1; + next_edl_record = next_edl_record->next; + } else if( next_edl_record->action == EDL_MUTE ) { + mixer_mute(); +#ifdef DEBUG_EDL + printf( "\nEDL_MUTE: [%f]\n", next_edl_record->start_sec ); +#endif + edl_decision = 1; + next_edl_record = next_edl_record->next; + } + } + } +#endif + //================= Keyboard events, SEEKing ==================== current_module="key_events"; @@ -1985,6 +2121,14 @@ if (stream->type==STREAMTYPE_DVDNAV && dvd_nav_still) osd_function= (v > 0) ? OSD_FFW : OSD_REW; } } break; +#ifdef USE_EDL + case MP_CMD_EDL_MARK: + if( edl_fd ) { + float v = d_video->pts; + fprintf( edl_fd, "%f %f %d\n", v-2, v, 0 ); + } + break; +#endif case MP_CMD_AUDIO_DELAY : { float v = cmd->args[0].v.f; audio_delay += v; @@ -2665,12 +2809,18 @@ if(rel_seek_secs || abs_seek_pos){ #ifdef USE_OSD // Set OSD: if(osd_level){ - int len=((demuxer->movi_end-demuxer->movi_start)>>8); - if (len>0 && sh_video){ - osd_visible=sh_video->fps; // 1 sec - vo_osd_progbar_type=0; - vo_osd_progbar_value=(demuxer->filepos-demuxer->movi_start)/len; - vo_osd_changed(OSDTYPE_PROGBAR); +#ifdef USE_EDL + if( !edl_decision ) { +#else + if( 1 ) { // Let the compiler optimize this out +#endif + int len=((demuxer->movi_end-demuxer->movi_start)>>8); + if (len>0 && sh_video){ + osd_visible=sh_video->fps; // 1 sec + vo_osd_progbar_type=0; + vo_osd_progbar_value=(demuxer->filepos-demuxer->movi_start)/len; + vo_osd_changed(OSDTYPE_PROGBAR); + } } } #endif @@ -2686,6 +2836,22 @@ if(rel_seek_secs || abs_seek_pos){ if(vo_spudec) spudec_reset(vo_spudec); } } +#ifdef USE_EDL + { + int x; + if( !edl_decision ) { + for( x = 0; x < num_edl_records; x++ ) { // FIXME: do binary search + // Find first EDL entry where start follows current time + if( edl_records[ x ].start_sec >= d_video->pts && edl_records[ x ].action != EDL_MUTE ) { + next_edl_record = &edl_records[ x ]; + break; + } + } + } else { + edl_decision = 0; + } + } +#endif rel_seek_secs=0; abs_seek_pos=0; frame_time_remaining=0;