Clone encoded WebRTC audio frame when deserializing RTCEncodedAudioFrame

This fixes the issue that structuredClone() returns an
RTCEncodedAudioFrame using the same underlying frame as the original.

Corresponding Video change: crrev.com/c/4584209

Bug: 1450844, 1456292
Change-Id: I9812ea3652b6ef4ae5f0f6d5a8d11abe456de1a7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4626956
Reviewed-by: Guido Urdaneta <guidou@chromium.org>
Commit-Queue: Guido Urdaneta <guidou@chromium.org>
Auto-Submit: Tony Herre <toprice@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1159935}
diff --git a/webrtc-encoded-transform/RTCPeerConnection-insertable-streams-audio.https.html b/webrtc-encoded-transform/RTCPeerConnection-insertable-streams-audio.https.html
index d4b6b72..ad5af10 100644
--- a/webrtc-encoded-transform/RTCPeerConnection-insertable-streams-audio.https.html
+++ b/webrtc-encoded-transform/RTCPeerConnection-insertable-streams-audio.https.html
@@ -12,7 +12,7 @@
 </head>
 <body>
 <script>
-async function testAudioFlow(t, negotiationFunction) {
+async function testAudioFlow(t, negotiationFunction, perFrameCallback = () => {}) {
   const caller = new RTCPeerConnection({encodedInsertableStreams:true});
   t.add_cleanup(() => caller.close());
   const callee = new RTCPeerConnection({encodedInsertableStreams:true});
@@ -70,50 +70,56 @@
 
   // Pass frames as they come from the encoder.
   for (let i = 0; i < numFramesPassthrough; i++) {
-    const result = await senderReader.read()
+    const result = await senderReader.read();
+    const frame = result.value;
     frameInfos.push({
-      data: result.value.data,
-      timestamp: result.value.timestamp,
-      type: result.value.type,
-      metadata: result.value.getMetadata(),
+      data: frame.data,
+      timestamp: frame.timestamp,
+      type: frame.type,
+      metadata: frame.getMetadata(),
       getMetadata() { return this.metadata; }
     });
-    senderWriter.write(result.value);
+    perFrameCallback(frame);
+    senderWriter.write(frame);
   }
 
   // Replace frame data with arbitrary buffers.
   for (let i = 0; i < numFramesReplaceData; i++) {
     const result = await senderReader.read()
+    const frame = result.value;
 
     const buffer = new ArrayBuffer(100);
     const int8View = new Int8Array(buffer);
     int8View.fill(i);
 
-    result.value.data = buffer;
+    frame.data = buffer;
     frameInfos.push({
-      data: result.value.data,
-      timestamp: result.value.timestamp,
-      type: result.value.type,
-      metadata: result.value.getMetadata(),
+      data: frame.data,
+      timestamp: frame.timestamp,
+      type: frame.type,
+      metadata: frame.getMetadata(),
       getMetadata() { return this.metadata; }
     });
-    senderWriter.write(result.value);
+    perFrameCallback(frame);
+    senderWriter.write(frame);
   }
 
   // Modify frame data.
   for (let i = 0; i < numFramesReplaceData; i++) {
     const result = await senderReader.read()
-    const int8View = new Int8Array(result.value.data);
+    const frame = result.value;
+    const int8View = new Int8Array(frame.data);
     int8View.fill(i);
 
     frameInfos.push({
-      data: result.value.data,
-      timestamp: result.value.timestamp,
-      type: result.value.type,
-      metadata: result.value.getMetadata(),
+      data: frame.data,
+      timestamp: frame.timestamp,
+      type: frame.type,
+      metadata: frame.getMetadata(),
       getMetadata() { return this.metadata; }
     });
-    senderWriter.write(result.value);
+    perFrameCallback(frame);
+    senderWriter.write(frame);
   }
 
   return ontrackPromise;
@@ -207,6 +213,26 @@
   assert_throws_dom("InvalidStateError", () => sender.createEncodedStreams());
 }, 'Creating streams twice throws');
 
+promise_test(async t => {
+  let clonedFrames = [];
+  function verifyFramesSerializeAndDeserialize(frame) {
+    // Clone encoded frames using structedClone (ie serialize + deserialize) and
+    // keep a reference.
+    const clone = structuredClone(frame);
+    clonedFrames.push(clone);
+  };
+
+  await testAudioFlow(
+    t, exchangeOfferAnswer, verifyFramesSerializeAndDeserialize);
+
+  // Ensure all of our cloned frames are still alive and well, despite the
+  // originals having been sent through the PeerConnection.
+  clonedFrames.forEach((clonedFrame) => {
+    assert_not_equals(clonedFrame.data.size, 0);
+    assert_not_equals(clonedFrame.timestamp, 0);
+  });
+}, 'Encoded frames serialize and deserialize into a deep clone');
+
 </script>
 </body>
 </html>