rpl.c
Go to the documentation of this file.
1 /*
2  * ARMovie/RPL demuxer
3  * Copyright (c) 2007 Christian Ohm, 2008 Eli Friedman
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include "libavutil/avstring.h"
23 #include "libavutil/dict.h"
24 #include "avformat.h"
25 #include "internal.h"
26 #include <stdlib.h>
27 
28 #define RPL_SIGNATURE "ARMovie\x0A"
29 #define RPL_SIGNATURE_SIZE 8
30 
31 /** 256 is arbitrary, but should be big enough for any reasonable file. */
32 #define RPL_LINE_LENGTH 256
33 
34 static int rpl_probe(AVProbeData *p)
35 {
36  if (memcmp(p->buf, RPL_SIGNATURE, RPL_SIGNATURE_SIZE))
37  return 0;
38 
39  return AVPROBE_SCORE_MAX;
40 }
41 
42 typedef struct RPLContext {
43  // RPL header data
45 
46  // Stream position data
47  uint32_t chunk_number;
48  uint32_t chunk_part;
49  uint32_t frame_in_part;
50 } RPLContext;
51 
52 static int read_line(AVIOContext * pb, char* line, int bufsize)
53 {
54  int i;
55  for (i = 0; i < bufsize - 1; i++) {
56  int b = avio_r8(pb);
57  if (b == 0)
58  break;
59  if (b == '\n') {
60  line[i] = '\0';
61  return url_feof(pb) ? -1 : 0;
62  }
63  line[i] = b;
64  }
65  line[i] = '\0';
66  return -1;
67 }
68 
69 static int32_t read_int(const char* line, const char** endptr, int* error)
70 {
71  unsigned long result = 0;
72  for (; *line>='0' && *line<='9'; line++) {
73  if (result > (0x7FFFFFFF - 9) / 10)
74  *error = -1;
75  result = 10 * result + *line - '0';
76  }
77  *endptr = line;
78  return result;
79 }
80 
81 static int32_t read_line_and_int(AVIOContext * pb, int* error)
82 {
83  char line[RPL_LINE_LENGTH];
84  const char *endptr;
85  *error |= read_line(pb, line, sizeof(line));
86  return read_int(line, &endptr, error);
87 }
88 
89 /** Parsing for fps, which can be a fraction. Unfortunately,
90  * the spec for the header leaves out a lot of details,
91  * so this is mostly guessing.
92  */
93 static AVRational read_fps(const char* line, int* error)
94 {
95  int64_t num, den = 1;
96  AVRational result;
97  num = read_int(line, &line, error);
98  if (*line == '.')
99  line++;
100  for (; *line>='0' && *line<='9'; line++) {
101  // Truncate any numerator too large to fit into an int64_t
102  if (num > (INT64_MAX - 9) / 10 || den > INT64_MAX / 10)
103  break;
104  num = 10 * num + *line - '0';
105  den *= 10;
106  }
107  if (!num)
108  *error = -1;
109  av_reduce(&result.num, &result.den, num, den, 0x7FFFFFFF);
110  return result;
111 }
112 
114 {
115  AVIOContext *pb = s->pb;
116  RPLContext *rpl = s->priv_data;
117  AVStream *vst = NULL, *ast = NULL;
118  int total_audio_size;
119  int error = 0;
120 
121  uint32_t i;
122 
123  int32_t audio_format, chunk_catalog_offset, number_of_chunks;
124  AVRational fps;
125 
126  char line[RPL_LINE_LENGTH];
127 
128  // The header for RPL/ARMovie files is 21 lines of text
129  // containing the various header fields. The fields are always
130  // in the same order, and other text besides the first
131  // number usually isn't important.
132  // (The spec says that there exists some significance
133  // for the text in a few cases; samples needed.)
134  error |= read_line(pb, line, sizeof(line)); // ARMovie
135  error |= read_line(pb, line, sizeof(line)); // movie name
136  av_dict_set(&s->metadata, "title" , line, 0);
137  error |= read_line(pb, line, sizeof(line)); // date/copyright
138  av_dict_set(&s->metadata, "copyright", line, 0);
139  error |= read_line(pb, line, sizeof(line)); // author and other
140  av_dict_set(&s->metadata, "author" , line, 0);
141 
142  // video headers
143  vst = avformat_new_stream(s, NULL);
144  if (!vst)
145  return AVERROR(ENOMEM);
147  vst->codec->codec_tag = read_line_and_int(pb, &error); // video format
148  vst->codec->width = read_line_and_int(pb, &error); // video width
149  vst->codec->height = read_line_and_int(pb, &error); // video height
150  vst->codec->bits_per_coded_sample = read_line_and_int(pb, &error); // video bits per sample
151  error |= read_line(pb, line, sizeof(line)); // video frames per second
152  fps = read_fps(line, &error);
153  avpriv_set_pts_info(vst, 32, fps.den, fps.num);
154 
155  // Figure out the video codec
156  switch (vst->codec->codec_tag) {
157 #if 0
158  case 122:
159  vst->codec->codec_id = AV_CODEC_ID_ESCAPE122;
160  break;
161 #endif
162  case 124:
164  // The header is wrong here, at least sometimes
165  vst->codec->bits_per_coded_sample = 16;
166  break;
167  case 130:
169  break;
170  default:
172  "RPL video format %i not supported yet!\n",
173  vst->codec->codec_tag);
175  }
176 
177  // Audio headers
178 
179  // ARMovie supports multiple audio tracks; I don't have any
180  // samples, though. This code will ignore additional tracks.
181  audio_format = read_line_and_int(pb, &error); // audio format ID
182  if (audio_format) {
183  ast = avformat_new_stream(s, NULL);
184  if (!ast)
185  return AVERROR(ENOMEM);
186  ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
187  ast->codec->codec_tag = audio_format;
188  ast->codec->sample_rate = read_line_and_int(pb, &error); // audio bitrate
189  ast->codec->channels = read_line_and_int(pb, &error); // number of audio channels
190  ast->codec->bits_per_coded_sample = read_line_and_int(pb, &error); // audio bits per sample
191  // At least one sample uses 0 for ADPCM, which is really 4 bits
192  // per sample.
193  if (ast->codec->bits_per_coded_sample == 0)
194  ast->codec->bits_per_coded_sample = 4;
195 
196  ast->codec->bit_rate = ast->codec->sample_rate *
197  ast->codec->bits_per_coded_sample *
198  ast->codec->channels;
199 
200  ast->codec->codec_id = AV_CODEC_ID_NONE;
201  switch (audio_format) {
202  case 1:
203  if (ast->codec->bits_per_coded_sample == 16) {
204  // 16-bit audio is always signed
205  ast->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
206  break;
207  }
208  // There are some other formats listed as legal per the spec;
209  // samples needed.
210  break;
211  case 101:
212  if (ast->codec->bits_per_coded_sample == 8) {
213  // The samples with this kind of audio that I have
214  // are all unsigned.
215  ast->codec->codec_id = AV_CODEC_ID_PCM_U8;
216  break;
217  } else if (ast->codec->bits_per_coded_sample == 4) {
218  ast->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_EA_SEAD;
219  break;
220  }
221  break;
222  }
223  if (ast->codec->codec_id == AV_CODEC_ID_NONE) {
225  "RPL audio format %i not supported yet!\n",
226  audio_format);
227  }
228  avpriv_set_pts_info(ast, 32, 1, ast->codec->bit_rate);
229  } else {
230  for (i = 0; i < 3; i++)
231  error |= read_line(pb, line, sizeof(line));
232  }
233 
234  rpl->frames_per_chunk = read_line_and_int(pb, &error); // video frames per chunk
235  if (rpl->frames_per_chunk > 1 && vst->codec->codec_tag != 124)
237  "Don't know how to split frames for video format %i. "
238  "Video stream will be broken!\n", vst->codec->codec_tag);
239 
240  number_of_chunks = read_line_and_int(pb, &error); // number of chunks in the file
241  // The number in the header is actually the index of the last chunk.
242  number_of_chunks++;
243 
244  error |= read_line(pb, line, sizeof(line)); // "even" chunk size in bytes
245  error |= read_line(pb, line, sizeof(line)); // "odd" chunk size in bytes
246  chunk_catalog_offset = // offset of the "chunk catalog"
247  read_line_and_int(pb, &error); // (file index)
248  error |= read_line(pb, line, sizeof(line)); // offset to "helpful" sprite
249  error |= read_line(pb, line, sizeof(line)); // size of "helpful" sprite
250  error |= read_line(pb, line, sizeof(line)); // offset to key frame list
251 
252  // Read the index
253  avio_seek(pb, chunk_catalog_offset, SEEK_SET);
254  total_audio_size = 0;
255  for (i = 0; !error && i < number_of_chunks; i++) {
256  int64_t offset, video_size, audio_size;
257  error |= read_line(pb, line, sizeof(line));
258  if (3 != sscanf(line, "%"SCNd64" , %"SCNd64" ; %"SCNd64,
259  &offset, &video_size, &audio_size))
260  error = -1;
261  av_add_index_entry(vst, offset, i * rpl->frames_per_chunk,
262  video_size, rpl->frames_per_chunk, 0);
263  if (ast)
264  av_add_index_entry(ast, offset + video_size, total_audio_size,
265  audio_size, audio_size * 8, 0);
266  total_audio_size += audio_size * 8;
267  }
268 
269  if (error) return AVERROR(EIO);
270 
271  return 0;
272 }
273 
275 {
276  RPLContext *rpl = s->priv_data;
277  AVIOContext *pb = s->pb;
278  AVStream* stream;
279  AVIndexEntry* index_entry;
280  uint32_t ret;
281 
282  if (rpl->chunk_part == s->nb_streams) {
283  rpl->chunk_number++;
284  rpl->chunk_part = 0;
285  }
286 
287  stream = s->streams[rpl->chunk_part];
288 
289  if (rpl->chunk_number >= stream->nb_index_entries)
290  return AVERROR_EOF;
291 
292  index_entry = &stream->index_entries[rpl->chunk_number];
293 
294  if (rpl->frame_in_part == 0)
295  if (avio_seek(pb, index_entry->pos, SEEK_SET) < 0)
296  return AVERROR(EIO);
297 
298  if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
299  stream->codec->codec_tag == 124) {
300  // We have to split Escape 124 frames because there are
301  // multiple frames per chunk in Escape 124 samples.
302  uint32_t frame_size;
303 
304  avio_skip(pb, 4); /* flags */
305  frame_size = avio_rl32(pb);
306  if (avio_seek(pb, -8, SEEK_CUR) < 0)
307  return AVERROR(EIO);
308 
309  ret = av_get_packet(pb, pkt, frame_size);
310  if (ret != frame_size) {
311  av_free_packet(pkt);
312  return AVERROR(EIO);
313  }
314  pkt->duration = 1;
315  pkt->pts = index_entry->timestamp + rpl->frame_in_part;
316  pkt->stream_index = rpl->chunk_part;
317 
318  rpl->frame_in_part++;
319  if (rpl->frame_in_part == rpl->frames_per_chunk) {
320  rpl->frame_in_part = 0;
321  rpl->chunk_part++;
322  }
323  } else {
324  ret = av_get_packet(pb, pkt, index_entry->size);
325  if (ret != index_entry->size) {
326  av_free_packet(pkt);
327  return AVERROR(EIO);
328  }
329 
330  if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
331  // frames_per_chunk should always be one here; the header
332  // parsing will warn if it isn't.
333  pkt->duration = rpl->frames_per_chunk;
334  } else {
335  // All the audio codecs supported in this container
336  // (at least so far) are constant-bitrate.
337  pkt->duration = ret * 8;
338  }
339  pkt->pts = index_entry->timestamp;
340  pkt->stream_index = rpl->chunk_part;
341  rpl->chunk_part++;
342  }
343 
344  // None of the Escape formats have keyframes, and the ADPCM
345  // format used doesn't have keyframes.
346  if (rpl->chunk_number == 0 && rpl->frame_in_part == 0)
347  pkt->flags |= AV_PKT_FLAG_KEY;
348 
349  return ret;
350 }
351 
353  .name = "rpl",
354  .long_name = NULL_IF_CONFIG_SMALL("RPL / ARMovie"),
355  .priv_data_size = sizeof(RPLContext),
359 };
static AVRational read_fps(const char *line, int *error)
Parsing for fps, which can be a fraction.
Definition: rpl.c:93
const char * s
Definition: avisynth_c.h:668
Bytestream IO Context.
Definition: avio.h:68
void av_free_packet(AVPacket *pkt)
Free a packet.
Definition: avpacket.c:242
int av_add_index_entry(AVStream *st, int64_t pos, int64_t timestamp, int size, int distance, int flags)
Add an index entry into a sorted list.
Definition: rpl.c:42
void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
int64_t pos
Definition: avformat.h:592
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:154
static int64_t audio_size
Definition: ffmpeg.c:126
static int read_line(AVIOContext *pb, char *line, int bufsize)
Definition: rpl.c:52
uint32_t frame_in_part
Definition: rpl.c:49
uint32_t chunk_part
Definition: rpl.c:48
int num
numerator
Definition: rational.h:44
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:199
AVIndexEntry * index_entries
Only used if the format does not support seeking natively.
Definition: avformat.h:822
int64_t avio_skip(AVIOContext *s, int64_t offset)
Skip given number of bytes forward.
Definition: aviobuf.c:256
int32_t frames_per_chunk
Definition: rpl.c:44
Format I/O context.
Definition: avformat.h:944
Public dictionary API.
static AVPacket pkt
Definition: demuxing.c:56
uint32_t chunk_number
Definition: rpl.c:47
#define b
Definition: input.c:42
AVStream * avformat_new_stream(AVFormatContext *s, const AVCodec *c)
Add a new stream to a media file.
AVStream ** streams
Definition: avformat.h:992
#define AVERROR_EOF
End of file.
Definition: error.h:55
int av_get_packet(AVIOContext *s, AVPacket *pkt, int size)
Allocate and read the payload of a packet and initialize its fields with default values.
int bits_per_coded_sample
bits per sample/pixel from the demuxer (needed for huffyuv).
int duration
Duration of this packet in AVStream->time_base units, 0 if unknown.
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
static const uint8_t frame_size[4]
Definition: g723_1_data.h:58
static int rpl_read_header(AVFormatContext *s)
Definition: rpl.c:113
AVDictionary * metadata
Definition: avformat.h:1092
struct RPLContext RPLContext
unsigned int avio_rl32(AVIOContext *s)
Definition: aviobuf.c:579
int64_t timestamp
Timestamp in AVStream.time_base units, preferably the time from which on correctly decoded frames are...
Definition: avformat.h:593
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: graph2dot.c:48
void av_log(void *avcl, int level, const char *fmt,...)
Send the specified message to the log if the level is less than or equal to the current av_log_level...
Definition: log.c:246
static const uint8_t offset[127][2]
Definition: vf_spp.c:70
int flags
A combination of AV_PKT_FLAG values.
int avio_r8(AVIOContext *s)
Definition: aviobuf.c:469
AVCodecContext * codec
Codec context associated with this stream.
Definition: avformat.h:662
int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max)
Reduce a fraction.
Definition: rational.c:36
unsigned char * buf
Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero.
Definition: avformat.h:336
unsigned int nb_streams
A list of all streams in the file.
Definition: avformat.h:991
static int rpl_probe(AVProbeData *p)
Definition: rpl.c:34
static int32_t read_int(const char *line, const char **endptr, int *error)
Definition: rpl.c:69
static int64_t video_size
Definition: ffmpeg.c:125
static int read_probe(AVProbeData *pd)
ret
Definition: avfilter.c:821
int width
picture width / height.
AVInputFormat ff_rpl_demuxer
Definition: rpl.c:352
int url_feof(AVIOContext *s)
feof() equivalent for AVIOContext.
Definition: aviobuf.c:280
static int read_header(FFV1Context *f)
Definition: ffv1dec.c:517
Stream structure.
Definition: avformat.h:643
NULL
Definition: eval.c:55
enum AVMediaType codec_type
enum AVCodecID codec_id
AVIOContext * pb
I/O context.
Definition: avformat.h:977
unsigned int codec_tag
fourcc (LSB first, so "ABCD" -> (&#39;D&#39;<<24) + (&#39;C&#39;<<16) + (&#39;B&#39;<<8) + &#39;A&#39;).
static int read_packet(AVFormatContext *ctx, AVPacket *pkt)
Definition: libcdio.c:114
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
Definition: dict.c:62
int nb_index_entries
Definition: avformat.h:824
synthesis window for stochastic i
rational number numerator/denominator
Definition: rational.h:43
This structure contains the data a format has to probe a file.
Definition: avformat.h:334
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFilterBuffer 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.Buffer references ownership and permissions
static int32_t read_line_and_int(AVIOContext *pb, int *error)
Definition: rpl.c:81
#define RPL_SIGNATURE
Definition: rpl.c:28
#define AVPROBE_SCORE_MAX
maximum score, half of that is used for file-extension-based detection
Definition: avformat.h:340
Main libavformat public API header.
static int rpl_read_packet(AVFormatContext *s, AVPacket *pkt)
Definition: rpl.c:274
int den
denominator
Definition: rational.h:45
void * priv_data
Format private data.
Definition: avformat.h:964
About Git write you should know how to use GIT properly Luckily Git comes with excellent documentation git help man git shows you the available git< command > help man git< command > shows information about the subcommand< command > The most comprehensive manual is the website Git Reference visit they are quite exhaustive You do not need a special username or password All you need is to provide a ssh public key to the Git server admin What follows now is a basic introduction to Git and some FFmpeg specific guidelines Read it at least if you are granted commit privileges to the FFmpeg project you are expected to be familiar with these rules I if not You can get git from etc no matter how small Every one of them has been saved from looking like a fool by this many times It s very easy for stray debug output or cosmetic modifications to slip please avoid problems through this extra level of scrutiny For cosmetics only commits you should e g by running git config global user name My Name git config global user email my email which is either set in your personal configuration file through git config core editor or set by one of the following environment VISUAL or EDITOR Log messages should be concise but descriptive Explain why you made a what you did will be obvious from the changes themselves most of the time Saying just bug fix or is bad Remember that people of varying skill levels look at and educate themselves while reading through your code Don t include filenames in log Git provides that information Possibly make the commit message have a descriptive first line
Definition: git-howto.txt:153
#define RPL_LINE_LENGTH
256 is arbitrary, but should be big enough for any reasonable file.
Definition: rpl.c:32
const char * name
A comma separated list of short names for the format.
Definition: avformat.h:461
This structure stores compressed data.
#define RPL_SIGNATURE_SIZE
Definition: rpl.c:29
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...