FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/pdvenc.c
Date: 2026-05-04 20:54:34
Exec Total Coverage
Lines: 54 64 84.4%
Functions: 3 3 100.0%
Branches: 22 30 73.3%

Line Branch Exec Source
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 {
35 FFZStream zstream;
36 uint8_t *previous_frame;
37 uint8_t *work_frame;
38 int row_size;
39 int frame_size;
40 int frame_number;
41 int last_keyframe;
42 } PDVEncContext;
43
44 2 static av_cold int encode_init(AVCodecContext *avctx)
45 {
46 2 PDVEncContext *s = avctx->priv_data;
47 int ret;
48
49 2 ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
50
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0)
51 return ret;
52
53 2 s->row_size = (avctx->width + 7) >> 3;
54
55 2 s->frame_size = s->row_size * avctx->height;
56
57 2 s->previous_frame = av_malloc(s->frame_size);
58 2 s->work_frame = av_malloc(s->frame_size);
59
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!s->previous_frame || !s->work_frame)
60 return AVERROR(ENOMEM);
61
62 2 avctx->bits_per_coded_sample = 1;
63
64 2 return ff_deflate_init(&s->zstream,
65
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 avctx->compression_level == FF_COMPRESSION_DEFAULT ?
66 Z_DEFAULT_COMPRESSION :
67 av_clip(avctx->compression_level, 0, 9),
68 avctx);
69 }
70
71 2 static av_cold int encode_end(AVCodecContext *avctx)
72 {
73 2 PDVEncContext *s = avctx->priv_data;
74
75 2 av_freep(&s->previous_frame);
76 2 av_freep(&s->work_frame);
77 2 ff_deflate_end(&s->zstream);
78
79 2 return 0;
80 }
81
82 6 static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
83 const AVFrame *frame, int *got_packet)
84 {
85 6 PDVEncContext *s = avctx->priv_data;
86 6 z_stream *const zstream = &s->zstream.zstream;
87 6 uint8_t *prev = s->previous_frame;
88 6 uint8_t *curr = s->work_frame;
89 uint8_t *payload;
90
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 times.
9 const int keyframe = s->frame_number == 0 || avctx->gop_size <= 1 ||
91
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 s->frame_number - s->last_keyframe >= avctx->gop_size;
92 int ret;
93 int zret;
94
95
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (s->frame_number == INT_MAX) {
96 av_log(avctx, AV_LOG_ERROR, "Frame counter reached INT_MAX.\n");
97 return AVERROR(EINVAL);
98 }
99
100 {
101 6 const uint8_t *src = frame->data[0];
102 6 const ptrdiff_t src_linesize = frame->linesize[0];
103
104
2/2
✓ Branch 0 taken 1728 times.
✓ Branch 1 taken 6 times.
1734 for (int y = 0; y < avctx->height; y++) {
105 1728 memcpy(curr + y * s->row_size, src, s->row_size);
106 1728 src += src_linesize;
107 }
108 }
109
110 6 ret = ff_get_encode_buffer(avctx, pkt, deflateBound(zstream, s->frame_size), 0);
111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (ret < 0)
112 return ret;
113
114
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 if (keyframe) {
115 4 payload = curr;
116 } else {
117
2/2
✓ Branch 0 taken 25344 times.
✓ Branch 1 taken 2 times.
25346 for (int i = 0; i < s->frame_size; i++)
118 25344 prev[i] ^= curr[i];
119 2 payload = prev;
120 }
121
122 6 zret = deflateReset(zstream);
123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (zret != Z_OK) {
124 av_log(avctx, AV_LOG_ERROR, "Could not reset deflate: %d.\n", zret);
125 return AVERROR_EXTERNAL;
126 }
127
128 6 zstream->next_in = payload;
129 6 zstream->avail_in = s->frame_size;
130 6 zstream->next_out = pkt->data;
131 6 zstream->avail_out = pkt->size;
132
133 6 zret = deflate(zstream, Z_FINISH);
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (zret != Z_STREAM_END) {
135 av_log(avctx, AV_LOG_ERROR, "Deflate failed with return code: %d.\n", zret);
136 return AVERROR_EXTERNAL;
137 }
138
139 6 pkt->size = zstream->total_out;
140
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 if (keyframe) {
141 4 pkt->flags |= AV_PKT_FLAG_KEY;
142 4 s->last_keyframe = s->frame_number;
143 }
144
145 6 FFSWAP(uint8_t*, s->previous_frame, s->work_frame);
146 6 s->frame_number++;
147 6 *got_packet = 1;
148
149 6 return 0;
150 }
151
152 const FFCodec ff_pdv_encoder = {
153 .p.name = "pdv",
154 CODEC_LONG_NAME("PDV (PlayDate Video)"),
155 .p.type = AVMEDIA_TYPE_VIDEO,
156 .p.id = AV_CODEC_ID_PDV,
157 .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE |
158 AV_CODEC_CAP_EXPERIMENTAL,
159 .priv_data_size = sizeof(PDVEncContext),
160 .init = encode_init,
161 FF_CODEC_ENCODE_CB(encode_frame),
162 .close = encode_end,
163 CODEC_PIXFMTS(AV_PIX_FMT_MONOBLACK),
164 .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
165 };
166