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 |
Context | Check | Description |
---|---|---|
andriy/default | pending | |
andriy/make | success | Make finished |
andriy/make_fate | success | Make fate finished |
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 --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",
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