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