Better support for RealVideo/RealAudio in Matroska. Includes the timestamp fixes from demux_real.c. Seeking is working. Added Matroska to the formats with variable FPS in video.c.

git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@10435 b3059339-0415-0410-9bf9-f77b7e298cf2
This commit is contained in:
mosu 2003-07-15 07:19:03 +00:00
parent a280f5ea1a
commit 9cbdc3b045
2 changed files with 159 additions and 36 deletions

View File

@ -163,10 +163,12 @@ typedef struct mkv_track {
int ok;
// Stuff for RealMedia packet assembly
// Stuff for RealMedia
bool realmedia;
demux_packet_t *rm_dp;
int rm_seqnum;
int rm_seqnum, rv_kf_base, rv_kf_pts;
float rv_pts; // previous video timestamp
float ra_pts; // previous audio timestamp
} mkv_track_t;
typedef struct mkv_demuxer {
@ -195,8 +197,8 @@ typedef struct mkv_demuxer {
int64_t *cluster_positions;
int num_cluster_pos;
int skip_to_keyframe;
int64_t skip_to_timecode;
bool v_skip_to_keyframe, a_skip_to_keyframe;
} mkv_demuxer_t;
typedef struct {
@ -1728,7 +1730,8 @@ extern "C" int demux_mkv_open(demuxer_t *demuxer) {
src = (unsigned char *)(rvp + 1);
bih = (BITMAPINFOHEADER *)realloc(bih, sizeof(BITMAPINFOHEADER) + 12);
bih->biSize += 12;
bih->biSize = 48;
bih->biPlanes = 1;
type2 = get_uint32_be(&rvp->type2);
if ((type2 == 0x10003000) || (type2 == 0x100030001))
bih->biCompression = mmioFOURCC('R', 'V', '1', '3');
@ -1956,6 +1959,8 @@ extern "C" int demux_mkv_open(demuxer_t *demuxer) {
((short *)(sh_a->wf + 1))[3] = get_uint32_be(&ra4p->coded_frame_size);
((short *)(sh_a->wf + 1))[4] = codecdata_length;
memcpy(((char *)(sh_a->wf + 1)) + 10, src, codecdata_length);
track->realmedia = true;
}
} else {
@ -2004,9 +2009,65 @@ extern "C" int demux_mkv_open(demuxer_t *demuxer) {
return 1;
}
// Taken from demux_real.c. Thanks to the original developpers :)
#define SKIP_BITS(n) buffer <<= n
#define SHOW_BITS(n) ((buffer) >> (32 - (n)))
static float real_fix_timestamp(mkv_track_t *track, unsigned char *s,
int timestamp, float frametime) {
float v_pts;
uint32_t buffer = (s[0] << 24) + (s[1] << 16) + (s[2] << 8) + s[3];
int kf = timestamp;
int pict_type;
int orig_kf;
if (!strcmp(track->codec_id, MKV_V_REALV30) ||
!strcmp(track->codec_id, MKV_V_REALV40)) {
if (!strcmp(track->codec_id, MKV_V_REALV30)) {
SKIP_BITS(3);
pict_type = SHOW_BITS(2);
SKIP_BITS(2 + 7);
}else{
SKIP_BITS(1);
pict_type = SHOW_BITS(2);
SKIP_BITS(2 + 7 + 3);
}
kf = SHOW_BITS(13); // kf= 2*SHOW_BITS(12);
orig_kf = kf;
if (pict_type <= 1) {
// I frame, sync timestamps:
track->rv_kf_base = timestamp - kf;
mp_msg(MSGT_DEMUX, MSGL_V, "\nTS: base=%08X\n", track->rv_kf_base);
kf = timestamp;
} else {
// P/B frame, merge timestamps:
int tmp = timestamp - track->rv_kf_base;
kf |= tmp & (~0x1fff); // combine with packet timestamp
if (kf < (tmp - 4096)) // workaround wrap-around problems
kf += 8192;
else if (kf > (tmp + 4096))
kf -= 8192;
kf += track->rv_kf_base;
}
if (pict_type != 3) { // P || I frame -> swap timestamps
int tmp = kf;
kf = track->rv_kf_pts;
track->rv_kf_pts = tmp;
}
mp_msg(MSGT_DEMUX, MSGL_V, "\nTS: %08X -> %08X (%04X) %d %02X %02X %02X "
"%02X %5d\n", timestamp, kf, orig_kf, pict_type, s[0], s[1], s[2],
s[3], kf - (int)(1000.0 * track->rv_pts));
}
v_pts = kf * 0.001f;
track->rv_pts = v_pts;
return v_pts;
}
static void handle_realvideo(demuxer_t *demuxer, DataBuffer &data,
bool keyframe, int &found_data) {
unsigned char *p, *buffer;
unsigned char *p;
dp_hdr_t *hdr;
int chunks, isize;
mkv_demuxer_t *mkv_d;
@ -2028,7 +2089,13 @@ static void handle_realvideo(demuxer_t *demuxer, DataBuffer &data,
hdr->chunktab = sizeof(dp_hdr_t) + isize;
dp->len = sizeof(dp_hdr_t) + isize + 8 * (chunks + 1);
dp->pts = hdr->timestamp;
if (mkv_d->v_skip_to_keyframe) {
dp->pts = mkv_d->last_pts;
mkv_d->video->rv_kf_base = 0;
mkv_d->video->rv_kf_pts = hdr->timestamp;
} else
dp->pts = real_fix_timestamp(mkv_d->video, &dp->buffer[sizeof(dp_hdr_t)],
hdr->timestamp, 1 / mkv_d->video->v_frate);
dp->pos = demuxer->filepos;
dp->flags = keyframe ? 0x10 : 0;
@ -2037,6 +2104,30 @@ static void handle_realvideo(demuxer_t *demuxer, DataBuffer &data,
found_data++;
}
static void handle_realaudio(demuxer_t *demuxer, DataBuffer &data,
bool keyframe, int &found_data) {
mkv_demuxer_t *mkv_d;
demux_packet_t *dp;
mkv_d = (mkv_demuxer_t *)demuxer->priv;
dp = new_demux_packet(data.Size());
memcpy(dp->buffer, data.Buffer(), data.Size());
if ((mkv_d->audio->ra_pts == mkv_d->last_pts) &&
!mkv_d->a_skip_to_keyframe)
dp->pts = 0;
else
dp->pts = mkv_d->last_pts;
mkv_d->audio->ra_pts = mkv_d->last_pts;
dp->pos = demuxer->filepos;
dp->flags = keyframe ? 0x10 : 0;
ds_add_packet(demuxer->audio, dp);
found_data++;
}
extern "C" int demux_mkv_fill_buffer(demuxer_t *d) {
demux_packet_t *dp;
demux_stream_t *ds;
@ -2047,6 +2138,8 @@ extern "C" int demux_mkv_fill_buffer(demuxer_t *d) {
EbmlStream *es;
KaxBlock *block;
int64_t block_duration, block_ref1, block_ref2;
bool use_this_block;
float current_pts;
mkv_d = (mkv_demuxer_t *)d->priv;
es = mkv_d->es;
@ -2186,40 +2279,67 @@ extern "C" int demux_mkv_fill_buffer(demuxer_t *d) {
(mkv_d->audio->tnum == block->TrackNum()))
ds = d->audio;
if (!mkv_d->skip_to_keyframe || // Not skipping is ok.
(((elements_found & 4) == 0) && // It's a key frame.
(ds != NULL) && // Corresponding track found
(ds == d->video))) { // track is our video track
mkv_d->last_pts = (float)(block->GlobalTimecode() / 1000000.0 -
mkv_d->first_tc) / 1000.0;
use_this_block = true;
current_pts = (float)(block->GlobalTimecode() / 1000000.0 -
mkv_d->first_tc) / 1000.0;
if (ds == d->audio) {
if (mkv_d->a_skip_to_keyframe &&
((elements_found & 4) == 4))
use_this_block = false;
else if (mkv_d->v_skip_to_keyframe)
use_this_block = false;
} else if ((current_pts * 1000) < mkv_d->skip_to_timecode)
use_this_block = false;
else if (ds == d->video) {
if (mkv_d->v_skip_to_keyframe &&
((elements_found & 4) == 4))
use_this_block = false;
} else if ((mkv_d->subs_track != NULL) &&
(mkv_d->subs_track->tnum == block->TrackNum())) {
if (!mkv_d->v_skip_to_keyframe)
handle_subtitles(d, block, block_duration);
use_this_block = false;
} else
use_this_block = false;
if (use_this_block) {
mkv_d->last_pts = current_pts;
d->filepos = mkv_d->in->getFilePointer();
mkv_d->last_filepos = d->filepos;
if ((ds != NULL) && ((block->GlobalTimecode() / 1000000 -
mkv_d->first_tc) >=
(uint64_t)mkv_d->skip_to_timecode)) {
for (i = 0; i < (int)block->NumberFrames(); i++) {
DataBuffer &data = block->GetBuffer(i);
if ((mkv_d->video != NULL) &&
mkv_d->video->realmedia &&
(mkv_d->video->tnum == block->TrackNum()))
handle_realvideo(d, data, (elements_found & 4) == 0,
found_data);
else {
dp = new_demux_packet(data.Size());
memcpy(dp->buffer, data.Buffer(), data.Size());
dp->pts = mkv_d->last_pts;
// keyframe?
dp->flags = (elements_found & 4) == 0 ? 1 : 0;
ds_add_packet(ds, dp);
found_data++;
}
for (i = 0; i < (int)block->NumberFrames(); i++) {
DataBuffer &data = block->GetBuffer(i);
if ((ds == d->video) && mkv_d->video->realmedia)
handle_realvideo(d, data, (elements_found & 4) == 0,
found_data);
else if ((ds == d->audio) && mkv_d->audio->realmedia)
handle_realaudio(d, data, (elements_found & 4) == 0,
found_data);
else {
dp = new_demux_packet(data.Size());
memcpy(dp->buffer, data.Buffer(), data.Size());
dp->flags = (elements_found & 4) == 0 ? 1 : 0;
dp->pts = mkv_d->last_pts;
ds_add_packet(ds, dp);
found_data++;
}
mkv_d->skip_to_keyframe = 0;
}
if (ds == d->video) {
mkv_d->v_skip_to_keyframe = false;
mkv_d->skip_to_timecode = 0;
} else if ((mkv_d->subs_track != NULL) &&
(mkv_d->subs_track->tnum == block->TrackNum()))
handle_subtitles(d, block, block_duration);
}
if (ds == d->audio)
mkv_d->a_skip_to_keyframe = false;
}
delete block;
@ -2426,10 +2546,12 @@ extern "C" void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs,
}
if (mkv_d->video != NULL)
mkv_d->skip_to_keyframe = 1;
mkv_d->v_skip_to_keyframe = true;
if (rel_seek_secs > 0.0)
mkv_d->skip_to_timecode = target_timecode;
mkv_d->a_skip_to_keyframe = true;
demux_mkv_fill_buffer(demuxer);
mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] New timecode: %lld\n",
(int64_t)(mkv_d->last_pts * 1000.0));

View File

@ -452,6 +452,7 @@ int video_read_frame(sh_video_t* sh_video,float* frame_time_ptr,unsigned char**
if(!force_fps) switch(demuxer->file_format){
case DEMUXER_TYPE_GIF:
case DEMUXER_TYPE_REAL:
case DEMUXER_TYPE_MATROSKA:
if(d_video->pts>0 && pts1>0 && d_video->pts>pts1)
frame_time=d_video->pts-pts1;
break;