1004 lines
23 KiB
C++
1004 lines
23 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* SigmaPlayer source project - AVI player source file.
|
|
* \file avi.cpp
|
|
* \author bombur
|
|
* \version 0.1
|
|
* \date 07.03.2007
|
|
*
|
|
* Based on AVILIB (C) 1999 Rainer Johanni <Rainer@Johanni.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
|
|
*/
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#include <libsp/sp_misc.h>
|
|
#include <libsp/sp_msg.h>
|
|
#include <libsp/sp_fip.h>
|
|
#include <libsp/sp_khwl.h>
|
|
#include <libsp/sp_mpeg.h>
|
|
#include <libsp/sp_cdrom.h>
|
|
|
|
#include "script.h"
|
|
#include "player.h"
|
|
#include "media.h"
|
|
|
|
#ifdef INTERNAL_VIDEO_PLAYER
|
|
|
|
#define VIDEO_INTERNAL
|
|
|
|
#include "audio.h"
|
|
#include "video.h"
|
|
#include "avi.h"
|
|
|
|
#include "script-vars.h"
|
|
|
|
#define MSG if (video_msg) msg
|
|
|
|
VideoAvi::VideoAvi()
|
|
{
|
|
type = VIDEO_CONTAINER_AVI;
|
|
|
|
video_strn = 0;
|
|
avi_flags = 0;
|
|
bitmap_info_header = NULL;
|
|
video_superindex.idx = NULL;
|
|
|
|
memset(video_tag1, 0, 4);
|
|
memset(video_tag2, 0, 4);
|
|
movi_start = 0;
|
|
idx_start = -1;
|
|
idx_offset = -1;
|
|
|
|
has_indx = false;
|
|
num_idx = 0;
|
|
|
|
buf_idx = (AviOldIndexEntry *)SPmalloc(MAX(max_buf_idx_size[0], max_buf_idx_size[1]));
|
|
buf_idx_size = 0;
|
|
|
|
cur_old_buf_idx = 0;
|
|
cur_buf_video_idx = 0;
|
|
|
|
cur_indx_buf_idx = 0;
|
|
cur_indx_buf_pos = 0;
|
|
cur_indx_base_offset = 0;
|
|
cur_buf_idx_size = 1;
|
|
}
|
|
|
|
VideoAvi::~VideoAvi()
|
|
{
|
|
SPSafeFree(bitmap_info_header);
|
|
SPSafeFree(video_superindex.idx);
|
|
SPSafeFree(buf_idx);
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
|
|
BOOL VideoAvi::Parse()
|
|
{
|
|
long i, n;
|
|
BYTE *hdrl_data;
|
|
BYTE data[256];
|
|
long header_offset = 0, hdrl_len = 0;
|
|
int j;
|
|
VIDEO_CHUNK_TYPE lasttag = VIDEO_CHUNK_UNKNOWN;
|
|
bool vids_strh_seen = false;
|
|
bool vids_strf_seen = false;
|
|
bool auds_strh_seen = false;
|
|
int num_stream = 0;
|
|
LONGLONG oldpos = -1, newpos = -1;
|
|
|
|
// Read first 12 bytes and check that this is an AVI file
|
|
if (video_read(data, 12) != 12)
|
|
{
|
|
msg_error("AVI: Read error.\n");
|
|
return FALSE;
|
|
}
|
|
if (strncasecmp((char *)data, "RIFF", 4) != 0 || strncasecmp((char *)data+8, "AVI ", 4) != 0)
|
|
{
|
|
msg_error("AVI: File header not found. Wrong file format.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
// Go through the AVI file and extract the header list,
|
|
// the start position of the 'movi' list and an optionally present idx1 tag
|
|
|
|
hdrl_data = 0;
|
|
movi_start = 0;
|
|
idx_start = -1;
|
|
|
|
// find all main AVI parts
|
|
for (;;)
|
|
{
|
|
// EOF?
|
|
if (video_read(data, 8) != 8)
|
|
break;
|
|
newpos = video_lseek(0, SEEK_CUR);
|
|
if (oldpos == newpos)
|
|
{
|
|
msg_error("AVI: AVI file is broken.\n");
|
|
return FALSE;
|
|
}
|
|
oldpos=newpos;
|
|
|
|
n = GET_DWORD(data+4);
|
|
n = PAD_EVEN(n);
|
|
|
|
if (strncasecmp((char *)data, "LIST", 4) == 0)
|
|
{
|
|
if (video_read(data, 4) != 4)
|
|
{
|
|
msg_error("AVI: Read error.\n");
|
|
return FALSE;
|
|
}
|
|
n -= 4;
|
|
if (n <= 0)
|
|
break;
|
|
if (strncasecmp((char *)data, "hdrl", 4) == 0)
|
|
{
|
|
hdrl_len = n;
|
|
hdrl_data = (BYTE *) SPmalloc(n);
|
|
if (hdrl_data == NULL)
|
|
return FALSE;
|
|
|
|
header_offset = (long)video_lseek(0, SEEK_CUR);
|
|
if (video_read(hdrl_data, n) != n)
|
|
{
|
|
msg_error("AVI: Read error.\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (strncasecmp((char *)data, "movi", 4) == 0)
|
|
{
|
|
movi_start = video_lseek(0, SEEK_CUR);
|
|
if (video_lseek(n, SEEK_CUR) == (LONGLONG)-1)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (video_lseek(n, SEEK_CUR) == (LONGLONG)-1)
|
|
break;
|
|
}
|
|
}
|
|
else if(strncasecmp((char *)data, "idx1", 4) == 0)
|
|
{
|
|
// n must be a multiple of 16, but the reading does not
|
|
// break if this is not the case
|
|
num_idx = n / sizeof(AviOldIndexEntry);
|
|
idx_start = video_lseek(0, SEEK_CUR);
|
|
|
|
if (buf_idx == NULL)
|
|
return FALSE;
|
|
buf_idx_size = MIN(n, max_buf_idx_size[0]);
|
|
// \warning! little-endian ONLY!
|
|
if (video_read((BYTE *)buf_idx, buf_idx_size) != buf_idx_size)
|
|
{
|
|
buf_idx_size = 0;
|
|
num_idx = 0;
|
|
}
|
|
video_lseek(n - buf_idx_size, SEEK_CUR);
|
|
}
|
|
else
|
|
video_lseek(n, SEEK_CUR);
|
|
}
|
|
|
|
if (hdrl_data == NULL)
|
|
{
|
|
msg_error("AVI: no AVI header found.\n");
|
|
return FALSE;
|
|
}
|
|
if (movi_start == 0)
|
|
{
|
|
msg_error("AVI: no movie data found.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
// parse header
|
|
for (i = 0; i < hdrl_len; )
|
|
{
|
|
if (strncasecmp((char *)hdrl_data+i,"LIST", 4) == 0)
|
|
{
|
|
i+= 12;
|
|
continue;
|
|
}
|
|
|
|
n = GET_DWORD(hdrl_data+i+4);
|
|
n = PAD_EVEN(n);
|
|
|
|
if (strncasecmp((char *)hdrl_data + i, "avih", 4) == 0)
|
|
{
|
|
i += 8;
|
|
avi_flags = GET_DWORD(hdrl_data+i+12);
|
|
if (avi_flags & AVI_FLAG_MUSTUSEINDEX)
|
|
{
|
|
msg_error("Avi: Reordered stream cannot be played correctly!\n");
|
|
}
|
|
if (!(avi_flags & AVI_FLAG_ISINTERLEAVED))
|
|
{
|
|
msg_error("Avi: Non-interleaved stream cannot be played correctly!\n");
|
|
}
|
|
/*
|
|
if (!(avi_flags & AVI_FLAG_TRUSTCKTYPE))
|
|
{
|
|
msg("Avi: We cannot trust key-frames? Maybe...\n");
|
|
}
|
|
*/
|
|
}
|
|
else if (strncasecmp((char *)hdrl_data + i, "strh", 4) == 0)
|
|
{
|
|
i += 8;
|
|
if (strncasecmp((char *)hdrl_data + i, "vids", 4) == 0 && !vids_strh_seen)
|
|
{
|
|
scale = GET_DWORD(hdrl_data+i+20);
|
|
rate = GET_DWORD(hdrl_data+i+24);
|
|
if (scale != 0)
|
|
fps = (float)rate / (float)scale;
|
|
video_frames = GET_DWORD(hdrl_data+i+32);
|
|
video_strn = num_stream;
|
|
vids_strh_seen = true;
|
|
lasttag = VIDEO_CHUNK_VIDEO;
|
|
}
|
|
else if (strncasecmp ((char *)hdrl_data+i,"auds",4) == 0 && ! auds_strh_seen)
|
|
{
|
|
//inc audio tracks
|
|
cur_track = num_tracks;
|
|
++num_tracks;
|
|
|
|
if (num_tracks > VIDEO_MAX_AUDIO_TRACKS)
|
|
{
|
|
msg_error("Avi: Only %d audio tracks supported!\n", VIDEO_MAX_AUDIO_TRACKS);
|
|
return FALSE;
|
|
}
|
|
|
|
track[cur_track].flags = GET_DWORD(hdrl_data+i+8);
|
|
track[cur_track].padrate = GET_DWORD(hdrl_data+i+24);
|
|
track[cur_track].audio_samples = GET_DWORD(hdrl_data+i+32);
|
|
track[cur_track].a_vbr = GET_DWORD(hdrl_data+i+44) == 0; // samplesize -- 0?
|
|
track[cur_track].audio_strn = num_stream;
|
|
track[cur_track].a_codech_off = header_offset + i;
|
|
lasttag = VIDEO_CHUNK_AUDIO;
|
|
}
|
|
else if (strncasecmp ((char *)hdrl_data+i, "iavs", 4) == 0 && ! auds_strh_seen)
|
|
{
|
|
msg_error("AVI: DV AVI Type 1 not supported.\n");
|
|
return FALSE;
|
|
}
|
|
else
|
|
lasttag = VIDEO_CHUNK_UNKNOWN;
|
|
num_stream++;
|
|
}
|
|
else if (strncasecmp((char *)hdrl_data+i, "dmlh", 4) == 0)
|
|
{
|
|
total_frames = GET_DWORD(hdrl_data+i+8);
|
|
i += 8;
|
|
}
|
|
else if (strncasecmp((char *)hdrl_data+i,"strf", 4) == 0)
|
|
{
|
|
i += 8;
|
|
if (lasttag == VIDEO_CHUNK_VIDEO)
|
|
{
|
|
RIFF_BITMAPINFOHEADER bih;
|
|
memcpy(&bih, hdrl_data + i, sizeof(RIFF_BITMAPINFOHEADER));
|
|
bih.bi_size = GET_DWORD((BYTE *)&bih.bi_size);
|
|
bitmap_info_header = (RIFF_BITMAPINFOHEADER *)SPmalloc(bih.bi_size);
|
|
if (bitmap_info_header != NULL)
|
|
{
|
|
memcpy(bitmap_info_header, hdrl_data + i, bih.bi_size);
|
|
}
|
|
|
|
width = GET_DWORD(hdrl_data+i+4);
|
|
height = GET_DWORD(hdrl_data+i+8);
|
|
memcpy(fourcc, hdrl_data+i+16, 4); fourcc[4] = 0;
|
|
|
|
vids_strf_seen = true;
|
|
}
|
|
else if (lasttag == VIDEO_CHUNK_AUDIO)
|
|
{
|
|
LONGLONG lpos = video_lseek(0, SEEK_CUR);
|
|
video_lseek(header_offset + i + sizeof(RIFF_WAVEFORMATEX), SEEK_SET);
|
|
|
|
RIFF_WAVEFORMATEX *wfe = audio_read_formatex(fd, hdrl_data + i, hdrl_len - i);
|
|
|
|
video_lseek(lpos, SEEK_SET);
|
|
|
|
if (wfe != NULL)
|
|
{
|
|
track[cur_track].a_codecf_off = header_offset + i;
|
|
track[cur_track].mp3rate = 8 * wfe->n_avg_bytes_per_sec / 1000;
|
|
int sampsize = MAX(((wfe->w_bits_per_sample + 7) / 8)
|
|
* wfe->n_channels, 4);
|
|
track[cur_track].audio_bytes =
|
|
track[cur_track].audio_samples * sampsize;
|
|
}
|
|
|
|
track[cur_track].wfe = wfe;
|
|
}
|
|
}
|
|
else if(strncasecmp((char *)hdrl_data+i, "indx", 4) == 0)
|
|
{
|
|
MSG("AVI: New v2.0 index detected!\n");
|
|
if (!(avi_flags & AVI_FLAG_TRUSTCKTYPE))
|
|
{
|
|
msg_error("AVI: Index may contain invalid keyframes!\n");
|
|
}
|
|
|
|
if (lasttag == VIDEO_CHUNK_VIDEO)
|
|
{
|
|
BYTE *a = hdrl_data + i;
|
|
memcpy (video_superindex.fcc, a, 4); a += 4;
|
|
video_superindex.dwSize = GET_DWORD(a); a += 4;
|
|
video_superindex.wLongsPerEntry = GET_WORD(a); a += 2;
|
|
video_superindex.bIndexSubType = *a; a += 1;
|
|
video_superindex.bIndexType = *a; a += 1;
|
|
video_superindex.nEntriesInUse = GET_DWORD(a); a += 4;
|
|
memcpy (video_superindex.dwChunkId, a, 4); a += 4;
|
|
|
|
// 3 * reserved
|
|
a += 4; a += 4; a += 4;
|
|
|
|
if (video_superindex.bIndexSubType != 0)
|
|
{
|
|
msg("AVI: Invalid ODML superindex header.\n");
|
|
}
|
|
|
|
int superidx_size = video_superindex.wLongsPerEntry
|
|
* video_superindex.nEntriesInUse * sizeof (DWORD);
|
|
video_superindex.idx = superidx_size < 256*1024 ?
|
|
(AviSuperIndexEntry *)SPmalloc (superidx_size) : NULL;
|
|
if (video_superindex.idx != NULL)
|
|
{
|
|
// position of ix## chunks
|
|
for (DWORD j = 0; j < video_superindex.nEntriesInUse; ++j)
|
|
{
|
|
video_superindex.idx[j].qwOffset = GET_ULONGLONG (a); a += 8;
|
|
video_superindex.idx[j].dwSize = GET_DWORD (a); a += 4;
|
|
video_superindex.idx[j].dwDuration = GET_DWORD (a); a += 4;
|
|
}
|
|
has_indx = true;
|
|
}
|
|
}
|
|
i += 8;
|
|
}
|
|
else if ((strncasecmp((char *)hdrl_data+i,"JUNK", 4) == 0) ||
|
|
(strncasecmp((char *)hdrl_data+i,"strn", 4) == 0) ||
|
|
(strncasecmp((char *)hdrl_data+i,"strd", 4) == 0) ||
|
|
(strncasecmp((char *)hdrl_data+i,"vprp", 4) == 0))
|
|
{
|
|
// do not reset lasttag
|
|
i += 8;
|
|
} else
|
|
{
|
|
i += 8;
|
|
lasttag = VIDEO_CHUNK_UNKNOWN;
|
|
}
|
|
i += n;
|
|
}
|
|
|
|
SPfree(hdrl_data);
|
|
|
|
if (!vids_strh_seen || !vids_strf_seen)
|
|
{
|
|
msg_error("AVI: No video found.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
video_tag1[0] = (char)(video_strn/10 + '0');
|
|
video_tag1[1] = (char)(video_strn%10 + '0');
|
|
video_tag1[2] = 'd';
|
|
video_tag1[3] = bitmap_info_header != NULL
|
|
&& bitmap_info_header->bi_compression == 0 ? 'b' : 'c';
|
|
|
|
video_tag2[0] = (char)(video_strn/10 + '0');
|
|
video_tag2[1] = (char)(video_strn%10 + '0');
|
|
video_tag2[2] = 'd';
|
|
video_tag2[3] = video_tag1[3] == 'b' ? 'c' : 'b';
|
|
|
|
// Audio tag is set to "99wb" if no audio present
|
|
if (track[0].wfe == NULL || track[0].wfe->n_channels < 1)
|
|
track[0].audio_strn = 99;
|
|
|
|
first_track = -1;
|
|
int good_first_track = -1;
|
|
for(i = 0, j = 0; j < num_tracks + 1; ++j)
|
|
{
|
|
if (j == video_strn)
|
|
continue;
|
|
if (first_track < 0)
|
|
first_track = i;
|
|
// if not disabled
|
|
if (good_first_track < 0 && (track[i].flags & 1) == 0)
|
|
good_first_track = i;
|
|
track[i].audio_tag[0] = (char)(j/10 + '0');
|
|
track[i].audio_tag[1] = (char)(j%10 + '0');
|
|
track[i].audio_tag[2] = 'w';
|
|
track[i].audio_tag[3] = 'b';
|
|
++i;
|
|
}
|
|
|
|
// set first non-disabled track.
|
|
// we can change them later with video_set_audio_track().
|
|
if (good_first_track >= 0)
|
|
first_track = good_first_track;
|
|
|
|
cur_track = first_track;
|
|
|
|
video_lseek(movi_start, SEEK_SET);
|
|
|
|
// if the file has an idx1, check if this is relative
|
|
// to the start of the file or to the start of the 'movi' list
|
|
idx_offset = -1;
|
|
if (idx_start > 0 && !has_indx)
|
|
{
|
|
int num_buf_idx = buf_idx_size / sizeof(AviOldIndexEntry);
|
|
bool found_av = false;
|
|
for (i = 0; i < num_buf_idx; i++)
|
|
{
|
|
if (strncasecmp((char *)&buf_idx[i].chunkid, (char *)video_tag1, 3) == 0)
|
|
{
|
|
found_av = true;
|
|
break;
|
|
}
|
|
}
|
|
// if no video chunks present in current buffer, search for audio...
|
|
if (!found_av)
|
|
{
|
|
for (i = 0; !found_av && i < num_buf_idx; i++)
|
|
{
|
|
for (j = 0; j < num_tracks; ++j)
|
|
{
|
|
if (strncasecmp((char *)&buf_idx[i].chunkid, track[j].audio_tag, 4) == 0)
|
|
{
|
|
found_av = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found_av)
|
|
{
|
|
LONGLONG pos = buf_idx[i].offset;
|
|
LONGLONG len = buf_idx[i].size;
|
|
if (pos >= 0 && len > 0)
|
|
{
|
|
video_lseek(pos, SEEK_SET);
|
|
if (video_read(data, 8) == 8)
|
|
{
|
|
// index from start of file?
|
|
if (strncasecmp((char *)data, (char *)&buf_idx[i].chunkid, 4) == 0
|
|
&& GET_DWORD(data + 4) == len)
|
|
{
|
|
idx_offset = 0;
|
|
}
|
|
}
|
|
if (idx_offset < 0)
|
|
{
|
|
video_lseek(pos + movi_start - 4,SEEK_SET);
|
|
if (video_read(data, 8) == 8)
|
|
{
|
|
// index from start of 'movi' list?
|
|
if (strncasecmp((char *)data, (char *)&buf_idx[i].chunkid, 4) == 0
|
|
&& GET_DWORD((unsigned char *)data+4) == len)
|
|
{
|
|
idx_offset = movi_start - 4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// broken index...
|
|
if (idx_offset >= 0)
|
|
{
|
|
AddIdx1Block();
|
|
}
|
|
}
|
|
|
|
// Reposition the file
|
|
video_lseek(movi_start, SEEK_SET);
|
|
abs_video_pos = -1;
|
|
video_pos_base = 0;
|
|
video_pos = 0;
|
|
audio_pos = 0;
|
|
cur_key_offs = last_chunk_offs = cur_offs = video_offs = movi_start;
|
|
frame_pos = 0;
|
|
last_frame_pos = -1;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
VIDEO_CHUNK_TYPE VideoAvi::GetNext(BYTE *buf, int buflen, int *pos, int *left, int *len)
|
|
{
|
|
// there was a partial chunk read
|
|
int tmp_read;
|
|
if (chunkleft > 0)
|
|
{
|
|
*len = MIN(chunkleft, buflen);
|
|
|
|
chunkleft -= buflen;
|
|
if (chunkleft < 0)
|
|
chunkleft = 0;
|
|
|
|
if (chunktype != VIDEO_CHUNK_UNKNOWN)
|
|
{
|
|
if ((tmp_read = video_read(buf, PAD_EVEN(*len))) == 0)
|
|
{
|
|
return VIDEO_CHUNK_EOF;
|
|
}
|
|
} else
|
|
video_lseek(PAD_EVEN(*len), SEEK_CUR);
|
|
|
|
if (chunkleft == 0)
|
|
{
|
|
if (chunktype == VIDEO_CHUNK_AUDIO_PARTIAL)
|
|
return VIDEO_CHUNK_AUDIO;
|
|
else if (chunktype == VIDEO_CHUNK_VIDEO_PARTIAL)
|
|
return VIDEO_CHUNK_VIDEO;
|
|
}
|
|
if (chunktype != VIDEO_CHUNK_UNKNOWN)
|
|
return chunktype;
|
|
|
|
//buf += PAD_EVEN(*len);
|
|
//*pos += PAD_EVEN(*len);
|
|
//*left -= PAD_EVEN(*len);
|
|
}
|
|
|
|
*len = 0;
|
|
chunktype = VIDEO_CHUNK_UNKNOWN;
|
|
//chunkleft = 0;
|
|
|
|
int curpos = *pos;
|
|
LONGLONG start_offs;
|
|
|
|
// in recovery mode, use buffered read
|
|
if (look4chunk)
|
|
{
|
|
buflen = video_read(buf, buflen - curpos);
|
|
if (buflen <= 0)
|
|
return VIDEO_CHUNK_EOF;
|
|
start_offs = cur_offs;
|
|
buflen += curpos;
|
|
}
|
|
|
|
|
|
// we're at the start of the chunk and got chunk header
|
|
while (curpos < buflen)
|
|
{
|
|
// if we got a list tag, ignore it
|
|
BYTE hdr_data[8];
|
|
char *hdr = (char *)hdr_data;
|
|
|
|
if (!look4chunk)
|
|
{
|
|
if ((tmp_read = video_read(hdr_data, 8)) == 0)
|
|
{
|
|
return VIDEO_CHUNK_EOF;
|
|
}
|
|
} else
|
|
{
|
|
curpos = (int)(cur_offs - start_offs);
|
|
hdr = (char *)(buf + curpos);
|
|
}
|
|
|
|
cur_offs += 8;
|
|
|
|
int n, reallen;
|
|
if (strncasecmp(hdr, "RIFF", 4) == 0 || strncasecmp(hdr, "LIST", 4) == 0)
|
|
{
|
|
ResetRecoveryMode();
|
|
video_lseek(4, SEEK_CUR);
|
|
|
|
cur_offs += 4;
|
|
continue;
|
|
}
|
|
if (*left < 8)
|
|
{
|
|
chunktype = VIDEO_CHUNK_FRAGMENT;
|
|
break;
|
|
}
|
|
|
|
reallen = n = GET_DWORD((BYTE *)hdr + 4);
|
|
|
|
//buf += 8;
|
|
//*pos += 8;
|
|
//*left -= 8;
|
|
bool partial = false;
|
|
|
|
frame_pos++;
|
|
|
|
if (strncasecmp(hdr, video_tag1, 3) == 0)
|
|
{
|
|
//msg("00dc\n");
|
|
//printf("00dc: [%d] n=%d %d/%d\n", frame_pos, n, *pos, buflen);
|
|
abs_video_pos++;
|
|
video_pos++;
|
|
if (n == 0)
|
|
continue;
|
|
|
|
ResetRecoveryMode();
|
|
|
|
int read_n;
|
|
if (*pos + n > buflen)
|
|
{
|
|
// XXXXXXXXXXXXXXX
|
|
if (no_partial)
|
|
{
|
|
frame_pos--;
|
|
abs_video_pos--;
|
|
video_pos--;
|
|
video_lseek(-8, SEEK_CUR);
|
|
return VIDEO_CHUNK_UNKNOWN;
|
|
}
|
|
|
|
chunkleft = n - buflen + *pos;
|
|
read_n = n = buflen - *pos;
|
|
partial = true;
|
|
|
|
} else
|
|
read_n = PAD_EVEN(n);
|
|
|
|
if ((tmp_read = video_read(buf, read_n)) == 0)
|
|
{
|
|
return VIDEO_CHUNK_EOF;
|
|
}
|
|
|
|
*len = n;
|
|
video_packet_len = reallen;
|
|
chunktype = partial ? VIDEO_CHUNK_VIDEO_PARTIAL : VIDEO_CHUNK_VIDEO;
|
|
last_chunk_offs = video_offs = cur_offs - 8;
|
|
cur_offs += read_n;
|
|
last_chunk_len = reallen;
|
|
break;
|
|
}
|
|
else if (cur_track >= 0 && strncasecmp(hdr, track[cur_track].audio_tag, 4) == 0)
|
|
{
|
|
//msg("01wb\n");
|
|
audio_pos++;
|
|
if (n == 0)
|
|
continue;
|
|
|
|
ResetRecoveryMode();
|
|
|
|
int read_n;
|
|
if (*pos + n > buflen)
|
|
{
|
|
// XXXXXXXXXXXXXXX
|
|
if (no_partial)
|
|
{
|
|
frame_pos--;
|
|
audio_pos--;
|
|
video_lseek(-8, SEEK_CUR);
|
|
return VIDEO_CHUNK_UNKNOWN;
|
|
}
|
|
|
|
chunkleft = n - buflen + *pos;
|
|
read_n = n = buflen - *pos;
|
|
partial = true;
|
|
} else
|
|
read_n = PAD_EVEN(n);
|
|
|
|
|
|
if ((tmp_read = video_read(buf, read_n)) == 0)
|
|
{
|
|
return VIDEO_CHUNK_EOF;
|
|
}
|
|
#if 0
|
|
{
|
|
FILE *fp = fopen("out-avi.mp3", "ab");
|
|
fwrite(buf, n, 1, fp);
|
|
fclose(fp);
|
|
}
|
|
#endif
|
|
*len = n;
|
|
chunktype = partial ? VIDEO_CHUNK_AUDIO_PARTIAL : VIDEO_CHUNK_AUDIO;
|
|
last_chunk_offs = cur_offs - 8;
|
|
cur_offs += read_n;
|
|
last_chunk_len = reallen;
|
|
track[cur_track].audio_pos++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (hdr[0] == 'i' && hdr[1] == 'd' && hdr[2] == 'x' && hdr[3] == '1')
|
|
{
|
|
ResetRecoveryMode();
|
|
|
|
//chunktype = VIDEO_CHUNK_EOF;
|
|
chunktype = VIDEO_CHUNK_UNKNOWN;
|
|
//buf += PAD_EVEN(n);
|
|
//*pos += PAD_EVEN(n);
|
|
//*left -= PAD_EVEN(n);
|
|
|
|
cur_offs += PAD_EVEN(n);
|
|
video_lseek(cur_offs, SEEK_SET);
|
|
|
|
//return chunktype;
|
|
continue;
|
|
}
|
|
else if ((hdr[2] == 'w' && hdr[3] == 'b') ||
|
|
(hdr[0] == 'i' && hdr[1] == 'x') ||
|
|
(hdr[0] == 'J' && hdr[1] == 'U' && hdr[2] == 'N' && hdr[3] == 'K'))
|
|
{
|
|
ResetRecoveryMode();
|
|
|
|
//buf += PAD_EVEN(n);
|
|
//*pos += PAD_EVEN(n);
|
|
//*left -= PAD_EVEN(n);
|
|
chunktype = VIDEO_CHUNK_UNKNOWN;
|
|
|
|
video_lseek(PAD_EVEN(n), SEEK_CUR);
|
|
|
|
cur_offs += PAD_EVEN(n);
|
|
}
|
|
// file is corrupted ?
|
|
else
|
|
{
|
|
cur_offs -= 7;
|
|
|
|
if (!look4chunk)
|
|
{
|
|
LONGLONG file_offs = video_lseek(0, SEEK_CUR) - 8;
|
|
msg("AVI: File corrupted @ " PRINTF_64d ". Trying to recover...\n", file_offs);
|
|
|
|
video_lseek(-7, SEEK_CUR);
|
|
|
|
chunkleft = 0;
|
|
look4chunk = true;
|
|
|
|
script_error_callback(SCRIPT_ERROR_CORRUPTED);
|
|
|
|
return VIDEO_CHUNK_RECOVERY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return chunktype;
|
|
}
|
|
|
|
int VideoAvi::GetNextIndexes()
|
|
{
|
|
if ((idx_offset >= 0 && idx_start > 0) || has_indx)
|
|
{
|
|
media_update_fip();
|
|
|
|
LONGLONG buf_idx_offs;
|
|
if (has_indx)
|
|
{
|
|
cur_buf_idx_size = video_superindex.idx[cur_indx_buf_idx].dwSize;
|
|
if (cur_indx_buf_pos >= cur_buf_idx_size)
|
|
{
|
|
cur_indx_buf_idx++;
|
|
cur_indx_buf_pos = 0;
|
|
}
|
|
if ((DWORD)cur_indx_buf_idx >= video_superindex.nEntriesInUse)
|
|
return -1;
|
|
|
|
cur_buf_idx_size -= cur_indx_buf_pos;
|
|
buf_idx_offs = video_superindex.idx[cur_indx_buf_idx].qwOffset + cur_indx_buf_pos;
|
|
} else
|
|
{
|
|
cur_buf_idx_size = (num_idx - cur_old_buf_idx) * sizeof(AviOldIndexEntry);
|
|
buf_idx_offs = idx_start + cur_old_buf_idx * sizeof(AviOldIndexEntry);
|
|
}
|
|
|
|
buf_idx_size = MIN(max_buf_idx_size[has_indx ? 1 : 0], cur_buf_idx_size);
|
|
|
|
if (buf_idx_size < 1)
|
|
return -1;
|
|
if (video_lseek(buf_idx_offs, SEEK_SET) < 0)
|
|
return -1;
|
|
buf_idx_size = video_read((BYTE *)buf_idx, buf_idx_size);
|
|
//msg("^^^^^^^ read = %d\n", buf_idx_size);
|
|
return has_indx ? AddIdx2Block() : AddIdx1Block();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int VideoAvi::GetNextKeyFrame()
|
|
{
|
|
int n = 0;
|
|
LONGLONG curpos = 0;
|
|
const int extra_bytes = 5;
|
|
BYTE buf[8 + extra_bytes];
|
|
|
|
//*len = 0;
|
|
if (buf_idx == NULL)
|
|
return -1;
|
|
|
|
// we're at the start of the chunk and got chunk header
|
|
for (int i = 0; ; i++)
|
|
{
|
|
// split our task...
|
|
if (i > 30)
|
|
{
|
|
media_update_fip();
|
|
//if (info_cnt++ > 1)
|
|
{
|
|
// info_cnt = 0;
|
|
video_update_info();
|
|
}
|
|
last_chunk_offs = curpos;
|
|
last_chunk_len = n;
|
|
return 0;
|
|
}
|
|
|
|
curpos = video_lseek(0, SEEK_CUR);
|
|
int extra = read(fd, buf, 8 + extra_bytes) - 8;
|
|
|
|
if (extra < 0)
|
|
break;
|
|
if (strncasecmp((char *)buf, "LIST", 4) == 0)
|
|
{
|
|
video_lseek(4 - extra, SEEK_CUR);
|
|
continue;
|
|
}
|
|
n = GET_DWORD(buf + 4);
|
|
frame_pos++;
|
|
if (strncasecmp((char *)buf, video_tag1, 3) == 0)
|
|
{
|
|
abs_video_pos++;
|
|
video_pos++;
|
|
scr = INT64(90000) * abs_video_pos * scale / rate;
|
|
if (video_fmt == RIFF_VIDEO_MPEG4)
|
|
{
|
|
// VOP start code
|
|
if (n > 4 && buf[8] == 0 && buf[9] == 0 && buf[10] == 1 && buf[11] == 0xb6)
|
|
{
|
|
if ((buf[12] & 0xc0) == 0) // I-frame
|
|
{
|
|
goto found;
|
|
}
|
|
}
|
|
// or we have to search for it...
|
|
else
|
|
{
|
|
int nleft = n - extra;
|
|
while (nleft > 0)
|
|
{
|
|
int nb = MIN(nleft, max_buf_idx_size[0]);
|
|
nb = video_read((BYTE *)buf_idx, nb);
|
|
if (nb == 0)
|
|
break;
|
|
BYTE *b = (BYTE *)buf_idx;
|
|
// find VOP start
|
|
int vlen = nb;
|
|
for (; vlen >= 0 && (b[0] != 0 || b[1] != 0
|
|
|| b[2] != 1 || b[3] != 0xb6); vlen--)
|
|
{
|
|
b++;
|
|
}
|
|
if (vlen > 0 && (b[4] & 0xc0) == 0) // I-frame
|
|
{
|
|
goto found;
|
|
}
|
|
extra += nb;
|
|
nleft -= nb;
|
|
}
|
|
}
|
|
}
|
|
else if (video_fmt == RIFF_VIDEO_DIV3)
|
|
{
|
|
// VOP start code
|
|
if (n > 2 && (buf[8] & 0xc0) == 0) // I-frame
|
|
{
|
|
goto found;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
else if (cur_track >= 0 && strncasecmp((char *)buf, track[cur_track].audio_tag, 4) == 0)
|
|
{
|
|
audio_pos++;
|
|
}
|
|
*/
|
|
video_lseek(PAD_EVEN(n) - extra, SEEK_CUR);
|
|
}
|
|
|
|
MSG("AVI: I-FRAME seek EOF!\n");
|
|
return -1;
|
|
|
|
found:
|
|
last_chunk_offs = curpos;
|
|
last_chunk_len = n;
|
|
|
|
cur_key_offs = video_offs = curpos;
|
|
video_lseek(video_offs, SEEK_SET);
|
|
MSG("AVI: I-FRAME at %d/%d (%d)\n", abs_video_pos, video_frames, curpos);
|
|
video_packet_len = n;
|
|
AddIndex(abs_video_pos, video_packet_len, video_offs);
|
|
return 1;
|
|
}
|
|
|
|
int VideoAvi::AddIdx1Block()
|
|
{
|
|
if (buf_idx == NULL || idx_offset < 0)
|
|
return -1;
|
|
|
|
AviOldIndexEntry *b = buf_idx;
|
|
DWORD chunkid1 = GET_DWORD((BYTE *)video_tag1);
|
|
DWORD chunkid2 = GET_DWORD((BYTE *)video_tag2);
|
|
for (int i = buf_idx_size / sizeof(AviOldIndexEntry); i > 0; i--, b++)
|
|
{
|
|
if (b->chunkid == chunkid1 || b->chunkid == chunkid2)
|
|
{
|
|
if (b->flags & AVI_IDX1_FLAG_KEYFRAME)
|
|
{
|
|
if (cur_buf_video_idx > last_key_pos + min_delta_keyframes)
|
|
{
|
|
if (AddIndex(cur_buf_video_idx, b->size, b->offset + idx_offset) < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
cur_buf_video_idx++;
|
|
}
|
|
}
|
|
cur_old_buf_idx += buf_idx_size / sizeof(AviOldIndexEntry);
|
|
return 0;
|
|
}
|
|
|
|
int VideoAvi::AddIdx2Block()
|
|
{
|
|
if (buf_idx == NULL)
|
|
return -1;
|
|
|
|
AviNewIndexEntry *b;
|
|
int bis = buf_idx_size;
|
|
if (cur_indx_buf_pos == 0) // read header
|
|
{
|
|
AviNewIndex *ni = (AviNewIndex *)buf_newidx;
|
|
cur_indx_base_offset = ni->qwBaseOffset - 8;
|
|
|
|
b = (AviNewIndexEntry *)(ni + 1);
|
|
bis -= sizeof(AviNewIndex);
|
|
}
|
|
else
|
|
b = buf_newidx;
|
|
for (int i = bis / sizeof(AviNewIndexEntry); i > 0; i--, b++)
|
|
{
|
|
if (!(b->size & AVI_IDX2_FLAG_KEYFRAME))
|
|
{
|
|
if (cur_buf_video_idx > last_key_pos + min_delta_keyframes)
|
|
{
|
|
if (AddIndex(cur_buf_video_idx, b->size, cur_indx_base_offset + b->offset) < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
cur_buf_video_idx++;
|
|
}
|
|
|
|
cur_indx_buf_pos += buf_idx_size;
|
|
return 0;
|
|
}
|
|
|
|
void VideoAvi::ResetRecoveryMode()
|
|
{
|
|
if (look4chunk)
|
|
{
|
|
video_lseek(cur_offs, SEEK_SET);
|
|
look4chunk = false;
|
|
script_update_variable(SCRIPT_VAR_PLAYER_SPEED);
|
|
}
|
|
}
|
|
|
|
#endif
|