I solved this over the past week and thought I'd post my solution here in case someone from the future stumbles across it and finds it useful.
Here is the crux of my problem: I'm trying to use VLC to obtain the byte-position in an MPEG-TS file stream corresponding to a given time. So, I'm calling libvlc_media_player_set_time() to set the time-based position, then calling libvlc_media_player_get_position() to get the offset into the file (as a percentage of its size).
The issue I was encountering was that the position being returned was not accurate. I know that VLC can very accurately seek to the time because it does a very nice binary-search to the PCR-based time location. But the position being returned was wrong. It was off by 90 seconds seeking half way into a 30 minute video. I needed it to be far more accurate than that.
First, I had to cross-compile VLC for Win32. Anyone attempting to do that should know these things:
1) Use the latest VideoLAN docker image.
2) Retrieve to source code from git, DO NOT USE A TARBALL.
3) Ignore anything you read on the wiki, and just run: extras/package/win32/build.sh -a i686
4) I could not get the prebuilt contrib package to work. But the build script will compile them for you.
5) If you need to exclude certain options to get it to build you can edit the build.sh script and set the variables as needed. Here's what I did (I didn't need the UI so I disabled it, which saves a lot of compilation time):
Code: Select all
CONTRIBFLAGS="$CONTRIBFLAGS --disable-libplacebo --disable-qt --disable-qtsvg --disable-qtdeclarative --disable-qtgraphicaleffects --disable-qtquickcontrols2"
...and...
Code: Select all
CONFIGFLAGS="--disable-faad --disable-vlc --disable-qt --disable-skins2"
NOTE: Those are two separate variables! "CONTRIBFLAGS" is NOT "CONFIGFLAGS". Use the former to confgure the contrib build and the later to configure the main build.
Once I had it compiled I discovered that the time seek was extremely accurate and at the end of the seek I simply wrote out the position to a file, because that's all I need this to do:
Code: Select all
--- a/modules/demux/mpeg/ts.c
+++ b/modules/demux/mpeg/ts.c
@@ -30,6 +30,8 @@
# include "config.h"
#endif
+#include <stdio.h>
+
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_access.h> /* DVB-specific things */
@@ -1007,6 +1009,11 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
ReadyQueuesPostSeek( p_demux );
es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
FROM_SCALE(p_pmt->pcr.i_first) + i64 - VLC_TS_0 );
+ if(1) {
+ FILE *fp = fopen("pos.txt","w+");
+ fprintf(fp, "%"PRIu64"\n", vlc_stream_Tell( p_sys->stream ) );
+ fclose(fp);
+ }
You cannot rely on get_position because when seeking to a time the INTF code first seeks to the percentage-based position (which is inaccurate) before it seeks to the time-based position. There is a comment in the code that this approach of stuffing a position update event into its Control queue is hackish, but that's what it does. Then the inaccurate position is cached and isn't updated even after the time-based seek. The calling code also has no way of knowing (that I could discern) whether it was stop()ping the playback too early and leaving the time seek in the queue. For my purposes I modified that like this:
Code: Select all
--- a/src/input/var.c
+++ b/src/input/var.c
@@ -609,22 +609,22 @@ static int TimeCallback( vlc_object_t *p_this, char const *psz_cmd,
input_thread_t *p_input = (input_thread_t*)p_this;
VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
+ input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &newval );
+
/* Update "position" for better intf behaviour */
const int64_t i_length = var_GetInteger( p_input, "length" );
if( i_length > 0 && newval.i_int >= 0 && newval.i_int <= i_length )
{
vlc_value_t val;
- val.f_float = (double)newval.i_int/(double)i_length;
- var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
+ // val.f_float = (double)newval.i_int/(double)i_length;
+ // var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
/*
* Notify the intf that a new event has been occurred.
* XXX this is a bit hackish but it's the only way to do it now.
*/
var_SetInteger( p_input, "intf-event", INPUT_EVENT_POSITION );
}
-
- input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &newval );
return VLC_SUCCESS;
With these two changes you can call into the set_time routine and wait until it's done by doing this:
Code: Select all
libvlc_media_player_play (mp);
while(!libvlc_media_player_is_seekable(mp))
Sleep(250);
libvlc_media_player_set_time(mp, offset);
for(int i = 0; i < 20 && !pos; i++ ) {
pos = libvlc_media_player_get_position(mp);
Sleep(250);
}
libvlc_media_player_release(mp);
And then just read the position out of the file.
Good luck!
-Alex