FFmpeg
pdvenc.c
Go to the documentation of this file.
1 /*
2  * PDV muxer
3  *
4  * Copyright (c) 2026 Priyanshu Thapliyal <priyanshuthapliyal2005@gmail.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 
23 #include "libavutil/mem.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/rational.h"
26 #include "avformat.h"
27 #include "avio_internal.h"
28 #include "mux.h"
29 
30 #define PDV_MAGIC "Playdate VID\x00\x00\x00\x00"
31 #define PDV_MAX_FRAMES UINT16_MAX
32 #define PDV_MAX_OFFSET ((1U << 30) - 1)
33 
34 typedef struct PDVMuxContext {
35  uint32_t *entries;
36  int nb_frames;
38  uint32_t fps_bits;
43 
45 {
46  PDVMuxContext *pdv = s->priv_data;
47 
48  av_freep(&pdv->entries);
49 }
50 
51 static int pdv_get_fps(AVFormatContext *s, AVStream *st, uint32_t *fps_bits)
52 {
53  AVRational rate = st->avg_frame_rate;
54  const AVRational zero = { 0, 1 };
55 
56  if (!rate.num || !rate.den)
57  rate = av_inv_q(st->time_base);
58  if (!rate.num || !rate.den) {
59  av_log(s, AV_LOG_ERROR, "A valid frame rate is required for PDV output.\n");
60  return AVERROR(EINVAL);
61  }
62 
63  if (av_cmp_q(rate, zero) <= 0) {
64  av_log(s, AV_LOG_ERROR, "Invalid frame rate for PDV output.\n");
65  return AVERROR(EINVAL);
66  }
67 
68  *fps_bits = av_q2intfloat(rate);
69  return 0;
70 }
71 
73 {
74  PDVMuxContext *pdv = s->priv_data;
75  AVStream *st;
76  int ret;
77 
78  if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
79  av_log(s, AV_LOG_ERROR, "PDV muxer requires seekable output.\n");
80  return AVERROR(EINVAL);
81  }
82 
83  st = s->streams[0];
84 
85  if (st->codecpar->width > UINT16_MAX || st->codecpar->height > UINT16_MAX) {
86  av_log(s, AV_LOG_ERROR, "Output dimensions exceed PDV limits.\n");
87  return AVERROR(EINVAL);
88  }
89 
90  ret = pdv_get_fps(s, st, &pdv->fps_bits);
91  if (ret < 0)
92  return ret;
93 
94  if (pdv->max_frames < 1 || pdv->max_frames > PDV_MAX_FRAMES) {
96  "The -max_frames option must be set to a value in [1, %u].\n",
98  return AVERROR(EINVAL);
99  }
100 
101  pdv->entries = av_malloc_array(pdv->max_frames + 1, sizeof(*pdv->entries));
102  if (!pdv->entries)
103  return AVERROR(ENOMEM);
104 
105  avio_write(s->pb, PDV_MAGIC, 16);
106  pdv->nb_frames_pos = avio_tell(s->pb);
107  avio_wl16(s->pb, 0);
108  avio_wl16(s->pb, 0);
109  avio_wl32(s->pb, pdv->fps_bits);
110  avio_wl16(s->pb, st->codecpar->width);
111  avio_wl16(s->pb, st->codecpar->height);
112 
113  pdv->table_pos = avio_tell(s->pb);
114  ffio_fill(s->pb, 0, 4LL * (pdv->max_frames + 1));
115  pdv->payload_start = avio_tell(s->pb);
116 
117  if (pdv->nb_frames_pos < 0 || pdv->table_pos < 0 || pdv->payload_start < 0)
118  return AVERROR(EIO);
119 
120  return 0;
121 }
122 
124 {
125  PDVMuxContext *pdv = s->priv_data;
126  int64_t offset = avio_tell(s->pb);
127  const uint32_t max_table_gap = 4U * pdv->max_frames;
128 
129  if (offset < 0)
130  return AVERROR(EIO);
131  offset -= pdv->payload_start;
132  if (offset < 0)
133  return AVERROR(EIO);
134 
135  if (pkt->size <= 0)
136  return AVERROR_INVALIDDATA;
137  if (pdv->nb_frames >= pdv->max_frames) {
138  av_log(s, AV_LOG_ERROR, "Too many frames for PDV output.\n");
139  return AVERROR(EINVAL);
140  }
141  if (offset > PDV_MAX_OFFSET - max_table_gap ||
142  pkt->size > PDV_MAX_OFFSET - max_table_gap - offset) {
143  av_log(s, AV_LOG_ERROR, "PDV payload exceeds container limits.\n");
144  return AVERROR(EINVAL);
145  }
146 
147  pdv->entries[pdv->nb_frames] = ((uint32_t)offset << 2) |
148  (pkt->flags & AV_PKT_FLAG_KEY ? 1 : 2);
149  avio_write(s->pb, pkt->data, pkt->size);
150 
151  pdv->nb_frames++;
152 
153  return 0;
154 }
155 
157 {
158  PDVMuxContext *pdv = s->priv_data;
159  int64_t payload_size = avio_tell(s->pb);
160  const uint32_t table_gap = 4U * (pdv->max_frames - pdv->nb_frames);
161  int64_t ret;
162 
163  if (payload_size < 0)
164  return AVERROR(EIO);
165  payload_size -= pdv->payload_start;
166  if (payload_size < 0 || payload_size > PDV_MAX_OFFSET - table_gap)
167  return AVERROR(EINVAL);
168 
169  pdv->entries[pdv->nb_frames] = (uint32_t)payload_size << 2;
170 
171  if ((ret = avio_seek(s->pb, pdv->nb_frames_pos, SEEK_SET)) < 0)
172  return ret;
173  avio_wl16(s->pb, pdv->nb_frames);
174 
175  if ((ret = avio_seek(s->pb, pdv->table_pos, SEEK_SET)) < 0)
176  return ret;
177  for (int i = 0; i <= pdv->nb_frames; i++) {
178  const uint32_t frame_off = (pdv->entries[i] >> 2) + table_gap;
179 
180  if (frame_off > PDV_MAX_OFFSET)
181  return AVERROR(EINVAL);
182  avio_wl32(s->pb, frame_off << 2 | (pdv->entries[i] & 3));
183  }
184 
185  if ((ret = avio_seek(s->pb, pdv->payload_start + payload_size, SEEK_SET)) < 0)
186  return ret;
187 
188  return 0;
189 }
190 
191 #define OFFSET(x) offsetof(PDVMuxContext, x)
192 #define ENC AV_OPT_FLAG_ENCODING_PARAM
193 static const AVOption options[] = {
194  { "max_frames", "maximum number of frames reserved in table (mandatory)",
195  OFFSET(max_frames), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, PDV_MAX_FRAMES, ENC },
196  { NULL },
197 };
198 
199 static const AVClass pdv_muxer_class = {
200  .class_name = "PDV muxer",
201  .item_name = av_default_item_name,
202  .option = options,
203  .version = LIBAVUTIL_VERSION_INT,
204  .category = AV_CLASS_CATEGORY_MUXER,
205 };
206 
208  .p.name = "pdv",
209  .p.long_name = NULL_IF_CONFIG_SMALL("PlayDate Video"),
210  .p.extensions = "pdv",
211  .p.priv_class = &pdv_muxer_class,
212  .p.audio_codec = AV_CODEC_ID_NONE,
213  .p.video_codec = AV_CODEC_ID_PDV,
214  .p.subtitle_codec = AV_CODEC_ID_NONE,
215  .priv_data_size = sizeof(PDVMuxContext),
216  .p.flags = AVFMT_NOTIMESTAMPS,
217  .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH |
219  .write_header = pdv_write_header,
220  .write_packet = pdv_write_packet,
221  .write_trailer = pdv_write_trailer,
222  .deinit = pdv_deinit,
223 };
AVOutputFormat::name
const char * name
Definition: avformat.h:506
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
opt.h
pdv_deinit
static void pdv_deinit(AVFormatContext *s)
Definition: pdvenc.c:44
rational.h
AVFMT_NOTIMESTAMPS
#define AVFMT_NOTIMESTAMPS
Format does not need / have any timestamps.
Definition: avformat.h:478
int64_t
long long int64_t
Definition: coverity.c:34
AVPacket::data
uint8_t * data
Definition: packet.h:595
AVOption
AVOption.
Definition: opt.h:429
AVStream::avg_frame_rate
AVRational avg_frame_rate
Average framerate.
Definition: avformat.h:833
FF_OFMT_FLAG_ONLY_DEFAULT_CODECS
#define FF_OFMT_FLAG_ONLY_DEFAULT_CODECS
If this flag is set, then the only permitted audio/video/subtitle codec ids are AVOutputFormat....
Definition: mux.h:59
AV_PKT_FLAG_KEY
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
Definition: packet.h:650
FFOutputFormat::p
AVOutputFormat p
The public AVOutputFormat.
Definition: mux.h:65
avio_wl16
void avio_wl16(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:440
av_q2intfloat
uint32_t av_q2intfloat(AVRational q)
Convert an AVRational to a IEEE 32-bit float expressed in fixed-point format.
Definition: rational.c:150
ff_pdv_muxer
const FFOutputFormat ff_pdv_muxer
Definition: pdvenc.c:207
PDV_MAGIC
#define PDV_MAGIC
Definition: pdvenc.c:30
pdv_get_fps
static int pdv_get_fps(AVFormatContext *s, AVStream *st, uint32_t *fps_bits)
Definition: pdvenc.c:51
avio_tell
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition: avio.h:494
PDVMuxContext::nb_frames_pos
int64_t nb_frames_pos
Definition: pdvenc.c:39
AVRational::num
int num
Numerator.
Definition: rational.h:59
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
PDVMuxContext::max_frames
int max_frames
Definition: pdvenc.c:37
s
#define s(width, name)
Definition: cbs_vp9.c:198
OFFSET
#define OFFSET(x)
Definition: pdvenc.c:191
pdv_write_packet
static int pdv_write_packet(AVFormatContext *s, AVPacket *pkt)
Definition: pdvenc.c:123
AVCodecParameters::width
int width
The width of the video frame in pixels.
Definition: codec_par.h:143
PDVMuxContext::nb_frames
int nb_frames
Definition: pdvenc.c:36
AVFormatContext
Format I/O context.
Definition: avformat.h:1263
AVStream::codecpar
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:767
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
pdv_write_header
static int pdv_write_header(AVFormatContext *s)
Definition: pdvenc.c:72
AVStream::time_base
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented.
Definition: avformat.h:783
NULL
#define NULL
Definition: coverity.c:32
PDVMuxContext::payload_start
int64_t payload_start
Definition: pdvenc.c:41
AVRational
Rational number (pair of numerator and denominator).
Definition: rational.h:58
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:242
options
Definition: swscale.c:45
FFOutputFormat
Definition: mux.h:61
PDV_MAX_OFFSET
#define PDV_MAX_OFFSET
Definition: pdvenc.c:32
ffio_fill
void ffio_fill(AVIOContext *s, int b, int64_t count)
Definition: aviobuf.c:192
AV_CODEC_ID_PDV
@ AV_CODEC_ID_PDV
Definition: codec_id.h:324
AVPacket::size
int size
Definition: packet.h:596
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:94
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
pdv_write_trailer
static int pdv_write_trailer(AVFormatContext *s)
Definition: pdvenc.c:156
pdv_muxer_class
static const AVClass pdv_muxer_class
Definition: pdvenc.c:199
avio_write
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition: aviobuf.c:206
PDV_MAX_FRAMES
#define PDV_MAX_FRAMES
Definition: pdvenc.c:31
avio_wl32
void avio_wl32(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:360
offset
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf offset
Definition: writing_filters.txt:86
PDVMuxContext::entries
uint32_t * entries
Definition: pdvenc.c:35
AVPacket::flags
int flags
A combination of AV_PKT_FLAG values.
Definition: packet.h:601
PDVMuxContext::table_pos
int64_t table_pos
Definition: pdvenc.c:40
zero
static int zero(InterplayACMContext *s, unsigned ind, unsigned col)
Definition: interplayacm.c:121
PDVMuxContext::fps_bits
uint32_t fps_bits
Definition: pdvenc.c:38
AV_CODEC_ID_NONE
@ AV_CODEC_ID_NONE
Definition: codec_id.h:50
avio_internal.h
AVCodecParameters::height
int height
The height of the video frame in pixels.
Definition: codec_par.h:150
av_malloc_array
#define av_malloc_array(a, b)
Definition: tableprint_vlc.h:32
FF_OFMT_FLAG_MAX_ONE_OF_EACH
#define FF_OFMT_FLAG_MAX_ONE_OF_EACH
If this flag is set, it indicates that for each codec type whose corresponding default codec (i....
Definition: mux.h:50
av_inv_q
static av_always_inline AVRational av_inv_q(AVRational q)
Invert a rational.
Definition: rational.h:159
av_cmp_q
static int av_cmp_q(AVRational a, AVRational b)
Compare two rationals.
Definition: rational.h:89
ret
ret
Definition: filter_design.txt:187
AVStream
Stream structure.
Definition: avformat.h:744
avio_seek
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:236
AVClass::class_name
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:81
avformat.h
U
#define U(x)
Definition: vpx_arith.h:37
AV_CLASS_CATEGORY_MUXER
@ AV_CLASS_CATEGORY_MUXER
Definition: log.h:32
AVIO_SEEKABLE_NORMAL
#define AVIO_SEEKABLE_NORMAL
Seeking works like for a local file.
Definition: avio.h:41
AVRational::den
int den
Denominator.
Definition: rational.h:60
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition: opt.h:259
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
ENC
#define ENC
Definition: pdvenc.c:192
mem.h
AVPacket
This structure stores compressed data.
Definition: packet.h:572
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61
pkt
static AVPacket * pkt
Definition: demux_decode.c:55
PDVMuxContext
Definition: pdvenc.c:34
options
static const AVOption options[]
Definition: pdvenc.c:193
mux.h