diff --git a/doc/APIchanges b/doc/APIchanges
index bc52a07964..4b3934b82c 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -14,6 +14,9 @@ libavutil:     2021-04-27
 
 API changes, most recent first:
 
+2023-0x-xx - xxxxxxxxxx - lavc 59.60.100
+  Allow AV_CODEC_FLAG_COPY_OPAQUE to be used with decoders.
+
 2023-01-29 - xxxxxxxxxx - lavc 59.59.100 - avcodec.h
   Add AV_CODEC_FLAG_COPY_OPAQUE and AV_CODEC_FLAG_FRAME_DURATION.
 
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 90b437ccbe..eba9ea73d7 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -242,9 +242,15 @@ typedef struct RcOverride{
  */
 #define AV_CODEC_FLAG_RECON_FRAME     (1 <<  6)
 /**
+ * @par decoding
+ * Request the decoder to propagate each packets AVPacket.opaque and
+ * AVPacket.opaque_ref to its corresponding output AVFrame.
+ *
+ * @par encoding:
  * Request the encoder to propagate each frame's AVFrame.opaque and
  * AVFrame.opaque_ref values to its corresponding output AVPacket.
  *
+ * @par
  * May only be set on encoders that have the
  * @ref AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE capability flag.
  *
@@ -265,6 +271,9 @@ typedef struct RcOverride{
  * .
  * When an output packet contains multiple frames, the opaque values will be
  * taken from the first of those.
+ *
+ * @note
+ * The converse holds for decoders, with frames and packets switched.
  */
 #define AV_CODEC_FLAG_COPY_OPAQUE     (1 <<  7)
 /**
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 0abc88737b..17b398e933 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -1291,7 +1291,8 @@ static int add_metadata_from_side_data(const AVPacket *avpkt, AVFrame *frame)
     return av_packet_unpack_dictionary(side_metadata, size, frame_md);
 }
 
-int ff_decode_frame_props_from_pkt(AVFrame *frame, const AVPacket *pkt)
+int ff_decode_frame_props_from_pkt(const AVCodecContext *avctx,
+                                   AVFrame *frame, const AVPacket *pkt)
 {
     static const struct {
         enum AVPacketSideDataType packet;
@@ -1336,6 +1337,13 @@ int ff_decode_frame_props_from_pkt(AVFrame *frame, const AVPacket *pkt)
         frame->flags = (frame->flags & ~AV_FRAME_FLAG_DISCARD);
     }
 
+    if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) {
+        int ret = av_buffer_replace(&frame->opaque_ref, pkt->opaque_ref);
+        if (ret < 0)
+            return ret;
+        frame->opaque = pkt->opaque;
+    }
+
     return 0;
 }
 
@@ -1344,7 +1352,7 @@ int ff_decode_frame_props(AVCodecContext *avctx, AVFrame *frame)
     const AVPacket *pkt = avctx->internal->last_pkt_props;
 
     if (!(ffcodec(avctx->codec)->caps_internal & FF_CODEC_CAP_SETS_FRAME_PROPS)) {
-        int ret = ff_decode_frame_props_from_pkt(frame, pkt);
+        int ret = ff_decode_frame_props_from_pkt(avctx, frame, pkt);
         if (ret < 0)
             return ret;
         frame->pkt_size     = (int)(intptr_t)pkt->opaque;
diff --git a/libavcodec/decode.h b/libavcodec/decode.h
index 906122b4a7..8430ffbd66 100644
--- a/libavcodec/decode.h
+++ b/libavcodec/decode.h
@@ -72,7 +72,8 @@ int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt);
 /**
  * Set various frame properties from the provided packet.
  */
-int ff_decode_frame_props_from_pkt(AVFrame *frame, const AVPacket *pkt);
+int ff_decode_frame_props_from_pkt(const AVCodecContext *avctx,
+                                   AVFrame *frame, const AVPacket *pkt);
 
 /**
  * Set various frame properties from the codec context / packet data.
diff --git a/libavcodec/libdav1d.c b/libavcodec/libdav1d.c
index b43af03732..f7d75f9439 100644
--- a/libavcodec/libdav1d.c
+++ b/libavcodec/libdav1d.c
@@ -288,6 +288,11 @@ static void libdav1d_flush(AVCodecContext *c)
     dav1d_flush(dav1d->c);
 }
 
+typedef struct OpaqueData {
+    void    *pkt_orig_opaque;
+    int64_t  reordered_opaque;
+} OpaqueData;
+
 static void libdav1d_data_free(const uint8_t *data, void *opaque) {
     AVBufferRef *buf = opaque;
 
@@ -307,6 +312,7 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame)
     Dav1dData *data = &dav1d->data;
     Dav1dPicture pic = { 0 }, *p = &pic;
     AVPacket *pkt;
+    OpaqueData *od = NULL;
 #if FF_DAV1D_VERSION_AT_LEAST(5,1)
     enum Dav1dEventFlags event_flags = 0;
 #endif
@@ -333,17 +339,19 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame)
             }
 
             pkt->buf = NULL;
-            pkt->opaque = NULL;
 
-            if (c->reordered_opaque != AV_NOPTS_VALUE) {
-                pkt->opaque = av_memdup(&c->reordered_opaque,
-                                        sizeof(c->reordered_opaque));
-                if (!pkt->opaque) {
+            if (c->reordered_opaque != AV_NOPTS_VALUE ||
+                (pkt->opaque && (c->flags & AV_CODEC_FLAG_COPY_OPAQUE))) {
+                od = av_mallocz(sizeof(*od));
+                if (!od) {
                     av_packet_free(&pkt);
                     dav1d_data_unref(data);
                     return AVERROR(ENOMEM);
                 }
+                od->pkt_orig_opaque  = pkt->opaque;
+                od->reordered_opaque = c->reordered_opaque;
             }
+            pkt->opaque = od;
 
             res = dav1d_data_wrap_user_data(data, (const uint8_t *)pkt,
                                             libdav1d_user_data_free, pkt);
@@ -423,13 +431,20 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame)
     ff_set_sar(c, frame->sample_aspect_ratio);
 
     pkt = (AVPacket *)p->m.user_data.data;
-    if (pkt->opaque)
-        memcpy(&frame->reordered_opaque, pkt->opaque, sizeof(frame->reordered_opaque));
+    od  = pkt->opaque;
+    if (od && od->reordered_opaque != AV_NOPTS_VALUE)
+        frame->reordered_opaque = od->reordered_opaque;
     else
         frame->reordered_opaque = AV_NOPTS_VALUE;
 
+    // restore the original user opaque value for
+    // ff_decode_frame_props_from_pkt()
+    pkt->opaque = od ? od->pkt_orig_opaque : NULL;
+    av_freep(&od);
+
     // match timestamps and packet size
-    res = ff_decode_frame_props_from_pkt(frame, pkt);
+    res = ff_decode_frame_props_from_pkt(c, frame, pkt);
+    pkt->opaque = NULL;
     if (res < 0)
         goto fail;
 
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 2ed4ef5547..499c6bb175 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
 
 #include "version_major.h"
 
-#define LIBAVCODEC_VERSION_MINOR  60
+#define LIBAVCODEC_VERSION_MINOR  61
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
