mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-27 07:23:11 +02:00
libtriple: implement mpeg/vdr playback
This commit is contained in:
@@ -21,7 +21,7 @@
|
|||||||
#define DBG(args...)
|
#define DBG(args...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int mp_syncPES(uint8_t *, int);
|
static int mp_syncPES(uint8_t *, int, bool quiet = false);
|
||||||
static int sync_ts(uint8_t *, int);
|
static int sync_ts(uint8_t *, int);
|
||||||
static inline uint16_t get_pid(uint8_t *buf);
|
static inline uint16_t get_pid(uint8_t *buf);
|
||||||
static void *start_playthread(void *c);
|
static void *start_playthread(void *c);
|
||||||
@@ -37,6 +37,13 @@ extern cDemux *audioDemux;
|
|||||||
extern cVideo *videoDecoder;
|
extern cVideo *videoDecoder;
|
||||||
extern cAudio *audioDecoder;
|
extern cAudio *audioDecoder;
|
||||||
|
|
||||||
|
static const char *FILETYPE[] = {
|
||||||
|
"FILETYPE_UNKNOWN",
|
||||||
|
"FILETYPE_TS",
|
||||||
|
"FILETYPE_MPG",
|
||||||
|
"FILETYPE_VDR"
|
||||||
|
};
|
||||||
|
|
||||||
cPlayback::cPlayback(int)
|
cPlayback::cPlayback(int)
|
||||||
{
|
{
|
||||||
INFO("\n");
|
INFO("\n");
|
||||||
@@ -135,11 +142,43 @@ bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned sho
|
|||||||
filelist_t file;
|
filelist_t file;
|
||||||
file.Name = std::string(filename);
|
file.Name = std::string(filename);
|
||||||
file.Size = s.st_size;
|
file.Size = s.st_size;
|
||||||
|
if (file.Name.rfind(".ts") == file.Name.length() - 3 ||
|
||||||
|
file.Name.rfind(".TS") == file.Name.length() - 3)
|
||||||
|
filetype = FILETYPE_TS;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (file.Name.rfind(".vdr") == file.Name.length() - 4)
|
||||||
|
{
|
||||||
|
filetype = FILETYPE_VDR;
|
||||||
|
std::string::size_type p = file.Name.rfind("info.vdr");
|
||||||
|
if (p == std::string::npos)
|
||||||
|
p = file.Name.rfind("index.vdr");
|
||||||
|
if (p != std::string::npos)
|
||||||
|
{
|
||||||
|
file.Name.replace(p, std::string::npos, "001.vdr");
|
||||||
|
INFO("replaced filename with '%s'\n", file.Name.c_str());
|
||||||
|
if (stat(file.Name.c_str(), &s))
|
||||||
|
{
|
||||||
|
INFO("filename does not exist? (%m)\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
file.Size = s.st_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
filetype = FILETYPE_MPG;
|
||||||
|
vpid = 0x40;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO("detected (ok, guessed) filetype: %s\n", FILETYPE[filetype]);
|
||||||
|
|
||||||
filelist.push_back(file);
|
filelist.push_back(file);
|
||||||
filelist_auto_add();
|
filelist_auto_add();
|
||||||
if (mf_open(0) < 0)
|
if (mf_open(0) < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
pts_start = pts_end = pts_curr = -1;
|
||||||
|
pesbuf_pos = 0;
|
||||||
curr_pos = 0;
|
curr_pos = 0;
|
||||||
inbuf_pos = 0;
|
inbuf_pos = 0;
|
||||||
inbuf_sync = 0;
|
inbuf_sync = 0;
|
||||||
@@ -149,13 +188,21 @@ bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned sho
|
|||||||
{
|
{
|
||||||
if (mp_seekSync(r - INBUF_SIZE) < 0)
|
if (mp_seekSync(r - INBUF_SIZE) < 0)
|
||||||
return false;
|
return false;
|
||||||
inbuf_read(); /* assume that we fill the buffer with one read() */
|
while(true) {
|
||||||
for (r = (inbuf_pos / 188) * 188; r > 0; r -= 188)
|
if (inbuf_read() <= 0)
|
||||||
{
|
break; // EOF
|
||||||
pts_end = get_pts(inbuf + r, false);
|
if (curr_pos >= r) //just to make sure...
|
||||||
if (pts_end > -1)
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (filetype == FILETYPE_TS)
|
||||||
|
for (r = (inbuf_pos / 188) * 188; r > 0; r -= 188)
|
||||||
|
{
|
||||||
|
pts_end = get_pts(inbuf + r, false);
|
||||||
|
if (pts_end > -1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pts_end = pts_curr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pts_end = -1; /* unknown */
|
pts_end = -1; /* unknown */
|
||||||
@@ -449,7 +496,7 @@ bool cPlayback::SetPosition(int position, bool absolute)
|
|||||||
}
|
}
|
||||||
|
|
||||||
oldspeed = playback_speed;
|
oldspeed = playback_speed;
|
||||||
if (oldspeed != 0)
|
// if (oldspeed != 0)
|
||||||
SetSpeed(0); /* request pause */
|
SetSpeed(0); /* request pause */
|
||||||
|
|
||||||
while (playstate == STATE_PLAY) /* playthread did not acknowledge pause */
|
while (playstate == STATE_PLAY) /* playthread did not acknowledge pause */
|
||||||
@@ -515,7 +562,10 @@ off_t cPlayback::seek_to_pts(int64_t pts)
|
|||||||
return newpos;
|
return newpos;
|
||||||
inbuf_pos = 0;
|
inbuf_pos = 0;
|
||||||
inbuf_sync = 0;
|
inbuf_sync = 0;
|
||||||
inbuf_read(); /* also updates current pts */
|
while (inbuf_pos < INBUF_SIZE * 8 / 10) {
|
||||||
|
if (inbuf_read() <= 0)
|
||||||
|
break; // EOF
|
||||||
|
}
|
||||||
if (pts_curr < pts_start)
|
if (pts_curr < pts_start)
|
||||||
tmppts = pts_curr + 0x200000000ULL - pts_start;
|
tmppts = pts_curr + 0x200000000ULL - pts_start;
|
||||||
else
|
else
|
||||||
@@ -929,9 +979,17 @@ ssize_t cPlayback::read_ts()
|
|||||||
ssize_t cPlayback::read_mpeg()
|
ssize_t cPlayback::read_mpeg()
|
||||||
{
|
{
|
||||||
ssize_t toread, ret, sync;
|
ssize_t toread, ret, sync;
|
||||||
toread = PESBUF_SIZE - pesbuf_pos;
|
//toread = PESBUF_SIZE - pesbuf_pos;
|
||||||
|
/* experiments found, that 80kB is the best buffer size, otherwise a/v sync seems
|
||||||
|
to suffer and / or audio stutters */
|
||||||
|
toread = 80 * 1024 - pesbuf_pos;
|
||||||
bool retry = true;
|
bool retry = true;
|
||||||
|
|
||||||
|
if (INBUF_SIZE - inbuf_pos < toread)
|
||||||
|
{
|
||||||
|
INFO("adjusting toread to %d due to inbuf full (old: %ld)\n", INBUF_SIZE - inbuf_pos, toread);
|
||||||
|
toread = INBUF_SIZE - inbuf_pos;
|
||||||
|
}
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
ret = read(in_fd, pesbuf + pesbuf_pos, toread);
|
ret = read(in_fd, pesbuf + pesbuf_pos, toread);
|
||||||
@@ -945,7 +1003,7 @@ ssize_t cPlayback::read_mpeg()
|
|||||||
}
|
}
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
INFO("failed: %m\n");
|
INFO("failed: %m, pesbuf_pos: %ld, toread: %ld\n", pesbuf_pos, toread);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
pesbuf_pos += ret;
|
pesbuf_pos += ret;
|
||||||
@@ -954,17 +1012,22 @@ ssize_t cPlayback::read_mpeg()
|
|||||||
int i;
|
int i;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
uint16_t pid = 0;
|
uint16_t pid = 0;
|
||||||
|
bool resync = true;
|
||||||
while (count < pesbuf_pos - 10)
|
while (count < pesbuf_pos - 10)
|
||||||
{
|
{
|
||||||
sync = mp_syncPES(pesbuf + count, pesbuf_pos - count - 10);
|
if (resync)
|
||||||
if (sync < 0)
|
|
||||||
{
|
{
|
||||||
INFO("cannot sync\n");
|
sync = mp_syncPES(pesbuf + count, pesbuf_pos - count - 10);
|
||||||
break;
|
if (sync < 0)
|
||||||
|
{
|
||||||
|
if (pesbuf_pos - count - 10 > 4)
|
||||||
|
INFO("cannot sync (count = %d, pesbuf_pos = %ld)\n", count, pesbuf_pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (sync)
|
||||||
|
INFO("needed sync %ld\n", sync);
|
||||||
|
count += sync;
|
||||||
}
|
}
|
||||||
if (sync)
|
|
||||||
INFO("needed sync\n");
|
|
||||||
count += sync;
|
|
||||||
uint8_t *ppes = pesbuf + count;
|
uint8_t *ppes = pesbuf + count;
|
||||||
int av = 0; // 1 = video, 2 = audio
|
int av = 0; // 1 = video, 2 = audio
|
||||||
int64_t pts;
|
int64_t pts;
|
||||||
@@ -979,6 +1042,7 @@ ssize_t cPlayback::read_mpeg()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
count += 14;
|
count += 14;
|
||||||
|
resync = true;
|
||||||
continue;
|
continue;
|
||||||
break;
|
break;
|
||||||
case 0xbd: // AC3
|
case 0xbd: // AC3
|
||||||
@@ -1066,6 +1130,7 @@ ssize_t cPlayback::read_mpeg()
|
|||||||
//if (! resync)
|
//if (! resync)
|
||||||
// DBG("Unknown stream id: 0x%X.\n", ppes[3]);
|
// DBG("Unknown stream id: 0x%X.\n", ppes[3]);
|
||||||
count++;
|
count++;
|
||||||
|
resync = true;
|
||||||
continue;
|
continue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1073,7 +1138,19 @@ ssize_t cPlayback::read_mpeg()
|
|||||||
int pesPacketLen = ((ppes[4] << 8) | ppes[5]) + 6;
|
int pesPacketLen = ((ppes[4] << 8) | ppes[5]) + 6;
|
||||||
if (count + pesPacketLen >= pesbuf_pos)
|
if (count + pesPacketLen >= pesbuf_pos)
|
||||||
{
|
{
|
||||||
INFO("buffer len: %d, pesPacketLen: %d :-(\n", pesbuf_pos - count, pesPacketLen);
|
DBG("buffer len: %ld, pesPacketLen: %d :-(\n", pesbuf_pos - count, pesPacketLen);
|
||||||
|
if (count != 0)
|
||||||
|
{
|
||||||
|
memmove(pesbuf, ppes, pesbuf_pos - count);
|
||||||
|
pesbuf_pos -= count;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tsPacksCount = pesPacketLen / 184;
|
||||||
|
if ((tsPacksCount + 1) * 188 > INBUF_SIZE - inbuf_pos)
|
||||||
|
{
|
||||||
|
INFO("not enough size in inbuf (needed %d, got %d)\n", (tsPacksCount + 1) * 188, INBUF_SIZE - inbuf_pos);
|
||||||
memmove(pesbuf, ppes, pesbuf_pos - count);
|
memmove(pesbuf, ppes, pesbuf_pos - count);
|
||||||
pesbuf_pos -= count;
|
pesbuf_pos -= count;
|
||||||
break;
|
break;
|
||||||
@@ -1081,7 +1158,6 @@ ssize_t cPlayback::read_mpeg()
|
|||||||
|
|
||||||
if (av)
|
if (av)
|
||||||
{
|
{
|
||||||
int tsPacksCount = pesPacketLen / 184;
|
|
||||||
int rest = pesPacketLen % 184;
|
int rest = pesPacketLen % 184;
|
||||||
|
|
||||||
// divide PES packet into small TS packets
|
// divide PES packet into small TS packets
|
||||||
@@ -1134,12 +1210,65 @@ off_t cPlayback::mp_seekSync(off_t pos)
|
|||||||
{
|
{
|
||||||
off_t npos = pos;
|
off_t npos = pos;
|
||||||
off_t ret;
|
off_t ret;
|
||||||
uint8_t pkt[188];
|
uint8_t pkt[1024];
|
||||||
|
|
||||||
ret = mf_lseek(npos);
|
ret = mf_lseek(npos);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
INFO("lseek ret < 0 (%m)\n");
|
INFO("lseek ret < 0 (%m)\n");
|
||||||
|
|
||||||
|
if (filetype != FILETYPE_TS)
|
||||||
|
{
|
||||||
|
int offset = 0;
|
||||||
|
int s;
|
||||||
|
ssize_t r;
|
||||||
|
bool retry = false;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
r = read(in_fd, &pkt[offset], 1024 - offset);
|
||||||
|
if (r < 0)
|
||||||
|
{
|
||||||
|
INFO("read failed: %m\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (r == 0) // EOF?
|
||||||
|
{
|
||||||
|
if (retry)
|
||||||
|
break;
|
||||||
|
if (mf_lseek(npos) < 0) /* next file in list? */
|
||||||
|
{
|
||||||
|
INFO("lseek ret < 0 (%m)\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
retry = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
s = mp_syncPES(pkt, r + offset, true);
|
||||||
|
if (s < 0)
|
||||||
|
{
|
||||||
|
/* if the last 3 bytes of the buffer were 00 00 01, then
|
||||||
|
mp_sync_PES would not find it. So keep them and check
|
||||||
|
again in the next iteration */
|
||||||
|
memmove(pkt, &pkt[r + offset - 3], 3);
|
||||||
|
npos += r;
|
||||||
|
offset = 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
npos += s;
|
||||||
|
INFO("sync after %lld\n", npos - pos);
|
||||||
|
ret = mf_lseek(npos);
|
||||||
|
if (ret < 0)
|
||||||
|
INFO("lseek ret < 0 (%m)\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (npos > (pos + 0x20000)) /* 128k enough? */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
INFO("could not sync to PES offset: %d r: %zd\n", offset, r);
|
||||||
|
return mf_lseek(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: use bigger buffer here, too and handle EOF / next splitfile */
|
||||||
while (read(in_fd, pkt, 1) > 0)
|
while (read(in_fd, pkt, 1) > 0)
|
||||||
{
|
{
|
||||||
//-- check every byte until sync word reached --
|
//-- check every byte until sync word reached --
|
||||||
@@ -1241,10 +1370,10 @@ int64_t cPlayback::get_pts(uint8_t *p, bool pes)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* returns: 0 == was already synchronous, > 0 == is now synchronous, -1 == could not sync */
|
/* returns: 0 == was already synchronous, > 0 == is now synchronous, -1 == could not sync */
|
||||||
static int mp_syncPES(uint8_t *buf, int len)
|
static int mp_syncPES(uint8_t *buf, int len, bool quiet)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
while (ret < len - 3)
|
while (ret < len - 4)
|
||||||
{
|
{
|
||||||
if (buf[ret + 2] != 0x01)
|
if (buf[ret + 2] != 0x01)
|
||||||
{
|
{
|
||||||
@@ -1261,10 +1390,19 @@ static int mp_syncPES(uint8_t *buf, int len)
|
|||||||
ret += 3;
|
ret += 3;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
/* all stream IDs are > 0x80 */
|
||||||
|
if ((buf[ret + 3] & 0x80) != 0x80)
|
||||||
|
{
|
||||||
|
/* we already checked for 00 00 01, if the stream ID
|
||||||
|
is not valid, we can skip those 3 bytes */
|
||||||
|
ret += 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
INFO("No valid PES signature found. %d Bytes deleted.\n", ret);
|
if (!quiet && len > 5) /* only warn if enough space was available... */
|
||||||
|
INFO("No valid PES signature found. %d Bytes deleted.\n", ret);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user