|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* This file is part of libbluray
|
|
Packit |
5e46da |
* Copyright (C) 2010-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 "graphics_processor.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "ig_decode.h"
|
|
Packit |
5e46da |
#include "pg_decode.h"
|
|
Packit |
5e46da |
#include "textst_decode.h"
|
|
Packit |
5e46da |
#include "pes_buffer.h"
|
|
Packit |
5e46da |
#include "m2ts_demux.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "util/macro.h"
|
|
Packit |
5e46da |
#include "util/logging.h"
|
|
Packit |
5e46da |
#include "util/bits.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include <string.h>
|
|
Packit |
5e46da |
#include <stdlib.h>
|
|
Packit |
5e46da |
#include <inttypes.h>
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#define GP_TRACE(...) do {} while (0)
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* segment types
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
typedef enum {
|
|
Packit |
5e46da |
PGS_PALETTE = 0x14,
|
|
Packit |
5e46da |
PGS_OBJECT = 0x15,
|
|
Packit |
5e46da |
PGS_PG_COMPOSITION = 0x16,
|
|
Packit |
5e46da |
PGS_WINDOW = 0x17,
|
|
Packit |
5e46da |
PGS_IG_COMPOSITION = 0x18,
|
|
Packit |
5e46da |
PGS_END_OF_DISPLAY = 0x80,
|
|
Packit |
5e46da |
/* Text subtitles */
|
|
Packit |
5e46da |
TGS_DIALOG_STYLE = 0x81,
|
|
Packit |
5e46da |
TGS_DIALOG_PRESENTATION = 0x82,
|
|
Packit |
5e46da |
} pgs_segment_type_e;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* PG_DISPLAY_SET
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void _free_dialogs(PG_DISPLAY_SET *s)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
unsigned ii;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
textst_free_dialog_style(&s->style);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < s->num_dialog; ii++) {
|
|
Packit |
5e46da |
textst_clean_dialog_presentation(&s->dialog[ii]);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
X_FREE(s->dialog);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->num_dialog = 0;
|
|
Packit |
5e46da |
s->total_dialog = 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void pg_display_set_free(PG_DISPLAY_SET **s)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (s && *s) {
|
|
Packit |
5e46da |
unsigned ii;
|
|
Packit |
5e46da |
for (ii = 0; ii < (*s)->num_object; ii++) {
|
|
Packit |
5e46da |
pg_clean_object(&(*s)->object[ii]);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
ig_free_interactive(&(*s)->ics);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
X_FREE((*s)->window);
|
|
Packit |
5e46da |
X_FREE((*s)->object);
|
|
Packit |
5e46da |
X_FREE((*s)->palette);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
_free_dialogs(*s);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
X_FREE(*s);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* segment handling
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static PES_BUFFER *_find_segment_by_idv(PES_BUFFER *p,
|
|
Packit |
5e46da |
uint8_t seg_type, unsigned idv_pos,
|
|
Packit |
5e46da |
uint8_t *idv, unsigned idv_len)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
while (p && (p->buf[0] != seg_type || memcmp(p->buf + idv_pos, idv, idv_len))) {
|
|
Packit |
5e46da |
p = p->next;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return p;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void _join_fragments(PES_BUFFER *p1, PES_BUFFER *p2, int data_pos)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
unsigned new_len = p1->len + p2->len - data_pos;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (p1->size < new_len) {
|
|
Packit |
5e46da |
uint8_t *tmp;
|
|
Packit |
5e46da |
p1->size = new_len + 1;
|
|
Packit |
5e46da |
tmp = realloc(p1->buf, p1->size);
|
|
Packit |
5e46da |
if (!tmp) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
|
|
Packit |
5e46da |
p1->size = 0;
|
|
Packit |
5e46da |
p1->len = 0;
|
|
Packit |
5e46da |
return;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
p1->buf = tmp;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
memcpy(p1->buf + p1->len, p2->buf + data_pos, p2->len - data_pos);
|
|
Packit |
5e46da |
p1->len = new_len;
|
|
Packit |
5e46da |
p2->len = 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* return 1 if segment is ready for decoding, 0 if more data is needed */
|
|
Packit |
5e46da |
static int _join_segment_fragments(struct pes_buffer_s *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
uint8_t type;
|
|
Packit |
5e46da |
unsigned id_pos = 0, id_len = 3, sd_pos = 6, data_pos = 0;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (p->len < 3) {
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* check segment type */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
type = p->buf[0];
|
|
Packit |
5e46da |
if (type == PGS_OBJECT) {
|
|
Packit |
5e46da |
id_pos = 3;
|
|
Packit |
5e46da |
sd_pos = 6;
|
|
Packit |
5e46da |
data_pos = 7;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
else if (type == PGS_IG_COMPOSITION) {
|
|
Packit |
5e46da |
id_pos = 8;
|
|
Packit |
5e46da |
sd_pos = 11;
|
|
Packit |
5e46da |
data_pos = 12;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
else {
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* check sequence descriptor - is segment complete ? */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_PG_SEQUENCE_DESCRIPTOR sd;
|
|
Packit |
5e46da |
BITBUFFER bb;
|
|
Packit |
5e46da |
bb_init(&bb, p->buf + sd_pos, 3);
|
|
Packit |
5e46da |
pg_decode_sequence_descriptor(&bb, &sd);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (sd.last_in_seq) {
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (!sd.first_in_seq) {
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* find next fragment(s) */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
PES_BUFFER *next;
|
|
Packit |
5e46da |
while (NULL != (next = _find_segment_by_idv(p->next, p->buf[0], id_pos, p->buf + id_pos, id_len))) {
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bb_init(&bb, next->buf + sd_pos, 3);
|
|
Packit |
5e46da |
pg_decode_sequence_descriptor(&bb, &sd);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
_join_fragments(p, next, data_pos);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pes_buffer_remove(&p, next);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (sd.last_in_seq) {
|
|
Packit |
5e46da |
/* set first + last in sequence descriptor */
|
|
Packit |
5e46da |
p->buf[sd_pos] = 0xff;
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* do not delay decoding if there are other segments queued (missing fragment ?) */
|
|
Packit |
5e46da |
return !!p->next;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* segment decoding
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _decode_wds(PG_DISPLAY_SET *s, BITBUFFER *bb, PES_BUFFER *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_PG_WINDOWS w;
|
|
Packit |
5e46da |
memset(&w, 0, sizeof(w));
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
(void)p;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!s->decoding) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE, "skipping orphan window definition segment\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->num_window = 0;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (pg_decode_windows(bb, &w)) {
|
|
Packit |
5e46da |
X_FREE(s->window);
|
|
Packit |
5e46da |
s->window = w.window;
|
|
Packit |
5e46da |
s->num_window = w.num_windows;
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pg_clean_windows(&w);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _decode_ods(PG_DISPLAY_SET *s, BITBUFFER *bb, PES_BUFFER *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (!s->decoding) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE, "skipping orphan object definition segment\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* search for object to be updated */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (s->object) {
|
|
Packit |
5e46da |
BITBUFFER bb_tmp = *bb;
|
|
Packit |
5e46da |
uint16_t id = bb_read(&bb_tmp, 16);
|
|
Packit |
5e46da |
unsigned ii;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < s->num_object; ii++) {
|
|
Packit |
5e46da |
if (s->object[ii].id == id) {
|
|
Packit |
5e46da |
if (pg_decode_object(bb, &s->object[ii])) {
|
|
Packit |
5e46da |
s->object[ii].pts = p->pts;
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
pg_clean_object(&s->object[ii]);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* add and decode new object */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_PG_OBJECT *tmp = realloc(s->object, sizeof(s->object[0]) * (s->num_object + 1));
|
|
Packit |
5e46da |
if (!tmp) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
s->object = tmp;
|
|
Packit |
5e46da |
memset(&s->object[s->num_object], 0, sizeof(s->object[0]));
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (pg_decode_object(bb, &s->object[s->num_object])) {
|
|
Packit |
5e46da |
s->object[s->num_object].pts = p->pts;
|
|
Packit |
5e46da |
s->num_object++;
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pg_clean_object(&s->object[s->num_object]);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _decode_pds(PG_DISPLAY_SET *s, BITBUFFER *bb, PES_BUFFER *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (!s->decoding) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE, "skipping orphan palette definition segment\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* search for palette to be updated */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (s->palette) {
|
|
Packit |
5e46da |
BITBUFFER bb_tmp = *bb;
|
|
Packit |
5e46da |
uint8_t id = bb_read(&bb_tmp, 8);
|
|
Packit |
5e46da |
unsigned ii;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < s->num_palette; ii++) {
|
|
Packit |
5e46da |
if (s->palette[ii].id == id) {
|
|
Packit |
5e46da |
int rr;
|
|
Packit |
5e46da |
if ( (s->ics && s->ics->composition_descriptor.state == 0) ||
|
|
Packit |
5e46da |
(s->pcs && s->pcs->composition_descriptor.state == 0)) {
|
|
Packit |
5e46da |
/* 8.8.3.1.1 */
|
|
Packit |
5e46da |
rr = pg_decode_palette_update(bb, &s->palette[ii]);
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
rr = pg_decode_palette(bb, &s->palette[ii]);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (rr) {
|
|
Packit |
5e46da |
s->palette[ii].pts = p->pts;
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* add and decode new palette */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_PG_PALETTE *tmp = realloc(s->palette, sizeof(s->palette[0]) * (s->num_palette + 1));
|
|
Packit |
5e46da |
if (!tmp) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
s->palette = tmp;
|
|
Packit |
5e46da |
memset(&s->palette[s->num_palette], 0, sizeof(s->palette[0]));
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (pg_decode_palette(bb, &s->palette[s->num_palette])) {
|
|
Packit |
5e46da |
s->palette[s->num_palette].pts = p->pts;
|
|
Packit |
5e46da |
s->num_palette++;
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static void _check_epoch_start(PG_DISPLAY_SET *s)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if ((s->pcs && s->pcs->composition_descriptor.state == 2) ||
|
|
Packit |
5e46da |
(s->ics && s->ics->composition_descriptor.state == 2)) {
|
|
Packit |
5e46da |
/* epoch start, drop all cached data */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
unsigned ii;
|
|
Packit |
5e46da |
for (ii = 0; ii < s->num_object; ii++) {
|
|
Packit |
5e46da |
pg_clean_object(&s->object[ii]);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->num_palette = 0;
|
|
Packit |
5e46da |
s->num_window = 0;
|
|
Packit |
5e46da |
s->num_object = 0;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->epoch_start = 1;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
} else {
|
|
Packit |
5e46da |
s->epoch_start = 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _decode_pcs(PG_DISPLAY_SET *s, BITBUFFER *bb, PES_BUFFER *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (s->complete) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "ERROR: updating complete (non-consumed) PG composition\n");
|
|
Packit |
5e46da |
s->complete = 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pg_free_composition(&s->pcs);
|
|
Packit |
5e46da |
s->pcs = calloc(1, sizeof(*s->pcs));
|
|
Packit |
5e46da |
if (!s->pcs) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!pg_decode_composition(bb, s->pcs)) {
|
|
Packit |
5e46da |
pg_free_composition(&s->pcs);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->pcs->pts = p->pts;
|
|
Packit |
5e46da |
s->valid_pts = p->pts;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
_check_epoch_start(s);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->decoding = 1;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _decode_ics(PG_DISPLAY_SET *s, BITBUFFER *bb, PES_BUFFER *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (s->complete) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "ERROR: updating complete (non-consumed) IG composition\n");
|
|
Packit |
5e46da |
s->complete = 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
ig_free_interactive(&s->ics);
|
|
Packit |
5e46da |
s->ics = calloc(1, sizeof(*s->ics));
|
|
Packit |
5e46da |
if (!s->ics) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!ig_decode_interactive(bb, s->ics)) {
|
|
Packit |
5e46da |
ig_free_interactive(&s->ics);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->ics->pts = p->pts;
|
|
Packit |
5e46da |
s->valid_pts = p->pts;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
_check_epoch_start(s);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->decoding = 1;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _decode_dialog_style(PG_DISPLAY_SET *s, BITBUFFER *bb)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
_free_dialogs(s);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->complete = 0;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->style = calloc(1, sizeof(*s->style));
|
|
Packit |
5e46da |
if (!s->style) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!textst_decode_dialog_style(bb, s->style)) {
|
|
Packit |
5e46da |
textst_free_dialog_style(&s->style);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (bb->p != bb->p_end - 2 || bb->i_left != 8) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "_decode_dialog_style() failed: bytes in buffer %d\n", (int)(bb->p_end - bb->p));
|
|
Packit |
5e46da |
textst_free_dialog_style(&s->style);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->total_dialog = bb_read(bb, 16);
|
|
Packit |
5e46da |
if (s->total_dialog < 1) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "_decode_dialog_style(): no dialog segments\n");
|
|
Packit |
5e46da |
textst_free_dialog_style(&s->style);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->dialog = calloc(s->total_dialog, sizeof(*s->dialog));
|
|
Packit |
5e46da |
if (!s->dialog) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
|
|
Packit |
5e46da |
s->total_dialog = 0;
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE, "_decode_dialog_style(): %d dialogs in stream\n", s->total_dialog);
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _decode_dialog_presentation(PG_DISPLAY_SET *s, BITBUFFER *bb)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (!s->style || s->total_dialog < 1) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE, "_decode_dialog_presentation() failed: style segment not decoded\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (s->num_dialog >= s->total_dialog) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "_decode_dialog_presentation(): unexpected dialog segment\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!textst_decode_dialog_presentation(bb, &s->dialog[s->num_dialog])) {
|
|
Packit |
5e46da |
textst_clean_dialog_presentation(&s->dialog[s->num_dialog]);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
s->num_dialog++;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (s->num_dialog == s->total_dialog) {
|
|
Packit |
5e46da |
s->complete = 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _decode_segment(PG_DISPLAY_SET *s, PES_BUFFER *p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BITBUFFER bb;
|
|
Packit |
5e46da |
bb_init(&bb, p->buf, p->len);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
uint8_t type = bb_read(&bb, 8);
|
|
Packit |
5e46da |
/*uint16_t len = */ bb_read(&bb, 16);
|
|
Packit |
5e46da |
switch (type) {
|
|
Packit |
5e46da |
case PGS_OBJECT:
|
|
Packit |
5e46da |
return _decode_ods(s, &bb, p);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case PGS_PALETTE:
|
|
Packit |
5e46da |
return _decode_pds(s, &bb, p);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case PGS_WINDOW:
|
|
Packit |
5e46da |
return _decode_wds(s, &bb, p);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case PGS_PG_COMPOSITION:
|
|
Packit |
5e46da |
return _decode_pcs(s, &bb, p);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case PGS_IG_COMPOSITION:
|
|
Packit |
5e46da |
return _decode_ics(s, &bb, p);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case PGS_END_OF_DISPLAY:
|
|
Packit |
5e46da |
if (!s->decoding) {
|
|
Packit |
5e46da |
/* avoid duplicate initialization / presenataton */
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE, "skipping orphan end of display segment\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
s->complete = 1;
|
|
Packit |
5e46da |
s->decoding = 0;
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case TGS_DIALOG_STYLE:
|
|
Packit |
5e46da |
return _decode_dialog_style(s, &bb);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
case TGS_DIALOG_PRESENTATION:
|
|
Packit |
5e46da |
return _decode_dialog_presentation(s, &bb);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
default:
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "unknown segment type 0x%x\n", type);
|
|
Packit |
5e46da |
break;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* mpeg-pes interface
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
#define MAX_STC_DTS_DIFF (INT64_C(90000 * 30)) /* 30 seconds */
|
|
Packit |
5e46da |
static int graphics_processor_decode_pes(PG_DISPLAY_SET **s, PES_BUFFER **p, int64_t stc)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (!s) {
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (*s == NULL) {
|
|
Packit |
5e46da |
*s = calloc(1, sizeof(PG_DISPLAY_SET));
|
|
Packit |
5e46da |
if (!*s) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
while (*p) {
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* time to decode next segment ? */
|
|
Packit |
5e46da |
if (stc >= 0 && (*p)->dts > stc) {
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* filter out values that seem to be incorrect (if stc is not updated) */
|
|
Packit |
5e46da |
int64_t diff = (*p)->dts - stc;
|
|
Packit |
5e46da |
if (diff < MAX_STC_DTS_DIFF) {
|
|
Packit |
5e46da |
GP_TRACE("Segment dts > stc (%"PRId64" > %"PRId64" ; diff %"PRId64")\n",
|
|
Packit |
5e46da |
(*p)->dts, stc, diff);
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* all fragments present ? */
|
|
Packit |
5e46da |
if (!_join_segment_fragments(*p)) {
|
|
Packit |
5e46da |
GP_TRACE("splitted segment not complete, waiting for next fragment\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if ((*p)->len <= 2) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_DECODE, "segment too short, skipping (%d bytes)\n", (*p)->len);
|
|
Packit |
5e46da |
pes_buffer_next(p);
|
|
Packit |
5e46da |
continue;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* decode segment */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
GP_TRACE("Decoding segment, dts %010"PRId64" pts %010"PRId64" len %d\n",
|
|
Packit |
5e46da |
(*p)->dts, (*p)->pts, (*p)->len);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
_decode_segment(*s, *p);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
pes_buffer_next(p);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if ((*s)->complete) {
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* mpeg-ts interface
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
struct graphics_processor_s {
|
|
Packit |
5e46da |
uint16_t pid;
|
|
Packit |
5e46da |
M2TS_DEMUX *demux;
|
|
Packit |
5e46da |
PES_BUFFER *queue;
|
|
Packit |
5e46da |
};
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
GRAPHICS_PROCESSOR *graphics_processor_init(void)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
GRAPHICS_PROCESSOR *p = calloc(1, sizeof(*p));
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return p;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void graphics_processor_free(GRAPHICS_PROCESSOR **p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (p && *p) {
|
|
Packit |
5e46da |
m2ts_demux_free(&(*p)->demux);
|
|
Packit |
5e46da |
pes_buffer_free(&(*p)->queue);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
X_FREE(*p);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
int graphics_processor_decode_ts(GRAPHICS_PROCESSOR *p,
|
|
Packit |
5e46da |
PG_DISPLAY_SET **s,
|
|
Packit |
5e46da |
uint16_t pid, uint8_t *unit, unsigned num_units,
|
|
Packit |
5e46da |
int64_t stc)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
unsigned ii;
|
|
Packit |
5e46da |
int result = 0;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (pid != p->pid) {
|
|
Packit |
5e46da |
m2ts_demux_free(&p->demux);
|
|
Packit |
5e46da |
pes_buffer_free(&p->queue);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
if (!p->demux) {
|
|
Packit |
5e46da |
p->demux = m2ts_demux_init(pid);
|
|
Packit |
5e46da |
if (!p->demux) {
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
p->pid = pid;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (ii = 0; ii < num_units; ii++) {
|
|
Packit |
5e46da |
pes_buffer_append(&p->queue, m2ts_demux(p->demux, unit));
|
|
Packit |
5e46da |
unit += 6144;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (p->queue) {
|
|
Packit |
5e46da |
result = graphics_processor_decode_pes(s, &p->queue, stc);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return result;
|
|
Packit |
5e46da |
}
|