shithub: opus

Download patch

ref: e0c6eae8cc789b1d80babe5219d505fbf9d91a88
parent: a561f120c97f47d12baa56c33d351cbe8b579ddc
author: Jean-Marc Valin <jmvalin@amazon.com>
date: Thu May 18 22:31:11 EDT 2023

Update the API to add an OpusDREDDecoder

--- a/include/opus.h
+++ b/include/opus.h
@@ -398,7 +398,14 @@
   */
 typedef struct OpusDecoder OpusDecoder;
 
+/** Opus DRED decoder.
+  * This contains the complete state of an Opus DRED decoder.
+  * It is position independent and can be freely copied.
+  * @see opus_dred_decoder_create,opus_dred_decoder_init
+  */
+typedef struct OpusDREDDecoder OpusDREDDecoder;
 
+
 /** Opus DRED state.
   * This contains the complete state of an Opus DRED packet.
   * It is position independent and can be freely copied.
@@ -519,7 +526,29 @@
   */
 OPUS_EXPORT void opus_decoder_destroy(OpusDecoder *st);
 
+/** Gets the size of an <code>OpusDREDDecoder</code> structure.
+  * @returns The size in bytes.
+  */
+OPUS_EXPORT int opus_dred_decoder_get_size(void);
 
+/** Allocates and initializes an OpusDREDDecoder state.
+  * @param [out] error <tt>int*</tt>: #OPUS_OK Success or @ref opus_errorcodes
+  */
+OPUS_EXPORT OpusDREDDecoder *opus_dred_decoder_create(int *error);
+
+/** Initializes an <code>OpusDREDDecoder</code> state.
+  * @param[in] st <tt>OpusDREDDecoder*</tt>: State to be initialized.
+  * @param [out] error <tt>int*</tt>: #OPUS_OK Success or @ref opus_errorcodes
+  */
+OPUS_EXPORT int opus_dred_decoder_init(OpusDREDDecoder *dec);
+
+/** Frees an <code>OpusDREDDecoder</code> allocated by opus_dred_decoder_create().
+  * @param[in] st <tt>OpusDREDDecoder*</tt>: State to be freed.
+  */
+OPUS_EXPORT void opus_dred_decoder_destroy(OpusDREDDecoder *dec);
+
+
+
 /** Gets the size of an <code>OpusDRED</code> structure.
   * @returns The size in bytes.
   */
@@ -544,13 +573,14 @@
   * @param [in] defer_processing <tt>int</tt>: Flag (0 or 1). If set to one, the CPU-intensive part of the DRED decoding is deferred until opus_dred_process() is called.
   * @returns Number of decoded DRED samples or @ref opus_errorcodes
   */
-OPUS_EXPORT int opus_dred_parse(OpusDRED *dred, const unsigned char *data, opus_int32 len, opus_int32 max_dred_samples, opus_int32 sampling_rate, int defer_processing) OPUS_ARG_NONNULL(1);
+OPUS_EXPORT int opus_dred_parse(OpusDREDDecoder *dred_dec, OpusDRED *dred, const unsigned char *data, opus_int32 len, opus_int32 max_dred_samples, opus_int32 sampling_rate, int defer_processing) OPUS_ARG_NONNULL(1);
 
 /** Finish decoding an Opus DRED packet. The function only needs to be called if opus_dred_parse() was called with defer_processing=1.
+  * The source and destination will often be the same DRED state.
   * @param [in] dred <tt>OpusDRED*</tt>: DRED state
   * @returns @ref opus_errorcodes
   */
-OPUS_EXPORT int opus_dred_process(OpusDRED *dred);
+OPUS_EXPORT int opus_dred_process(OpusDREDDecoder *dred_dec, const OpusDRED *src, OpusDRED *dst);
 
 /** Decode audio from an Opus DRED packet with floating point output.
   * @param [in] st <tt>OpusDecoder*</tt>: Decoder state
--- a/src/opus_decoder.c
+++ b/src/opus_decoder.c
@@ -1089,6 +1089,69 @@
    return opus_packet_get_nb_samples(packet, len, dec->Fs);
 }
 
+struct OpusDREDDecoder {
+   int arch;
+   opus_uint32 magic;
+};
+
+#if defined(ENABLE_HARDENING) || defined(ENABLE_ASSERTIONS)
+static void validate_dred_decoder(OpusDREDDecoder *st)
+{
+   celt_assert(st->magic == 0xD8EDDEC0);
+#ifdef OPUS_ARCHMASK
+   celt_assert(st->arch >= 0);
+   celt_assert(st->arch <= OPUS_ARCHMASK);
+#endif
+}
+#define VALIDATE_DRED_DECODER(st) validate_dred_decoder(st)
+#else
+#define VALIDATE_DRED_DECODER(st)
+#endif
+
+
+int opus_dred_decoder_get_size(void)
+{
+  return sizeof(OpusDREDDecoder);
+}
+
+int opus_dred_decoder_init(OpusDREDDecoder *dec)
+{
+   dec->arch = opus_select_arch();
+   /* To make sure nobody forgets to init, use a magic number. */
+   dec->magic = 0xD8EDDEC0;
+   return OPUS_OK;
+}
+
+OpusDREDDecoder *opus_dred_decoder_create(int *error)
+{
+   int ret;
+   OpusDREDDecoder *dec;
+   dec = (OpusDREDDecoder *)opus_alloc(opus_dred_get_size());
+   if (dec == NULL)
+   {
+      if (error)
+         *error = OPUS_ALLOC_FAIL;
+      return NULL;
+   }
+   ret = opus_dred_decoder_init(dec);
+   if (error)
+      *error = ret;
+   if (ret != OPUS_OK)
+   {
+      opus_free(dec);
+      dec = NULL;
+   }
+   return dec;
+}
+
+void opus_dred_decoder_destroy(OpusDREDDecoder *dec)
+{
+   dec->magic = 0xDE57801D;
+   free(dec);
+}
+
+
+
 #ifdef ENABLE_NEURAL_FEC
 static int dred_find_payload(const unsigned char *data, opus_int32 len, const unsigned char **payload)
 {
@@ -1144,11 +1207,12 @@
 }
 #endif
 
-int opus_dred_parse(OpusDRED *dred, const unsigned char *data, opus_int32 len, opus_int32 max_dred_samples, opus_int32 sampling_rate, int defer_processing)
+int opus_dred_parse(OpusDREDDecoder *dred_dec, OpusDRED *dred, const unsigned char *data, opus_int32 len, opus_int32 max_dred_samples, opus_int32 sampling_rate, int defer_processing)
 {
 #ifdef ENABLE_NEURAL_FEC
    const unsigned char *payload;
    opus_int32 payload_len;
+   VALIDATE_DRED_DECODER(dred_dec);
    payload_len = dred_find_payload(data, len, &payload);
    if (payload_len < 0)
       return payload_len;
@@ -1160,7 +1224,7 @@
       min_feature_frames = IMIN(2 + offset, 2*DRED_NUM_REDUNDANCY_FRAMES);
       dred_ec_decode(dred, payload, payload_len, min_feature_frames);
       if (!defer_processing)
-         opus_dred_process(dred);
+         opus_dred_process(dred_dec, dred, dred);
       return dred->nb_latents*sampling_rate/25 - sampling_rate/50;
    }
    return 0;
@@ -1175,13 +1239,18 @@
 #endif
 }
 
-int opus_dred_process(OpusDRED *dred)
+int opus_dred_process(OpusDREDDecoder *dred_dec, const OpusDRED *src, OpusDRED *dst)
 {
 #ifdef ENABLE_NEURAL_FEC
-   if (dred->process_stage == 2)
+   if (dred_dec == NULL)
+      return OPUS_BAD_ARG;
+   VALIDATE_DRED_DECODER(dred_dec);
+   if (src != dst)
+      OPUS_COPY(dst, src, 1);
+   if (dst->process_stage == 2)
       return OPUS_OK;
-   DRED_rdovae_decode_all(dred->fec_features, dred->state, dred->latents, dred->nb_latents);
-   dred->process_stage = 2;
+   DRED_rdovae_decode_all(dst->fec_features, dst->state, dst->latents, dst->nb_latents);
+   dst->process_stage = 2;
    return OPUS_OK;
 #else
    (void)dred;
--- a/src/opus_demo.c
+++ b/src/opus_demo.c
@@ -218,6 +218,7 @@
     OpusEncoder *enc=NULL;
     OpusDecoder *dec=NULL;
     OpusDRED *dred=NULL;
+    OpusDREDDecoder *dred_dec=NULL;
     int args;
     int len;
     int frame_size, channels;
@@ -630,6 +631,7 @@
        opus_encoder_ctl(enc, OPUS_SET_EXPERT_FRAME_DURATION(variable_duration));
        frame_size = 2*48000;
     }
+    dred_dec = opus_dred_decoder_create(&err);
     dred = opus_dred_create(&err);
     while (!stop)
     {
@@ -803,7 +805,7 @@
                 opus_decoder_ctl(dec, OPUS_GET_LAST_PACKET_DURATION(&output_samples));
                 dred_input = lost_count*output_samples*100/sampling_rate;
                 /* Only decode the amount we need to fill in the gap. */
-                opus_dred_parse(dred, data, len, IMIN(100, IMAX(0, dred_input))*480, 48000, 0);
+                opus_dred_parse(dred_dec, dred, data, len, IMIN(100, IMAX(0, dred_input))*480, 48000, 0);
             }
             /* FIXME: Figure out how to trigger the decoder when the last packet of the file is lost. */
             for (fr=0;fr<run_decoder;fr++) {
@@ -917,6 +919,7 @@
     opus_encoder_destroy(enc);
     opus_decoder_destroy(dec);
     opus_dred_destroy(dred);
+    opus_dred_decoder_destroy(dred_dec);
     free(data);
     if (fin)
         fclose(fin);
--