FFmpeg
pdvenc.c
Go to the documentation of this file.
1 /*
2  * PDV video encoder
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/common.h"
24 #include "libavutil/imgutils.h"
25 #include "libavutil/mem.h"
26 #include "avcodec.h"
27 #include "codec_internal.h"
28 #include "encode.h"
29 #include "zlib_wrapper.h"
30 
31 #include <limits.h>
32 #include <zlib.h>
33 
34 typedef struct PDVEncContext {
36  uint8_t *previous_frame;
37  uint8_t *work_frame;
38  int row_size;
43 
45 {
46  PDVEncContext *s = avctx->priv_data;
47  size_t frame_size;
48  int ret;
49 
50  if (avctx->pix_fmt != AV_PIX_FMT_MONOBLACK) {
51  av_log(avctx, AV_LOG_ERROR, "Only monob pixel format is supported.\n");
52  return AVERROR(EINVAL);
53  }
54 
55  ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
56  if (ret < 0)
57  return ret;
58 
59  s->row_size = (avctx->width + 7) >> 3;
60 
61  ret = av_size_mult(s->row_size, avctx->height, &frame_size);
62  if (ret < 0 || frame_size > INT_MAX) {
63  av_log(avctx, AV_LOG_ERROR,
64  "Cannot allocate frame buffer for dimensions %dx%d.\n",
65  avctx->width, avctx->height);
66  return AVERROR(EINVAL);
67  }
68  s->frame_size = frame_size;
69 
70  s->previous_frame = av_malloc(s->frame_size);
71  s->work_frame = av_malloc(s->frame_size);
72  if (!s->previous_frame || !s->work_frame)
73  goto fail;
74 
75  avctx->bits_per_coded_sample = 1;
76 
77  ret = ff_deflate_init(&s->zstream,
79  Z_DEFAULT_COMPRESSION :
80  av_clip(avctx->compression_level, 0, 9),
81  avctx);
82  if (ret < 0)
83  goto fail;
84 
85  return 0;
86 
87 fail:
88  av_freep(&s->work_frame);
89  av_freep(&s->previous_frame);
90  return ret < 0 ? ret : AVERROR(ENOMEM);
91 }
92 
93 static av_cold int encode_end(AVCodecContext *avctx)
94 {
95  PDVEncContext *s = avctx->priv_data;
96 
97  av_freep(&s->previous_frame);
98  av_freep(&s->work_frame);
99  ff_deflate_end(&s->zstream);
100 
101  return 0;
102 }
103 
105  const AVFrame *frame, int *got_packet)
106 {
107  PDVEncContext *s = avctx->priv_data;
108  z_stream *const zstream = &s->zstream.zstream;
109  uint8_t *prev = s->previous_frame;
110  uint8_t *curr = s->work_frame;
111  uint8_t *payload;
112  const int keyframe = s->frame_number == 0 || avctx->gop_size <= 1 ||
113  s->frame_number - s->last_keyframe >= avctx->gop_size;
114  int ret;
115  int zret;
116 
117  if (s->frame_number == INT_MAX) {
118  av_log(avctx, AV_LOG_ERROR, "Frame counter reached INT_MAX.\n");
119  return AVERROR(EINVAL);
120  }
121 
122  {
123  const uint8_t *src = frame->data[0];
124  const ptrdiff_t src_linesize = frame->linesize[0];
125 
126  for (int y = 0; y < avctx->height; y++) {
127  memcpy(curr + y * s->row_size, src, s->row_size);
128  src += src_linesize;
129  }
130  }
131 
132  ret = ff_get_encode_buffer(avctx, pkt, deflateBound(zstream, s->frame_size), 0);
133  if (ret < 0)
134  return ret;
135 
136  if (keyframe) {
137  payload = curr;
138  } else {
139  for (int i = 0; i < s->frame_size; i++)
140  prev[i] ^= curr[i];
141  payload = prev;
142  }
143 
144  zret = deflateReset(zstream);
145  if (zret != Z_OK) {
146  av_log(avctx, AV_LOG_ERROR, "Could not reset deflate: %d.\n", zret);
147  return AVERROR_EXTERNAL;
148  }
149 
150  zstream->next_in = payload;
151  zstream->avail_in = s->frame_size;
152  zstream->next_out = pkt->data;
153  zstream->avail_out = pkt->size;
154 
155  zret = deflate(zstream, Z_FINISH);
156  if (zret != Z_STREAM_END) {
157  av_log(avctx, AV_LOG_ERROR, "Deflate failed with return code: %d.\n", zret);
158  return AVERROR_EXTERNAL;
159  }
160 
161  pkt->size = zstream->total_out;
162  if (keyframe) {
164  s->last_keyframe = s->frame_number;
165  }
166 
167  FFSWAP(uint8_t*, s->previous_frame, s->work_frame);
168  s->frame_number++;
169  *got_packet = 1;
170 
171  return 0;
172 }
173 
175  .p.name = "pdv",
176  CODEC_LONG_NAME("PDV (PlayDate Video)"),
177  .p.type = AVMEDIA_TYPE_VIDEO,
178  .p.id = AV_CODEC_ID_PDV,
181  .priv_data_size = sizeof(PDVEncContext),
182  .init = encode_init,
184  .close = encode_end,
186  .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
187 };
av_size_mult
int av_size_mult(size_t a, size_t b, size_t *r)
Multiply two size_t values checking for overflow.
Definition: mem.c:567
CODEC_PIXFMTS
#define CODEC_PIXFMTS(...)
Definition: codec_internal.h:392
av_clip
#define av_clip
Definition: common.h:100
FF_CODEC_CAP_INIT_CLEANUP
#define FF_CODEC_CAP_INIT_CLEANUP
The codec allows calling the close function for deallocation even if the init function returned a fai...
Definition: codec_internal.h:42
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
ff_pdv_encoder
const FFCodec ff_pdv_encoder
Definition: pdvenc.c:174
PDVEncContext::frame_size
int frame_size
Definition: pdvenc.c:39
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:434
AVPacket::data
uint8_t * data
Definition: packet.h:588
encode.h
FFCodec
Definition: codec_internal.h:127
ff_deflate_end
void ff_deflate_end(FFZStream *zstream)
Wrapper around deflateEnd().
FF_COMPRESSION_DEFAULT
#define FF_COMPRESSION_DEFAULT
Definition: avcodec.h:1236
AV_PKT_FLAG_KEY
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
Definition: packet.h:643
PDVEncContext::previous_frame
uint8_t * previous_frame
Definition: pdvenc.c:36
PDVEncContext::work_frame
uint8_t * work_frame
Definition: pdvenc.c:37
FFCodec::p
AVCodec p
The public AVCodec.
Definition: codec_internal.h:131
fail
#define fail()
Definition: checkasm.h:223
FF_CODEC_ENCODE_CB
#define FF_CODEC_ENCODE_CB(func)
Definition: codec_internal.h:359
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
AV_CODEC_CAP_EXPERIMENTAL
#define AV_CODEC_CAP_EXPERIMENTAL
Codec is experimental and is thus avoided in favor of non experimental encoders.
Definition: codec.h:87
zlib_wrapper.h
av_cold
#define av_cold
Definition: attributes.h:111
s
#define s(width, name)
Definition: cbs_vp9.c:198
frame_size
int frame_size
Definition: mxfenc.c:2487
AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE
#define AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE
This encoder can reorder user opaque values from input AVFrames and return them with corresponding ou...
Definition: codec.h:144
encode_frame
static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet)
Definition: pdvenc.c:104
PDVEncContext
Definition: pdvenc.c:34
limits.h
CODEC_LONG_NAME
#define CODEC_LONG_NAME(str)
Definition: codec_internal.h:332
PDVEncContext::frame_number
int frame_number
Definition: pdvenc.c:40
AV_PIX_FMT_MONOBLACK
@ AV_PIX_FMT_MONOBLACK
Y , 1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb.
Definition: pixfmt.h:83
PDVEncContext::last_keyframe
int last_keyframe
Definition: pdvenc.c:41
deflate
static void deflate(uint8_t *dst, const uint8_t *p1, int width, int threshold, const uint8_t *coordinates[], int coord, int maxc)
Definition: vf_neighbor.c:161
AV_CODEC_ID_PDV
@ AV_CODEC_ID_PDV
Definition: codec_id.h:324
init
int(* init)(AVBSFContext *ctx)
Definition: dts2pts.c:550
AV_CODEC_CAP_DR1
#define AV_CODEC_CAP_DR1
Codec uses get_buffer() or get_encode_buffer() for allocating buffers and supports custom allocators.
Definition: codec.h:52
AVPacket::size
int size
Definition: packet.h:589
AVCodecContext::gop_size
int gop_size
the number of pictures in a group of pictures, or 0 for intra_only
Definition: avcodec.h:1017
codec_internal.h
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
PDVEncContext::zstream
FFZStream zstream
Definition: pdvenc.c:35
AVERROR_EXTERNAL
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:59
AVPacket::flags
int flags
A combination of AV_PKT_FLAG values.
Definition: packet.h:594
AVCodecContext::bits_per_coded_sample
int bits_per_coded_sample
bits per sample/pixel from the demuxer (needed for huffyuv).
Definition: avcodec.h:1558
av_malloc
#define av_malloc(s)
Definition: ops_asmgen.c:44
common.h
AVCodec::name
const char * name
Name of the codec implementation.
Definition: codec.h:179
AVCodecContext::height
int height
Definition: avcodec.h:600
AVCodecContext::pix_fmt
enum AVPixelFormat pix_fmt
Pixel format, see AV_PIX_FMT_xxx.
Definition: avcodec.h:639
avcodec.h
PDVEncContext::row_size
int row_size
Definition: pdvenc.c:38
ret
ret
Definition: filter_design.txt:187
FFSWAP
#define FFSWAP(type, a, b)
Definition: macros.h:52
frame
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
Definition: filter_design.txt:265
AVCodecContext
main external API structure.
Definition: avcodec.h:439
encode_init
static av_cold int encode_init(AVCodecContext *avctx)
Definition: pdvenc.c:44
ff_get_encode_buffer
int ff_get_encode_buffer(AVCodecContext *avctx, AVPacket *avpkt, int64_t size, int flags)
Get a buffer for a packet.
Definition: encode.c:105
encode_end
static av_cold int encode_end(AVCodecContext *avctx)
Definition: pdvenc.c:93
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:200
FFZStream
Definition: zlib_wrapper.h:27
mem.h
AVPacket
This structure stores compressed data.
Definition: packet.h:565
AVCodecContext::priv_data
void * priv_data
Definition: avcodec.h:466
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
AVCodecContext::width
int width
picture width / height.
Definition: avcodec.h:600
imgutils.h
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
pkt
static AVPacket * pkt
Definition: demux_decode.c:55
av_image_check_size
int av_image_check_size(unsigned int w, unsigned int h, int log_offset, void *log_ctx)
Check if the given dimension of an image is valid, meaning that all bytes of the image can be address...
Definition: imgutils.c:318
src
#define src
Definition: vp8dsp.c:248
ff_deflate_init
int ff_deflate_init(FFZStream *zstream, int level, void *logctx)
Wrapper around deflateInit().
AVCodecContext::compression_level
int compression_level
Definition: avcodec.h:1235