FFmpeg
Loading...
Searching...
No Matches
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
44
46{
47 PDVMuxContext *pdv = s->priv_data;
48
49 av_freep(&pdv->entries);
50}
51
52static int pdv_get_fps(AVFormatContext *s, AVStream *st, uint32_t *fps_bits)
53{
54 AVRational rate = st->avg_frame_rate;
55 const AVRational zero = { 0, 1 };
56
57 if (!rate.num || !rate.den)
58 rate = av_inv_q(st->time_base);
59 if (!rate.num || !rate.den) {
60 av_log(s, AV_LOG_ERROR, "A valid frame rate is required for PDV output.\n");
61 return AVERROR(EINVAL);
62 }
63
64 if (av_cmp_q(rate, zero) <= 0) {
65 av_log(s, AV_LOG_ERROR, "Invalid frame rate for PDV output.\n");
66 return AVERROR(EINVAL);
67 }
68
69 *fps_bits = av_q2intfloat(rate);
70 return 0;
71}
72
74{
75 PDVMuxContext *pdv = s->priv_data;
76 AVStream *st;
77 int ret;
78
79 if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
80 av_log(s, AV_LOG_ERROR, "PDV muxer requires seekable output.\n");
81 return AVERROR(EINVAL);
82 }
83
84 st = s->streams[0];
85
86 if (st->codecpar->width > UINT16_MAX || st->codecpar->height > UINT16_MAX) {
87 av_log(s, AV_LOG_ERROR, "Output dimensions exceed PDV limits.\n");
88 return AVERROR(EINVAL);
89 }
90
91 ret = pdv_get_fps(s, st, &pdv->fps_bits);
92 if (ret < 0)
93 return ret;
94
95 if (pdv->max_frames < 1 || pdv->max_frames > PDV_MAX_FRAMES) {
97 "The -max_frames option must be set to a value in [1, %u].\n",
99 return AVERROR(EINVAL);
100 }
101
102 pdv->entries = av_malloc_array(pdv->max_frames + 1, sizeof(*pdv->entries));
103 if (!pdv->entries)
104 return AVERROR(ENOMEM);
105
106 avio_write(s->pb, PDV_MAGIC, 16);
107 pdv->nb_frames_pos = avio_tell(s->pb);
108 if (pdv->nb_frames_pos < 0)
109 return pdv->nb_frames_pos;
110 avio_wl16(s->pb, 0);
111 avio_wl16(s->pb, 0);
112 avio_wl32(s->pb, pdv->fps_bits);
113 avio_wl16(s->pb, st->codecpar->width);
114 avio_wl16(s->pb, st->codecpar->height);
115
116 pdv->table_pos = avio_tell(s->pb);
117 if (pdv->table_pos < 0)
118 return pdv->table_pos;
119 ffio_fill(s->pb, 0, 4LL * (pdv->max_frames + 1));
120 pdv->payload_start = avio_tell(s->pb);
121 if (pdv->payload_start < 0)
122 return pdv->payload_start;
123
124 return 0;
125}
126
128{
129 PDVMuxContext *pdv = s->priv_data;
130 int64_t offset = avio_tell(s->pb);
131 const uint32_t max_table_gap = 4U * pdv->max_frames;
132
133 if (offset < 0)
134 return offset;
135 offset -= pdv->payload_start;
136 if (offset < 0)
137 return AVERROR(EINVAL);
138
139 if (pkt->size <= 0)
140 return AVERROR_INVALIDDATA;
141 if (pdv->nb_frames >= pdv->max_frames) {
142 av_log(s, AV_LOG_ERROR, "Too many frames for PDV output.\n");
143 return AVERROR(EINVAL);
144 }
145 if (offset > PDV_MAX_OFFSET - max_table_gap ||
146 pkt->size > PDV_MAX_OFFSET - max_table_gap - offset) {
147 av_log(s, AV_LOG_ERROR, "PDV payload exceeds container limits.\n");
148 return AVERROR(EINVAL);
149 }
150
151 pdv->entries[pdv->nb_frames] = ((uint32_t)offset << 2) |
152 (pkt->flags & AV_PKT_FLAG_KEY ? 1 : 2);
153 avio_write(s->pb, pkt->data, pkt->size);
154
155 pdv->nb_frames++;
156
157 return 0;
158}
159
161{
162 PDVMuxContext *pdv = s->priv_data;
163 int64_t payload_size = avio_tell(s->pb);
164 const uint32_t table_gap = 4U * (pdv->max_frames - pdv->nb_frames);
165 int64_t ret;
166
167 if (payload_size < 0)
168 return payload_size;
169 payload_size -= pdv->payload_start;
170 if (payload_size < 0 || payload_size > PDV_MAX_OFFSET - table_gap)
171 return AVERROR(EINVAL);
172
173 pdv->entries[pdv->nb_frames] = (uint32_t)payload_size << 2;
174
175 if ((ret = avio_seek(s->pb, pdv->nb_frames_pos, SEEK_SET)) < 0)
176 return ret;
177 avio_wl16(s->pb, pdv->nb_frames);
178
179 if ((ret = avio_seek(s->pb, pdv->table_pos, SEEK_SET)) < 0)
180 return ret;
181 for (int i = 0; i <= pdv->nb_frames; i++) {
182 const uint32_t frame_off = (pdv->entries[i] >> 2) + table_gap;
183
184 if (frame_off > PDV_MAX_OFFSET)
185 return AVERROR(EINVAL);
186 avio_wl32(s->pb, frame_off << 2 | (pdv->entries[i] & 3));
187 }
188
189 if ((ret = avio_seek(s->pb, pdv->payload_start + payload_size, SEEK_SET)) < 0)
190 return ret;
191
192 return 0;
193}
194
195#define OFFSET(x) offsetof(PDVMuxContext, x)
196#define ENC AV_OPT_FLAG_ENCODING_PARAM
197static const AVOption options[] = {
198 { "max_frames", "maximum number of frames reserved in table (mandatory)",
199 OFFSET(max_frames), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, PDV_MAX_FRAMES, ENC },
200 { NULL },
201};
202
203static const AVClass pdv_muxer_class = {
204 .class_name = "PDV muxer",
205 .item_name = av_default_item_name,
206 .option = options,
207 .version = LIBAVUTIL_VERSION_INT,
208 .category = AV_CLASS_CATEGORY_MUXER,
209};
210
212 .p.name = "pdv",
213 .p.long_name = NULL_IF_CONFIG_SMALL("PlayDate Video"),
214 .p.extensions = "pdv",
215 .p.priv_class = &pdv_muxer_class,
216 .p.audio_codec = AV_CODEC_ID_NONE,
217 .p.video_codec = AV_CODEC_ID_PDV,
218 .p.subtitle_codec = AV_CODEC_ID_NONE,
219 .priv_data_size = sizeof(PDVMuxContext),
220 .p.flags = AVFMT_NOTIMESTAMPS,
221 .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH |
223 .write_header = pdv_write_header,
224 .write_packet = pdv_write_packet,
225 .write_trailer = pdv_write_trailer,
226 .deinit = pdv_deinit,
227};
const FFOutputFormat ff_pdv_muxer
Definition pdvenc.c:211
Main libavformat public API header.
#define AVFMT_NOTIMESTAMPS
Format does not need / have any timestamps.
Definition avformat.h:500
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition aviobuf.c:236
#define AVIO_SEEKABLE_NORMAL
Seeking works like for a local file.
Definition avio.h:41
void avio_wl32(AVIOContext *s, unsigned int val)
Definition aviobuf.c:360
void avio_wl16(AVIOContext *s, unsigned int val)
Definition aviobuf.c:440
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition avio.h:494
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition aviobuf.c:206
void ffio_fill(AVIOContext *s, int b, int64_t count)
Definition aviobuf.c:192
#define ENC
Definition caca.c:198
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define s(width, name)
Definition cbs_vp9.c:198
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static AVPacket * pkt
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_CODEC_ID_PDV
Definition codec_id.h:315
@ AV_CODEC_ID_NONE
Definition codec_id.h:48
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
Definition packet.h:650
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:241
static int av_cmp_q(AVRational a, AVRational b)
Compare two rationals.
Definition rational.h:89
static av_always_inline AVRational av_inv_q(AVRational q)
Invert a rational.
Definition rational.h:159
uint32_t av_q2intfloat(AVRational q)
Convert an AVRational to a IEEE 32-bit float expressed in fixed-point format.
Definition rational.c:154
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
static int zero(InterplayACMContext *s, unsigned ind, unsigned col)
unsigned offset
Definition libaomenc.c:763
#define PDV_MAGIC
Definition pdvdec.c:28
static const AVClass pdv_muxer_class
Definition pdvenc.c:203
static int pdv_get_fps(AVFormatContext *s, AVStream *st, uint32_t *fps_bits)
Definition pdvenc.c:52
static int pdv_write_packet(AVFormatContext *s, AVPacket *pkt)
Definition pdvenc.c:127
#define PDV_MAX_FRAMES
Definition pdvenc.c:31
static int pdv_write_header(AVFormatContext *s)
Definition pdvenc.c:73
#define PDV_MAX_OFFSET
Definition pdvenc.c:32
#define OFFSET(x)
Definition pdvenc.c:195
static void pdv_deinit(AVFormatContext *s)
Definition pdvenc.c:45
static int pdv_write_trailer(AVFormatContext *s)
Definition pdvenc.c:160
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:97
@ AV_CLASS_CATEGORY_MUXER
Definition log.h:32
Memory handling functions.
#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
#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
AVOptions.
Utilities for rational number calculation.
Describe the class of an AVClass context structure.
Definition log.h:76
int height
The height of the video frame in pixels.
Definition codec_par.h:150
int width
The width of the video frame in pixels.
Definition codec_par.h:143
Format I/O context.
Definition avformat.h:1335
AVOption.
Definition opt.h:428
This structure stores compressed data.
Definition packet.h:580
Rational number (pair of numerator and denominator).
Definition rational.h:58
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
Stream structure.
Definition avformat.h:768
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition avformat.h:791
AVRational avg_frame_rate
Average framerate.
Definition avformat.h:857
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented.
Definition avformat.h:807
uint32_t fps_bits
Definition pdvenc.c:39
int64_t table_pos
Definition pdvenc.c:41
int nb_frames
Definition pdvenc.c:37
int max_frames
Definition pdvenc.c:38
uint32_t * entries
Definition pdvenc.c:36
int64_t nb_frames_pos
Definition pdvenc.c:40
int64_t payload_start
Definition pdvenc.c:42
#define av_malloc_array(a, b)
#define av_freep(p)
#define av_log(a,...)