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 {
50  int64_t stab_pos;
53  int64_t packet_count;
55 
57 {
58  AVIOContext *pb = format_context->pb;
59  /* The bits in these two 32-bit integers contain info about the contents of this sample */
60  int32_t info1 = 0;
61  int32_t info2 = 0;
62 
63  if (pkt->audio) {
64  /* Always the same, carries no more information than "this is audio" */
65  info1 = 0xFFFFFFFF;
66  info2 = 1;
67  } else {
68  info1 = pkt->pts;
69  info2 = pkt->duration;
70  /* The top bit being set indicates a key frame */
71  if (!pkt->keyframe)
72  info1 |= 1U << 31;
73  }
74 
75  /* Write the 16-byte sample info packet to the STAB chunk in the header */
76  avio_wb32(pb, pkt->index);
77  avio_wb32(pb, pkt->size);
78  avio_wb32(pb, info1);
79  avio_wb32(pb, info2);
80 
81  return 0;
82 }
83 
84 static int film_write_packet(AVFormatContext *format_context, AVPacket *pkt)
85 {
86  FILMPacket *metadata;
87  AVIOContext *pb = format_context->pb;
88  FILMOutputContext *film = format_context->priv_data;
89  int encoded_buf_size = 0;
90  enum AVCodecID codec_id;
91 
92  /* Track the metadata used to write the header and add it to the linked list */
93  metadata = av_mallocz(sizeof(FILMPacket));
94  if (!metadata)
95  return AVERROR(ENOMEM);
96  metadata->audio = pkt->stream_index == film->audio_index;
97  metadata->keyframe = pkt->flags & AV_PKT_FLAG_KEY;
98  metadata->pts = pkt->pts;
99  metadata->duration = pkt->duration;
100  metadata->size = pkt->size;
101  if (film->last == NULL) {
102  metadata->index = 0;
103  } else {
104  metadata->index = film->last->index + film->last->size;
105  film->last->next = metadata;
106  }
107  metadata->next = NULL;
108  if (film->start == NULL)
109  film->start = metadata;
110  film->packet_count++;
111  film->last = metadata;
112 
113  codec_id = format_context->streams[pkt->stream_index]->codecpar->codec_id;
114 
115  /* Sega Cinepak has an extra two-byte header; write dummy data there,
116  * then adjust the cvid header to accommodate for the extra size */
117  if (codec_id == AV_CODEC_ID_CINEPAK) {
118  encoded_buf_size = AV_RB24(&pkt->data[1]);
119  /* Already Sega Cinepak, so no need to reformat the packets */
120  if (encoded_buf_size != pkt->size && (pkt->size % encoded_buf_size) != 0) {
121  avio_write(pb, pkt->data, pkt->size);
122  } else {
123  uint8_t padding[2] = {0, 0};
124  /* In Sega Cinepak, the reported size in the Cinepak header is
125  * 8 bytes too short. However, the size in the STAB section of the header
126  * is correct, taking into account the extra two bytes. */
127  AV_WB24(&pkt->data[1], pkt->size - 8 + 2);
128  metadata->size += 2;
129 
130  avio_write(pb, pkt->data, 10);
131  avio_write(pb, padding, 2);
132  avio_write(pb, &pkt->data[10], pkt->size - 10);
133  }
134  } else {
135  /* Other formats can just be written as-is */
136  avio_write(pb, pkt->data, pkt->size);
137  }
138 
139  return 0;
140 }
141 
143 {
144  /* 0 (PCM) and 2 (ADX) are the only known values */
145  switch (codec_id) {
148  return 0;
150  return 2;
151  default:
152  return -1;
153  }
154 }
155 
156 static int film_init(AVFormatContext *format_context)
157 {
158  FILMOutputContext *film = format_context->priv_data;
159  film->audio_index = -1;
160  film->video_index = -1;
161  film->stab_pos = 0;
162  film->packet_count = 0;
163  film->start = NULL;
164  film->last = NULL;
165 
166  for (int i = 0; i < format_context->nb_streams; i++) {
167  AVStream *st = format_context->streams[i];
168  if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
169  if (film->audio_index > -1) {
170  av_log(format_context, AV_LOG_ERROR, "Sega FILM allows a maximum of one audio stream.\n");
171  return AVERROR(EINVAL);
172  }
173  if (get_audio_codec_id(st->codecpar->codec_id) < 0) {
174  av_log(format_context, AV_LOG_ERROR,
175  "Incompatible audio stream format.\n");
176  return AVERROR(EINVAL);
177  }
178  film->audio_index = i;
179  }
180 
181  if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
182  if (film->video_index > -1) {
183  av_log(format_context, AV_LOG_ERROR, "Sega FILM allows a maximum of one video stream.\n");
184  return AVERROR(EINVAL);
185  }
186  if (st->codecpar->codec_id != AV_CODEC_ID_CINEPAK &&
188  av_log(format_context, AV_LOG_ERROR,
189  "Incompatible video stream format.\n");
190  return AVERROR(EINVAL);
191  }
192  if (st->codecpar->format != AV_PIX_FMT_RGB24) {
193  av_log(format_context, AV_LOG_ERROR,
194  "Pixel format must be rgb24.\n");
195  return AVERROR(EINVAL);
196  }
197  film->video_index = i;
198  }
199  }
200 
201  if (film->video_index == -1) {
202  av_log(format_context, AV_LOG_ERROR, "No video stream present.\n");
203  return AVERROR(EINVAL);
204  }
205 
206  return 0;
207 }
208 
209 static int shift_data(AVFormatContext *format_context, int64_t shift_size)
210 {
211  int ret = 0;
212  int64_t pos, pos_end;
213  uint8_t *buf, *read_buf[2];
214  int read_buf_id = 0;
215  int read_size[2];
216  AVIOContext *read_pb;
217 
218  buf = av_malloc(shift_size * 2);
219  if (!buf)
220  return AVERROR(ENOMEM);
221  read_buf[0] = buf;
222  read_buf[1] = buf + shift_size;
223 
224  /* Write the header at the beginning of the file, shifting all content as necessary;
225  * based on the approach used by MOV faststart. */
226  avio_flush(format_context->pb);
227  ret = format_context->io_open(format_context, &read_pb, format_context->url, AVIO_FLAG_READ, NULL);
228  if (ret < 0) {
229  av_log(format_context, AV_LOG_ERROR, "Unable to re-open %s output file to "
230  "write the header\n", format_context->url);
231  av_free(buf);
232  return ret;
233  }
234 
235  /* mark the end of the shift to up to the last data we wrote, and get ready
236  * for writing */
237  pos_end = avio_tell(format_context->pb);
238  avio_seek(format_context->pb, shift_size, SEEK_SET);
239 
240  /* start reading at where the new header will be placed */
241  avio_seek(read_pb, 0, SEEK_SET);
242  pos = avio_tell(read_pb);
243 
244 #define READ_BLOCK do { \
245  read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id], shift_size); \
246  read_buf_id ^= 1; \
247 } while (0)
248 
249  /* shift data by chunk of at most shift_size */
250  READ_BLOCK;
251  do {
252  int n;
253  READ_BLOCK;
254  n = read_size[read_buf_id];
255  if (n <= 0)
256  break;
257  avio_write(format_context->pb, read_buf[read_buf_id], n);
258  pos += n;
259  } while (pos < pos_end);
260  ff_format_io_close(format_context, &read_pb);
261 
262  av_free(buf);
263  return 0;
264 }
265 
266 static int film_write_header(AVFormatContext *format_context)
267 {
268  int ret = 0;
269  int64_t sample_table_size, stabsize, headersize;
270  AVIOContext *pb = format_context->pb;
271  FILMOutputContext *film = format_context->priv_data;
272  FILMPacket *prev, *packet;
273  AVStream *video = NULL;
274 
275  /* Calculate how much we need to reserve for the header;
276  * this is the amount the rest of the data will be shifted up by. */
277  sample_table_size = film->packet_count * 16;
278  stabsize = 16 + sample_table_size;
279  headersize = 16 + /* FILM header base */
280  32 + /* FDSC chunk */
281  stabsize;
282 
283  ret = shift_data(format_context, headersize);
284  if (ret < 0)
285  return ret;
286  /* Seek back to the beginning to start writing the header now */
287  avio_seek(pb, 0, SEEK_SET);
288 
289  /* First, write the FILM header; this is very simple */
290 
291  ffio_wfourcc(pb, "FILM");
292  avio_wb32(pb, 48 + stabsize);
293  /* This seems to be okay to hardcode, since this muxer targets 1.09 features;
294  * videos produced by this muxer are readable by 1.08 and lower players. */
295  ffio_wfourcc(pb, "1.09");
296  /* I have no idea what this field does, might be reserved */
297  avio_wb32(pb, 0);
298 
299  /* Next write the FDSC (file description) chunk */
300  ffio_wfourcc(pb, "FDSC");
301  avio_wb32(pb, 0x20); /* Size of FDSC chunk */
302 
303  video = format_context->streams[film->video_index];
304 
305  /* The only two supported codecs; raw video is rare */
306  switch (video->codecpar->codec_id) {
307  case AV_CODEC_ID_CINEPAK:
308  ffio_wfourcc(pb, "cvid");
309  break;
311  ffio_wfourcc(pb, "raw ");
312  break;
313  }
314 
315  avio_wb32(pb, video->codecpar->height);
316  avio_wb32(pb, video->codecpar->width);
317  avio_w8(pb, 24); /* Bits per pixel - observed to always be 24 */
318 
319  if (film->audio_index > -1) {
320  AVStream *audio = format_context->streams[film->audio_index];
321  int audio_codec = get_audio_codec_id(audio->codecpar->codec_id);
322 
323  avio_w8(pb, audio->codecpar->channels); /* Audio channels */
324  avio_w8(pb, audio->codecpar->bits_per_coded_sample); /* Audio bit depth */
325  avio_w8(pb, audio_codec); /* Compression - 0 is PCM, 2 is ADX */
326  avio_wb16(pb, audio->codecpar->sample_rate); /* Audio sampling rate */
327  } else {
328  /* Set all these fields to 0 if there's no audio */
329  avio_w8(pb, 0);
330  avio_w8(pb, 0);
331  avio_w8(pb, 0);
332  avio_wb16(pb, 0);
333  }
334 
335  /* I have no idea what this pair of fields does either, might be reserved */
336  avio_wb32(pb, 0);
337  avio_wb16(pb, 0);
338 
339  /* Finally, write the STAB (sample table) chunk */
340  ffio_wfourcc(pb, "STAB");
341  avio_wb32(pb, 16 + (film->packet_count * 16));
342  /* Framerate base frequency. Here we're assuming that the frame rate is even.
343  * In real world Sega FILM files, there are usually a couple of approaches:
344  * a) framerate base frequency is the same as the framerate, and ticks
345  * increment by 1 every frame, or
346  * b) framerate base frequency is a much larger number, and ticks
347  * increment by larger steps every frame.
348  * The latter occurs even in cases where the frame rate is even; for example, in
349  * Lunar: Silver Star Story, the base frequency is 600 and each frame, the ticks
350  * are incremented by 25 for an evenly spaced framerate of 24fps. */
351  avio_wb32(pb, av_q2d(av_inv_q(video->time_base)));
352 
353  avio_wb32(pb, film->packet_count);
354 
355  /* Finally, write out each packet's data to the header */
356  packet = film->start;
357  while (packet != NULL) {
358  film_write_packet_to_header(format_context, packet);
359  prev = packet;
360  packet = packet->next;
361  av_freep(&prev);
362  }
363  film->start = film->last = NULL;
364 
365  return 0;
366 }
367 
368 static void film_deinit(AVFormatContext *format_context)
369 {
370  FILMOutputContext *film = format_context->priv_data;
371  FILMPacket *packet = film->start;
372  while (packet != NULL) {
373  FILMPacket *next = packet->next;
374  av_free(packet);
375  packet = next;
376  }
377  film->start = film->last = NULL;
378 }
379 
381  .name = "film_cpk",
382  .long_name = NULL_IF_CONFIG_SMALL("Sega FILM / CPK"),
383  .extensions = "cpk",
384  .priv_data_size = sizeof(FILMOutputContext),
385  .audio_codec = AV_CODEC_ID_PCM_S16BE_PLANAR,
386  .video_codec = AV_CODEC_ID_CINEPAK,
387  .init = film_init,
390  .deinit = film_deinit,
391 };
static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int unqueue)
Definition: ffmpeg.c:698
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:1935
#define NULL
Definition: coverity.c:32
Bytestream IO Context.
Definition: avio.h:161
int32_t index
Definition: segafilmenc.c:43
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:4036
int size
Definition: avcodec.h:1534
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:241
#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:1353
AVOutputFormat ff_segafilm_muxer
Definition: segafilmenc.c:380
uint8_t
#define av_malloc(s)
int width
Video only.
Definition: avcodec.h:4102
int64_t duration
Duration of this packet in AVStream->time_base units, 0 if unknown.
Definition: avcodec.h:1551
void ff_format_io_close(AVFormatContext *s, AVIOContext **pb)
Definition: utils.c:5697
#define READ_BLOCK
AVStream ** streams
A list of all streams in the file.
Definition: avformat.h:1421
uint8_t * data
Definition: avcodec.h:1533
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:213
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:1565
#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:217
#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:52
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:1449
unsigned int pos
Definition: spdifenc.c:410
static int film_init(AVFormatContext *format_context)
Definition: segafilmenc.c:156
enum AVMediaType codec_type
General type of the encoded data.
Definition: avcodec.h:4032
struct FILMPacket * next
Definition: segafilmenc.c:44
int flags
A combination of AV_PKT_FLAG values.
Definition: avcodec.h:1539
static int film_write_packet(AVFormatContext *format_context, AVPacket *pkt)
Definition: segafilmenc.c:84
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:1409
static int write_trailer(AVFormatContext *s1)
Definition: v4l2enc.c:94
const char * name
Definition: avformat.h:500
static int shift_data(AVFormatContext *format_context, int64_t shift_size)
Definition: segafilmenc.c:209
int32_t
#define AV_WB24(p, d)
Definition: intreadwrite.h:450
static int film_write_header(AVFormatContext *format_context)
Definition: segafilmenc.c:266
enum AVCodecID codec_id
Definition: vaapi_decode.c:369
Stream structure.
Definition: avformat.h:876
FILMPacket * start
Definition: segafilmenc.c:51
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:1395
void avio_w8(AVIOContext *s, int b)
Definition: aviobuf.c:191
int32_t duration
Definition: segafilmenc.c:41
void avio_flush(AVIOContext *s)
Force flushing of buffered data.
Definition: aviobuf.c:233
void avio_wb16(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:473
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:4146
Main libavformat public API header.
static int get_audio_codec_id(enum AVCodecID codec_id)
Definition: segafilmenc.c:142
int64_t packet_count
Definition: segafilmenc.c:53
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:1381
static void film_deinit(AVFormatContext *format_context)
Definition: segafilmenc.c:368
int bits_per_coded_sample
The number of bits per sample in the codedwords.
Definition: avcodec.h:4078
int channels
Audio only.
Definition: avcodec.h:4142
void avio_wb32(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:375
#define av_freep(p)
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1023
int stream_index
Definition: avcodec.h:1535
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented...
Definition: avformat.h:905
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:1510
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: avcodec.h:1526
static int film_write_packet_to_header(AVFormatContext *format_context, FILMPacket *pkt)
Definition: segafilmenc.c:56