1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Sega FILM Format (CPK) Muxer
3cabdff1aSopenharmony_ci * Copyright (C) 2003 The FFmpeg project
4cabdff1aSopenharmony_ci * Copyright (C) 2018 Misty De Meo
5cabdff1aSopenharmony_ci *
6cabdff1aSopenharmony_ci * This file is part of FFmpeg.
7cabdff1aSopenharmony_ci *
8cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
9cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
10cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
11cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
12cabdff1aSopenharmony_ci *
13cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
14cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
15cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16cabdff1aSopenharmony_ci * Lesser General Public License for more details.
17cabdff1aSopenharmony_ci *
18cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
19cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
20cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21cabdff1aSopenharmony_ci */
22cabdff1aSopenharmony_ci
23cabdff1aSopenharmony_ci/**
24cabdff1aSopenharmony_ci * @file
25cabdff1aSopenharmony_ci * Sega FILM (.cpk) file muxer
26cabdff1aSopenharmony_ci * @author Misty De Meo <misty@brew.sh>
27cabdff1aSopenharmony_ci *
28cabdff1aSopenharmony_ci * @see For more information regarding the Sega FILM file format, visit:
29cabdff1aSopenharmony_ci *   http://wiki.multimedia.cx/index.php?title=Sega_FILM
30cabdff1aSopenharmony_ci */
31cabdff1aSopenharmony_ci
32cabdff1aSopenharmony_ci#include "libavutil/avassert.h"
33cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
34cabdff1aSopenharmony_ci#include "libavcodec/bytestream.h"
35cabdff1aSopenharmony_ci#include "avformat.h"
36cabdff1aSopenharmony_ci#include "avio_internal.h"
37cabdff1aSopenharmony_ci#include "mux.h"
38cabdff1aSopenharmony_ci
39cabdff1aSopenharmony_citypedef struct FILMOutputContext {
40cabdff1aSopenharmony_ci    AVIOContext *header;
41cabdff1aSopenharmony_ci    unsigned index;
42cabdff1aSopenharmony_ci    int audio_index;
43cabdff1aSopenharmony_ci    int video_index;
44cabdff1aSopenharmony_ci} FILMOutputContext;
45cabdff1aSopenharmony_ci
46cabdff1aSopenharmony_cistatic int film_write_packet(AVFormatContext *format_context, AVPacket *pkt)
47cabdff1aSopenharmony_ci{
48cabdff1aSopenharmony_ci    AVIOContext *pb = format_context->pb;
49cabdff1aSopenharmony_ci    FILMOutputContext *film = format_context->priv_data;
50cabdff1aSopenharmony_ci    int encoded_buf_size, size = pkt->size;
51cabdff1aSopenharmony_ci    uint32_t info1, info2;
52cabdff1aSopenharmony_ci    enum AVCodecID codec_id;
53cabdff1aSopenharmony_ci
54cabdff1aSopenharmony_ci    codec_id = format_context->streams[pkt->stream_index]->codecpar->codec_id;
55cabdff1aSopenharmony_ci
56cabdff1aSopenharmony_ci    /* Sega Cinepak has an extra two-byte header; write dummy data there,
57cabdff1aSopenharmony_ci     * then adjust the cvid header to accommodate for the extra size */
58cabdff1aSopenharmony_ci    if (codec_id == AV_CODEC_ID_CINEPAK) {
59cabdff1aSopenharmony_ci        encoded_buf_size = AV_RB24(&pkt->data[1]);
60cabdff1aSopenharmony_ci        /* Already Sega Cinepak, so no need to reformat the packets */
61cabdff1aSopenharmony_ci        if (encoded_buf_size != pkt->size && (pkt->size % encoded_buf_size) != 0) {
62cabdff1aSopenharmony_ci            avio_write(pb, pkt->data, pkt->size);
63cabdff1aSopenharmony_ci        } else {
64cabdff1aSopenharmony_ci            /* In Sega Cinepak, the reported size in the Cinepak header is
65cabdff1aSopenharmony_ci             * 8 bytes too short. However, the size in the STAB section of the header
66cabdff1aSopenharmony_ci             * is correct, taking into account the extra two bytes. */
67cabdff1aSopenharmony_ci            AV_WB24(&pkt->data[1], pkt->size - 8 + 2);
68cabdff1aSopenharmony_ci            size += 2;
69cabdff1aSopenharmony_ci
70cabdff1aSopenharmony_ci            avio_write(pb, pkt->data, 10);
71cabdff1aSopenharmony_ci            avio_wb16(pb, 0);
72cabdff1aSopenharmony_ci            avio_write(pb, &pkt->data[10], pkt->size - 10);
73cabdff1aSopenharmony_ci        }
74cabdff1aSopenharmony_ci    } else {
75cabdff1aSopenharmony_ci        /* Other formats can just be written as-is */
76cabdff1aSopenharmony_ci        avio_write(pb, pkt->data, pkt->size);
77cabdff1aSopenharmony_ci    }
78cabdff1aSopenharmony_ci
79cabdff1aSopenharmony_ci    /* Add the 16-byte sample info entry to the dynamic buffer
80cabdff1aSopenharmony_ci     * for the STAB chunk in the header */
81cabdff1aSopenharmony_ci    pb = film->header;
82cabdff1aSopenharmony_ci    avio_wb32(pb, film->index);
83cabdff1aSopenharmony_ci    film->index += size;
84cabdff1aSopenharmony_ci    avio_wb32(pb, size);
85cabdff1aSopenharmony_ci    if (film->audio_index == pkt->stream_index) {
86cabdff1aSopenharmony_ci        /* Always the same, carries no more information than "this is audio" */
87cabdff1aSopenharmony_ci        info1 = 0xFFFFFFFF;
88cabdff1aSopenharmony_ci        info2 = 1;
89cabdff1aSopenharmony_ci    } else {
90cabdff1aSopenharmony_ci        info1 = pkt->pts;
91cabdff1aSopenharmony_ci        info2 = pkt->duration;
92cabdff1aSopenharmony_ci        /* The top bit being set indicates a key frame */
93cabdff1aSopenharmony_ci        if (!(pkt->flags & AV_PKT_FLAG_KEY))
94cabdff1aSopenharmony_ci            info1 |= 1U << 31;
95cabdff1aSopenharmony_ci    }
96cabdff1aSopenharmony_ci    avio_wb32(pb, info1);
97cabdff1aSopenharmony_ci    avio_wb32(pb, info2);
98cabdff1aSopenharmony_ci
99cabdff1aSopenharmony_ci    return pb->error;
100cabdff1aSopenharmony_ci}
101cabdff1aSopenharmony_ci
102cabdff1aSopenharmony_cistatic int get_audio_codec_id(enum AVCodecID codec_id)
103cabdff1aSopenharmony_ci{
104cabdff1aSopenharmony_ci    /* 0 (PCM) and 2 (ADX) are the only known values */
105cabdff1aSopenharmony_ci    switch (codec_id) {
106cabdff1aSopenharmony_ci    case AV_CODEC_ID_PCM_S8_PLANAR:
107cabdff1aSopenharmony_ci    case AV_CODEC_ID_PCM_S16BE_PLANAR:
108cabdff1aSopenharmony_ci        return 0;
109cabdff1aSopenharmony_ci    case AV_CODEC_ID_ADPCM_ADX:
110cabdff1aSopenharmony_ci        return 2;
111cabdff1aSopenharmony_ci    default:
112cabdff1aSopenharmony_ci        return -1;
113cabdff1aSopenharmony_ci    }
114cabdff1aSopenharmony_ci}
115cabdff1aSopenharmony_ci
116cabdff1aSopenharmony_cistatic int film_init(AVFormatContext *format_context)
117cabdff1aSopenharmony_ci{
118cabdff1aSopenharmony_ci    FILMOutputContext *film = format_context->priv_data;
119cabdff1aSopenharmony_ci    int ret;
120cabdff1aSopenharmony_ci
121cabdff1aSopenharmony_ci    film->audio_index = -1;
122cabdff1aSopenharmony_ci    film->video_index = -1;
123cabdff1aSopenharmony_ci
124cabdff1aSopenharmony_ci    for (int i = 0; i < format_context->nb_streams; i++) {
125cabdff1aSopenharmony_ci        AVStream *st = format_context->streams[i];
126cabdff1aSopenharmony_ci        if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
127cabdff1aSopenharmony_ci            if (film->audio_index > -1) {
128cabdff1aSopenharmony_ci                av_log(format_context, AV_LOG_ERROR, "Sega FILM allows a maximum of one audio stream.\n");
129cabdff1aSopenharmony_ci                return AVERROR(EINVAL);
130cabdff1aSopenharmony_ci            }
131cabdff1aSopenharmony_ci            if (get_audio_codec_id(st->codecpar->codec_id) < 0) {
132cabdff1aSopenharmony_ci                av_log(format_context, AV_LOG_ERROR,
133cabdff1aSopenharmony_ci                       "Incompatible audio stream format.\n");
134cabdff1aSopenharmony_ci                return AVERROR(EINVAL);
135cabdff1aSopenharmony_ci            }
136cabdff1aSopenharmony_ci            film->audio_index = i;
137cabdff1aSopenharmony_ci        }
138cabdff1aSopenharmony_ci
139cabdff1aSopenharmony_ci        if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
140cabdff1aSopenharmony_ci            if (film->video_index > -1) {
141cabdff1aSopenharmony_ci                av_log(format_context, AV_LOG_ERROR, "Sega FILM allows a maximum of one video stream.\n");
142cabdff1aSopenharmony_ci                return AVERROR(EINVAL);
143cabdff1aSopenharmony_ci            }
144cabdff1aSopenharmony_ci            if (st->codecpar->codec_id != AV_CODEC_ID_CINEPAK &&
145cabdff1aSopenharmony_ci                st->codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) {
146cabdff1aSopenharmony_ci                av_log(format_context, AV_LOG_ERROR,
147cabdff1aSopenharmony_ci                       "Incompatible video stream format.\n");
148cabdff1aSopenharmony_ci                return AVERROR(EINVAL);
149cabdff1aSopenharmony_ci            }
150cabdff1aSopenharmony_ci            if (st->codecpar->format != AV_PIX_FMT_RGB24) {
151cabdff1aSopenharmony_ci                av_log(format_context, AV_LOG_ERROR,
152cabdff1aSopenharmony_ci                       "Pixel format must be rgb24.\n");
153cabdff1aSopenharmony_ci                return AVERROR(EINVAL);
154cabdff1aSopenharmony_ci            }
155cabdff1aSopenharmony_ci            film->video_index = i;
156cabdff1aSopenharmony_ci        }
157cabdff1aSopenharmony_ci    }
158cabdff1aSopenharmony_ci
159cabdff1aSopenharmony_ci    if (film->video_index == -1) {
160cabdff1aSopenharmony_ci        av_log(format_context, AV_LOG_ERROR, "No video stream present.\n");
161cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
162cabdff1aSopenharmony_ci    }
163cabdff1aSopenharmony_ci    if ((ret = avio_open_dyn_buf(&film->header)) < 0)
164cabdff1aSopenharmony_ci        return ret;
165cabdff1aSopenharmony_ci    ffio_fill(film->header, 0, 16 + 32 + 16);
166cabdff1aSopenharmony_ci
167cabdff1aSopenharmony_ci    return 0;
168cabdff1aSopenharmony_ci}
169cabdff1aSopenharmony_ci
170cabdff1aSopenharmony_cistatic int write_header(AVFormatContext *format_context, uint8_t *header,
171cabdff1aSopenharmony_ci                        unsigned header_size)
172cabdff1aSopenharmony_ci{
173cabdff1aSopenharmony_ci    int ret = ff_format_shift_data(format_context, 0, header_size);
174cabdff1aSopenharmony_ci    if (ret < 0)
175cabdff1aSopenharmony_ci        return ret;
176cabdff1aSopenharmony_ci
177cabdff1aSopenharmony_ci    avio_seek(format_context->pb, 0, SEEK_SET);
178cabdff1aSopenharmony_ci    avio_write(format_context->pb, header, header_size);
179cabdff1aSopenharmony_ci
180cabdff1aSopenharmony_ci    return 0;
181cabdff1aSopenharmony_ci}
182cabdff1aSopenharmony_ci
183cabdff1aSopenharmony_cistatic int film_write_header(AVFormatContext *format_context)
184cabdff1aSopenharmony_ci{
185cabdff1aSopenharmony_ci    int ret = 0;
186cabdff1aSopenharmony_ci    unsigned stabsize, headersize, packet_count;
187cabdff1aSopenharmony_ci    FILMOutputContext *film = format_context->priv_data;
188cabdff1aSopenharmony_ci    AVStream *video = NULL;
189cabdff1aSopenharmony_ci    uint8_t *header, *ptr;
190cabdff1aSopenharmony_ci
191cabdff1aSopenharmony_ci    /* Calculate how much we need to reserve for the header;
192cabdff1aSopenharmony_ci     * this is the amount the rest of the data will be shifted up by. */
193cabdff1aSopenharmony_ci    headersize = avio_get_dyn_buf(film->header, &header);
194cabdff1aSopenharmony_ci    if (headersize < 64) {
195cabdff1aSopenharmony_ci        av_assert1(film->header->error < 0);
196cabdff1aSopenharmony_ci        return film->header->error;
197cabdff1aSopenharmony_ci    }
198cabdff1aSopenharmony_ci    packet_count = (headersize - 64) / 16;
199cabdff1aSopenharmony_ci    stabsize = 16 + 16 * packet_count;
200cabdff1aSopenharmony_ci    headersize = 16 + /* FILM header base */
201cabdff1aSopenharmony_ci                 32 + /* FDSC chunk */
202cabdff1aSopenharmony_ci                 stabsize;
203cabdff1aSopenharmony_ci
204cabdff1aSopenharmony_ci    /* Write the header at the position in the buffer reserved for it.
205cabdff1aSopenharmony_ci     * First, write the FILM header; this is very simple */
206cabdff1aSopenharmony_ci    ptr = header;
207cabdff1aSopenharmony_ci    bytestream_put_be32(&ptr, MKBETAG('F', 'I', 'L', 'M'));
208cabdff1aSopenharmony_ci    bytestream_put_be32(&ptr, headersize);
209cabdff1aSopenharmony_ci    /* This seems to be okay to hardcode, since this muxer targets 1.09 features;
210cabdff1aSopenharmony_ci     * videos produced by this muxer are readable by 1.08 and lower players. */
211cabdff1aSopenharmony_ci    bytestream_put_be32(&ptr, MKBETAG('1', '.', '0', '9'));
212cabdff1aSopenharmony_ci    /* I have no idea what the next four bytes do, might be reserved */
213cabdff1aSopenharmony_ci    ptr += 4;
214cabdff1aSopenharmony_ci
215cabdff1aSopenharmony_ci    /* Next write the FDSC (file description) chunk */
216cabdff1aSopenharmony_ci    bytestream_put_be32(&ptr, MKBETAG('F', 'D', 'S', 'C'));
217cabdff1aSopenharmony_ci    bytestream_put_be32(&ptr, 0x20); /* Size of FDSC chunk */
218cabdff1aSopenharmony_ci
219cabdff1aSopenharmony_ci    video = format_context->streams[film->video_index];
220cabdff1aSopenharmony_ci
221cabdff1aSopenharmony_ci    /* The only two supported codecs; raw video is rare */
222cabdff1aSopenharmony_ci    switch (video->codecpar->codec_id) {
223cabdff1aSopenharmony_ci    case AV_CODEC_ID_CINEPAK:
224cabdff1aSopenharmony_ci        bytestream_put_be32(&ptr, MKBETAG('c', 'v', 'i', 'd'));
225cabdff1aSopenharmony_ci        break;
226cabdff1aSopenharmony_ci    case AV_CODEC_ID_RAWVIDEO:
227cabdff1aSopenharmony_ci        bytestream_put_be32(&ptr, MKBETAG('r', 'a', 'w', ' '));
228cabdff1aSopenharmony_ci        break;
229cabdff1aSopenharmony_ci    }
230cabdff1aSopenharmony_ci
231cabdff1aSopenharmony_ci    bytestream_put_be32(&ptr, video->codecpar->height);
232cabdff1aSopenharmony_ci    bytestream_put_be32(&ptr, video->codecpar->width);
233cabdff1aSopenharmony_ci    bytestream_put_byte(&ptr, 24); /* Bits per pixel - observed to always be 24 */
234cabdff1aSopenharmony_ci
235cabdff1aSopenharmony_ci    if (film->audio_index > -1) {
236cabdff1aSopenharmony_ci        AVStream *audio = format_context->streams[film->audio_index];
237cabdff1aSopenharmony_ci        int audio_codec = get_audio_codec_id(audio->codecpar->codec_id);
238cabdff1aSopenharmony_ci
239cabdff1aSopenharmony_ci        bytestream_put_byte(&ptr, audio->codecpar->ch_layout.nb_channels); /* Audio channels */
240cabdff1aSopenharmony_ci        bytestream_put_byte(&ptr, audio->codecpar->bits_per_coded_sample); /* Audio bit depth */
241cabdff1aSopenharmony_ci        bytestream_put_byte(&ptr, audio_codec); /* Compression - 0 is PCM, 2 is ADX */
242cabdff1aSopenharmony_ci        bytestream_put_be16(&ptr, audio->codecpar->sample_rate); /* Audio sampling rate */
243cabdff1aSopenharmony_ci    } else {
244cabdff1aSopenharmony_ci        /* If there is no audio, all the audio fields should be set to zero.
245cabdff1aSopenharmony_ci         * ffio_fill() already did this for us. */
246cabdff1aSopenharmony_ci        ptr += 1 + 1 + 1 + 2;
247cabdff1aSopenharmony_ci    }
248cabdff1aSopenharmony_ci
249cabdff1aSopenharmony_ci    /* I have no idea what this pair of fields does either, might be reserved */
250cabdff1aSopenharmony_ci    ptr += 4 + 2;
251cabdff1aSopenharmony_ci
252cabdff1aSopenharmony_ci    /* Finally, write the STAB (sample table) chunk */
253cabdff1aSopenharmony_ci    bytestream_put_be32(&ptr, MKBETAG('S', 'T', 'A', 'B'));
254cabdff1aSopenharmony_ci    bytestream_put_be32(&ptr, stabsize);
255cabdff1aSopenharmony_ci    /* Framerate base frequency. Here we're assuming that the frame rate is even.
256cabdff1aSopenharmony_ci     * In real world Sega FILM files, there are usually a couple of approaches:
257cabdff1aSopenharmony_ci     * a) framerate base frequency is the same as the framerate, and ticks
258cabdff1aSopenharmony_ci     *    increment by 1 every frame, or
259cabdff1aSopenharmony_ci     * b) framerate base frequency is a much larger number, and ticks
260cabdff1aSopenharmony_ci     *    increment by larger steps every frame.
261cabdff1aSopenharmony_ci     * The latter occurs even in cases where the frame rate is even; for example, in
262cabdff1aSopenharmony_ci     * Lunar: Silver Star Story, the base frequency is 600 and each frame, the ticks
263cabdff1aSopenharmony_ci     * are incremented by 25 for an evenly spaced framerate of 24fps. */
264cabdff1aSopenharmony_ci    bytestream_put_be32(&ptr, av_q2d(av_inv_q(video->time_base)));
265cabdff1aSopenharmony_ci
266cabdff1aSopenharmony_ci    bytestream_put_be32(&ptr, packet_count);
267cabdff1aSopenharmony_ci
268cabdff1aSopenharmony_ci    /* Finally, shift the data and write out the header. */
269cabdff1aSopenharmony_ci    ret = write_header(format_context, header, headersize);
270cabdff1aSopenharmony_ci    if (ret < 0)
271cabdff1aSopenharmony_ci        return ret;
272cabdff1aSopenharmony_ci
273cabdff1aSopenharmony_ci    return 0;
274cabdff1aSopenharmony_ci}
275cabdff1aSopenharmony_ci
276cabdff1aSopenharmony_cistatic void film_deinit(AVFormatContext *format_context)
277cabdff1aSopenharmony_ci{
278cabdff1aSopenharmony_ci    FILMOutputContext *film = format_context->priv_data;
279cabdff1aSopenharmony_ci
280cabdff1aSopenharmony_ci    ffio_free_dyn_buf(&film->header);
281cabdff1aSopenharmony_ci}
282cabdff1aSopenharmony_ci
283cabdff1aSopenharmony_ciconst AVOutputFormat ff_segafilm_muxer = {
284cabdff1aSopenharmony_ci    .name           = "film_cpk",
285cabdff1aSopenharmony_ci    .long_name      = NULL_IF_CONFIG_SMALL("Sega FILM / CPK"),
286cabdff1aSopenharmony_ci    .extensions     = "cpk",
287cabdff1aSopenharmony_ci    .priv_data_size = sizeof(FILMOutputContext),
288cabdff1aSopenharmony_ci    .audio_codec    = AV_CODEC_ID_PCM_S16BE_PLANAR,
289cabdff1aSopenharmony_ci    .video_codec    = AV_CODEC_ID_CINEPAK,
290cabdff1aSopenharmony_ci    .init           = film_init,
291cabdff1aSopenharmony_ci    .write_trailer  = film_write_header,
292cabdff1aSopenharmony_ci    .write_packet   = film_write_packet,
293cabdff1aSopenharmony_ci    .deinit         = film_deinit,
294cabdff1aSopenharmony_ci};
295