FFmpeg  4.4.5
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 {
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  amv->apad = av_packet_alloc();
187  if (!amv->apad)
188  return AVERROR(ENOMEM);
189  if ((ret = av_new_packet(amv->apad, amv->ablock_align)) < 0) {
190  av_packet_free(&amv->apad);
191  return ret;
192  }
193 
195  memset(amv->apad->data, 0, amv->ablock_align);
196  AV_WL32(amv->apad->data + 4, amv->aframe_size);
197 
198  amv->vpad = av_packet_alloc();
199  if (!amv->vpad) {
200  av_packet_free(&amv->apad);
201  return AVERROR(ENOMEM);
202  }
204  amv->vpad->duration = 1;
205  return 0;
206 }
207 
209 {
210  AMVContext *amv = s->priv_data;
211 
212  av_packet_free(&amv->apad);
213  av_packet_free(&amv->vpad);
214 }
215 
217 {
218  int64_t tag_list, tag_str;
219 
221 
222  tag_list = amv_start_tag(s->pb, "LIST");
223  ffio_wfourcc(s->pb, "strl");
224  tag_str = ff_start_tag(s->pb, "strh");
226  ff_end_tag(s->pb, tag_str);
227 
228  tag_str = ff_start_tag(s->pb, "strf");
230  ff_end_tag(s->pb, tag_str);
231 
232  amv_end_tag(s->pb, tag_list);
233 }
234 
236 {
238  AVIOContext *pb = s->pb;
239  int64_t tag_list, tag_str;
240 
242 
243  tag_list = amv_start_tag(pb, "LIST");
244  ffio_wfourcc(pb, "strl");
245  tag_str = ff_start_tag(pb, "strh");
247  ff_end_tag(pb, tag_str);
248 
249  /* Bodge an (incorrect) WAVEFORMATEX (+2 pad bytes) */
250  tag_str = ff_start_tag(pb, "strf");
251  AV_WL16(buf + 0, 1);
252  AV_WL16(buf + 2, par->channels);
253  AV_WL32(buf + 4, par->sample_rate);
254  AV_WL32(buf + 8, par->sample_rate * par->channels * 2);
255  AV_WL16(buf + 12, 2);
256  AV_WL16(buf + 14, 16);
257  AV_WL16(buf + 16, 0);
258  AV_WL16(buf + 18, 0);
260  ff_end_tag(pb, tag_str);
261 
262  amv_end_tag(pb, tag_list);
263 }
264 
266 {
267  AMVContext *amv = s->priv_data;
268  AVIOContext *pb = s->pb;
269  AVStream *vst = s->streams[AMV_STREAM_VIDEO];
270  AVStream *ast = s->streams[AMV_STREAM_AUDIO];
271  uint8_t amvh[56] = {0};
272  int64_t list1;
273 
274  amv->riff_start = amv_start_tag(pb, "RIFF");
275  ffio_wfourcc(pb, "AMV ");
276  list1 = amv_start_tag(pb, "LIST");
277  ffio_wfourcc(pb, "hdrl");
278 
279  ffio_wfourcc(pb, "amvh");
280  avio_wl32(pb, 56);
281 
282  AV_WL32(amvh + 0, amv->us_per_frame);
283  AV_WL32(amvh + 32, vst->codecpar->width);
284  AV_WL32(amvh + 36, vst->codecpar->height);
285  AV_WL32(amvh + 40, vst->time_base.den);
286  AV_WL32(amvh + 44, vst->time_base.num);
287  AV_WL32(amvh + 48, 0);
288  AV_WL32(amvh + 52, 0); /* duration, filled in later. */
289 
290  avio_write(pb, amvh, sizeof(amvh));
291  amv->offset_duration = avio_tell(pb) - 4;
292 
293  amv_write_vlist(s, vst->codecpar);
294  amv_write_alist(s, ast->codecpar);
295  amv_end_tag(pb, list1);
296 
297  amv->movi_list = amv_start_tag(pb, "LIST");
298  ffio_wfourcc(pb, "movi");
299  return 0;
300 }
301 
303 {
304  AMVContext *amv = s->priv_data;
305 
307  ffio_wfourcc(s->pb, "00dc");
308  else if (pkt->stream_index == AMV_STREAM_AUDIO)
309  ffio_wfourcc(s->pb, "01wb");
310  else
311  av_assert0(0);
312 
313  if (pkt->stream_index == AMV_STREAM_AUDIO && pkt->size != amv->ablock_align) {
314  /* Can happen when remuxing files produced by another encoder. */
315  av_log(s, AV_LOG_WARNING, "Invalid audio packet size (%d != %d)\n",
316  pkt->size, amv->ablock_align);
317  }
318 
319  avio_wl32(s->pb, pkt->size);
320  avio_write(s->pb, pkt->data, pkt->size);
321 
322  amv->lastpts[pkt->stream_index] += pkt->duration;
323  amv->last_stream = pkt->stream_index;
324  return 0;
325 }
326 
328 {
329  AMVContext *amv = s->priv_data;
330  int stream_index = pkt->stream_index;
331 
332  if (stream_index != amv->last_stream)
333  return 0;
334 
335  stream_index = (stream_index + 1) % s->nb_streams;
336  if (stream_index == AMV_STREAM_VIDEO)
337  return amv_write_packet_internal(s, amv->vpad);
338  else if (stream_index == AMV_STREAM_AUDIO)
339  return amv_write_packet_internal(s, amv->apad);
340  else
341  av_assert0(0);
342 
343  return AVERROR(EINVAL);
344 }
345 
347 {
348  AMVContext *amv = s->priv_data;
349  int ret;
350 
351  /* Add a dummy frame if we've received two of the same index. */
352  if ((ret = amv_pad(s, pkt)) < 0)
353  return ret;
354 
355  if ((ret = amv_write_packet_internal(s, pkt)) < 0)
356  return ret;
357 
359  /* Save the last packet for padding. */
360  av_packet_unref(amv->vpad);
361  if ((ret = av_packet_ref(amv->vpad, pkt)) < 0)
362  return ret;
363  }
364 
365  return 0;
366 }
367 
369 {
370  AMVContext *amv = s->priv_data;
371  AVStream *vst = s->streams[AMV_STREAM_VIDEO];
372  AVStream *ast = s->streams[AMV_STREAM_AUDIO];
373  int64_t maxpts, ret;
374  int hh, mm, ss;
375 
376  /* Pad-out one last audio frame if needed. */
377  if (amv->last_stream == AMV_STREAM_VIDEO) {
378  if ((ret = amv_write_packet_internal(s, amv->apad)) < 0)
379  return ret;
380  }
381 
382  amv_end_tag(s->pb, amv->movi_list);
383  amv_end_tag(s->pb, amv->riff_start);
384 
385  ffio_wfourcc(s->pb, "AMV_");
386  ffio_wfourcc(s->pb, "END_");
387 
388  if ((ret = avio_seek(s->pb, amv->offset_duration, SEEK_SET)) < 0)
389  return ret;
390 
391  /* Go back and write the duration. */
392  maxpts = FFMAX(
395  );
396 
397  ss = maxpts / AV_TIME_BASE;
398  mm = ss / 60;
399  hh = mm / 60;
400  ss %= 60;
401  mm %= 60;
402 
403  avio_w8(s->pb, ss);
404  avio_w8(s->pb, mm);
405  avio_wl16(s->pb, hh);
406  return 0;
407 }
408 
410  .name = "amv",
411  .long_name = NULL_IF_CONFIG_SMALL("AMV"),
412  .mime_type = "video/amv",
413  .extensions = "amv",
414  .priv_data_size = sizeof(AMVContext),
415  .audio_codec = AV_CODEC_ID_ADPCM_IMA_AMV,
416  .video_codec = AV_CODEC_ID_AMV,
417  .init = amv_init,
418  .deinit = amv_deinit,
422 };
static void amv_deinit(AVFormatContext *s)
Definition: amvenc.c:208
AVOutputFormat ff_amv_muxer
Definition: amvenc.c:409
static void amv_write_alist(AVFormatContext *s, AVCodecParameters *par)
Definition: amvenc.c:235
static av_cold int amv_init(AVFormatContext *s)
Definition: amvenc.c:93
static int amv_write_header(AVFormatContext *s)
Definition: amvenc.c:265
#define AMV_VIDEO_STRH_SIZE
Definition: amvenc.c:49
#define AMV_AUDIO_STRH_SIZE
Definition: amvenc.c:51
#define AMV_STREAM_VIDEO
Definition: amvenc.c:47
static void amv_write_vlist(AVFormatContext *s, AVCodecParameters *par)
Definition: amvenc.c:216
static int64_t amv_start_tag(AVIOContext *pb, const char *tag)
Definition: amvenc.c:76
#define AMV_STREAM_AUDIO
Definition: amvenc.c:48
static int amv_write_packet(AVFormatContext *s, AVPacket *pkt)
Definition: amvenc.c:346
static void amv_end_tag(AVIOContext *pb, int64_t start)
Definition: amvenc.c:83
static int amv_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
Definition: amvenc.c:302
static int amv_pad(AVFormatContext *s, AVPacket *pkt)
Definition: amvenc.c:327
#define AMV_VIDEO_STRF_SIZE
Definition: amvenc.c:50
static int amv_write_trailer(AVFormatContext *s)
Definition: amvenc.c:368
#define AMV_AUDIO_STRF_SIZE
Definition: amvenc.c:52
#define AMV_STREAM_COUNT
Definition: amvenc.c:46
#define av_cold
Definition: attributes.h:88
uint8_t
int32_t
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
Main libavformat public API header.
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:253
#define AVIO_SEEKABLE_NORMAL
Seeking works like for a local file.
Definition: avio.h:40
void avio_wl32(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:375
void avio_wl16(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:455
void avio_w8(AVIOContext *s, int b)
Definition: aviobuf.c:203
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
void ffio_fill(AVIOContext *s, int b, int count)
Definition: aviobuf.c:211
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:31
#define ss(width, name, subs,...)
Definition: cbs_vp9.c:261
#define s(width, name)
Definition: cbs_vp9.c:257
#define FFMAX(a, b)
Definition: common.h:103
long long int64_t
Definition: coverity.c:34
static const struct exif_tag tag_list[]
Definition: exif.h:43
static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int unqueue)
Definition: ffmpeg.c:730
static void write_header(FFV1Context *f)
Definition: ffv1enc.c:346
const char * avcodec_get_name(enum AVCodecID id)
Get the name of a codec.
Definition: utils.c:477
@ AV_CODEC_ID_ADPCM_IMA_AMV
Definition: codec_id.h:372
@ AV_CODEC_ID_AMV
Definition: codec_id.h:156
void av_packet_free(AVPacket **pkt)
Free the packet, if the packet is reference counted, it will be unreferenced first.
Definition: avpacket.c:75
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition: avpacket.c:634
AVPacket * av_packet_alloc(void)
Allocate an AVPacket and set its fields to default values.
Definition: avpacket.c:64
int av_packet_ref(AVPacket *dst, const AVPacket *src)
Setup a new reference to the data described by a given packet.
Definition: avpacket.c:641
int av_new_packet(AVPacket *pkt, int size)
Allocate the payload of a packet and initialize its fields with default values.
Definition: avpacket.c:99
#define AVERROR(e)
Definition: error.h:43
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
Definition: log.h:220
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:200
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
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
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
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:254
#define AV_TIME_BASE_Q
Internal time base represented as fractional value.
Definition: avutil.h:260
static int bad(InterplayACMContext *s, unsigned ind, unsigned col)
Definition: interplayacm.c:116
#define AV_WL32(p, v)
Definition: intreadwrite.h:426
#define AV_WL16(p, v)
Definition: intreadwrite.h:412
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:117
#define FFALIGN(x, a)
Definition: macros.h:48
uint32_t tag
Definition: movenc.c:1611
internal header for RIFF based (de)muxers do NOT include this in end user applications
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
unsigned int pos
Definition: spdifenc.c:412
int64_t riff_start
Definition: amvenc.c:56
AVPacket * apad
Definition: amvenc.c:65
int last_stream
Definition: amvenc.c:59
AVPacket * vpad
Definition: amvenc.c:66
int32_t ablock_align
Definition: amvenc.c:64
int32_t us_per_frame
Definition: amvenc.c:61
int64_t lastpts[AMV_STREAM_COUNT]
Definition: amvenc.c:72
int64_t offset_duration
Definition: amvenc.c:58
int32_t aframe_size
Definition: amvenc.c:63
int64_t movi_list
Definition: amvenc.c:57
This struct describes the properties of an encoded stream.
Definition: codec_par.h:52
int frame_size
Audio only.
Definition: codec_par.h:181
int channels
Audio only.
Definition: codec_par.h:166
int width
Video only.
Definition: codec_par.h:126
int block_align
Audio only.
Definition: codec_par.h:177
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: codec_par.h:60
int sample_rate
Audio only.
Definition: codec_par.h:170
Format I/O context.
Definition: avformat.h:1232
Bytestream IO Context.
Definition: avio.h:161
const char * name
Definition: avformat.h:491
This structure stores compressed data.
Definition: packet.h:346
int stream_index
Definition: packet.h:371
int size
Definition: packet.h:370
int64_t duration
Duration of this packet in AVStream->time_base units, 0 if unknown.
Definition: packet.h:387
uint8_t * data
Definition: packet.h:369
int num
Numerator.
Definition: rational.h:59
int den
Denominator.
Definition: rational.h:60
Stream structure.
Definition: avformat.h:873
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1038
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented.
Definition: avformat.h:902
#define av_log(a,...)
AVPacket * pkt
Definition: movenc.c:59
static int write_trailer(AVFormatContext *s1)
Definition: v4l2enc.c:98
if(ret< 0)
Definition: vf_mcdeint.c:282