DXR will be turned off on Tuesday, December 29th. It will redirect to Searchfox.
See the announcement on Discourse.

DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
<!DOCTYPE HTML>
<html>
<head>
  <title>Test MediaRecorder Record AudioContext Node</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
  <script type="text/javascript" src="manifest.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=968109">Mozilla Bug 968109</a>

<script class="testbody" type="text/javascript">

SimpleTest.waitForExplicitFinish();

function setUpSource(contextType, nodeType) {
  // Use contextType to choose offline or real-time context.
  const context = contextType == "offline"?
    new OfflineAudioContext(2 , 80920, 44100) : new AudioContext();
  const buffer = context.createBuffer(2, 80920, context.sampleRate);
  for (let i = 0; i < 80920; ++i) {
    buffer.getChannelData(0)[i] = Math.sin(1000 * 2 * Math.PI * i / context.sampleRate);
    buffer.getChannelData(1)[i] = Math.sin(1000 * 2 * Math.PI * i / context.sampleRate);
  }

  const source = context.createBufferSource();
  source.buffer = buffer;
  source.loop = true;

  source.start(0);

  // nodeType decides which node in graph should be the source of recording.
  let node;
  if (nodeType == "source") {
    node = source;
  } else if (nodeType == "splitter") {
    const splitter = context.createChannelSplitter();
    source.connect(splitter);
    node = splitter;
  } else if (nodeType == "destination") {
    source.connect(context.destination);
    node = context.destination;
  }
  // Explicitly start offline context.
  if (contextType == "offline") {
    context.startRendering();
  }

  return node;
}

async function testRecord(source, mimeType) {
  const isOffline = source.context instanceof OfflineAudioContext;
  const recorder = new MediaRecorder(source, 0, {mimeType});
  is(recorder.mimeType, mimeType, "Mime type is set");
  const extendedMimeType = `${mimeType || "audio/ogg"}; codecs=opus`;

  recorder.onwarning = () => ok(false, "should not fire onwarning");
  recorder.onerror = () => ok(false, "should not fire onerror");
  if (!isOffline) {
    recorder.onstop = () => ok(false, "should not fire stop yet");
  }

  recorder.start(1000);
  is("recording", recorder.state, "state should become recording after calling start()");
  is(recorder.mimeType, mimeType, "Mime type is not changed by start()");

  await new Promise(r => recorder.onstart = r);
  is(recorder.mimeType, extendedMimeType, "Mime type is fully defined");

  const chunks = [];
  let {data} = await new Promise(r => recorder.ondataavailable = r);
  if (!isOffline) {
    is(recorder.state, "recording", "Expected to still be recording");
  }
  is(data.type, extendedMimeType, "Blob has fully defined mimetype");
  isnot(data.size, 0, "should get data and its length should be > 0");
  chunks.push(data);

  if (isOffline) {
    await new Promise(r => recorder.onstop = r);
    is(recorder.state, "inactive", "Offline context should end by itself");
  } else {
    is(recorder.state, "recording", "Expected to still be recording");
    recorder.stop();
    ({data} = await new Promise(r => recorder.ondataavailable = r));
    is(recorder.state, "inactive", "Expected to be inactive after last blob");
    isnot(data.size, 0, "should get data and its length should be > 0");
    chunks.push(data);

    await new Promise(r => recorder.onstop = r);
    is(recorder.state, "inactive", "state should remain inactive after stop event");
  }
  return new Blob(chunks, {type: chunks[0].type});
}

addLoadEvent(async () => {
  const src = setUpSource();
  let didThrow = false;
  try {
    new MediaRecorder(src);
  } catch (e) {
    didThrow = true;
  }
  ok(didThrow, "MediaRecorder(AudioNode) should be hidden behind a pref");

  await SpecialPowers.pushPrefEnv({set: [
      ["media.recorder.audio_node.enabled", true],
    ]});

  // Test with various context and source node types.
  for (const mimeType of [
    "audio/ogg",
    "audio/webm",
    "video/webm",
    "",
  ]) {
    for (const {context, node} of [
      {context: "", node: "source"},
      {context: "", node: "splitter"},
      {context: "offline", node: "destination"},
    ]) {
      info(`Testing recording ${context || "regular"} context and ${node} ` +
           `node with mimeType '${mimeType}'`);
      await testRecord(setUpSource(context, node), mimeType);
    }
  }

  SimpleTest.finish();
});

</script>
</pre>
</body>
</html>