[FFmpeg-devel] [PATCH] libavformat/rtpdec_jpeg.c: quantization table headers not sent in every frame packet

Hayden Myers HMyers at skylinenet.net
Tue Aug 24 02:23:12 EEST 2021


MJPEG streams coming from Genetec VMS provide a quantization table
for each frame, but only in the first packet. Before the packet data is
copied to the frame buffer, a check is done to compare the fragment
offset against the frame - header.  The header is computed at
the beginning of each frame. The offset ends up with a value of -132
because the header size includes the quantization table data, but the
packet buffer doesn't.

Created a function to detect if a quantization header isn't present when
it should be, and use this to offset the extra bytes reported in the
jpeg header.

Signed-off-by: Hayden Myers <hmyers at skylinenet.net>

cleanup
---
 libavformat/rtpdec_jpeg.c | 54 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 50 insertions(+), 4 deletions(-)

diff --git a/libavformat/rtpdec_jpeg.c b/libavformat/rtpdec_jpeg.c
index b32d074136..6e1c6d6b44 100644
--- a/libavformat/rtpdec_jpeg.c
+++ b/libavformat/rtpdec_jpeg.c
@@ -211,6 +211,52 @@ static void create_default_qtables(uint8_t *qtables, uint8_t q)
     }
 }

+/*
+ * If the q header isn't present in th packet, subtract from the
+ * jpeg header. The first packet in the frame could contain the q header.
+ *
+ * Some implementation do not include the quanization header with each packet.
+ * I'm specifically calling out Genetec VMS, but it could be rooted in specific
+ * cameras.
+ *
+ * @param ctx - The context, just used for logging.
+ * @param buf - current packet buffer
+ * @param q - quantizer value
+ *
+ * @return the number of bytes to remove if q header isn't present in the packet
+ * ,or 0 if the header is detected, or q <=127.
+ *
+ */
+static int get_q_hdr_bytes_to_remove(AVFormatContext *ctx, const uint8_t *buf,
+                                     uint8_t q)
+{
+    int ret=0, mbz=0, precision=0;
+
+    /* Use the first byte to detect if the quantization table header is
+     * present.  If mbz isn't zero, and the  precision byte isn't <= 1 quant
+     * table isn't present, and we're into jpeg image payload data already.
+     */
+    mbz = AV_RB8(buf); /* reserved byte should always be 0 */
+    precision  = AV_RB8(buf + 1);    /* size of coefficients */
+    /* best attempt to determine if the q header is present. Dont remove
+     * anything if a qtable is detected or q < 128.
+     */
+    if ((mbz == 0 && precision <= 1) || q < 128) {
+        ret = 0;
+    // no header detected in pkt, must remove q hdr bytes
+    } else {
+        //1 byte - reserved MBZ
+        //1 byte - precision
+        //2 bytes - length
+        ret += 4;
+        //128 bytes - 2 * 64 byte tables (8bit precision)
+        ret += 128;
+        av_log(ctx,AV_LOG_WARNING,
+             "Q header missing, reducing jpeg->hdr_size\n");
+    }
+    return ret;
+}
+
 static int jpeg_parse_packet(AVFormatContext *ctx, PayloadContext *jpeg,
                              AVStream *st, AVPacket *pkt, uint32_t *timestamp,
                              const uint8_t *buf, int len, uint16_t seq,
@@ -220,7 +266,7 @@ static int jpeg_parse_packet(AVFormatContext *ctx, PayloadContext *jpeg,
     const uint8_t *qtables = NULL;
     uint16_t qtable_len;
     uint32_t off;
-    int ret, dri = 0;
+    int ret, dri = 0, q_hdr_bytes_to_remove = 0;

     if (len < 8) {
         av_log(ctx, AV_LOG_ERROR, "Too short RTP/JPEG packet.\n");
@@ -235,6 +281,7 @@ static int jpeg_parse_packet(AVFormatContext *ctx, PayloadContext *jpeg,
     height = AV_RB8(buf + 7);   /* frame height in 8 pixel blocks */
     buf += 8;
     len -= 8;
+    q_hdr_bytes_to_remove = get_q_hdr_bytes_to_remove(ctx, buf, q);

     if (type & 0x40) {
         if (len < 4) {
@@ -350,9 +397,8 @@ static int jpeg_parse_packet(AVFormatContext *ctx, PayloadContext *jpeg,
         return AVERROR_INVALIDDATA;
     }

-    if (off != avio_tell(jpeg->frame) - jpeg->hdr_size) {
-        av_log(ctx, AV_LOG_ERROR,
-               "Missing packets; dropping frame.\n");
+    if (off != (avio_tell(jpeg->frame) - (jpeg->hdr_size - q_hdr_bytes_to_remove))) {
+        av_log(ctx, AV_LOG_ERROR, "Missing packets; dropping frame.\n");
         return AVERROR(EAGAIN);
     }

--
2.20.1
Hayden Myers
Principal Software Engineer
t: (410) 590-2027
-------------- next part --------------
A non-text attachment was scrubbed...
Name: image219806.png
Type: image/png
Size: 36556 bytes
Desc: image219806.png
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20210823/cfaf0f9b/attachment.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: image702984.png
Type: image/png
Size: 7149 bytes
Desc: image702984.png
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20210823/cfaf0f9b/attachment-0001.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: image797673.png
Type: image/png
Size: 842 bytes
Desc: image797673.png
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20210823/cfaf0f9b/attachment-0002.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: image774070.png
Type: image/png
Size: 817 bytes
Desc: image774070.png
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20210823/cfaf0f9b/attachment-0003.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: image207987.png
Type: image/png
Size: 919 bytes
Desc: image207987.png
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20210823/cfaf0f9b/attachment-0004.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: image579044.png
Type: image/png
Size: 653 bytes
Desc: image579044.png
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20210823/cfaf0f9b/attachment-0005.png>


More information about the ffmpeg-devel mailing list