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
43
45{
46 PDVMuxContext *pdv = s->priv_data;
47
48 av_freep(&pdv->entries);
49}
50
51static 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
193static 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
199static 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};
const FFOutputFormat ff_pdv_muxer
Definition pdvenc.c:207
Main libavformat public API header.
#define AVFMT_NOTIMESTAMPS
Format does not need / have any timestamps.
Definition avformat.h:498
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:199
static int pdv_get_fps(AVFormatContext *s, AVStream *st, uint32_t *fps_bits)
Definition pdvenc.c:51
static int pdv_write_packet(AVFormatContext *s, AVPacket *pkt)
Definition pdvenc.c:123
#define PDV_MAX_FRAMES
Definition pdvenc.c:31
static int pdv_write_header(AVFormatContext *s)
Definition pdvenc.c:72
#define PDV_MAX_OFFSET
Definition pdvenc.c:32
#define OFFSET(x)
Definition pdvenc.c:191
static void pdv_deinit(AVFormatContext *s)
Definition pdvenc.c:44
static int pdv_write_trailer(AVFormatContext *s)
Definition pdvenc.c:156
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
@ 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:1333
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:766
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition avformat.h:789
AVRational avg_frame_rate
Average framerate.
Definition avformat.h:855
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented.
Definition avformat.h:805
uint32_t fps_bits
Definition pdvenc.c:38
int64_t table_pos
Definition pdvenc.c:40
int nb_frames
Definition pdvenc.c:36
int max_frames
Definition pdvenc.c:37
uint32_t * entries
Definition pdvenc.c:35
int64_t nb_frames_pos
Definition pdvenc.c:39
int64_t payload_start
Definition pdvenc.c:41
#define av_malloc_array(a, b)
#define av_freep(p)
#define av_log(a,...)