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