FFmpeg
msrleenc.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2023 Tomas Härdin
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /**
22  * @file
23  * MSRLE encoder
24  * @see https://wiki.multimedia.cx/index.php?title=Microsoft_RLE
25  */
26 
27 // TODO: pal4 mode?
28 
29 #include "bytestream.h"
30 #include "codec_internal.h"
31 #include "encode.h"
32 
33 #include "libavutil/attributes.h"
34 
35 typedef struct MSRLEContext {
36  int curframe;
38 } MSRLEContext;
39 
41 {
42  MSRLEContext *s = avctx->priv_data;
43 
44  avctx->bits_per_coded_sample = 8;
45  s->last_frame = av_frame_alloc();
46  if (!s->last_frame)
47  return AVERROR(ENOMEM);
48 
49  return 0;
50 }
51 
52 static void write_run(AVCodecContext *avctx, uint8_t **data, int len, int value)
53 {
54  // we're allowed to write odd runs
55  while (len >= 255) {
56  bytestream_put_byte(data, 255);
57  bytestream_put_byte(data, value);
58  len -= 255;
59  }
60  if (len >= 1) {
61  // this is wasteful when len == 1 and sometimes when len == 2
62  // but sometimes we have no choice. also write_absolute()
63  // relies on this
64  bytestream_put_byte(data, len);
65  bytestream_put_byte(data, value);
66  }
67 }
68 
69 static void write_absolute(AVCodecContext *avctx, uint8_t **data,
70  const uint8_t *line, int len)
71 {
72  // writing 255 would be wasteful here due to the padding requirement
73  while (len >= 254) {
74  bytestream_put_byte(data, 0);
75  bytestream_put_byte(data, 254);
77  line += 254;
78  len -= 254;
79  }
80  if (len == 1) {
81  // it's less wasteful to write single pixels as runs
82  // not to mention that absolute mode requires >= 3 pixels
83  write_run(avctx, data, 1, line[0]);
84  } else if (len == 2) {
85  write_run(avctx, data, 1, line[0]);
86  write_run(avctx, data, 1, line[1]);
87  } else if (len > 0) {
88  bytestream_put_byte(data, 0);
89  bytestream_put_byte(data, len);
91  if (len & 1)
92  bytestream_put_byte(data, 0);
93  }
94 }
95 
96 static void write_delta(AVCodecContext *avctx, uint8_t **data, int delta)
97 {
98  // we let the yskip logic handle the case where we want to delta
99  // to following lines. it's not perfect but it's easier than finding
100  // the optimal combination of end-of-lines and deltas to reach any
101  // following position including places where dx < 0
102  while (delta >= 255) {
103  bytestream_put_byte(data, 0);
104  bytestream_put_byte(data, 2);
105  bytestream_put_byte(data, 255);
106  bytestream_put_byte(data, 0);
107  delta -= 255;
108  }
109  if (delta > 0) {
110  bytestream_put_byte(data, 0);
111  bytestream_put_byte(data, 2);
112  bytestream_put_byte(data, delta);
113  bytestream_put_byte(data, 0);
114  }
115 }
116 
117 static void write_yskip(AVCodecContext *avctx, uint8_t **data, int yskip)
118 {
119  if (yskip < 4)
120  return;
121  // we have yskip*2 nul bytess
122  *data -= 2*yskip;
123  // the end-of-line counts as one skip
124  yskip--;
125  while (yskip >= 255) {
126  bytestream_put_byte(data, 0);
127  bytestream_put_byte(data, 2);
128  bytestream_put_byte(data, 0);
129  bytestream_put_byte(data, 255);
130  yskip -= 255;
131  }
132  if (yskip > 0) {
133  bytestream_put_byte(data, 0);
134  bytestream_put_byte(data, 2);
135  bytestream_put_byte(data, 0);
136  bytestream_put_byte(data, yskip);
137  }
138  bytestream_put_be16(data, 0x0000);
139 }
140 
141 // used both to encode lines in keyframes and to encode lines between deltas
142 static void encode_line(AVCodecContext *avctx, uint8_t **data,
143  const uint8_t *line, int length)
144 {
145  int run = 0, last = -1, absstart = 0;
146  if (length == 0)
147  return;
148  for (int x = 0; x < length; x++) {
149  if (last == line[x]) {
150  run++;
151  if (run == 3)
152  write_absolute(avctx, data, &line[absstart], x - absstart - 2);
153  } else {
154  if (run >= 3) {
155  write_run(avctx, data, run, last);
156  absstart = x;
157  }
158  run = 1;
159  }
160  last = line[x];
161  }
162  if (run >= 3)
163  write_run(avctx, data, run, last);
164  else
165  write_absolute(avctx, data, &line[absstart], length - absstart);
166 }
167 
168 static int encode(AVCodecContext *avctx, AVPacket *pkt,
169  const AVFrame *pict, int keyframe, int *got_keyframe)
170 {
171  MSRLEContext *s = avctx->priv_data;
172  uint8_t *data = pkt->data;
173 
174  /* Compare the current frame to the last frame, or code the entire frame
175  if keyframe != 0. We're continually outputting pairs of bytes:
176 
177  00 00 end of line
178  00 01 end of bitmap
179  00 02 dx dy delta. move pointer to x+dx, y+dy
180  00 ll dd dd .. absolute (verbatim) mode. ll >= 3
181  rr dd run. rr >= 1
182 
183  For keyframes we only have absolute mode and runs at our disposal, and
184  we are not allowed to end a line early. If this happens when keyframe == 0
185  then *got_keyframe is set to 1 and s->curframe is reset.
186  */
187  *got_keyframe = 1; // set to zero whenever we use a feature that makes this a not-keyframe
188 
189  if (keyframe) {
190  for (int y = avctx->height-1; y >= 0; y--) {
191  uint8_t *line = &pict->data[0][y*pict->linesize[0]];
192  encode_line(avctx, &data, line, avctx->width);
193  bytestream_put_be16(&data, 0x0000); // end of line
194  }
195  } else {
196  // compare to previous frame
197  int yskip = 0; // we can encode large skips using deltas
198  for (int y = avctx->height-1; y >= 0; y--) {
199  const uint8_t *line = &pict->data[0][y*pict->linesize[0]];
200  const uint8_t *prev = &s->last_frame->data[0][y*s->last_frame->linesize[0]];
201  // we need at least 5 pixels in a row for a delta to be worthwhile
202  int delta = 0, linestart = 0, encoded = 0;
203  for (int x = 0; x < avctx->width; x++) {
204  if (line[x] == prev[x]) {
205  delta++;
206  if (delta == 5) {
207  int len = x - linestart - 4;
208  if (len > 0) {
209  write_yskip(avctx, &data, yskip);
210  yskip = 0;
211  encode_line(avctx, &data, &line[linestart], len);
212  encoded = 1;
213  }
214  linestart = -1;
215  }
216  } else {
217  if (delta >= 5) {
218  write_yskip(avctx, &data, yskip);
219  yskip = 0;
220  write_delta(avctx, &data, delta);
221  *got_keyframe = 0;
222  encoded = 1;
223  }
224  delta = 0;
225  if (linestart == -1)
226  linestart = x;
227  }
228  }
229  if (delta < 5) {
230  write_yskip(avctx, &data, yskip);
231  yskip = 0;
232  encode_line(avctx, &data, &line[linestart], avctx->width - linestart);
233  encoded = 1;
234  } else
235  *got_keyframe = 0;
236  bytestream_put_be16(&data, 0x0000); // end of line
237  if (!encoded)
238  yskip++;
239  else
240  yskip = 0;
241  }
242  write_yskip(avctx, &data, yskip);
243  }
244  bytestream_put_be16(&data, 0x0001); // end of bitmap
245  pkt->size = data - pkt->data;
246  return 0;
247 }
248 
250  const AVFrame *pict, int *got_packet)
251 {
252  MSRLEContext *s = avctx->priv_data;
253  int ret, got_keyframe;
254 
255  if ((ret = ff_alloc_packet(avctx, pkt, (
256  avctx->width*2 /* worst case = rle every pixel */ + 2 /*end of line */
257  ) * avctx->height + 2 /* end of bitmap */ + FF_INPUT_BUFFER_MIN_SIZE)))
258  return ret;
259 
260  if (pict->data[1]) {
262  if (!side_data)
263  return AVERROR(ENOMEM);
264  memcpy(side_data, pict->data[1], AVPALETTE_SIZE);
265  }
266 
267  if ((ret = encode(avctx, pkt, pict, s->curframe == 0, &got_keyframe)))
268  return ret;
269 
270  if (got_keyframe) {
272  s->curframe = 0;
273  }
274  if (++s->curframe >= avctx->gop_size)
275  s->curframe = 0;
276  *got_packet = 1;
277 
278  return av_frame_replace(s->last_frame, pict);
279 }
280 
282 {
283  MSRLEContext *s = avctx->priv_data;
284  av_frame_free(&s->last_frame);
285  return 0;
286 }
287 
289  .p.name = "msrle",
290  CODEC_LONG_NAME("Microsoft RLE"),
291  .p.type = AVMEDIA_TYPE_VIDEO,
292  .p.id = AV_CODEC_ID_MSRLE,
293  .p.capabilities = AV_CODEC_CAP_DR1,
294  .priv_data_size = sizeof(MSRLEContext),
297  .close = msrle_encode_close,
299  .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
300 };
msrle_encode_init
static av_cold int msrle_encode_init(AVCodecContext *avctx)
Definition: msrleenc.c:40
CODEC_PIXFMTS
#define CODEC_PIXFMTS(...)
Definition: codec_internal.h:391
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
MSRLEContext
Definition: msrleenc.c:35
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:64
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:427
AVPacket::data
uint8_t * data
Definition: packet.h:558
encode.h
data
const char data[16]
Definition: mxf.c:149
FFCodec
Definition: codec_internal.h:127
AV_PKT_FLAG_KEY
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
Definition: packet.h:613
FF_INPUT_BUFFER_MIN_SIZE
#define FF_INPUT_BUFFER_MIN_SIZE
Used by some encoders as upper bound for the length of headers.
Definition: encode.h:33
MSRLEContext::curframe
int curframe
Definition: msrleenc.c:36
AVFrame::data
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:448
write_yskip
static void write_yskip(AVCodecContext *avctx, uint8_t **data, int yskip)
Definition: msrleenc.c:117
FFCodec::p
AVCodec p
The public AVCodec.
Definition: codec_internal.h:131
AV_PKT_DATA_PALETTE
@ AV_PKT_DATA_PALETTE
An AV_PKT_DATA_PALETTE side data packet contains exactly AVPALETTE_SIZE bytes worth of palette.
Definition: packet.h:47
FF_CODEC_ENCODE_CB
#define FF_CODEC_ENCODE_CB(func)
Definition: codec_internal.h:358
av_frame_alloc
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition: frame.c:52
pkt
AVPacket * pkt
Definition: movenc.c:60
av_cold
#define av_cold
Definition: attributes.h:90
write_delta
static void write_delta(AVCodecContext *avctx, uint8_t **data, int delta)
Definition: msrleenc.c:96
s
#define s(width, name)
Definition: cbs_vp9.c:198
msrle_encode_frame
static int msrle_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *pict, int *got_packet)
Definition: msrleenc.c:249
CODEC_LONG_NAME
#define CODEC_LONG_NAME(str)
Definition: codec_internal.h:331
run
uint8_t run
Definition: svq3.c:207
AVPALETTE_SIZE
#define AVPALETTE_SIZE
Definition: pixfmt.h:32
init
int(* init)(AVBSFContext *ctx)
Definition: dts2pts.c:368
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:559
AVCodecContext::gop_size
int gop_size
the number of pictures in a group of pictures, or 0 for intra_only
Definition: avcodec.h:1005
codec_internal.h
write_run
static void write_run(AVCodecContext *avctx, uint8_t **data, int len, int value)
Definition: msrleenc.c:52
AV_CODEC_ID_MSRLE
@ AV_CODEC_ID_MSRLE
Definition: codec_id.h:97
line
Definition: graph2dot.c:48
attributes.h
AVPacket::flags
int flags
A combination of AV_PKT_FLAG values.
Definition: packet.h:564
encode
static int encode(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *pict, int keyframe, int *got_keyframe)
Definition: msrleenc.c:168
AVCodecContext::bits_per_coded_sample
int bits_per_coded_sample
bits per sample/pixel from the demuxer (needed for huffyuv).
Definition: avcodec.h:1546
bytestream_put_buffer
static av_always_inline void bytestream_put_buffer(uint8_t **b, const uint8_t *src, unsigned int size)
Definition: bytestream.h:372
delta
float delta
Definition: vorbis_enc_data.h:430
value
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 default value
Definition: writing_filters.txt:86
AVCodec::name
const char * name
Name of the codec implementation.
Definition: codec.h:179
ff_msrle_encoder
const FFCodec ff_msrle_encoder
Definition: msrleenc.c:288
len
int len
Definition: vorbis_enc_data.h:426
AVCodecContext::height
int height
Definition: avcodec.h:592
AV_PIX_FMT_PAL8
@ AV_PIX_FMT_PAL8
8 bits with AV_PIX_FMT_RGB32 palette
Definition: pixfmt.h:84
ret
ret
Definition: filter_design.txt:187
msrle_encode_close
static av_cold int msrle_encode_close(AVCodecContext *avctx)
Definition: msrleenc.c:281
av_frame_replace
int av_frame_replace(AVFrame *dst, const AVFrame *src)
Ensure the destination frame refers to the same data described by the source frame,...
Definition: frame.c:376
AVCodecContext
main external API structure.
Definition: avcodec.h:431
av_packet_new_side_data
uint8_t * av_packet_new_side_data(AVPacket *pkt, enum AVPacketSideDataType type, size_t size)
Allocate new information of a packet.
Definition: packet.c:232
write_absolute
static void write_absolute(AVCodecContext *avctx, uint8_t **data, const uint8_t *line, int len)
Definition: msrleenc.c:69
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:200
MSRLEContext::last_frame
AVFrame * last_frame
Definition: msrleenc.c:37
AVPacket
This structure stores compressed data.
Definition: packet.h:535
AVCodecContext::priv_data
void * priv_data
Definition: avcodec.h:458
AVCodecContext::width
int width
picture width / height.
Definition: avcodec.h:592
bytestream.h
AVFrame::linesize
int linesize[AV_NUM_DATA_POINTERS]
For video, a positive or negative value, which is typically indicating the size in bytes of each pict...
Definition: frame.h:472
encode_line
static void encode_line(AVCodecContext *avctx, uint8_t **data, const uint8_t *line, int length)
Definition: msrleenc.c:142
ff_alloc_packet
int ff_alloc_packet(AVCodecContext *avctx, AVPacket *avpkt, int64_t size)
Check AVPacket size and allocate data.
Definition: encode.c:60