|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* This file is part of libbluray
|
|
Packit |
5e46da |
* Copyright (C) 2013 Petri Hintukainen <phintuka@users.sourceforge.net>
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
* This library is free software; you can redistribute it and/or
|
|
Packit |
5e46da |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
5e46da |
* License as published by the Free Software Foundation; either
|
|
Packit |
5e46da |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
5e46da |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
5e46da |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
5e46da |
* Lesser General Public License for more details.
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
5e46da |
* License along with this library. If not, see
|
|
Packit |
5e46da |
* <http://www.gnu.org/licenses/>.
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#if HAVE_CONFIG_H
|
|
Packit |
5e46da |
#include "config.h"
|
|
Packit |
5e46da |
#endif
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "m2ts_filter.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "hdmv_pids.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "util/logging.h"
|
|
Packit |
5e46da |
#include "util/macro.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include <inttypes.h>
|
|
Packit |
5e46da |
#include <stdlib.h>
|
|
Packit |
5e46da |
#include <string.h>
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#ifdef _WIN32
|
|
Packit |
5e46da |
#include <stdio.h>
|
|
Packit |
5e46da |
#endif
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#define M2TS_TRACE(...) BD_DEBUG(DBG_STREAM,__VA_ARGS__)
|
|
Packit |
5e46da |
//#define M2TS_TRACE(...) do {} while(0)
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
struct m2ts_filter_s
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
uint16_t *wipe_pid;
|
|
Packit |
5e46da |
uint16_t *pass_pid;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
int64_t in_pts;
|
|
Packit |
5e46da |
int64_t out_pts;
|
|
Packit |
5e46da |
uint32_t pat_packets; /* how many packets to search for PAT (seeked pat_packets packets before the actual seek point) */
|
|
Packit |
5e46da |
uint8_t pat_seen;
|
|
Packit |
5e46da |
};
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
M2TS_FILTER *m2ts_filter_init(int64_t in_pts, int64_t out_pts,
|
|
Packit |
5e46da |
unsigned num_video, unsigned num_audio,
|
|
Packit |
5e46da |
unsigned num_ig, unsigned num_pg)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
M2TS_FILTER *p = calloc(1, sizeof(*p));
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (p) {
|
|
Packit |
5e46da |
unsigned ii, npid;
|
|
Packit |
5e46da |
uint16_t *pid;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
p->in_pts = in_pts;
|
|
Packit |
5e46da |
p->out_pts = out_pts;
|
|
Packit |
5e46da |
p->wipe_pid = calloc(num_audio + num_video + num_ig + num_pg + 1, sizeof(uint16_t));
|
|
Packit |
5e46da |
p->pass_pid = calloc(num_audio + num_video + num_ig + num_pg + 1, sizeof(uint16_t));
|
|
Packit |
5e46da |
if (!p->pass_pid || !p->wipe_pid) {
|
|
Packit |
5e46da |
m2ts_filter_close(&p);
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pid = (in_pts >= 0) ? p->wipe_pid : p->pass_pid;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0, npid = 0; ii < num_video; ii++) {
|
|
Packit |
5e46da |
pid[npid++] = HDMV_PID_VIDEO + ii;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
for (ii = 0; ii < num_audio; ii++) {
|
|
Packit |
5e46da |
pid[npid++] = HDMV_PID_AUDIO_FIRST + ii;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
for (ii = 0; ii < num_ig; ii++) {
|
|
Packit |
5e46da |
pid[npid++] = HDMV_PID_IG_FIRST + ii;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
for (ii = 0; ii < num_pg; ii++) {
|
|
Packit |
5e46da |
pid[npid++] = HDMV_PID_PG_FIRST + ii;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return p;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void m2ts_filter_close(M2TS_FILTER **p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (p && *p) {
|
|
Packit |
5e46da |
X_FREE((*p)->wipe_pid);
|
|
Packit |
5e46da |
X_FREE((*p)->pass_pid);
|
|
Packit |
5e46da |
X_FREE(*p);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#define DUMPLIST(msg,list) \
|
|
Packit |
5e46da |
{ \
|
|
Packit |
5e46da |
unsigned ii = 0; \
|
|
Packit |
5e46da |
fprintf(stderr, "list " msg " : "); \
|
|
Packit |
5e46da |
for (ii = 0; list[ii]; ii++) { \
|
|
Packit |
5e46da |
fprintf(stderr, " 0x%04x", list[ii]); \
|
|
Packit |
5e46da |
} \
|
|
Packit |
5e46da |
fprintf(stderr, "\n"); \
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _pid_in_list(uint16_t *list, uint16_t pid)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
for (; *list && *list <= pid; list++) {
|
|
Packit |
5e46da |
if (*list == pid) {
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void _remove_pid(uint16_t *list, uint16_t pid)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
for (; *list && *list != pid; list++) ;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (; *list; list++) {
|
|
Packit |
5e46da |
list[0] = list[1];
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void _add_pid(uint16_t *list, uint16_t pid)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
for (; *list && *list < pid; list++) ;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (; *list; list++) {
|
|
Packit |
5e46da |
uint16_t tmp = *list;
|
|
Packit |
5e46da |
*list = pid;
|
|
Packit |
5e46da |
pid = tmp;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
*list = pid;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int64_t _parse_timestamp(const uint8_t *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int64_t ts;
|
|
Packit |
5e46da |
ts = ((int64_t)(p[0] & 0x0E)) << 29;
|
|
Packit |
5e46da |
ts |= p[1] << 22;
|
|
Packit |
5e46da |
ts |= (p[2] & 0xFE) << 14;
|
|
Packit |
5e46da |
ts |= p[3] << 7;
|
|
Packit |
5e46da |
ts |= (p[4] & 0xFE) >> 1;
|
|
Packit |
5e46da |
return ts;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int64_t _es_timestamp(const uint8_t *buf, unsigned len)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (buf[0] || buf[1] || buf[2] != 1) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE, "invalid BDAV TS\n");
|
|
Packit |
5e46da |
return -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (len < 9) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE, "invalid BDAV TS (no payload ?)\n");
|
|
Packit |
5e46da |
return -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* Parse PES header */
|
|
Packit |
5e46da |
unsigned pes_pid = buf[3];
|
|
Packit |
5e46da |
if (pes_pid != 0xbf) {
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
unsigned pts_exists = buf[7] & 0x80;
|
|
Packit |
5e46da |
if (pts_exists) {
|
|
Packit |
5e46da |
int64_t pts = _parse_timestamp(buf + 9);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return pts;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void m2ts_filter_seek(M2TS_FILTER *p, uint32_t pat_packets, int64_t in_pts)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
M2TS_TRACE("seek notify\n");
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* move all pids to wipe list */
|
|
Packit |
5e46da |
uint16_t *pid = p->pass_pid;
|
|
Packit |
5e46da |
while (*pid) {
|
|
Packit |
5e46da |
_add_pid(p->wipe_pid, *pid);
|
|
Packit |
5e46da |
*pid = 0;
|
|
Packit |
5e46da |
pid++;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
p->in_pts = in_pts;
|
|
Packit |
5e46da |
p->pat_seen = 0;
|
|
Packit |
5e46da |
p->pat_packets = pat_packets;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _filter_es_pts(M2TS_FILTER *p, const uint8_t *buf, uint16_t pid)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
unsigned tp_error = buf[4+1] & 0x80;
|
|
Packit |
5e46da |
unsigned payload_exists = buf[4+3] & 0x10;
|
|
Packit |
5e46da |
int payload_offset = (buf[4+3] & 0x20) ? buf[4+4] + 5 : 4;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (buf[4] != 0x47) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "missing sync byte. scrambled data ? Filtering aborted.\n");
|
|
Packit |
5e46da |
return -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (tp_error || !payload_exists || payload_offset >= 188) {
|
|
Packit |
5e46da |
M2TS_TRACE("skipping packet (no payload)\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (_pid_in_list(p->wipe_pid, pid)) {
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
int64_t pts = _es_timestamp(buf + 4 + payload_offset, 188 - payload_offset);
|
|
Packit |
5e46da |
if (pts >= p->in_pts && (p->out_pts < 0 || pts <= p->out_pts)) {
|
|
Packit |
5e46da |
M2TS_TRACE("Pid 0x%04x pts %"PRId64" passed IN timestamp %"PRId64" (pts %"PRId64")\n",
|
|
Packit |
5e46da |
pid, pts, p->in_pts, pts);
|
|
Packit |
5e46da |
_remove_pid(p->wipe_pid, pid);
|
|
Packit |
5e46da |
_add_pid(p->pass_pid, pid);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
M2TS_TRACE("Pid 0x%04x pts %"PRId64" outside of clip (%"PRId64"-%"PRId64" -> keep wiping out\n",
|
|
Packit |
5e46da |
pid, pts, p->in_pts, p->out_pts);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (p->out_pts >= 0) {
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* Note: we can't compare against in_pts here (after passing it once):
|
|
Packit |
5e46da |
* PG / IG streams can have timestamps before in_time (except for composition segments), and those are valid.
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
if (_pid_in_list(p->pass_pid, pid)) {
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
int64_t pts = _es_timestamp(buf + 4 + payload_offset, 188 - payload_offset);
|
|
Packit |
5e46da |
if (pts >= p->out_pts) {
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* audio/video streams are cutted after out_time (unit with pts==out_time is included in the clip).
|
|
Packit |
5e46da |
* PG/IG streams are cutted before out_time (unit with pts==out_time is dropped out).
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
if (pts > p->out_pts ||
|
|
Packit |
5e46da |
IS_HDMV_PID_PG(pid) ||
|
|
Packit |
5e46da |
IS_HDMV_PID_IG(pid)) {
|
|
Packit |
5e46da |
M2TS_TRACE("Pid 0x%04x passed OUT timestamp %"PRId64" (pts %"PRId64") -> start wiping\n", pid, p->out_pts, pts);
|
|
Packit |
5e46da |
_remove_pid(p->pass_pid, pid);
|
|
Packit |
5e46da |
_add_pid(p->wipe_pid, pid);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void _wipe_packet(uint8_t *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
/* set pid to 0x1fff (padding) */
|
|
Packit |
5e46da |
p[4 + 2] = 0xff;
|
|
Packit |
5e46da |
p[4 + 1] |= 0x1f;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
int m2ts_filter(M2TS_FILTER *p, uint8_t *buf)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
uint8_t *end = buf + 6144;
|
|
Packit |
5e46da |
int result = 0;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (; buf < end; buf += 192) {
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
uint16_t pid = ((buf[4+1] & 0x1f) << 8) | buf[4+2];
|
|
Packit |
5e46da |
if (pid == HDMV_PID_PAT) {
|
|
Packit |
5e46da |
p->pat_seen = 1;
|
|
Packit |
5e46da |
p->pat_packets = 0;
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (p->pat_packets) {
|
|
Packit |
5e46da |
p->pat_packets--;
|
|
Packit |
5e46da |
if (!p->pat_seen) {
|
|
Packit |
5e46da |
M2TS_TRACE("Wiping pid 0x%04x (inside seek buffer, no PAT)\n", pid);
|
|
Packit |
5e46da |
_wipe_packet(buf);
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
M2TS_TRACE("NOT Wiping pid 0x%04x (inside seek buffer, PAT seen)\n", pid);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (pid < HDMV_PID_VIDEO) {
|
|
Packit |
5e46da |
/* pass PMT, PCR, SIT */
|
|
Packit |
5e46da |
/*M2TS_TRACE("NOT Wiping pid 0x%04x (< 0x1011)\n", pid);*/
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
#if 0
|
|
Packit |
5e46da |
/* no PAT yet ? */
|
|
Packit |
5e46da |
if (!p->pat_seen) {
|
|
Packit |
5e46da |
/* Wipe packet (pid -> padding stream) */
|
|
Packit |
5e46da |
M2TS_TRACE("Wiping pid 0x%04x before PAT\n", pid);
|
|
Packit |
5e46da |
_wipe_packet(buf);
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
#endif
|
|
Packit |
5e46da |
/* payload start indicator ? check ES timestamp */
|
|
Packit |
5e46da |
unsigned pusi = buf[4+1] & 0x40;
|
|
Packit |
5e46da |
if (pusi) {
|
|
Packit |
5e46da |
if (_filter_es_pts(p, buf, pid) < 0)
|
|
Packit |
5e46da |
return -1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (_pid_in_list(p->wipe_pid, pid)) {
|
|
Packit |
5e46da |
/* Wipe packet (pid -> padding stream) */
|
|
Packit |
5e46da |
M2TS_TRACE("Wiping pid 0x%04x\n", pid);
|
|
Packit |
5e46da |
_wipe_packet(buf);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*result = !!(p->after ? p->wipe_pid[0] : p->pass_pid[0]);*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return result;
|
|
Packit |
5e46da |
}
|