1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Copyright (c) 2020 John Stebbins <jstebbins.hb@gmail.com>
3cabdff1aSopenharmony_ci *
4cabdff1aSopenharmony_ci * This file is part of FFmpeg.
5cabdff1aSopenharmony_ci *
6cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
7cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
8cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
9cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
10cabdff1aSopenharmony_ci *
11cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
12cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
13cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14cabdff1aSopenharmony_ci * Lesser General Public License for more details.
15cabdff1aSopenharmony_ci *
16cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
17cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
18cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19cabdff1aSopenharmony_ci */
20cabdff1aSopenharmony_ci
21cabdff1aSopenharmony_ci/**
22cabdff1aSopenharmony_ci * @file
23cabdff1aSopenharmony_ci * This bitstream filter merges PGS subtitle packets containing incomplete
24cabdff1aSopenharmony_ci * set of segments into a single packet
25cabdff1aSopenharmony_ci *
26cabdff1aSopenharmony_ci * Packets already containing a complete set of segments will be passed through
27cabdff1aSopenharmony_ci * unchanged.
28cabdff1aSopenharmony_ci */
29cabdff1aSopenharmony_ci
30cabdff1aSopenharmony_ci#include "libavutil/attributes.h"
31cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
32cabdff1aSopenharmony_ci#include "libavutil/log.h"
33cabdff1aSopenharmony_ci#include "bsf.h"
34cabdff1aSopenharmony_ci#include "bsf_internal.h"
35cabdff1aSopenharmony_ci
36cabdff1aSopenharmony_cienum PGSSegmentType {
37cabdff1aSopenharmony_ci    PALETTE_SEGMENT         = 0x14,
38cabdff1aSopenharmony_ci    OBJECT_SEGMENT          = 0x15,
39cabdff1aSopenharmony_ci    PRESENTATION_SEGMENT    = 0x16,
40cabdff1aSopenharmony_ci    WINDOW_SEGMENT          = 0x17,
41cabdff1aSopenharmony_ci    END_DISPLAY_SET_SEGMENT = 0x80,
42cabdff1aSopenharmony_ci};
43cabdff1aSopenharmony_ci
44cabdff1aSopenharmony_citypedef struct PGSMergeContext {
45cabdff1aSopenharmony_ci    AVPacket *buffer_pkt, *in;
46cabdff1aSopenharmony_ci    int presentation_found;
47cabdff1aSopenharmony_ci    int pkt_flags;
48cabdff1aSopenharmony_ci} PGSMergeContext;
49cabdff1aSopenharmony_ci
50cabdff1aSopenharmony_cistatic av_cold void frame_merge_flush(AVBSFContext *bsf)
51cabdff1aSopenharmony_ci{
52cabdff1aSopenharmony_ci    PGSMergeContext *ctx = bsf->priv_data;
53cabdff1aSopenharmony_ci
54cabdff1aSopenharmony_ci    ctx->presentation_found = ctx->pkt_flags = 0;
55cabdff1aSopenharmony_ci    av_packet_unref(ctx->in);
56cabdff1aSopenharmony_ci    av_packet_unref(ctx->buffer_pkt);
57cabdff1aSopenharmony_ci}
58cabdff1aSopenharmony_ci
59cabdff1aSopenharmony_cistatic int frame_merge_output(PGSMergeContext *ctx, AVPacket *dst, AVPacket *src)
60cabdff1aSopenharmony_ci{
61cabdff1aSopenharmony_ci    if (!ctx->presentation_found)
62cabdff1aSopenharmony_ci        ctx->pkt_flags |= AV_PKT_FLAG_CORRUPT;
63cabdff1aSopenharmony_ci    ctx->presentation_found = 0;
64cabdff1aSopenharmony_ci    src->flags    |= ctx->pkt_flags;
65cabdff1aSopenharmony_ci    ctx->pkt_flags = 0;
66cabdff1aSopenharmony_ci    av_packet_move_ref(dst, src);
67cabdff1aSopenharmony_ci    return 0;
68cabdff1aSopenharmony_ci}
69cabdff1aSopenharmony_ci
70cabdff1aSopenharmony_cistatic int frame_merge_filter(AVBSFContext *bsf, AVPacket *out)
71cabdff1aSopenharmony_ci{
72cabdff1aSopenharmony_ci    PGSMergeContext *ctx = bsf->priv_data;
73cabdff1aSopenharmony_ci    AVPacket *in = ctx->in, *pkt = ctx->buffer_pkt;
74cabdff1aSopenharmony_ci    int ret, size, pos, display = 0, presentation = 0;
75cabdff1aSopenharmony_ci    unsigned int i;
76cabdff1aSopenharmony_ci
77cabdff1aSopenharmony_ci    if (!in->data) {
78cabdff1aSopenharmony_ci        ret = ff_bsf_get_packet_ref(bsf, in);
79cabdff1aSopenharmony_ci        if (ret == AVERROR_EOF && pkt->data) {
80cabdff1aSopenharmony_ci            // Output remaining data
81cabdff1aSopenharmony_ci            ctx->pkt_flags |= AV_PKT_FLAG_CORRUPT;
82cabdff1aSopenharmony_ci            return frame_merge_output(ctx, out, pkt);
83cabdff1aSopenharmony_ci        }
84cabdff1aSopenharmony_ci        if (ret < 0)
85cabdff1aSopenharmony_ci            return ret;
86cabdff1aSopenharmony_ci    }
87cabdff1aSopenharmony_ci    if (!in->size) {
88cabdff1aSopenharmony_ci        av_packet_unref(in);
89cabdff1aSopenharmony_ci        return AVERROR(EAGAIN);
90cabdff1aSopenharmony_ci    }
91cabdff1aSopenharmony_ci    in->flags &= ~AV_PKT_FLAG_KEY; // Will be detected in the stream
92cabdff1aSopenharmony_ci
93cabdff1aSopenharmony_ci    // Validate packet data and find display_end segment
94cabdff1aSopenharmony_ci    size = in->size;
95cabdff1aSopenharmony_ci    i = 0;
96cabdff1aSopenharmony_ci    while (i + 3 <= in->size) {
97cabdff1aSopenharmony_ci        uint8_t segment_type = in->data[i];
98cabdff1aSopenharmony_ci        int     segment_len  = AV_RB16(in->data + i + 1) + 3;
99cabdff1aSopenharmony_ci
100cabdff1aSopenharmony_ci        if (i + segment_len > in->size)
101cabdff1aSopenharmony_ci            break; // Invalid, segments can't span packets
102cabdff1aSopenharmony_ci        if (segment_type == PRESENTATION_SEGMENT && ctx->presentation_found)
103cabdff1aSopenharmony_ci            break; // Invalid, there can be only one
104cabdff1aSopenharmony_ci        if (segment_type == PRESENTATION_SEGMENT) {
105cabdff1aSopenharmony_ci            uint8_t state;
106cabdff1aSopenharmony_ci            if (segment_len < 11)
107cabdff1aSopenharmony_ci                break; // Invalid presentation segment length
108cabdff1aSopenharmony_ci            ctx->presentation_found = presentation = 1;
109cabdff1aSopenharmony_ci            state = in->data[i + 10] & 0xc0;
110cabdff1aSopenharmony_ci            if (state)
111cabdff1aSopenharmony_ci                ctx->pkt_flags |= AV_PKT_FLAG_KEY;
112cabdff1aSopenharmony_ci            else
113cabdff1aSopenharmony_ci                ctx->pkt_flags &= ~AV_PKT_FLAG_KEY;
114cabdff1aSopenharmony_ci        }
115cabdff1aSopenharmony_ci        i += segment_len;
116cabdff1aSopenharmony_ci        if (segment_type == END_DISPLAY_SET_SEGMENT) {
117cabdff1aSopenharmony_ci            size    = i;
118cabdff1aSopenharmony_ci            display = 1;
119cabdff1aSopenharmony_ci            break;
120cabdff1aSopenharmony_ci        }
121cabdff1aSopenharmony_ci    }
122cabdff1aSopenharmony_ci    if (display && pkt->size == 0 && size == in->size) // passthrough
123cabdff1aSopenharmony_ci        return frame_merge_output(ctx, out, in);
124cabdff1aSopenharmony_ci    if (!display && i != in->size) {
125cabdff1aSopenharmony_ci        av_log(bsf, AV_LOG_WARNING, "Failed to parse PGS segments.\n");
126cabdff1aSopenharmony_ci        // force output what we have
127cabdff1aSopenharmony_ci        size    = in->size;
128cabdff1aSopenharmony_ci        display = 1;
129cabdff1aSopenharmony_ci        ctx->pkt_flags |= AV_PKT_FLAG_CORRUPT;
130cabdff1aSopenharmony_ci    }
131cabdff1aSopenharmony_ci
132cabdff1aSopenharmony_ci    if (presentation) {
133cabdff1aSopenharmony_ci        ret = av_packet_copy_props(pkt, in);
134cabdff1aSopenharmony_ci        if (ret < 0)
135cabdff1aSopenharmony_ci            goto fail;
136cabdff1aSopenharmony_ci    }
137cabdff1aSopenharmony_ci    pos = pkt->size;
138cabdff1aSopenharmony_ci    ret = av_grow_packet(pkt, size);
139cabdff1aSopenharmony_ci    if (ret < 0)
140cabdff1aSopenharmony_ci        goto fail;
141cabdff1aSopenharmony_ci    memcpy(pkt->data + pos, in->data, size);
142cabdff1aSopenharmony_ci
143cabdff1aSopenharmony_ci    if (size == in->size)
144cabdff1aSopenharmony_ci        av_packet_unref(in);
145cabdff1aSopenharmony_ci    else {
146cabdff1aSopenharmony_ci        in->data += size;
147cabdff1aSopenharmony_ci        in->size -= size;
148cabdff1aSopenharmony_ci    }
149cabdff1aSopenharmony_ci
150cabdff1aSopenharmony_ci    if (display)
151cabdff1aSopenharmony_ci        return frame_merge_output(ctx, out, pkt);
152cabdff1aSopenharmony_ci    return AVERROR(EAGAIN);
153cabdff1aSopenharmony_ci
154cabdff1aSopenharmony_cifail:
155cabdff1aSopenharmony_ci    frame_merge_flush(bsf);
156cabdff1aSopenharmony_ci    return ret;
157cabdff1aSopenharmony_ci}
158cabdff1aSopenharmony_ci
159cabdff1aSopenharmony_cistatic av_cold int frame_merge_init(AVBSFContext *bsf)
160cabdff1aSopenharmony_ci{
161cabdff1aSopenharmony_ci    PGSMergeContext *ctx = bsf->priv_data;
162cabdff1aSopenharmony_ci
163cabdff1aSopenharmony_ci    ctx->in  = av_packet_alloc();
164cabdff1aSopenharmony_ci    ctx->buffer_pkt = av_packet_alloc();
165cabdff1aSopenharmony_ci    if (!ctx->in || !ctx->buffer_pkt)
166cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
167cabdff1aSopenharmony_ci
168cabdff1aSopenharmony_ci    return 0;
169cabdff1aSopenharmony_ci}
170cabdff1aSopenharmony_ci
171cabdff1aSopenharmony_cistatic av_cold void frame_merge_close(AVBSFContext *bsf)
172cabdff1aSopenharmony_ci{
173cabdff1aSopenharmony_ci    PGSMergeContext *ctx = bsf->priv_data;
174cabdff1aSopenharmony_ci
175cabdff1aSopenharmony_ci    av_packet_free(&ctx->in);
176cabdff1aSopenharmony_ci    av_packet_free(&ctx->buffer_pkt);
177cabdff1aSopenharmony_ci}
178cabdff1aSopenharmony_ci
179cabdff1aSopenharmony_cistatic const enum AVCodecID frame_merge_codec_ids[] = {
180cabdff1aSopenharmony_ci    AV_CODEC_ID_HDMV_PGS_SUBTITLE, AV_CODEC_ID_NONE,
181cabdff1aSopenharmony_ci};
182cabdff1aSopenharmony_ci
183cabdff1aSopenharmony_ciconst FFBitStreamFilter ff_pgs_frame_merge_bsf = {
184cabdff1aSopenharmony_ci    .p.name         = "pgs_frame_merge",
185cabdff1aSopenharmony_ci    .p.codec_ids    = frame_merge_codec_ids,
186cabdff1aSopenharmony_ci    .priv_data_size = sizeof(PGSMergeContext),
187cabdff1aSopenharmony_ci    .init           = frame_merge_init,
188cabdff1aSopenharmony_ci    .flush          = frame_merge_flush,
189cabdff1aSopenharmony_ci    .close          = frame_merge_close,
190cabdff1aSopenharmony_ci    .filter         = frame_merge_filter,
191cabdff1aSopenharmony_ci};
192