[MPlayer-G2-dev] Re: mp-G2: play_frame()
Andriy N. Gritsenko
andrej at lucky.net
Thu May 8 15:37:25 CEST 2003
Hi!
OOPS, sorry, I've forgotten to attach the file. :(
It's only concept, not really workink subroutine. Sorry, I don't have
enough time now to do all researches on G2 streaming but someone here are
working on that problem too so I think it will be very useful to join our
thoughts. :)
Yesterday's evening I've spent some time to write play_frame() a bit.
See it in attachment. It allows multiple play contexts and multiple audio
or video streams, has no static vars, and is independent of other parts
of G2 as much as possible. Some comments:
1) I didn't do some audio/video specific parts since I didn't get yet how
to pull audio and video frames from chain, sorry.
2) I've changed skiplimit variable to float. Some time ago I wanted to
run mplayer with -skiplimit 0.2 since it may allow me significally
decrease number of dropped valuable frames despite of little A-V
desync.
3) Many applications may have more than one play context (for example,
player with multiscreen ability, video editor, streaming server, etc.)
so have static variables (such as sh_audio or video_out) isn't good
solution. So I've implemented struct play_context. :)
4) Since it's allowed to have more than one video or audio stream, each
stream has to have his own sync mechanism, it dissalows to have static
vars so I've moved statics for that into sh_audio and sh_video (may be,
we will implement some struct chain_t instead of sh_audio_t/sh_video_t
for unification later?). These variables are:
double skipcnt; // counter how many skips are possible now
double pts; // presentation timestamp of last gotten frame
double corr; // correction time for this stream chain
int skipped; // number of skipped (dropped) non-duplicate frames
int dupped; // number of inserted duplicate frames
5) play_frame() will work in any application (player, encoder, etc.) with
no changes and hacks. :)
6) Input chain (from source stream to play_frame subroutine) have not
push frame to output chain (audio/video out or muxer) directly but
play_frame() will do that instead. It's because only play_frame() may
decide if frame has to be dropped or duplicated. Anyway each input
chain must have appropriate output chain. :)
Any questions/comments are welcomed but don't ask me wrote all missed
stuff - I have no enough time for researches on G2 streams now and they
are too complex to solve it without knowing the details. Sorry for that.
With best wishes.
Andriy.
-------------- next part --------------
#include "ctl.h"
float skiplimit = -1; // config variable!
typedef struct {
sh_audio_t **sh_audio;
ao_functions_t **audio_out;
int audio_streams; // chains sh_audio and audio_out
sh_video_t **sh_video;
vo_instance_t **video_out;
int video_streams; // chains sh_video and video_out
double pts; // presentation timestamp at end of last played frame
} play_context;
/* play the frame at timestamp pts with frame duration duration */
int play_frame (play_context *ctx, double pts, double duration) {
int i;
//double seek = check_control();
//pts += seek;
// play all audio
for (i = 0; i < ctx->audio_streams; i++) {
sh_audio_t *sh_a = ctx->sh_audio[i];
ao_functions_t *ao = ctx->audio_out[i];
if (sh_a->ds->eof) continue; // input ended
// since it multistreamed now, pts must be added into sh_audio_t!
do {
// do seek if pts and ctx->pts are too different (may be only in player)
//...
// decode audio
//...
// play audio
//...
// update sh_a->pts
//...
} while (sh_a->pts + sh_a->corr < pts+duration);
}
// play all video
for (i = 0; i < ctx->video_streams; i++) {
sh_video_t *sh_v = ctx->sh_video[i];
vo_instance_t *vo = ctx->video_out[i];
static struct mp_image_s *mpi = NULL;
if (sh_v->ds->eof) continue; // input ended
// do seek if pts and ctx->pts are too different (may be only in player)
//...
// check if we jumped past current frame
if (sh_v->pts + sh_v->corr < pts) { // we have to skip that frame
if (skiplimit > 0)
sh_v->skipcnt += skiplimit;
while (skiplimit < 0 || sh_v->skipcnt >= 1) { // skip frame(s) if possible
// decode videoframe (just for dropping now)
sh_v->vfilter->get_image(sh_v->vfilter, mpi);
if (mpi) { // if it isn't duplicate/dropped
sh_v->skipcnt -= 1.0;
sh_v->skipped += 1;
}
// videoframe skipped!
// update sh_v->pts
//...
// checks for success
if (sh_v->ds->eof || sh_v->pts + sh_v->corr >= pts+duration)
break;
}
continue; // next video
}
// we can process that frame
if (skiplimit > 0)
sh_v->skipcnt -= skiplimit;
// duplicate current frame if it's need and possible
if ((skiplimit < 0 || sh_v->skipcnt <= -1) &&
sh_v->pts + sh_v->corr >= pts+duration) {
sh_v->dupped += 1;
// push duplicate frame with duration duration
//...
sh_v->corr += duration;
sh_v->skipcnt += 1.0;
}
// process videoframe(s)
while (sh_v->pts + sh_v->corr < pts+duration) {
double frametime; // frame duration
// decode videoframe (for play, full process)
sh_v->vfilter->get_image(sh_v->vfilter, mpi);
// get frame duration
//...
if (!mpi && sh_v->corr > 0) { // if it is duplicate/dropped
// update sh_v->pts and drop it if possible
sh_v->pts += frametime;
sh_v->corr -= frametime;
continue;
}
if (!mpi)
sh_v->dupped += 1;
// play videoframe (even duplicate one)
//...
sh_v->pts += frametime;
break; // we cannot play more than one frame once
}
}
// update ctx->pts
ctx->pts = pts + duration;
}
More information about the MPlayer-G2-dev
mailing list