diff --git a/libavformat/Makefile b/libavformat/Makefile
index 62d8cbb54e..4495047e3a 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -46,6 +46,7 @@ OBJS-$(CONFIG_RTPDEC)                    += rdt.o                       \
                                             rtpdec_hevc.o               \
                                             rtpdec_ilbc.o               \
                                             rtpdec_jpeg.o               \
+                                            rtpdec_jpeg2000.o           \
                                             rtpdec_latm.o               \
                                             rtpdec_mpa_robust.o         \
                                             rtpdec_mpeg12.o             \
diff --git a/libavformat/rtpdec.c b/libavformat/rtpdec.c
index 3d5b200099..b47dfdfebc 100644
--- a/libavformat/rtpdec.c
+++ b/libavformat/rtpdec.c
@@ -118,6 +118,7 @@ static const RTPDynamicProtocolHandler *rtp_dynamic_protocol_handler_list[] = {
     &ff_vorbis_dynamic_handler,
     &ff_vp8_dynamic_handler,
     &ff_vp9_dynamic_handler,
+    &ff_jpeg2000_dynamic_handler,
     &gsm_dynamic_handler,
     &l24_dynamic_handler,
     &opus_dynamic_handler,
diff --git a/libavformat/rtpdec_formats.h b/libavformat/rtpdec_formats.h
index dad2b8ac1b..78ea4fb384 100644
--- a/libavformat/rtpdec_formats.h
+++ b/libavformat/rtpdec_formats.h
@@ -89,5 +89,6 @@ extern const RTPDynamicProtocolHandler ff_vc2hq_dynamic_handler;
 extern const RTPDynamicProtocolHandler ff_vorbis_dynamic_handler;
 extern const RTPDynamicProtocolHandler ff_vp8_dynamic_handler;
 extern const RTPDynamicProtocolHandler ff_vp9_dynamic_handler;
+extern const RTPDynamicProtocolHandler ff_jpeg2000_dynamic_handler;
 
 #endif /* AVFORMAT_RTPDEC_FORMATS_H */
diff --git a/libavformat/rtpdec_jpeg2000.c b/libavformat/rtpdec_jpeg2000.c
new file mode 100644
index 0000000000..b5337a9cdb
--- /dev/null
+++ b/libavformat/rtpdec_jpeg2000.c
@@ -0,0 +1,116 @@
+/*
+ * Code for the RTP depacketization of JPEG2000.
+ * 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
+ */
+
+/**
+ * @file
+ * @brief JPEG2000 / RTP Code
+ * @author Gautam Ramakrishnan
+ */
+
+#include "rtpdec_formats.h"
+#include "avio_internal.h"
+#include "internal.h"
+#include "libavutil/attributes.h"
+#include "libavutil/avstring.h"
+#include "libavcodec/get_bits.h"
+
+#define PAYLOAD_HDR_SIZ 8
+
+/**
+ * RTP/JPEG specific private data.
+ */
+struct PayloadContext {
+    AVIOContext *frame;         // current frame buffer
+    uint32_t    timestamp;      // current frame timestamp
+};
+
+static void jpeg2000_close_context(PayloadContext *data)
+{
+    ffio_free_dyn_buf(&data->frame);
+}
+
+static int jpeg2000_parse_packet(AVFormatContext *ctx, PayloadContext *data,
+                                AVStream *st, AVPacket *pkt, uint32_t *timestamp,
+                                const uint8_t *buf, int len, uint16_t seq,
+                                int flags)
+{
+    int ret;
+    int off;
+
+    if (len < 8) {
+        av_log(ctx, AV_LOG_ERROR, "Too short RTP/JPEG packet.\n");
+        return AVERROR_INVALIDDATA;
+    }
+    off = (uint64_t)AV_RB64(buf) & 0xFFFFFF;
+    buf += 8;
+    len -= 8;
+    if (!off) {
+        /* Skip the current frame in case of the end packet
+         * has been lost somewhere. */
+        ffio_free_dyn_buf(&data->frame);
+
+        if ((ret = avio_open_dyn_buf(&data->frame)) < 0)
+            return ret;
+        data->timestamp = *timestamp;
+    }
+    if (!data->frame) {
+        av_log(ctx, AV_LOG_ERROR,
+               "Received packet without a start chunk; dropping frame.\n");
+        return AVERROR(EAGAIN);
+    }
+
+    if (data->timestamp != *timestamp) {
+        /* Skip the current frame if timestamp is incorrect.
+         * A start packet has been lost somewhere. */
+        ffio_free_dyn_buf(&data->frame);
+        av_log(ctx, AV_LOG_ERROR, "RTP timestamps don't match.\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (off != avio_tell(data->frame)) {
+        av_log(ctx, AV_LOG_ERROR,
+               "Missing packets; dropping frame.\n");
+        return AVERROR(EAGAIN);
+    }
+    /* Copy data to frame buffer. */
+    avio_write(data->frame, buf, len);
+
+    if (flags & RTP_FLAG_MARKER) {
+        /* Prepare the JPEG2000 packet. */
+        if ((ret = ff_rtp_finalize_packet(pkt, &data->frame, st->index)) < 0) {
+            av_log(ctx, AV_LOG_ERROR,
+                   "Error occurred when getting frame buffer.\n");
+            return ret;
+        }
+
+        return 0;
+    }
+    return AVERROR(EAGAIN);
+}
+
+const RTPDynamicProtocolHandler ff_jpeg2000_dynamic_handler = {
+    .enc_name           = "jpeg2000",
+    .codec_type         = AVMEDIA_TYPE_VIDEO,
+    .codec_id           = AV_CODEC_ID_JPEG2000,
+    .priv_data_size     = sizeof(PayloadContext),
+    .parse_packet       = jpeg2000_parse_packet,
+    .close              = jpeg2000_close_context,
+};
