Tomas Härdin: > +typedef struct MSRLEContext { > + const AVClass *class; > + int curframe; > + AVFrame *last_frame; > +} MSRLEContext; > + > +static av_cold int msrle_encode_init(AVCodecContext *avctx) > +{ > + avctx->bits_per_coded_sample = 8; > + return 0; > +} > + > +static void write_run(AVCodecContext *avctx, uint8_t **data, int len, int > value) > +{ > + // we're allowed to write odd runs > + while (len >= 255) { > + bytestream_put_byte(data, 255); > + bytestream_put_byte(data, value); > + len -= 255; > + } > + if (len >= 1) { > + // this is wasteful when len == 1 and sometimes when len == 2 > + // but sometimes we have no choice. also write_absolute() > + // relies on this > + bytestream_put_byte(data, len); > + bytestream_put_byte(data, value); > + } > +} > + > +static void write_absolute(AVCodecContext *avctx, uint8_t **data, uint8_t > *line, int len) > +{ > + // writing 255 would be wasteful here due to the padding requirement > + while (len >= 254) { > + bytestream_put_byte(data, 0); > + bytestream_put_byte(data, 254); > + bytestream_put_buffer(data, line, 254); > + line += 254; > + len -= 254; > + } > + if (len == 1) { > + // it's less wasteful to write single pixels as runs > + // not to mention that absolute mode requires >= 3 pixels > + write_run(avctx, data, 1, line[0]); > + } else if (len == 2) { > + write_run(avctx, data, 1, line[0]); > + write_run(avctx, data, 1, line[1]); > + } else if (len > 0) { > + bytestream_put_byte(data, 0); > + bytestream_put_byte(data, len); > + bytestream_put_buffer(data, line, len); > + if (len & 1) > + bytestream_put_byte(data, 0); > + } > +} > + > +static void write_delta(AVCodecContext *avctx, uint8_t **data, int delta) > +{ > + // we let the yskip logic handle the case where we want to delta > + // to following lines. it's not perfect but it's easier than finding > + // the optimal combination of end-of-lines and deltas to reach any > + // following position including places where dx < 0 > + while (delta >= 255) { > + bytestream_put_byte(data, 0); > + bytestream_put_byte(data, 2); > + bytestream_put_byte(data, 255); > + bytestream_put_byte(data, 0); > + delta -= 255; > + } > + if (delta > 0) { > + bytestream_put_byte(data, 0); > + bytestream_put_byte(data, 2); > + bytestream_put_byte(data, delta); > + bytestream_put_byte(data, 0); > + } > +} > + > +static void write_yskip(AVCodecContext *avctx, uint8_t **data, int yskip) > +{ > + if (yskip < 4) > + return; > + // we have yskip*2 nul bytess > + *data -= 2*yskip; > + // the end-of-line counts as one skip > + yskip--; > + while (yskip >= 255) { > + bytestream_put_byte(data, 0); > + bytestream_put_byte(data, 2); > + bytestream_put_byte(data, 0); > + bytestream_put_byte(data, 255); > + yskip -= 255; > + } > + if (yskip > 0) { > + bytestream_put_byte(data, 0); > + bytestream_put_byte(data, 2); > + bytestream_put_byte(data, 0); > + bytestream_put_byte(data, yskip); > + } > + bytestream_put_be16(data, 0x0000); > +} > + > +// used both to encode lines in keyframes and to encode lines between deltas > +static void encode_line(AVCodecContext *avctx, uint8_t **data, uint8_t > *line, int length) > +{ > + int run = 0, last = -1, absstart = 0; > + if (length == 0) > + return; > + for (int x = 0; x < length; x++) { > + if (last == line[x]) { > + run++; > + if (run == 3) > + write_absolute(avctx, data, &line[absstart], x - absstart - > 2); > + } else { > + if (run >= 3) { > + write_run(avctx, data, run, last); > + absstart = x; > + } > + run = 1; > + } > + last = line[x]; > + } > + if (run >= 3) > + write_run(avctx, data, run, last); > + else > + write_absolute(avctx, data, &line[absstart], length - absstart); > +} > + > +static int encode(AVCodecContext *avctx, AVPacket *pkt, > + const AVFrame *pict, int keyframe, int *got_keyframe) > +{ > + MSRLEContext *s = avctx->priv_data; > + uint8_t *data = pkt->data; > + > + /* Compare the current frame to the last frame, or code the entire frame > + if keyframe != 0. We're continually outputting pairs of bytes: > + > + 00 00 end of line > + 00 01 end of bitmap > + 00 02 dx dy delta. move pointer to x+dx, y+dy > + 00 ll dd dd .. absolute (verbatim) mode. ll >= 3 > + rr dd run. rr >= 1 > + > + For keyframes we only have absolute mode and runs at our disposal, > and > + we are not allowed to end a line early. If this happens when > keyframe == 0 > + then *got_keyframe is set to 1 and s->curframe is reset. > + */ > + *got_keyframe = 1; // set to zero whenever we use a feature that makes > this a not-keyframe > + > + if (keyframe) { > + for (int y = avctx->height-1; y >= 0; y--) { > + uint8_t *line = &pict->data[0][y*pict->linesize[0]]; > + encode_line(avctx, &data, line, avctx->width); > + bytestream_put_be16(&data, 0x0000); // end of line > + } > + } else { > + // compare to previous frame > + int yskip = 0; // we can encode large skips using deltas > + for (int y = avctx->height-1; y >= 0; y--) { > + uint8_t *line = &pict->data[0][y*pict->linesize[0]]; > + uint8_t *prev = > &s->last_frame->data[0][y*s->last_frame->linesize[0]];
Should be const. > + // we need at least 5 pixels in a row for a delta to be > worthwhile > + int delta = 0, linestart = 0, encoded = 0; > + for (int x = 0; x < avctx->width; x++) { > + if (line[x] == prev[x]) { > + delta++; > + if (delta == 5) { > + int len = x - linestart - 4; > + if (len > 0) { > + write_yskip(avctx, &data, yskip); > + yskip = 0; > + encode_line(avctx, &data, &line[linestart], len); > + encoded = 1; > + } > + linestart = -1; > + } > + } else { > + if (delta >= 5) { > + write_yskip(avctx, &data, yskip); > + yskip = 0; > + write_delta(avctx, &data, delta); > + *got_keyframe = 0; > + encoded = 1; > + } > + delta = 0; > + if (linestart == -1) > + linestart = x; > + } > + } > + if (delta < 5) { > + write_yskip(avctx, &data, yskip); > + yskip = 0; > + encode_line(avctx, &data, &line[linestart], avctx->width - > linestart); > + encoded = 1; > + } else > + *got_keyframe = 0; > + bytestream_put_be16(&data, 0x0000); // end of line > + if (!encoded) > + yskip++; > + else > + yskip = 0; > + } > + write_yskip(avctx, &data, yskip); > + } > + bytestream_put_be16(&data, 0x0001); // end of bitmap > + pkt->size = data - pkt->data; > + return 0; > } > + > +static int msrle_encode_frame(AVCodecContext *avctx, AVPacket *pkt, > + const AVFrame *pict, int *got_packet) > +{ > + MSRLEContext *s = avctx->priv_data; > + int ret, got_keyframe; > + > + if ((ret = ff_alloc_packet(avctx, pkt, ( > + avctx->width*2 /* worst case = rle every pixel */ + 2 /*end > of line */ > + ) * avctx->height + 2 /* end of bitmap */ + > AV_INPUT_BUFFER_MIN_SIZE))) > + return ret; > + > + if (pict->data[1]) { > + uint8_t *side_data = av_packet_new_side_data(pkt, > AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); > + memcpy(side_data, pict->data[1], AVPALETTE_SIZE); > + } > + > + if ((ret = encode(avctx, pkt, pict, s->curframe == 0, &got_keyframe))) > + return ret; > + > + if (got_keyframe) { > + pkt->flags |= AV_PKT_FLAG_KEY; > + s->curframe = 0; > + } > + if (++s->curframe >= avctx->gop_size) > + s->curframe = 0; > + *got_packet = 1; > + > + if (!s->last_frame) > + s->last_frame = av_frame_alloc(); > + else > + av_frame_unref(s->last_frame); > + > + av_frame_ref(s->last_frame, pict); Wouldn't it be simpler to allocate this frame during init and then use av_frame_replace() here? Apart from that: You need to check the av_frame_ref(). > + return 0; > +} > + > +static int msrle_encode_close(AVCodecContext *avctx) > +{ > + MSRLEContext *s = avctx->priv_data; > + av_frame_free(&s->last_frame); > + return 0; > +} > + > +static const AVClass msrle_class = { > + .class_name = "Microsoft RLE encoder", > + .item_name = av_default_item_name, > + .version = LIBAVUTIL_VERSION_INT, > +}; An AVClass is pointless without options, so you should just remove it (and the AVClass* from the context). > + > +const FFCodec ff_msrle_encoder = { > + .p.name = "msrle", > + CODEC_LONG_NAME("Microsoft RLE"), > + .p.type = AVMEDIA_TYPE_VIDEO, > + .p.id = AV_CODEC_ID_MSRLE, > + .p.capabilities = AV_CODEC_CAP_DR1, > + .priv_data_size = sizeof(MSRLEContext), > + .init = msrle_encode_init, > + FF_CODEC_ENCODE_CB(msrle_encode_frame), > + .close = msrle_encode_close, > + .p.pix_fmts = (const enum AVPixelFormat[]){ > + AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE > + }, > + .p.priv_class = &msrle_class, > + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, > +}; _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".