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 };
init
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:35
AVOutputFormat::name
const char * name
Definition: avformat.h:491
AVERROR
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
FILMPacket::keyframe
int keyframe
Definition: segafilmenc.c:39
FILMOutputContext::stab_pos
int64_t stab_pos
Definition: segafilmenc.c:50
AVCodecParameters::codec_type
enum AVMediaType codec_type
General type of the encoded data.
Definition: codec_par.h:56
ffio_wfourcc
static av_always_inline void ffio_wfourcc(AVIOContext *pb, const uint8_t *s)
Definition: avio_internal.h:58
AV_CODEC_ID_RAWVIDEO
@ AV_CODEC_ID_RAWVIDEO
Definition: codec_id.h:62
film_init
static int film_init(AVFormatContext *format_context)
Definition: segafilmenc.c:156
AVFormatContext::streams
AVStream ** streams
A list of all streams in the file.
Definition: avformat.h:1403
film_write_packet_to_header
static int film_write_packet_to_header(AVFormatContext *format_context, FILMPacket *pkt)
Definition: segafilmenc.c:56
AVPacket::data
uint8_t * data
Definition: packet.h:355
AV_CODEC_ID_PCM_S16BE_PLANAR
@ AV_CODEC_ID_PCM_S16BE_PLANAR
Definition: codec_id.h:331
AVPacket::duration
int64_t duration
Duration of this packet in AVStream->time_base units, 0 if unknown.
Definition: packet.h:373
film_write_packet
static int film_write_packet(AVFormatContext *format_context, AVPacket *pkt)
Definition: segafilmenc.c:84
AV_PKT_FLAG_KEY
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
Definition: packet.h:388
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:31
FILMPacket::index
int32_t index
Definition: segafilmenc.c:43
FILMOutputContext::video_index
int video_index
Definition: segafilmenc.c:49
AVCodecParameters::channels
int channels
Audio only.
Definition: codec_par.h:166
FILMOutputContext
Definition: segafilmenc.c:47
U
#define U(x)
Definition: vp56_arith.h:37
avio_tell
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition: avio.h:557
shift_data
static int shift_data(AVFormatContext *format_context, int64_t shift_size)
Definition: segafilmenc.c:209
get_audio_codec_id
static int get_audio_codec_id(enum AVCodecID codec_id)
Definition: segafilmenc.c:142
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
FILMPacket::size
int32_t size
Definition: segafilmenc.c:42
intreadwrite.h
AVMEDIA_TYPE_AUDIO
@ AVMEDIA_TYPE_AUDIO
Definition: avutil.h:202
av_q2d
static double av_q2d(AVRational a)
Convert an AVRational to a double.
Definition: rational.h:104
codec_id
enum AVCodecID codec_id
Definition: vaapi_decode.c:369
int32_t
int32_t
Definition: audio_convert.c:194
avio_flush
void avio_flush(AVIOContext *s)
Force flushing of buffered data.
Definition: aviobuf.c:233
FILMPacket::next
struct FILMPacket * next
Definition: segafilmenc.c:44
AVFormatContext
Format I/O context.
Definition: avformat.h:1335
internal.h
AVStream::codecpar
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1012
NULL
#define NULL
Definition: coverity.c:32
write_trailer
static int write_trailer(AVFormatContext *s1)
Definition: v4l2enc.c:98
AV_CODEC_ID_CINEPAK
@ AV_CODEC_ID_CINEPAK
Definition: codec_id.h:92
AVFormatContext::pb
AVIOContext * pb
I/O context.
Definition: avformat.h:1377
avio_w8
void avio_w8(AVIOContext *s, int b)
Definition: aviobuf.c:191
AVCodecParameters::sample_rate
int sample_rate
Audio only.
Definition: codec_par.h:170
AVCodecID
AVCodecID
Identify the syntax and semantics of the bitstream.
Definition: codec_id.h:46
FILMPacket::duration
int32_t duration
Definition: segafilmenc.c:41
AVFormatContext::nb_streams
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:1391
FILMPacket::pts
int32_t pts
Definition: segafilmenc.c:40
AVIOContext
Bytestream IO Context.
Definition: avio.h:161
AV_PIX_FMT_RGB24
@ AV_PIX_FMT_RGB24
packed RGB 8:8:8, 24bpp, RGBRGB...
Definition: pixfmt.h:68
AV_CODEC_ID_ADPCM_ADX
@ AV_CODEC_ID_ADPCM_ADX
Definition: codec_id.h:349
AVPacket::size
int size
Definition: packet.h:356
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:188
AVFormatContext::url
char * url
input or output URL.
Definition: avformat.h:1431
FILMOutputContext::packet_count
int64_t packet_count
Definition: segafilmenc.c:53
AV_WB24
#define AV_WB24(p, d)
Definition: intreadwrite.h:450
FILMPacket
Definition: segafilmenc.c:37
avio_write
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition: aviobuf.c:213
avio_wb32
void avio_wb32(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:375
FILMOutputContext::last
FILMPacket * last
Definition: segafilmenc.c:52
AVPacket::flags
int flags
A combination of AV_PKT_FLAG values.
Definition: packet.h:361
write_packet
static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int unqueue)
Definition: ffmpeg.c:702
FILMPacket::audio
int audio
Definition: segafilmenc.c:38
AVOutputFormat
Definition: avformat.h:490
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:269
AVPacket::pts
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: packet.h:348
avio_internal.h
ff_segafilm_muxer
AVOutputFormat ff_segafilm_muxer
Definition: segafilmenc.c:380
uint8_t
uint8_t
Definition: audio_convert.c:194
av_mallocz
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:237
film_deinit
static void film_deinit(AVFormatContext *format_context)
Definition: segafilmenc.c:368
av_inv_q
static av_always_inline AVRational av_inv_q(AVRational q)
Invert a rational.
Definition: rational.h:159
ret
ret
Definition: filter_design.txt:187
AVStream
Stream structure.
Definition: avformat.h:865
avio_seek
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:241
pos
unsigned int pos
Definition: spdifenc.c:412
avformat.h
FILMOutputContext::audio_index
int audio_index
Definition: segafilmenc.c:48
pkt
static AVPacket pkt
Definition: demuxing_decoding.c:54
video
A Quick Description Of Rate Distortion Theory We want to encode a video
Definition: rate_distortion.txt:3
AVFormatContext::io_open
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:1917
AVPacket::stream_index
int stream_index
Definition: packet.h:357
ff_format_io_close
void ff_format_io_close(AVFormatContext *s, AVIOContext **pb)
Definition: utils.c:5695
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:674
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
AVCodecParameters::bits_per_coded_sample
int bits_per_coded_sample
The number of bits per sample in the codedwords.
Definition: codec_par.h:102
AVCodecParameters::format
int format
Definition: codec_par.h:84
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
AVCodecParameters::codec_id
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: codec_par.h:60
AVPacket
This structure stores compressed data.
Definition: packet.h:332
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
AV_CODEC_ID_PCM_S8_PLANAR
@ AV_CODEC_ID_PCM_S8_PLANAR
Definition: codec_id.h:328
film_write_header
static int film_write_header(AVFormatContext *format_context)
Definition: segafilmenc.c:266
avio_wb16
void avio_wb16(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:453
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:28
AV_RB24
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:93
AVFormatContext::priv_data
void * priv_data
Format private data.
Definition: avformat.h:1363
READ_BLOCK
#define READ_BLOCK
FILMOutputContext::start
FILMPacket * start
Definition: segafilmenc.c:51