[FFmpeg-devel] [PATCH 1/3] avformat/mlvdec: demux LJ92 huffman comressed frames

Peter Ross pross at xvid.org
Fri Dec 13 07:59:12 EET 2024


A minimal DNG header is added to each LJ92 compressed frame, allowing
thme to be decoded by the TIFF decoder. The TIFF decoder is responsible
for setting up the MJPEG decoder, signalling the correct s->bayer flag,
and setting pix_fmt.

The LJ92 compressed frames can be muxed out to DNG files, and manipulated
in DNG software. Tested with darktable.

Contributor: South East <8billion.people at gmail.com>
---
 libavformat/mlvdec.c | 86 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 84 insertions(+), 2 deletions(-)

diff --git a/libavformat/mlvdec.c b/libavformat/mlvdec.c
index 1a6d38f37c..269f211e9f 100644
--- a/libavformat/mlvdec.c
+++ b/libavformat/mlvdec.c
@@ -26,6 +26,9 @@
 
 #include <time.h>
 
+#include "libavcodec/bytestream.h"
+#include "libavcodec/tiff.h"
+#include "libavcodec/tiff_common.h"
 #include "libavutil/imgutils.h"
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mem.h"
@@ -44,6 +47,7 @@
 
 #define MLV_AUDIO_CLASS_WAV  1
 
+#define MLV_CLASS_FLAG_LJ92  0x20
 #define MLV_CLASS_FLAG_DELTA 0x40
 #define MLV_CLASS_FLAG_LZMA  0x80
 
@@ -307,6 +311,9 @@ static int read_header(AVFormatContext *avctx)
             vst->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
             vst->codecpar->codec_tag = 0;
             break;
+        case MLV_CLASS_FLAG_LJ92|MLV_VIDEO_CLASS_RAW:
+            vst->codecpar->codec_id = AV_CODEC_ID_TIFF;
+            break;
         case MLV_VIDEO_CLASS_JPEG:
             vst->codecpar->codec_id = AV_CODEC_ID_MJPEG;
             vst->codecpar->codec_tag = 0;
@@ -400,6 +407,74 @@ static int read_header(AVFormatContext *avctx)
     return 0;
 }
 
+static void write_tiff_short(PutByteContext *pb, int tag, int value)
+{
+    bytestream2_put_le16(pb, tag);
+    bytestream2_put_le16(pb, TIFF_SHORT);
+    bytestream2_put_le32(pb, 1);
+    bytestream2_put_le16(pb, value);
+    bytestream2_put_le16(pb, 0);
+}
+
+static void write_tiff_long(PutByteContext *pb, int tag, int value)
+{
+    bytestream2_put_le16(pb, tag);
+    bytestream2_put_le16(pb, TIFF_LONG);
+    bytestream2_put_le32(pb, 1);
+    bytestream2_put_le32(pb, value);
+}
+
+static void write_tiff_byte4(PutByteContext *pb, int tag, int v1, int v2, int v3, int v4)
+{
+    bytestream2_put_le16(pb, tag);
+    bytestream2_put_le16(pb, TIFF_BYTE);
+    bytestream2_put_le32(pb, 4);
+    bytestream2_put_byte(pb, v1);
+    bytestream2_put_byte(pb, v2);
+    bytestream2_put_byte(pb, v3);
+    bytestream2_put_byte(pb, v4);
+}
+
+static int get_packet_lj92(AVStream *st, AVIOContext *pbio, AVPacket *pkt, int64_t size)
+{
+    PutByteContext pbctx, *pb = &pbctx;
+    int ret, header_size;
+    uint8_t *stripofs;
+
+#define MAX_HEADER_SIZE 2048
+    if ((ret = av_new_packet(pkt, size + MAX_HEADER_SIZE)) < 0)
+        return ret;
+
+    bytestream2_init_writer(pb, pkt->data, MAX_HEADER_SIZE);
+
+    bytestream2_put_le16(pb, 0x4949);
+    bytestream2_put_le16(pb, 42);
+    bytestream2_put_le32(pb, 8);
+
+    bytestream2_put_le16(pb, 11); /* nb_entries */
+
+    write_tiff_long(pb, TIFF_WIDTH, st->codecpar->width);
+    write_tiff_long(pb, TIFF_HEIGHT, st->codecpar->height);
+    write_tiff_long(pb, TIFF_BPP, 16);
+    write_tiff_short(pb, TIFF_COMPR, TIFF_NEWJPEG);
+    write_tiff_short(pb, TIFF_PHOTOMETRIC, TIFF_PHOTOMETRIC_LINEAR_RAW);
+    write_tiff_long(pb, TIFF_STRIP_OFFS, 0);
+    stripofs = pb->buffer - 4;
+    write_tiff_long(pb, TIFF_SAMPLES_PER_PIXEL, 1);
+    write_tiff_long(pb, TIFF_STRIP_SIZE, size);
+    write_tiff_byte4(pb, TIFF_CFA_PATTERN, 0, 1, 1, 2);
+    write_tiff_byte4(pb, DNG_VERSION, 1, 4, 0, 0);
+    write_tiff_long(pb, DNG_WHITE_LEVEL, 16000);
+
+    header_size = bytestream2_size_p(pb);
+    AV_WL32(stripofs, header_size);
+    ret = avio_read(pbio, pkt->data + header_size, size);
+    if (ret < 0)
+        return ret;
+
+    return 0;
+}
+
 static int read_packet(AVFormatContext *avctx, AVPacket *pkt)
 {
     MlvContext *mlv = avctx->priv_data;
@@ -435,15 +510,22 @@ static int read_packet(AVFormatContext *avctx, AVPacket *pkt)
     if (size < 16)
         return AVERROR_INVALIDDATA;
     avio_skip(pb, 12); //timestamp, frameNumber
-    if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+    size -= 12;
+    if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
         avio_skip(pb, 8); // cropPosX, cropPosY, panPosX, panPosY
+        size -= 8;
+    }
     space = avio_rl32(pb);
     avio_skip(pb, space);
+    size -= space;
 
     if ((mlv->class[st->id] & (MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA))) {
         ret = AVERROR_PATCHWELCOME;
     } else if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
-        ret = av_get_packet(pb, pkt, (st->codecpar->width * st->codecpar->height * st->codecpar->bits_per_coded_sample + 7) >> 3);
+        if (st->codecpar->codec_id == AV_CODEC_ID_TIFF)
+            ret = get_packet_lj92(st, pb, pkt, size);
+        else
+            ret = av_get_packet(pb, pkt, (st->codecpar->width * st->codecpar->height * st->codecpar->bits_per_coded_sample + 7) >> 3);
     } else { // AVMEDIA_TYPE_AUDIO
         if (space > UINT_MAX - 24 || size < (24 + space))
             return AVERROR_INVALIDDATA;
-- 
2.45.2

-- Peter
(A907 E02F A6E5 0CD2 34CD 20D2 6760 79C5 AC40 DD6B)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 195 bytes
Desc: not available
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20241213/0f8f17ad/attachment.sig>


More information about the ffmpeg-devel mailing list