summaryrefslogtreecommitdiffstats
path: root/dom/media/test/test_mediarecorder_record_audionode.html
blob: 8a57437b81b8b9a61e9e837b488dfd9fd5c736d8 (plain)
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>