FFmpeg
segafilmenc.c
Go to the documentation of this file.
1 /*
2  * Sega FILM Format (CPK) Muxer
3  * Copyright (C) 2003 The FFmpeg project
4  * Copyright (C) 2018 Misty De Meo
5  *
6  * This file is part of FFmpeg.
7  *
8  * FFmpeg is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * FFmpeg is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with FFmpeg; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 /**
24  * @file
25  * Sega FILM (.cpk) file muxer
26  * @author Misty De Meo <misty@brew.sh>
27  *
28  * @see For more information regarding the Sega FILM file format, visit:
29  * http://wiki.multimedia.cx/index.php?title=Sega_FILM
30  */
31 
32 #include "libavutil/intreadwrite.h"
33 #include "avformat.h"
34 #include "internal.h"
35 #include "avio_internal.h"
36 
37 typedef struct FILMPacket {
38  int audio;
39  int keyframe;
44  struct FILMPacket *next;
45 } FILMPacket;
46 
47 typedef struct FILMOutputContext {
48  const AVClass *class;
51  int64_t stab_pos;
54  int64_t packet_count;
56 
58 {
59  AVIOContext *pb = format_context->pb;
60  /* The bits in these two 32-bit integers contain info about the contents of this sample */
61  int32_t info1 = 0;
62  int32_t info2 = 0;
63 
64  if (pkt->audio) {
65  /* Always the same, carries no more information than "this is audio" */
66  info1 = 0xFFFFFFFF;
67  info2 = 1;
68  } else {
69  info1 = pkt->pts;
70  info2 = pkt->duration;
71  /* The top bit being set indicates a key frame */
72  if (!pkt->keyframe)
73  info1 |= 1U << 31;
74  }
75 
76  /* Write the 16-byte sample info packet to the STAB chunk in the header */
77  avio_wb32(pb, pkt->index);
78  avio_wb32(pb, pkt->size);
79  avio_wb32(pb, info1);
80  avio_wb32(pb, info2);
81 
82  return 0;
83 }
84 
85 static int film_write_packet(AVFormatContext *format_context, AVPacket *pkt)
86 {
87  FILMPacket *metadata;
88  AVIOContext *pb = format_context->pb;
89  FILMOutputContext *film = format_context->priv_data;
90  int encoded_buf_size = 0;
91  enum AVCodecID codec_id;
92 
93  /* Track the metadata used to write the header and add it to the linked list */
94  metadata = av_mallocz(sizeof(FILMPacket));
95  if (!metadata)
96  return AVERROR(ENOMEM);
97  metadata->audio = pkt->stream_index == film->audio_index;
98  metadata->keyframe = pkt->flags & AV_PKT_FLAG_KEY;
99  metadata->pts = pkt->pts;
100  metadata->duration = pkt->duration;
101  metadata->size = pkt->size;
102  if (film->last == NULL) {
103  metadata->index = 0;
104  } else {
105  metadata->index = film->last->index + film->last->size;
106  film->last->next = metadata;
107  }
108  metadata->next = NULL;
109  if (film->start == NULL)
110  film->start = metadata;
111  film->packet_count++;
112  film->last = metadata;
113 
114  codec_id = format_context->streams[pkt->stream_index]->codecpar->codec_id;
115 
116  /* Sega Cinepak has an extra two-byte header; write dummy data there,
117  * then adjust the cvid header to accommodate for the extra size */
118  if (codec_id == AV_CODEC_ID_CINEPAK) {
119  encoded_buf_size = AV_RB24(&pkt->data[1]);
120  /* Already Sega Cinepak, so no need to reformat the packets */
121  if (encoded_buf_size != pkt->size && (pkt->size % encoded_buf_size) != 0) {
122  avio_write(pb, pkt->data, pkt->size);
123  } else {
124  uint8_t padding[2] = {0, 0};
125  /* In Sega Cinepak, the reported size in the Cinepak header is
126  * 8 bytes too short. However, the size in the STAB section of the header
127  * is correct, taking into account the extra two bytes. */
128  AV_WB24(&pkt->data[1], pkt->size - 8 + 2);
129  metadata->size += 2;
130 
131  avio_write(pb, pkt->data, 10);
132  avio_write(pb, padding, 2);
133  avio_write(pb, &pkt->data[10], pkt->size - 10);
134  }
135  } else {
136  /* Other formats can just be written as-is */
137  avio_write(pb, pkt->data, pkt->size);
138  }
139 
140  return 0;
141 }
142 
144 {
145  /* 0 (PCM) and 2 (ADX) are the only known values */
146  switch (codec_id) {
149  return 0;
151  return 2;
152  default:
153  return -1;
154  }
155 }
156 
157 static int film_init(AVFormatContext *format_context)
158 {
159  AVStream *audio = NULL;
160  FILMOutputContext *film = format_context->priv_data;
161  film->audio_index = -1;
162  film->video_index = -1;
163  film->stab_pos = 0;
164  film->packet_count = 0;
165  film->start = NULL;
166  film->last = NULL;
167 
168  for (int i = 0; i < format_context->nb_streams; i++) {
169  AVStream *st = format_context->streams[i];
170  if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
171  if (film->audio_index > -1) {
172  av_log(format_context, AV_LOG_ERROR, "Sega FILM allows a maximum of one audio stream.\n");
173  return AVERROR(EINVAL);
174  }
175  film->audio_index = i;
176  audio = st;
177  }
178 
179  if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
180  if (film->video_index > -1) {
181  av_log(format_context, AV_LOG_ERROR, "Sega FILM allows a maximum of one video stream.\n");
182  return AVERROR(EINVAL);
183  }
184  if (st->codecpar->codec_id != AV_CODEC_ID_CINEPAK &&
186  av_log(format_context, AV_LOG_ERROR,
187  "Incompatible video stream format.\n");
188  return AVERROR(EINVAL);
189  }
190  if (st->codecpar->format != AV_PIX_FMT_RGB24) {
191  av_log(format_context, AV_LOG_ERROR,
192  "Pixel format must be rgb24.\n");
193  return AVERROR(EINVAL);
194  }
195  film->video_index = i;
196  }
197  }
198 
199  if (film->video_index == -1) {
200  av_log(format_context, AV_LOG_ERROR, "No video stream present.\n");
201  return AVERROR(EINVAL);
202  }
203 
204  if (audio != NULL && get_audio_codec_id(audio->codecpar->codec_id) < 0) {
205  av_log(format_context, AV_LOG_ERROR, "Incompatible audio stream format.\n");
206  return AVERROR(EINVAL);
207  }
208 
209  return 0;
210 }
211 
212 static int shift_data(AVFormatContext *format_context, int64_t shift_size)
213 {
214  int ret = 0;
215  int64_t pos, pos_end;
216  uint8_t *buf, *read_buf[2];
217  int read_buf_id = 0;
218  int read_size[2];
219  AVIOContext *read_pb;
220 
221  buf = av_malloc(shift_size * 2);
222  if (!buf)
223  return AVERROR(ENOMEM);
224  read_buf[0] = buf;
225  read_buf[1] = buf + shift_size;
226 
227  /* Write the header at the beginning of the file, shifting all content as necessary;
228  * based on the approach used by MOV faststart. */
229  avio_flush(format_context->pb);
230  ret = format_context->io_open(format_context, &read_pb, format_context->url, AVIO_FLAG_READ, NULL);
231  if (ret < 0) {
232  av_log(format_context, AV_LOG_ERROR, "Unable to re-open %s output file to "
233  "write the header\n", format_context->url);
234  av_free(buf);
235  return ret;
236  }
237 
238  /* mark the end of the shift to up to the last data we wrote, and get ready
239  * for writing */
240  pos_end = avio_tell(format_context->pb);
241  avio_seek(format_context->pb, shift_size, SEEK_SET);
242 
243  /* start reading at where the new header will be placed */
244  avio_seek(read_pb, 0, SEEK_SET);
245  pos = avio_tell(read_pb);
246 
247 #define READ_BLOCK do { \
248  read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id], shift_size); \
249  read_buf_id ^= 1; \
250 } while (0)
251 
252  /* shift data by chunk of at most shift_size */
253  READ_BLOCK;
254  do {
255  int n;
256  READ_BLOCK;
257  n = read_size[read_buf_id];
258  if (n <= 0)
259  break;
260  avio_write(format_context->pb, read_buf[read_buf_id], n);
261  pos += n;
262  } while (pos < pos_end);
263  ff_format_io_close(format_context, &read_pb);
264 
265  av_free(buf);
266  return 0;
267 }
268 
269 static int film_write_header(AVFormatContext *format_context)
270 {
271  int ret = 0;
272  int64_t sample_table_size, stabsize, headersize;
273  int8_t audio_codec;
274  AVIOContext *pb = format_context->pb;
275  FILMOutputContext *film = format_context->priv_data;
276  FILMPacket *prev, *packet;
277  AVStream *audio = NULL;
278  AVStream *video = NULL;
279 
280  /* Calculate how much we need to reserve for the header;
281  * this is the amount the rest of the data will be shifted up by. */
282  sample_table_size = film->packet_count * 16;
283  stabsize = 16 + sample_table_size;
284  headersize = 16 + /* FILM header base */
285  32 + /* FDSC chunk */
286  stabsize;
287 
288  ret = shift_data(format_context, headersize);
289  if (ret < 0)
290  return ret;
291  /* Seek back to the beginning to start writing the header now */
292  avio_seek(pb, 0, SEEK_SET);
293 
294  if (film->audio_index > -1)
295  audio = format_context->streams[film->audio_index];
296  if (film->video_index > -1)
297  video = format_context->streams[film->video_index];
298 
299  if (audio != NULL) {
300  audio_codec = get_audio_codec_id(audio->codecpar->codec_id);
301  if (audio_codec < 0) {
302  av_log(format_context, AV_LOG_ERROR, "Incompatible audio stream format.\n");
303  return AVERROR(EINVAL);
304  }
305  }
306 
307  /* First, write the FILM header; this is very simple */
308 
309  ffio_wfourcc(pb, "FILM");
310  avio_wb32(pb, 48 + stabsize);
311  /* This seems to be okay to hardcode, since this muxer targets 1.09 features;
312  * videos produced by this muxer are readable by 1.08 and lower players. */
313  ffio_wfourcc(pb, "1.09");
314  /* I have no idea what this field does, might be reserved */
315  avio_wb32(pb, 0);
316 
317  /* Next write the FDSC (file description) chunk */
318  ffio_wfourcc(pb, "FDSC");
319  avio_wb32(pb, 0x20); /* Size of FDSC chunk */
320 
321  /* The only two supported codecs; raw video is rare */
322  switch (video->codecpar->codec_id) {
323  case AV_CODEC_ID_CINEPAK:
324  ffio_wfourcc(pb, "cvid");
325  break;
327  ffio_wfourcc(pb, "raw ");
328  break;
329  }
330 
331  avio_wb32(pb, video->codecpar->height);
332  avio_wb32(pb, video->codecpar->width);
333  avio_w8(pb, 24); /* Bits per pixel - observed to always be 24 */
334 
335  if (audio != NULL) {
336  avio_w8(pb, audio->codecpar->channels); /* Audio channels */
337  avio_w8(pb, audio->codecpar->bits_per_coded_sample); /* Audio bit depth */
338  avio_w8(pb, audio_codec); /* Compression - 0 is PCM, 2 is ADX */
339  avio_wb16(pb, audio->codecpar->sample_rate); /* Audio sampling rate */
340  } else {
341  /* Set all these fields to 0 if there's no audio */
342  avio_w8(pb, 0);
343  avio_w8(pb, 0);
344  avio_w8(pb, 0);
345  avio_wb16(pb, 0);
346  }
347 
348  /* I have no idea what this pair of fields does either, might be reserved */
349  avio_wb32(pb, 0);
350  avio_wb16(pb, 0);
351 
352  /* Finally, write the STAB (sample table) chunk */
353  ffio_wfourcc(pb, "STAB");
354  avio_wb32(pb, 16 + (film->packet_count * 16));
355  /* Framerate base frequency. Here we're assuming that the frame rate is even.
356  * In real world Sega FILM files, there are usually a couple of approaches:
357  * a) framerate base frequency is the same as the framerate, and ticks
358  * increment by 1 every frame, or
359  * b) framerate base frequency is a much larger number, and ticks
360  * increment by larger steps every frame.
361  * The latter occurs even in cases where the frame rate is even; for example, in
362  * Lunar: Silver Star Story, the base frequency is 600 and each frame, the ticks
363  * are incremented by 25 for an evenly spaced framerate of 24fps. */
364  avio_wb32(pb, av_q2d(av_inv_q(video->time_base)));
365 
366  avio_wb32(pb, film->packet_count);
367 
368  /* Finally, write out each packet's data to the header */
369  packet = film->start;
370  while (packet != NULL) {
371  film_write_packet_to_header(format_context, packet);
372  prev = packet;
373  packet = packet->next;
374  av_freep(&prev);
375  }
376 
377  return 0;
378 }
379 
380 static const AVClass film_muxer_class = {
381  .class_name = "Sega FILM muxer",
382  .item_name = av_default_item_name,
383  .version = LIBAVUTIL_VERSION_INT,
384 };
385 
387  .name = "film_cpk",
388  .long_name = NULL_IF_CONFIG_SMALL("Sega FILM / CPK"),
389  .extensions = "cpk",
390  .priv_data_size = sizeof(FILMOutputContext),
391  .audio_codec = AV_CODEC_ID_PCM_S16BE_PLANAR,
392  .video_codec = AV_CODEC_ID_CINEPAK,
393  .init = film_init,
396  .priv_class = &film_muxer_class,
397 };
static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int unqueue)
Definition: ffmpeg.c:690
int(* io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **options)
A callback for opening new IO streams.
Definition: avformat.h:1939
#define NULL
Definition: coverity.c:32
Bytestream IO Context.
Definition: avio.h:161
int32_t index
Definition: segafilmenc.c:43
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
packed RGB 8:8:8, 24bpp, RGBRGB...
Definition: pixfmt.h:68
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:35
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: avcodec.h:3981
int size
Definition: avcodec.h:1494
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:246
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:191
#define AVIO_FLAG_READ
read-only
Definition: avio.h:674
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:236
static AVPacket pkt
Format I/O context.
Definition: avformat.h:1357
AVOutputFormat ff_segafilm_muxer
Definition: segafilmenc.c:386
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:72
uint8_t
#define av_malloc(s)
int width
Video only.
Definition: avcodec.h:4047
int64_t duration
Duration of this packet in AVStream->time_base units, 0 if unknown.
Definition: avcodec.h:1511
void ff_format_io_close(AVFormatContext *s, AVIOContext **pb)
Definition: utils.c:5682
#define READ_BLOCK
AVStream ** streams
A list of all streams in the file.
Definition: avformat.h:1425
static const AVClass film_muxer_class
Definition: segafilmenc.c:380
uint8_t * data
Definition: avcodec.h:1493
static double av_q2d(AVRational a)
Convert an AVRational to a double.
Definition: rational.h:104
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition: avio.h:557
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition: aviobuf.c:218
static av_always_inline void ffio_wfourcc(AVIOContext *pb, const uint8_t *s)
Definition: avio_internal.h:58
#define av_log(a,...)
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
Definition: avcodec.h:1525
#define U(x)
Definition: vp56_arith.h:37
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
AVCodecID
Identify the syntax and semantics of the bitstream.
Definition: avcodec.h:215
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
int32_t pts
Definition: segafilmenc.c:40
FILMPacket * last
Definition: segafilmenc.c:53
int32_t size
Definition: segafilmenc.c:42
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:186
char * url
input or output URL.
Definition: avformat.h:1453
static int film_init(AVFormatContext *format_context)
Definition: segafilmenc.c:157
enum AVMediaType codec_type
General type of the encoded data.
Definition: avcodec.h:3977
struct FILMPacket * next
Definition: segafilmenc.c:44
int flags
A combination of AV_PKT_FLAG values.
Definition: avcodec.h:1499
static int film_write_packet(AVFormatContext *format_context, AVPacket *pkt)
Definition: segafilmenc.c:85
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:1413
static int write_trailer(AVFormatContext *s1)
Definition: v4l2enc.c:94
const char * name
Definition: avformat.h:505
static int shift_data(AVFormatContext *format_context, int64_t shift_size)
Definition: segafilmenc.c:212
int32_t
#define AV_WB24(p, d)
Definition: intreadwrite.h:450
static int film_write_header(AVFormatContext *format_context)
Definition: segafilmenc.c:269
int n
Definition: avisynth_c.h:760
enum AVCodecID codec_id
Definition: vaapi_decode.c:364
Stream structure.
Definition: avformat.h:880
FILMPacket * start
Definition: segafilmenc.c:52
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_WB64 unsigned int_TMPL AV_WB32 unsigned int_TMPL AV_RB24
Definition: bytestream.h:87
AVIOContext * pb
I/O context.
Definition: avformat.h:1399
void avio_w8(AVIOContext *s, int b)
Definition: aviobuf.c:196
void * buf
Definition: avisynth_c.h:766
Describe the class of an AVClass context structure.
Definition: log.h:67
int32_t duration
Definition: segafilmenc.c:41
void avio_flush(AVIOContext *s)
Force flushing of buffered data.
Definition: aviobuf.c:238
void avio_wb16(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:478
static av_always_inline AVRational av_inv_q(AVRational q)
Invert a rational.
Definition: rational.h:159
int sample_rate
Audio only.
Definition: avcodec.h:4091
Main libavformat public API header.
static int get_audio_codec_id(enum AVCodecID codec_id)
Definition: segafilmenc.c:143
int64_t packet_count
Definition: segafilmenc.c:54
A Quick Description Of Rate Distortion Theory We want to encode a video
#define av_free(p)
int keyframe
Definition: segafilmenc.c:39
void * priv_data
Format private data.
Definition: avformat.h:1385
int bits_per_coded_sample
The number of bits per sample in the codedwords.
Definition: avcodec.h:4023
int channels
Audio only.
Definition: avcodec.h:4087
void avio_wb32(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:380
#define av_freep(p)
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1027
int stream_index
Definition: avcodec.h:1495
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented...
Definition: avformat.h:909
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Frame references ownership and permissions
This structure stores compressed data.
Definition: avcodec.h:1470
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: avcodec.h:1486
static int film_write_packet_to_header(AVFormatContext *format_context, FILMPacket *pkt)
Definition: segafilmenc.c:57