diff mbox series

[FFmpeg-devel,RFC,2/2] libavformat/rtpenc_jpeg2000 JPEG2000 RTP Muxer

Message ID 20200722192553.9688-2-gautamramk@gmail.com
State New
Headers show
Series [FFmpeg-devel,RFC,1/2] libavformat/rtpdec_jpeg2000: RTP Demuxing for JPEG2000 | expand

Checks

Context Check Description
andriy/default pending
andriy/make success Make finished
andriy/make_fate success Make fate finished

Commit Message

Gautam Ramakrishnan July 22, 2020, 7:25 p.m. UTC
From: Gautam Ramakrishnan <gautamramk@gmail.com>

This patch adds support to mux JPEG2000 streams over
RTP.
---
 libavformat/Makefile          |   1 +
 libavformat/rtpenc.c          |   4 ++
 libavformat/rtpenc.h          |   1 +
 libavformat/rtpenc_jpeg2000.c | 121 ++++++++++++++++++++++++++++++++++
 libavformat/sdp.c             |  32 +++++++++
 5 files changed, 159 insertions(+)
 create mode 100644 libavformat/rtpenc_jpeg2000.c

Comments

Gautam Ramakrishnan July 22, 2020, 7:27 p.m. UTC | #1
On Thu, Jul 23, 2020 at 12:56 AM <gautamramk@gmail.com> wrote:
>
> From: Gautam Ramakrishnan <gautamramk@gmail.com>
>
> This patch adds support to mux JPEG2000 streams over
> RTP.
> ---
>  libavformat/Makefile          |   1 +
>  libavformat/rtpenc.c          |   4 ++
>  libavformat/rtpenc.h          |   1 +
>  libavformat/rtpenc_jpeg2000.c | 121 ++++++++++++++++++++++++++++++++++
>  libavformat/sdp.c             |  32 +++++++++
>  5 files changed, 159 insertions(+)
>  create mode 100644 libavformat/rtpenc_jpeg2000.c
>
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 4495047e3a..f2d260fada 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -459,6 +459,7 @@ OBJS-$(CONFIG_RTP_MUXER)                 += rtp.o         \
>                                              rtpenc_h263_rfc2190.o \
>                                              rtpenc_h264_hevc.o    \
>                                              rtpenc_jpeg.o \
> +                                            rtpenc_jpeg2000.o \
>                                              rtpenc_mpv.o     \
>                                              rtpenc.o      \
>                                              rtpenc_vc2hq.o              \
> diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c
> index 9ef7e9094d..1af9e3f455 100644
> --- a/libavformat/rtpenc.c
> +++ b/libavformat/rtpenc.c
> @@ -84,6 +84,7 @@ static int is_supported(enum AVCodecID id)
>      case AV_CODEC_ID_MJPEG:
>      case AV_CODEC_ID_SPEEX:
>      case AV_CODEC_ID_OPUS:
> +    case AV_CODEC_ID_JPEG2000:
>          return 1;
>      default:
>          return 0;
> @@ -619,6 +620,9 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
>      case AV_CODEC_ID_MJPEG:
>          ff_rtp_send_jpeg(s1, pkt->data, size);
>          break;
> +    case AV_CODEC_ID_JPEG2000:
> +        ff_rtp_send_jpeg2000(s1, pkt->data, size);
> +        break;
>      case AV_CODEC_ID_OPUS:
>          if (size > s->max_payload_size) {
>              av_log(s1, AV_LOG_ERROR,
> diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h
> index 62dc9ab10a..0db339f2ee 100644
> --- a/libavformat/rtpenc.h
> +++ b/libavformat/rtpenc.h
> @@ -95,6 +95,7 @@ void ff_rtp_send_vc2hq(AVFormatContext *s1, const uint8_t *buf, int size, int in
>  void ff_rtp_send_vp8(AVFormatContext *s1, const uint8_t *buff, int size);
>  void ff_rtp_send_vp9(AVFormatContext *s1, const uint8_t *buff, int size);
>  void ff_rtp_send_jpeg(AVFormatContext *s1, const uint8_t *buff, int size);
> +void ff_rtp_send_jpeg2000(AVFormatContext *s1, const uint8_t *buff, int size);
>
>  const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *av_restrict start,
>                                                    const uint8_t *av_restrict end);
> diff --git a/libavformat/rtpenc_jpeg2000.c b/libavformat/rtpenc_jpeg2000.c
> new file mode 100644
> index 0000000000..699bc2e1b9
> --- /dev/null
> +++ b/libavformat/rtpenc_jpeg2000.c
> @@ -0,0 +1,121 @@
> +/*
> + * RTP JPEG2000 video Packetizer, RFC 5371
> + * Copyright (c) 2020 Gautam Ramakrishnan
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "libavcodec/bytestream.h"
> +#include "libavutil/intreadwrite.h"
> +#include "rtpenc.h"
> +
> +#define PAYLOAD_HDR_SIZ 8
> +
> +static void send_packet(AVFormatContext *s1, const uint8_t *buf, int start, int end,
> +                   int header, int tileno, int cstream_start, int last)
> +{
> +    RTPMuxContext *s = s1->priv_data;
> +    int unit_len = end - start;
> +    int send_left = end - start;
> +    while (send_left > 0) {
> +        int len = FFMIN(send_left, s->max_payload_size - PAYLOAD_HDR_SIZ);
> +        uint8_t flags = 0;
> +        if (unit_len <= s->max_payload_size - PAYLOAD_HDR_SIZ)
> +            flags |= 3 << 3;
> +        else if (header && (send_left - len))
> +            flags |= 1 << 3;
> +        else if (header && !(send_left - len))
> +            flags |= 2 << 3;
> +        if (header)
> +            flags |= 1;
> +        bytestream_put_byte(&s->buf_ptr, flags);
> +        bytestream_put_byte(&s->buf_ptr, 255);
> +        bytestream_put_be16(&s->buf_ptr, tileno);
> +        bytestream_put_byte(&s->buf_ptr, 0);
> +        bytestream_put_be24(&s->buf_ptr, start - cstream_start);
> +        memcpy(s->buf_ptr, buf + start, len);
> +        ff_rtp_send_data(s1, s->buf, len + PAYLOAD_HDR_SIZ, last && send_left <= len);
> +        send_left -= len;
> +        s->buf_ptr = s->buf;
> +        start += len;
> +    }
> +}
> +
> +void ff_rtp_send_jpeg2000(AVFormatContext *s1, const uint8_t *buf, int size)
> +{
> +    RTPMuxContext *s = s1->priv_data;
> +    int i = 0;
> +    int packet_start = -1;
> +    int packet_end = -1;
> +    int cstream_start = -1;
> +    int tileno = 0;
> +    int sod_found = 0;
> +    int end_found = 0;
> +
> +    s->buf_ptr   = s->buf;
> +    s->timestamp = s->cur_timestamp;
> +
> +    while (AV_RB16(&buf[i]) != 0xFF4F && i < size)
> +        i++;
> +    if (i == size) {
> +        av_log(s1, AV_LOG_ERROR, "Codestream Not found.\n");
> +        return;
> +    }
> +    packet_start = i;
> +    cstream_start = i;
> +    while (AV_RB16(&buf[i]) != 0xFF90 && i < size)
> +        i++;
> +    if (i == size) {
> +        av_log(s1, AV_LOG_ERROR, "Cannot find end of main header.\n");
> +        return;
> +    }
> +    packet_end = i;
> +    send_packet(s1, buf, packet_start, packet_end, 1, 0, cstream_start, 0);
> +    while (i < size) {
> +        if (AV_RB16(&buf[i]) == 0xFF90) {
> +            packet_start = i;
> +            i += 4;
> +            tileno = AV_RB16(&buf[i]);
> +            i += 6;
> +            while (AV_RB16(&buf[i]) != 0xFF93 && i < size)
> +                i++;
> +            if (AV_RB16(&buf[i]) == 0xFF93)
> +                i += 2;
> +            else {
> +                av_log(s1, AV_LOG_ERROR, "Cannot find end of TP header.\n");
> +                return;
> +            }
> +            packet_end = i;
> +            sod_found = 1;
> +            send_packet(s1, buf, packet_start, packet_end, 0, tileno, cstream_start, 0);
> +        } else if (sod_found) {
> +            packet_start = i;
> +            sod_found = 0;
> +            while (AV_RB16(&buf[i]) != 0xFF90 && AV_RB16(&buf[i]) != 0xFFD9 && i < size) {
> +                i++;
> +            }
> +            if (AV_RB16(&buf[i]) == 0xFFD9) {
> +                packet_end = i+2;
> +                end_found = 1;
> +            } else
> +                packet_end = i;
> +            send_packet(s1, buf, packet_start, packet_end, 0, tileno, cstream_start, end_found);
> +        }
> +        if (AV_RB16(&buf[i]) == 0xFFD9)
> +            break;
> +    }
> +}
> diff --git a/libavformat/sdp.c b/libavformat/sdp.c
> index 2ce1a62262..c8ca119d2d 100644
> --- a/libavformat/sdp.c
> +++ b/libavformat/sdp.c
> @@ -673,6 +673,38 @@ static char *sdp_write_media_attributes(char *buff, int size, AVStream *st, int
>                  av_strlcatf(buff, size, "a=rtpmap:%d JPEG/90000\r\n",
>                                           payload_type);
>              break;
> +        case AV_CODEC_ID_JPEG2000: {
> +            const char *pix_fmt;
> +            switch (p->format) {
> +            case AV_PIX_FMT_YUV420P:
> +                pix_fmt = "YCbCr-4:2:0";
> +                break;
> +            case AV_PIX_FMT_YUV422P:
> +                pix_fmt = "YCbCr-4:2:2";
> +                break;
> +            case AV_PIX_FMT_YUV444P:
> +                pix_fmt = "YCbCr-4:4:4";
> +                break;
> +            case AV_PIX_FMT_RGB24:
> +                pix_fmt = "RGB";
> +                break;
> +            case AV_PIX_FMT_RGB32:
> +                pix_fmt = "RGBA";
> +                break;
> +            case AV_PIX_FMT_BGR24:
> +                pix_fmt = "BGR";
> +                break;
> +            case AV_PIX_FMT_BGR32:
> +                pix_fmt = "BGRA";
> +            case AV_PIX_FMT_GRAY8:
> +                pix_fmt = "GRAYSCALE";
> +            }
> +            if (payload_type >= RTP_PT_PRIVATE)
> +                av_strlcatf(buff, size, "a=rtpmap:%d JPEG2000/90000\r\n"
> +                                        "a=fmtp:%d sampling=%s\r\n",
> +                                         payload_type, payload_type, pix_fmt);
> +            break;
> +        }
>          case AV_CODEC_ID_ADPCM_G722:
>              if (payload_type >= RTP_PT_PRIVATE)
>                  av_strlcatf(buff, size, "a=rtpmap:%d G722/%d/%d\r\n",
> --
> 2.17.1
>

The demuxer in the previous patch was tested against a gstreamer server.
The muxer was tested by streaming to ffplay which acted as a RECORD
server for jpeg2000.
diff mbox series

Patch

diff --git a/libavformat/Makefile b/libavformat/Makefile
index 4495047e3a..f2d260fada 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -459,6 +459,7 @@  OBJS-$(CONFIG_RTP_MUXER)                 += rtp.o         \
                                             rtpenc_h263_rfc2190.o \
                                             rtpenc_h264_hevc.o    \
                                             rtpenc_jpeg.o \
+                                            rtpenc_jpeg2000.o \
                                             rtpenc_mpv.o     \
                                             rtpenc.o      \
                                             rtpenc_vc2hq.o              \
diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c
index 9ef7e9094d..1af9e3f455 100644
--- a/libavformat/rtpenc.c
+++ b/libavformat/rtpenc.c
@@ -84,6 +84,7 @@  static int is_supported(enum AVCodecID id)
     case AV_CODEC_ID_MJPEG:
     case AV_CODEC_ID_SPEEX:
     case AV_CODEC_ID_OPUS:
+    case AV_CODEC_ID_JPEG2000:
         return 1;
     default:
         return 0;
@@ -619,6 +620,9 @@  static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
     case AV_CODEC_ID_MJPEG:
         ff_rtp_send_jpeg(s1, pkt->data, size);
         break;
+    case AV_CODEC_ID_JPEG2000:
+        ff_rtp_send_jpeg2000(s1, pkt->data, size);
+        break;
     case AV_CODEC_ID_OPUS:
         if (size > s->max_payload_size) {
             av_log(s1, AV_LOG_ERROR,
diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h
index 62dc9ab10a..0db339f2ee 100644
--- a/libavformat/rtpenc.h
+++ b/libavformat/rtpenc.h
@@ -95,6 +95,7 @@  void ff_rtp_send_vc2hq(AVFormatContext *s1, const uint8_t *buf, int size, int in
 void ff_rtp_send_vp8(AVFormatContext *s1, const uint8_t *buff, int size);
 void ff_rtp_send_vp9(AVFormatContext *s1, const uint8_t *buff, int size);
 void ff_rtp_send_jpeg(AVFormatContext *s1, const uint8_t *buff, int size);
+void ff_rtp_send_jpeg2000(AVFormatContext *s1, const uint8_t *buff, int size);
 
 const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *av_restrict start,
                                                   const uint8_t *av_restrict end);
diff --git a/libavformat/rtpenc_jpeg2000.c b/libavformat/rtpenc_jpeg2000.c
new file mode 100644
index 0000000000..699bc2e1b9
--- /dev/null
+++ b/libavformat/rtpenc_jpeg2000.c
@@ -0,0 +1,121 @@ 
+/*
+ * RTP JPEG2000 video Packetizer, RFC 5371
+ * Copyright (c) 2020 Gautam Ramakrishnan
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavcodec/bytestream.h"
+#include "libavutil/intreadwrite.h"
+#include "rtpenc.h"
+
+#define PAYLOAD_HDR_SIZ 8
+
+static void send_packet(AVFormatContext *s1, const uint8_t *buf, int start, int end,
+                   int header, int tileno, int cstream_start, int last)
+{
+    RTPMuxContext *s = s1->priv_data;
+    int unit_len = end - start;
+    int send_left = end - start;
+    while (send_left > 0) {
+        int len = FFMIN(send_left, s->max_payload_size - PAYLOAD_HDR_SIZ);
+        uint8_t flags = 0;
+        if (unit_len <= s->max_payload_size - PAYLOAD_HDR_SIZ)
+            flags |= 3 << 3;
+        else if (header && (send_left - len))
+            flags |= 1 << 3;
+        else if (header && !(send_left - len))
+            flags |= 2 << 3;
+        if (header)
+            flags |= 1;
+        bytestream_put_byte(&s->buf_ptr, flags);
+        bytestream_put_byte(&s->buf_ptr, 255);
+        bytestream_put_be16(&s->buf_ptr, tileno);
+        bytestream_put_byte(&s->buf_ptr, 0);
+        bytestream_put_be24(&s->buf_ptr, start - cstream_start);
+        memcpy(s->buf_ptr, buf + start, len);
+        ff_rtp_send_data(s1, s->buf, len + PAYLOAD_HDR_SIZ, last && send_left <= len);
+        send_left -= len;
+        s->buf_ptr = s->buf;
+        start += len;
+    }
+}
+
+void ff_rtp_send_jpeg2000(AVFormatContext *s1, const uint8_t *buf, int size)
+{
+    RTPMuxContext *s = s1->priv_data;
+    int i = 0;
+    int packet_start = -1;
+    int packet_end = -1;
+    int cstream_start = -1;
+    int tileno = 0;
+    int sod_found = 0;
+    int end_found = 0;
+
+    s->buf_ptr   = s->buf;
+    s->timestamp = s->cur_timestamp;
+
+    while (AV_RB16(&buf[i]) != 0xFF4F && i < size)
+        i++;
+    if (i == size) {
+        av_log(s1, AV_LOG_ERROR, "Codestream Not found.\n");
+        return;
+    }
+    packet_start = i;
+    cstream_start = i;
+    while (AV_RB16(&buf[i]) != 0xFF90 && i < size)
+        i++;
+    if (i == size) {
+        av_log(s1, AV_LOG_ERROR, "Cannot find end of main header.\n");
+        return;
+    }
+    packet_end = i;
+    send_packet(s1, buf, packet_start, packet_end, 1, 0, cstream_start, 0);
+    while (i < size) {
+        if (AV_RB16(&buf[i]) == 0xFF90) {
+            packet_start = i;
+            i += 4;
+            tileno = AV_RB16(&buf[i]);
+            i += 6;
+            while (AV_RB16(&buf[i]) != 0xFF93 && i < size)
+                i++;
+            if (AV_RB16(&buf[i]) == 0xFF93)
+                i += 2;
+            else {
+                av_log(s1, AV_LOG_ERROR, "Cannot find end of TP header.\n");
+                return;
+            }
+            packet_end = i;
+            sod_found = 1;
+            send_packet(s1, buf, packet_start, packet_end, 0, tileno, cstream_start, 0);
+        } else if (sod_found) {
+            packet_start = i;
+            sod_found = 0;
+            while (AV_RB16(&buf[i]) != 0xFF90 && AV_RB16(&buf[i]) != 0xFFD9 && i < size) {
+                i++;
+            }
+            if (AV_RB16(&buf[i]) == 0xFFD9) {
+                packet_end = i+2;
+                end_found = 1;
+            } else
+                packet_end = i;
+            send_packet(s1, buf, packet_start, packet_end, 0, tileno, cstream_start, end_found);
+        }
+        if (AV_RB16(&buf[i]) == 0xFFD9)
+            break;
+    }  
+}
diff --git a/libavformat/sdp.c b/libavformat/sdp.c
index 2ce1a62262..c8ca119d2d 100644
--- a/libavformat/sdp.c
+++ b/libavformat/sdp.c
@@ -673,6 +673,38 @@  static char *sdp_write_media_attributes(char *buff, int size, AVStream *st, int
                 av_strlcatf(buff, size, "a=rtpmap:%d JPEG/90000\r\n",
                                          payload_type);
             break;
+        case AV_CODEC_ID_JPEG2000: {
+            const char *pix_fmt;
+            switch (p->format) {
+            case AV_PIX_FMT_YUV420P:
+                pix_fmt = "YCbCr-4:2:0";
+                break;
+            case AV_PIX_FMT_YUV422P:
+                pix_fmt = "YCbCr-4:2:2";
+                break;
+            case AV_PIX_FMT_YUV444P:
+                pix_fmt = "YCbCr-4:4:4";
+                break;
+            case AV_PIX_FMT_RGB24:
+                pix_fmt = "RGB";
+                break;
+            case AV_PIX_FMT_RGB32:
+                pix_fmt = "RGBA";
+                break;
+            case AV_PIX_FMT_BGR24:
+                pix_fmt = "BGR";
+                break;
+            case AV_PIX_FMT_BGR32:
+                pix_fmt = "BGRA";
+            case AV_PIX_FMT_GRAY8:
+                pix_fmt = "GRAYSCALE";
+            }
+            if (payload_type >= RTP_PT_PRIVATE)
+                av_strlcatf(buff, size, "a=rtpmap:%d JPEG2000/90000\r\n"
+                                        "a=fmtp:%d sampling=%s\r\n",
+                                         payload_type, payload_type, pix_fmt);
+            break;
+        }
         case AV_CODEC_ID_ADPCM_G722:
             if (payload_type >= RTP_PT_PRIVATE)
                 av_strlcatf(buff, size, "a=rtpmap:%d G722/%d/%d\r\n",