Blame src/libbluray/decoders/m2ts_filter.c

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
}