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 <hmy...@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
_______________________________________________ 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".