tabCapture: no refresh frame if tab content has no update or mouse movement |
||||||
Issue descriptionChrome Version: (copy from chrome://version) Chromium ToT OS: (e.g. Win10, MacOS 10.12, etc...) All desktop platforms What steps will reproduce the problem? (1)connect a tabcapture mediastream to a mediarecorder, (2)won't get dataavailable callbacks until you interact with the tab What is the expected result? get output frame even there is no update on the captured tab. What happens instead? No output frame if there is no update on the captured tab. Please use labels and text to provide additional information. When you share a tab through desktopCapture to peer, there is automatic frame refreshes at a giving interval. The reason is when a stream is added into webrtc sink at [1], a refresh timer will be started. And there is no such a timer if only getUserMedia is called, same to both tabCapture and webContentCapture by desktopCapture [1]: https://cs.chromium.org/chromium/src/content/renderer/media/webrtc/media_stream_video_webrtc_sink.cc?l=333
,
Jul 11
This code here ensures that all MediaStream video tracks will deliver at least one "refresh" frame whenever a sink is added: https://cs.chromium.org/chromium/src/content/renderer/media/stream/media_stream_video_track.cc?rcl=7a0a490dd3d9d640e088aee823bd069c74a8ef64&l=314 So, perhaps there's a bug in content::VideoTrackRecorder (https://cs.chromium.org/chromium/src/content/renderer/media_recorder/video_track_recorder.cc)? Assigning to feature owner for further debugging/investigation... Side note: WebRTC's refresh timer is sort of a hack because, in the past, WebRTC's bandwidth estimation failed if no data was being sent over its network stack. It's very power-inefficient to force senders to keep capturing+encoding+sending frames when nothing has changed, so I recommend that be fixed if it hasn't already. I'm not sure who would own that.
,
Jul 11
Sorry for forgetting the detail that the first several frames when a sink is added are well received. Then there might be no more frames later if nothing has changed. Not sure if this is good for the case of recording a tab.
,
Jul 11
This is working as intended considering how tab capture and MediaRecorder works. Tab capture pushes new frame iff there is a change. MediaRecorder connects to the tab capture video track as it is and records each incoming frame. If there is no update for a while from the tab capture video track, that won't matter in the recorded file as each frame will be timestamped accordingly. Like miu@ explained, adding a refresh timer like WebRTC sink does would be another hack that results in redundant recordings and events. With or without refresh timer, recorded file(.webm, mp4, etc.) should look the same. Feel free to reopen the bug if there is a case where recorded file isn't working as expected.
,
Jul 11
As long as the recording looks like the tab I agree there's no bug here.
,
Jul 11
When using MediaRecorder, there are no dataavailable callbacks until you mouse over the captured tab. I think #2 may not be happening in all cases. See background page log output when using this demo extension: https://drive.google.com/file/d/1Z-aDJ-CNbq7yAqBScq1rN0bLcuIn-WJv/view?usp=sharing
,
Jul 11
juberti@, actually there are two frames when I test with your demo extension, without interacting the target tab. 0.007: recording started 0.131: dataavailable: 9582 4.945: dataavailable: 246
,
Jul 12
This is what I see (on Mac OS 10.13.5, Version 69.0.3484.0 (Official Build) canary (64-bit))
0.000: starting recording
background.js:47 MediaRecorder supported types:
background.js:50 video/webm
background.js:50 video/webm;codecs=vp8
background.js:50 video/webm;codecs=vp9
background.js:50 video/webm;codecs=h264
background.js:90 0.019: gotstream MediaStream {id: "YIcYs67YnXilf2jxCEoW2Qxgd2npB3xw0zgl", active: true, onaddtrack: null, onremovetrack: null, onactive: null, …}
background.js:101 0.023: recording started
background.js:98 0.153: dataavailable: 1
,
Jul 12
Oh my test step was not right. Yes I saw same as in #8. By adding logs into codes, it appears that tabCapture delivered the initial frames as normal, but for some reason they are not wired into mediarecoder.
,
Jul 12
To make it clear, the issue we are focusing on now is why the first dataavailable event is coming with size 1 and there isn't an additional event until mouse hovers. Can you confirm? I added some debug logs to track that. It looks related to the arg passed to MediaRecorder.start(timeslice) call. - When I modify background.js in the demo extension to |mediaRecorder.start(1)|, I can see events being fired with the first click as expected. - Currently that is set to |mediaRecorder.start(100)|. How that is wired up in code is that only the event coming at time T is fired and all events until T+100ms is silenced. The events start firing at |slice_origin_timestamp_+100ms| and |slice_origin_timestamp_| is initialized at MediaRecorderHandler::Start() call. - The initial captured frame gets wired up into media recorder and encoded appropriately in all my tests. It is passed to webm muxer as well with an appropriate size. Then, we wait for WebmMuxer::Write() callback[0]. WebmMuxer::Write() callback always has size 1 for its first output. Sometimes it takes after |slice_origin_timestamp_+100ms| to send the first callback. Then, we output |dataavailable: 1|. Sometimes more callback happen until then and we output |dataavailable: >1|. [0] https://cs.chromium.org/chromium/src/media/muxers/webm_muxer.h?dr=CSs&g=0&l=100 I believe we can modify when |slice_origin_timestamp_| gets set for more consistent results. mcasas@ WDYT considering the spec?
,
Jul 12
#10: Correct, if the JS says mediaRecorder.start(T), then nothing is going to be received in the JS ondataavailable event handler until at least T ms have elapsed -or- JS calls MediaRecorder.requestData() explicitly. The code in [1] will buffer for the user until said T has elapsed (which is signalled by WebmMuxer via the |last_in_slice| parameters). So, all seems WAI. Note that media_recoder.start() will buffer forever! That said, 1 ms might be OK depending on the application, i.e. low latency desired _and_ we expect variable frame rates, e.g. bursts, but in those cases I'd let the Renderer code buffer for me and call as fast as possible requestData() from JS... [1] https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc?q=Media_recorder.cc&sq=package:chromium&dr&l=355
,
Jul 12
,
Jul 12
#10: yes, that's the unexpected behavior. I would have expected to get an ondataavailable with a keyframe after 100ms.
#11: even after T (in this case, 100) ms have elapsed, no ondataavailable is invoked. I don't think this is WAI.
#11: isn't the point of the T parameter to avoid needing to do setInterval(() => { r.requestData, T) })?
,
Jul 12
Thanks for the input mcasas@. My concern was only about the the first ever event fired. Suppose we are capturing and recording a single frame and |timeslice| is set to a large number(>500ms), we don't have any events fired as all recording happens within |timeslice| and there is nothing after. requestData() can easily workaround this, but should this be the default behavior? Section 2.3 of the spec sounds like there should be an event fired after |timeslice| ms with the accumulated data.
,
Jul 12
I did change to start(1) and the problem was resolved. I am getting some intermittent other errors related to chunk parsing when feeding the chunks into a <video>, but I need to collect more info.
,
Jul 12
On second glance, "minimum of timeslice milliseconds" may be enough to put this within WAI actually. Still we can make it more clear. Link to the latest spec: https://w3c.github.io/mediacapture-record/MediaRecorder.html#mediarecorder-methods
,
Jul 13
Spoke too soon. Even with .start(1), I'm still seeing cases where no initial frame is provided, e.g.: 0.000: starting recording 0.005: gotstream 0.157: ondataavailable(4) [no more callbacks]
,
Jul 13
#17, which platform and Chrome build do you get this? I cannot repro on linux with Chromium ToT.
,
Jul 13
Mac, Version 69.0.3489.0 (Official Build) canary (64-bit) It seems to depend on the site. Using google.com, I can easily repro this.
,
Jul 17
I managed to get some repro on Mac as well with mediaRecorder.start(1). However, when I add debug logs, I can no longer get a repro which hints at timing. I added the below CHECK which should ideally not hit when |timeslice_| is minimum(1) but it still hits. Looks like ondataavailable events are coming within 1 ms and we are missing them. mediaRecorder.start(0) is the only way to receive all of them properly.
diff --git a/content/renderer/media_recorder/media_recorder_handler.cc b/content/renderer/media_recorder/media_recorder_handler.cc
index 8e5f6b142e24..9bdd4844d70a 100644
--- a/content/renderer/media_recorder/media_recorder_handler.cc
+++ b/content/renderer/media_recorder/media_recorder_handler.cc
@@ -517,6 +517,7 @@ void MediaRecorderHandler::WriteData(base::StringPiece data) {
}
const bool last_in_slice = now > slice_origin_timestamp_ + timeslice_;
+ CHECK(last_in_slice);
DVLOG_IF(1, last_in_slice) << "Slice finished @ " << now;
if (last_in_slice)
slice_origin_timestamp_ = now;
,
Aug 2
|
||||||
►
Sign in to add a comment |
||||||
Comment 1 by braveyao@chromium.org
, Jul 10Owner: m...@chromium.org
Status: Available (was: Untriaged)