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 typedef struct MSRLEContext {
34  int curframe;
36 } MSRLEContext;
37 
39 {
40  MSRLEContext *s = avctx->priv_data;
41 
42  avctx->bits_per_coded_sample = 8;
43  s->last_frame = av_frame_alloc();
44  if (!s->last_frame)
45  return AVERROR(ENOMEM);
46 
47  return 0;
48 }
49 
50 static void write_run(AVCodecContext *avctx, uint8_t **data, int len, int value)
51 {
52  // we're allowed to write odd runs
53  while (len >= 255) {
54  bytestream_put_byte(data, 255);
55  bytestream_put_byte(data, value);
56  len -= 255;
57  }
58  if (len >= 1) {
59  // this is wasteful when len == 1 and sometimes when len == 2
60  // but sometimes we have no choice. also write_absolute()
61  // relies on this
62  bytestream_put_byte(data, len);
63  bytestream_put_byte(data, value);
64  }
65 }
66 
67 static void write_absolute(AVCodecContext *avctx, uint8_t **data,
68  const uint8_t *line, int len)
69 {
70  // writing 255 would be wasteful here due to the padding requirement
71  while (len >= 254) {
72  bytestream_put_byte(data, 0);
73  bytestream_put_byte(data, 254);
75  line += 254;
76  len -= 254;
77  }
78  if (len == 1) {
79  // it's less wasteful to write single pixels as runs
80  // not to mention that absolute mode requires >= 3 pixels
81  write_run(avctx, data, 1, line[0]);
82  } else if (len == 2) {
83  write_run(avctx, data, 1, line[0]);
84  write_run(avctx, data, 1, line[1]);
85  } else if (len > 0) {
86  bytestream_put_byte(data, 0);
87  bytestream_put_byte(data, len);
89  if (len & 1)
90  bytestream_put_byte(data, 0);
91  }
92 }
93 
94 static void write_delta(AVCodecContext *avctx, uint8_t **data, int delta)
95 {
96  // we let the yskip logic handle the case where we want to delta
97  // to following lines. it's not perfect but it's easier than finding
98  // the optimal combination of end-of-lines and deltas to reach any
99  // following position including places where dx < 0
100  while (delta >= 255) {
101  bytestream_put_byte(data, 0);
102  bytestream_put_byte(data, 2);
103  bytestream_put_byte(data, 255);
104  bytestream_put_byte(data, 0);
105  delta -= 255;
106  }
107  if (delta > 0) {
108  bytestream_put_byte(data, 0);
109  bytestream_put_byte(data, 2);
110  bytestream_put_byte(data, delta);
111  bytestream_put_byte(data, 0);
112  }
113 }
114 
115 static void write_yskip(AVCodecContext *avctx, uint8_t **data, int yskip)
116 {
117  if (yskip < 4)
118  return;
119  // we have yskip*2 nul bytess
120  *data -= 2*yskip;
121  // the end-of-line counts as one skip
122  yskip--;
123  while (yskip >= 255) {
124  bytestream_put_byte(data, 0);
125  bytestream_put_byte(data, 2);
126  bytestream_put_byte(data, 0);
127  bytestream_put_byte(data, 255);
128  yskip -= 255;
129  }
130  if (yskip > 0) {
131  bytestream_put_byte(data, 0);
132  bytestream_put_byte(data, 2);
133  bytestream_put_byte(data, 0);
134  bytestream_put_byte(data, yskip);
135  }
136  bytestream_put_be16(data, 0x0000);
137 }
138 
139 // used both to encode lines in keyframes and to encode lines between deltas
140 static void encode_line(AVCodecContext *avctx, uint8_t **data,
141  const uint8_t *line, int length)
142 {
143  int run = 0, last = -1, absstart = 0;
144  if (length == 0)
145  return;
146  for (int x = 0; x < length; x++) {
147  if (last == line[x]) {
148  run++;
149  if (run == 3)
150  write_absolute(avctx, data, &line[absstart], x - absstart - 2);
151  } else {
152  if (run >= 3) {
153  write_run(avctx, data, run, last);
154  absstart = x;
155  }
156  run = 1;
157  }
158  last = line[x];
159  }
160  if (run >= 3)
161  write_run(avctx, data, run, last);
162  else
163  write_absolute(avctx, data, &line[absstart], length - absstart);
164 }
165 
166 static int encode(AVCodecContext *avctx, AVPacket *pkt,
167  const AVFrame *pict, int keyframe, int *got_keyframe)
168 {
169  MSRLEContext *s = avctx->priv_data;
170  uint8_t *data = pkt->data;
171 
172  /* Compare the current frame to the last frame, or code the entire frame
173  if keyframe != 0. We're continually outputting pairs of bytes:
174 
175  00 00 end of line
176  00 01 end of bitmap
177  00 02 dx dy delta. move pointer to x+dx, y+dy
178  00 ll dd dd .. absolute (verbatim) mode. ll >= 3
179  rr dd run. rr >= 1
180 
181  For keyframes we only have absolute mode and runs at our disposal, and
182  we are not allowed to end a line early. If this happens when keyframe == 0
183  then *got_keyframe is set to 1 and s->curframe is reset.
184  */
185  *got_keyframe = 1; // set to zero whenever we use a feature that makes this a not-keyframe
186 
187  if (keyframe) {
188  for (int y = avctx->height-1; y >= 0; y--) {
189  uint8_t *line = &pict->data[0][y*pict->linesize[0]];
190  encode_line(avctx, &data, line, avctx->width);
191  bytestream_put_be16(&data, 0x0000); // end of line
192  }
193  } else {
194  // compare to previous frame
195  int yskip = 0; // we can encode large skips using deltas
196  for (int y = avctx->height-1; y >= 0; y--) {
197  const uint8_t *line = &pict->data[0][y*pict->linesize[0]];
198  const uint8_t *prev = &s->last_frame->data[0][y*s->last_frame->linesize[0]];
199  // we need at least 5 pixels in a row for a delta to be worthwhile
200  int delta = 0, linestart = 0, encoded = 0;
201  for (int x = 0; x < avctx->width; x++) {
202  if (line[x] == prev[x]) {
203  delta++;
204  if (delta == 5) {
205  int len = x - linestart - 4;
206  if (len > 0) {
207  write_yskip(avctx, &data, yskip);
208  yskip = 0;
209  encode_line(avctx, &data, &line[linestart], len);
210  encoded = 1;
211  }
212  linestart = -1;
213  }
214  } else {
215  if (delta >= 5) {
216  write_yskip(avctx, &data, yskip);
217  yskip = 0;
218  write_delta(avctx, &data, delta);
219  *got_keyframe = 0;
220  encoded = 1;
221  }
222  delta = 0;
223  if (linestart == -1)
224  linestart = x;
225  }
226  }
227  if (delta < 5) {
228  write_yskip(avctx, &data, yskip);
229  yskip = 0;
230  encode_line(avctx, &data, &line[linestart], avctx->width - linestart);
231  encoded = 1;
232  } else
233  *got_keyframe = 0;
234  bytestream_put_be16(&data, 0x0000); // end of line
235  if (!encoded)
236  yskip++;
237  else
238  yskip = 0;
239  }
240  write_yskip(avctx, &data, yskip);
241  }
242  bytestream_put_be16(&data, 0x0001); // end of bitmap
243  pkt->size = data - pkt->data;
244  return 0;
245 }
246 
248  const AVFrame *pict, int *got_packet)
249 {
250  MSRLEContext *s = avctx->priv_data;
251  int ret, got_keyframe;
252 
253  if ((ret = ff_alloc_packet(avctx, pkt, (
254  avctx->width*2 /* worst case = rle every pixel */ + 2 /*end of line */
255  ) * avctx->height + 2 /* end of bitmap */ + FF_INPUT_BUFFER_MIN_SIZE)))
256  return ret;
257 
258  if (pict->data[1]) {
260  if (!side_data)
261  return AVERROR(ENOMEM);
262  memcpy(side_data, pict->data[1], AVPALETTE_SIZE);
263  }
264 
265  if ((ret = encode(avctx, pkt, pict, s->curframe == 0, &got_keyframe)))
266  return ret;
267 
268  if (got_keyframe) {
270  s->curframe = 0;
271  }
272  if (++s->curframe >= avctx->gop_size)
273  s->curframe = 0;
274  *got_packet = 1;
275 
276  return av_frame_replace(s->last_frame, pict);
277 }
278 
280 {
281  MSRLEContext *s = avctx->priv_data;
282  av_frame_free(&s->last_frame);
283  return 0;
284 }
285 
287  .p.name = "msrle",
288  CODEC_LONG_NAME("Microsoft RLE"),
289  .p.type = AVMEDIA_TYPE_VIDEO,
290  .p.id = AV_CODEC_ID_MSRLE,
291  .p.capabilities = AV_CODEC_CAP_DR1,
292  .priv_data_size = sizeof(MSRLEContext),
295  .close = msrle_encode_close,
296  .p.pix_fmts = (const enum AVPixelFormat[]){
298  },
299  .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
300 };
msrle_encode_init
static av_cold int msrle_encode_init(AVCodecContext *avctx)
Definition: msrleenc.c:38
AVPixelFormat
AVPixelFormat
Pixel format.
Definition: pixfmt.h:71
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:33
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:130
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:344
AVPacket::data
uint8_t * data
Definition: packet.h:522
encode.h
data
const char data[16]
Definition: mxf.c:148
FFCodec
Definition: codec_internal.h:127
AV_PKT_FLAG_KEY
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
Definition: packet.h:577
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:34
AVFrame::data
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:365
write_yskip
static void write_yskip(AVCodecContext *avctx, uint8_t **data, int yskip)
Definition: msrleenc.c:115
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:296
av_frame_alloc
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition: frame.c:118
pkt
AVPacket * pkt
Definition: movenc.c:59
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:94
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:247
CODEC_LONG_NAME
#define CODEC_LONG_NAME(str)
Definition: codec_internal.h:272
run
uint8_t run
Definition: svq3.c:203
AVPALETTE_SIZE
#define AVPALETTE_SIZE
Definition: pixfmt.h:32
init
int(* init)(AVBSFContext *ctx)
Definition: dts2pts.c:365
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:523
AVCodecContext::gop_size
int gop_size
the number of pictures in a group of pictures, or 0 for intra_only
Definition: avcodec.h:1031
codec_internal.h
write_run
static void write_run(AVCodecContext *avctx, uint8_t **data, int len, int value)
Definition: msrleenc.c:50
AV_CODEC_ID_MSRLE
@ AV_CODEC_ID_MSRLE
Definition: codec_id.h:97
line
Definition: graph2dot.c:48
AVPacket::flags
int flags
A combination of AV_PKT_FLAG values.
Definition: packet.h:528
encode
static int encode(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *pict, int keyframe, int *got_keyframe)
Definition: msrleenc.c:166
AVCodecContext::bits_per_coded_sample
int bits_per_coded_sample
bits per sample/pixel from the demuxer (needed for huffyuv).
Definition: avcodec.h:1567
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:194
ff_msrle_encoder
const FFCodec ff_msrle_encoder
Definition: msrleenc.c:286
len
int len
Definition: vorbis_enc_data.h:426
AVCodecContext::height
int height
Definition: avcodec.h:618
AV_PIX_FMT_PAL8
@ AV_PIX_FMT_PAL8
8 bits with AV_PIX_FMT_RGB32 palette
Definition: pixfmt.h:84
msrle_encode_close
static int msrle_encode_close(AVCodecContext *avctx)
Definition: msrleenc.c:279
ret
ret
Definition: filter_design.txt:187
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:453
AVCodecContext
main external API structure.
Definition: avcodec.h:445
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: avpacket.c:231
AV_PIX_FMT_NONE
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:72
write_absolute
static void write_absolute(AVCodecContext *avctx, uint8_t **data, const uint8_t *line, int len)
Definition: msrleenc.c:67
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
MSRLEContext::last_frame
AVFrame * last_frame
Definition: msrleenc.c:35
AVPacket
This structure stores compressed data.
Definition: packet.h:499
AVCodecContext::priv_data
void * priv_data
Definition: avcodec.h:472
AVCodecContext::width
int width
picture width / height.
Definition: avcodec.h:618
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:389
encode_line
static void encode_line(AVCodecContext *avctx, uint8_t **data, const uint8_t *line, int length)
Definition: msrleenc.c:140
ff_alloc_packet
int ff_alloc_packet(AVCodecContext *avctx, AVPacket *avpkt, int64_t size)
Check AVPacket size and allocate data.
Definition: encode.c:61