FFmpeg
amvenc.c
Go to the documentation of this file.
1 /*
2  * AMV muxer
3  *
4  * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.com)
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 #include "avformat.h"
23 #include "riff.h"
24 #include "internal.h"
25 #include "avio_internal.h"
26 #include "libavutil/intreadwrite.h"
27 #include "libavutil/avassert.h"
28 
29 /*
30  * Things to note:
31  * - AMV is a hard-coded (and broken) subset of AVI. It's not worth sullying the
32  * existing AVI muxer with its filth.
33  * - No separate demuxer as the existing AVI demuxer can handle these.
34  * - The sizes of certain tags are deliberately set to 0 as some players break
35  * when they're set correctly. Ditto with some header fields.
36  * - There is no index.
37  * - Players are **very** sensitive to the frame order and sizes.
38  * - Frames must be strictly interleaved as V-A, any V-V or A-A will
39  * cause crashes.
40  * - Variable video frame sizes seem to be handled fine.
41  * - Variable audio frame sizes cause crashes.
42  * - If audio is shorter than video, it's padded with silence.
43  * - If video is shorter than audio, the most recent frame is repeated.
44  */
45 
46 #define AMV_STREAM_COUNT 2
47 #define AMV_STREAM_VIDEO 0
48 #define AMV_STREAM_AUDIO 1
49 #define AMV_VIDEO_STRH_SIZE 56
50 #define AMV_VIDEO_STRF_SIZE 36
51 #define AMV_AUDIO_STRH_SIZE 48
52 #define AMV_AUDIO_STRF_SIZE 20 /* sizeof(WAVEFORMATEX) + 2 */
53 
54 typedef struct AMVContext
55 {
56  int64_t riff_start;
57  int64_t movi_list;
58  int64_t offset_duration;
60 
61  int32_t us_per_frame; /* Microseconds per frame. */
62 
63  int32_t aframe_size; /* Expected audio frame size. */
64  int32_t ablock_align; /* Expected audio block align. */
65  AVPacket apad; /* Dummy audio packet for padding. */
66  AVPacket vpad; /* Most recent video frame, for padding. */
67 
68  /*
69  * Cumulative PTS values for each stream, used for the final
70  * duration calculcation.
71  */
73 } AMVContext;
74 
75 /* ff_{start,end}_tag(), but sets the size to 0. */
76 static int64_t amv_start_tag(AVIOContext *pb, const char *tag)
77 {
78  ffio_wfourcc(pb, tag);
79  avio_wl32(pb, 0);
80  return avio_tell(pb);
81 }
82 
83 static void amv_end_tag(AVIOContext *pb, int64_t start)
84 {
85  int64_t pos;
86  av_assert0((start&1) == 0);
87 
88  pos = avio_tell(pb);
89  if (pos & 1)
90  avio_w8(pb, 0);
91 }
92 
94 {
95  AMVContext *amv = s->priv_data;
96  AVStream *vst, *ast;
97  int ret;
98 
99  amv->last_stream = -1;
100 
101  if (s->nb_streams != AMV_STREAM_COUNT) {
102  av_log(s, AV_LOG_ERROR, "AMV files only support 2 streams\n");
103  return AVERROR(EINVAL);
104  }
105 
106  vst = s->streams[AMV_STREAM_VIDEO];
107  ast = s->streams[AMV_STREAM_AUDIO];
108 
109  if (vst->codecpar->codec_id != AV_CODEC_ID_AMV) {
110  av_log(s, AV_LOG_ERROR, "First AMV stream must be %s\n",
112  return AVERROR(EINVAL);
113  }
114 
116  av_log(s, AV_LOG_ERROR, "Second AMV stream must be %s\n",
118  return AVERROR(EINVAL);
119  }
120 
121  /* These files are broken-enough as they are. They shouldn't be streamed. */
122  if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
123  av_log(s, AV_LOG_ERROR, "Stream not seekable, unable to write output file\n");
124  return AVERROR(EINVAL);
125  }
126 
129  amv->ablock_align = 8 + (FFALIGN(amv->aframe_size, 2) / 2);
130 
131  av_log(s, AV_LOG_TRACE, "us_per_frame = %d\n", amv->us_per_frame);
132  av_log(s, AV_LOG_TRACE, "aframe_size = %d\n", amv->aframe_size);
133  av_log(s, AV_LOG_TRACE, "ablock_align = %d\n", amv->ablock_align);
134 
135  /*
136  * Bail if the framerate's too high. Prevents the audio frame size from
137  * getting too small. 63fps is the closest value to 60fps that divides
138  * cleanly, so cap it there.
139  */
140  if (amv->us_per_frame < 15873) {
141  av_log(s, AV_LOG_ERROR, "Refusing to mux >63fps video\n");
142  return AVERROR(EINVAL);
143  }
144 
145  /*
146  * frame_size will be set if coming from the encoder.
147  * Make sure the its been configured correctly. The audio frame duration
148  * needs to match that of the video.
149  */
150  if (ast->codecpar->frame_size) {
151  AVCodecParameters *par = ast->codecpar;
152  int bad = 0;
153 
154  if (par->frame_size != amv->aframe_size) {
155  av_log(s, AV_LOG_ERROR, "Invalid audio frame size. Got %d, wanted %d\n",
156  par->frame_size, amv->aframe_size);
157  bad = 1;
158  }
159 
160  if (par->block_align != amv->ablock_align) {
161  av_log(s, AV_LOG_ERROR, "Invalid audio block align. Got %d, wanted %d\n",
162  par->block_align, amv->ablock_align);
163  bad = 1;
164  }
165 
166  if (bad) {
167  av_log(s, AV_LOG_ERROR, "Try -block_size %d\n", amv->aframe_size);
168  return AVERROR(EINVAL);
169  }
170 
171  if (ast->codecpar->sample_rate % amv->aframe_size) {
172  av_log(s, AV_LOG_ERROR, "Audio sample rate not a multiple of the frame size.\n"
173  "Please change video frame rate. Suggested rates: 10,14,15,18,21,25,30\n");
174  return AVERROR(EINVAL);
175  }
176  } else {
177  /* If remuxing from the same source, then this will match the video. */
179  if (aus != amv->us_per_frame) {
180  av_log(s, AV_LOG_ERROR, "Cannot remux streams with a different time base\n");
181  return AVERROR(EINVAL);
182  }
183  }
184 
185  /* Allocate and fill dummy packet so we can pad the audio. */
186  if ((ret = av_new_packet(&amv->apad, amv->ablock_align)) < 0)
187  return ret;
188 
190  memset(amv->apad.data, 0, amv->ablock_align);
191  AV_WL32(amv->apad.data + 4, amv->aframe_size);
192 
193  av_init_packet(&amv->vpad);
195  amv->vpad.duration = 1;
196  return 0;
197 }
198 
200 {
201  AMVContext *amv = s->priv_data;
202 
203  av_packet_unref(&amv->apad);
204  av_packet_unref(&amv->vpad);
205 }
206 
208 {
209  int64_t tag_list, tag_str;
210 
212 
213  tag_list = amv_start_tag(s->pb, "LIST");
214  ffio_wfourcc(s->pb, "strl");
215  tag_str = ff_start_tag(s->pb, "strh");
217  ff_end_tag(s->pb, tag_str);
218 
219  tag_str = ff_start_tag(s->pb, "strf");
221  ff_end_tag(s->pb, tag_str);
222 
223  amv_end_tag(s->pb, tag_list);
224 }
225 
227 {
229  AVIOContext *pb = s->pb;
230  int64_t tag_list, tag_str;
231 
233 
234  tag_list = amv_start_tag(pb, "LIST");
235  ffio_wfourcc(pb, "strl");
236  tag_str = ff_start_tag(pb, "strh");
238  ff_end_tag(pb, tag_str);
239 
240  /* Bodge an (incorrect) WAVEFORMATEX (+2 pad bytes) */
241  tag_str = ff_start_tag(pb, "strf");
242  AV_WL16(buf + 0, 1);
243  AV_WL16(buf + 2, par->channels);
244  AV_WL32(buf + 4, par->sample_rate);
245  AV_WL32(buf + 8, par->sample_rate * par->channels * 2);
246  AV_WL16(buf + 12, 2);
247  AV_WL16(buf + 14, 16);
248  AV_WL16(buf + 16, 0);
249  AV_WL16(buf + 18, 0);
251  ff_end_tag(pb, tag_str);
252 
253  amv_end_tag(pb, tag_list);
254 }
255 
257 {
258  AMVContext *amv = s->priv_data;
259  AVIOContext *pb = s->pb;
260  AVStream *vst = s->streams[AMV_STREAM_VIDEO];
261  AVStream *ast = s->streams[AMV_STREAM_AUDIO];
262  uint8_t amvh[56] = {0};
263  int64_t list1;
264 
265  amv->riff_start = amv_start_tag(pb, "RIFF");
266  ffio_wfourcc(pb, "AMV ");
267  list1 = amv_start_tag(pb, "LIST");
268  ffio_wfourcc(pb, "hdrl");
269 
270  ffio_wfourcc(pb, "amvh");
271  avio_wl32(pb, 56);
272 
273  AV_WL32(amvh + 0, amv->us_per_frame);
274  AV_WL32(amvh + 32, vst->codecpar->width);
275  AV_WL32(amvh + 36, vst->codecpar->height);
276  AV_WL32(amvh + 40, vst->time_base.den);
277  AV_WL32(amvh + 44, vst->time_base.num);
278  AV_WL32(amvh + 48, 0);
279  AV_WL32(amvh + 52, 0); /* duration, filled in later. */
280 
281  avio_write(pb, amvh, sizeof(amvh));
282  amv->offset_duration = avio_tell(pb) - 4;
283 
284  amv_write_vlist(s, vst->codecpar);
285  amv_write_alist(s, ast->codecpar);
286  amv_end_tag(pb, list1);
287 
288  amv->movi_list = amv_start_tag(pb, "LIST");
289  ffio_wfourcc(pb, "movi");
290  return 0;
291 }
292 
294 {
295  AMVContext *amv = s->priv_data;
296 
297  if (pkt->stream_index == AMV_STREAM_VIDEO)
298  ffio_wfourcc(s->pb, "00dc");
299  else if (pkt->stream_index == AMV_STREAM_AUDIO)
300  ffio_wfourcc(s->pb, "01wb");
301  else
302  av_assert0(0);
303 
304  if (pkt->stream_index == AMV_STREAM_AUDIO && pkt->size != amv->ablock_align) {
305  /* Can happen when remuxing files produced by another encoder. */
306  av_log(s, AV_LOG_WARNING, "Invalid audio packet size (%d != %d)\n",
307  pkt->size, amv->ablock_align);
308  }
309 
310  avio_wl32(s->pb, pkt->size);
311  avio_write(s->pb, pkt->data, pkt->size);
312 
313  amv->lastpts[pkt->stream_index] += pkt->duration;
314  amv->last_stream = pkt->stream_index;
315  return 0;
316 }
317 
319 {
320  AMVContext *amv = s->priv_data;
321  int stream_index = pkt->stream_index;
322 
323  if (stream_index != amv->last_stream)
324  return 0;
325 
326  stream_index = (stream_index + 1) % s->nb_streams;
327  if (stream_index == AMV_STREAM_VIDEO)
328  return amv_write_packet_internal(s, &amv->vpad);
329  else if (stream_index == AMV_STREAM_AUDIO)
330  return amv_write_packet_internal(s, &amv->apad);
331  else
332  av_assert0(0);
333 
334  return AVERROR(EINVAL);
335 }
336 
338 {
339  AMVContext *amv = s->priv_data;
340  int ret;
341 
342  /* Add a dummy frame if we've received two of the same index. */
343  if ((ret = amv_pad(s, pkt)) < 0)
344  return ret;
345 
346  if ((ret = amv_write_packet_internal(s, pkt)) < 0)
347  return ret;
348 
349  if (pkt->stream_index == AMV_STREAM_VIDEO) {
350  /* Save the last packet for padding. */
351  av_packet_unref(&amv->vpad);
352  if ((ret = av_packet_ref(&amv->vpad, pkt)) < 0)
353  return ret;
354  }
355 
356  return 0;
357 }
358 
360 {
361  AMVContext *amv = s->priv_data;
362  AVStream *vst = s->streams[AMV_STREAM_VIDEO];
363  AVStream *ast = s->streams[AMV_STREAM_AUDIO];
364  int64_t maxpts, ret;
365  int hh, mm, ss;
366 
367  /* Pad-out one last audio frame if needed. */
368  if (amv->last_stream == AMV_STREAM_VIDEO) {
369  if ((ret = amv_write_packet_internal(s, &amv->apad)) < 0)
370  return ret;
371  }
372 
373  amv_end_tag(s->pb, amv->movi_list);
374  amv_end_tag(s->pb, amv->riff_start);
375 
376  ffio_wfourcc(s->pb, "AMV_");
377  ffio_wfourcc(s->pb, "END_");
378 
379  if ((ret = avio_seek(s->pb, amv->offset_duration, SEEK_SET)) < 0)
380  return ret;
381 
382  /* Go back and write the duration. */
383  maxpts = FFMAX(
386  );
387 
388  ss = maxpts / AV_TIME_BASE;
389  mm = ss / 60;
390  hh = mm / 60;
391  ss %= 60;
392  mm %= 60;
393 
394  avio_w8(s->pb, ss);
395  avio_w8(s->pb, mm);
396  avio_wl16(s->pb, hh);
397  return 0;
398 }
399 
401  .name = "amv",
402  .long_name = NULL_IF_CONFIG_SMALL("AMV"),
403  .mime_type = "video/amv",
404  .extensions = "amv",
405  .priv_data_size = sizeof(AMVContext),
406  .audio_codec = AV_CODEC_ID_ADPCM_IMA_AMV,
407  .video_codec = AV_CODEC_ID_AMV,
408  .init = amv_init,
409  .deinit = amv_deinit,
413 };
static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int unqueue)
Definition: ffmpeg.c:704
#define AMV_VIDEO_STRH_SIZE
Definition: amvenc.c:49
void avio_wl16(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:459
Bytestream IO Context.
Definition: avio.h:161
void ff_end_tag(AVIOContext *pb, int64_t start)
Definition: riffenc.c:38
int64_t ff_start_tag(AVIOContext *pb, const char *tag)
Definition: riffenc.c:31
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:200
static const struct exif_tag tag_list[]
Definition: exif.h:43
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:35
static void amv_deinit(AVFormatContext *s)
Definition: amvenc.c:199
int64_t lastpts[AMV_STREAM_COUNT]
Definition: amvenc.c:72
static void amv_end_tag(AVIOContext *pb, int64_t start)
Definition: amvenc.c:83
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: codec_par.h:60
int num
Numerator.
Definition: rational.h:59
int size
Definition: packet.h:364
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:253
#define AMV_STREAM_COUNT
Definition: amvenc.c:46
static AVPacket pkt
This struct describes the properties of an encoded stream.
Definition: codec_par.h:52
int frame_size
Audio only.
Definition: codec_par.h:181
#define AMV_STREAM_AUDIO
Definition: amvenc.c:48
Format I/O context.
Definition: avformat.h:1239
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
void avio_wl32(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:379
uint8_t
#define av_cold
Definition: attributes.h:88
int width
Video only.
Definition: codec_par.h:126
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
Definition: log.h:220
#define AMV_AUDIO_STRH_SIZE
Definition: amvenc.c:51
int64_t duration
Duration of this packet in AVStream->time_base units, 0 if unknown.
Definition: packet.h:381
AVStream ** streams
A list of all streams in the file.
Definition: avformat.h:1307
uint8_t * data
Definition: packet.h:363
uint32_t tag
Definition: movenc.c:1597
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:225
static av_always_inline void ffio_wfourcc(AVIOContext *pb, const uint8_t *s)
Definition: avio_internal.h:58
#define FFALIGN(x, a)
Definition: macros.h:48
#define av_log(a,...)
int av_packet_ref(AVPacket *dst, const AVPacket *src)
Setup a new reference to the data described by a given packet.
Definition: avpacket.c:615
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
Rescale a 64-bit integer by 2 rational numbers.
Definition: mathematics.c:142
int av_new_packet(AVPacket *pkt, int size)
Allocate the payload of a packet and initialize its fields with default values.
Definition: avpacket.c:88
static int amv_write_packet(AVFormatContext *s, AVPacket *pkt)
Definition: amvenc.c:337
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
static av_cold int amv_init(AVFormatContext *s)
Definition: amvenc.c:93
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:115
unsigned int pos
Definition: spdifenc.c:410
simple assert() macros that are a bit more flexible than ISO C assert().
#define FFMAX(a, b)
Definition: common.h:94
static int amv_pad(AVFormatContext *s, AVPacket *pkt)
Definition: amvenc.c:318
int32_t us_per_frame
Definition: amvenc.c:61
static int amv_write_header(AVFormatContext *s)
Definition: amvenc.c:256
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:1295
int block_align
Audio only.
Definition: codec_par.h:177
#define ss(width, name, subs,...)
Definition: cbs_vp9.c:261
int seekable
A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.
Definition: avio.h:260
int64_t av_rescale(int64_t a, int64_t b, int64_t c)
Rescale a 64-bit integer with rounding to nearest.
Definition: mathematics.c:129
void ffio_fill(AVIOContext *s, int b, int count)
Definition: aviobuf.c:211
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:254
static int amv_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
Definition: amvenc.c:293
static int write_trailer(AVFormatContext *s1)
Definition: v4l2enc.c:98
const char * name
Definition: avformat.h:500
internal header for RIFF based (de)muxers do NOT include this in end user applications ...
int32_t
#define s(width, name)
Definition: cbs_vp9.c:257
AVPacket vpad
Definition: amvenc.c:66
int64_t movi_list
Definition: amvenc.c:57
if(ret)
Stream structure.
Definition: avformat.h:880
int64_t offset_duration
Definition: amvenc.c:58
#define AVIO_SEEKABLE_NORMAL
Seeking works like for a local file.
Definition: avio.h:40
#define AV_TIME_BASE_Q
Internal time base represented as fractional value.
Definition: avutil.h:260
AVIOContext * pb
I/O context.
Definition: avformat.h:1281
void avio_w8(AVIOContext *s, int b)
Definition: aviobuf.c:203
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition: avpacket.c:606
AVOutputFormat ff_amv_muxer
Definition: amvenc.c:400
int32_t aframe_size
Definition: amvenc.c:63
int last_stream
Definition: amvenc.c:59
int64_t riff_start
Definition: amvenc.c:56
int sample_rate
Audio only.
Definition: codec_par.h:170
Main libavformat public API header.
static void amv_write_alist(AVFormatContext *s, AVCodecParameters *par)
Definition: amvenc.c:226
#define AV_WL16(p, v)
Definition: intreadwrite.h:412
static int64_t amv_start_tag(AVIOContext *pb, const char *tag)
Definition: amvenc.c:76
#define AMV_AUDIO_STRF_SIZE
Definition: amvenc.c:52
const char * avcodec_get_name(enum AVCodecID id)
Get the name of a codec.
Definition: utils.c:1221
void av_init_packet(AVPacket *pkt)
Initialize optional fields of a packet with default values.
Definition: avpacket.c:35
int den
Denominator.
Definition: rational.h:60
void * priv_data
Format private data.
Definition: avformat.h:1267
static int amv_write_trailer(AVFormatContext *s)
Definition: amvenc.c:359
static void write_header(FFV1Context *f)
Definition: ffv1enc.c:346
int channels
Audio only.
Definition: codec_par.h:166
#define AMV_STREAM_VIDEO
Definition: amvenc.c:47
AVPacket apad
Definition: amvenc.c:65
static int bad(InterplayACMContext *s, unsigned ind, unsigned col)
Definition: interplayacm.c:116
#define AMV_VIDEO_STRF_SIZE
Definition: amvenc.c:50
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1045
static void amv_write_vlist(AVFormatContext *s, AVCodecParameters *par)
Definition: amvenc.c:207
int stream_index
Definition: packet.h:365
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: packet.h:340
int32_t ablock_align
Definition: amvenc.c:64
#define AV_WL32(p, v)
Definition: intreadwrite.h:426