<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE bugzilla SYSTEM "https://www.w3.org/Bugs/Public/page.cgi?id=bugzilla.dtd">

<bugzilla version="5.0.4"
          urlbase="https://www.w3.org/Bugs/Public/"
          
          maintainer="sysbot+bugzilla@w3.org"
>

    <bug>
          <bug_id>26316</bug_id>
          
          <creation_ts>2014-07-12 04:40:34 +0000</creation_ts>
          <short_desc>[MSE] End of stream algorithm closes, re-opens MediaSource</short_desc>
          <delta_ts>2014-07-25 18:14:05 +0000</delta_ts>
          <reporter_accessible>1</reporter_accessible>
          <cclist_accessible>1</cclist_accessible>
          <classification_id>1</classification_id>
          <classification>Unclassified</classification>
          <product>HTML WG</product>
          <component>Media Source Extensions</component>
          <version>unspecified</version>
          <rep_platform>PC</rep_platform>
          <op_sys>All</op_sys>
          <bug_status>RESOLVED</bug_status>
          <resolution>FIXED</resolution>
          
          
          <bug_file_loc></bug_file_loc>
          <status_whiteboard></status_whiteboard>
          <keywords></keywords>
          <priority>P2</priority>
          <bug_severity>normal</bug_severity>
          <target_milestone>---</target_milestone>
          
          
          <everconfirmed>1</everconfirmed>
          <reporter name="Jer Noble">jer.noble</reporter>
          <assigned_to name="Aaron Colwell">acolwell</assigned_to>
          <cc>acolwell</cc>
    
    <cc>mike</cc>
    
    <cc>public-html-media</cc>
    
    <cc>wolenetz</cc>
          
          <qa_contact name="HTML WG Bugzilla archive list">public-html-bugzilla</qa_contact>

      

      

      

          <comment_sort_order>oldest_to_newest</comment_sort_order>  
          <long_desc isprivate="0" >
    <commentid>108877</commentid>
    <comment_count>0</comment_count>
    <who name="Jer Noble">jer.noble</who>
    <bug_when>2014-07-12 04:40:34 +0000</bug_when>
    <thetext>2.4.7 End of stream algorithm
&gt; 1. Change the readyState attribute value to &quot;ended&quot;.
&gt; 2. Queue a task to fire a simple event named sourceended at the MediaSource.
&gt; 3. ↪︎ If error is not set
&gt;  3.1 Run the duration change algorithm with new duration set to the highest end time reported by the buffered attribute across all SourceBuffer objects in sourceBuffers.

2.4.6 Duration change
&gt; 4. If the new duration is less than old duration, then call remove(new duration, old duration) on all objects in sourceBuffers.

3.2 Methods, remove
&gt; 6. If the readyState attribute of the parent media source is in the &quot;ended&quot; state then run the following steps:
&gt;  6.1 Set the readyState attribute of the parent media source to &quot;open&quot;&quot;
&gt;  6.2 Queue a task to fire a simple event named sourceopen at the parent media source .

Calling endOfStream() on a MediaSource with a partially-appended SourceBuffer will result in:

MediaSource.readyState: open -&gt; ended
MediaSource.readyState: ended -&gt; open
MediaSource &apos;sourceended&apos; fires
MediaSource &apos;sourceopen&apos; fires</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>108878</commentid>
    <comment_count>1</comment_count>
    <who name="Jer Noble">jer.noble</who>
    <bug_when>2014-07-12 05:38:44 +0000</bug_when>
    <thetext>Moving §2.4.7 steps 1 &amp; 2 after step 3 seems to solve the problem.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>108919</commentid>
    <comment_count>2</comment_count>
    <who name="Matt Wolenetz">wolenetz</who>
    <bug_when>2014-07-14 20:47:55 +0000</bug_when>
    <thetext>I&apos;m not sure that I understand the problem.
As part of endOfStream(), &quot;If the updating attribute equals true on any SourceBuffer in sourceBuffers, then throw an INVALID_STATE_ERR exception and abort these steps.&quot;
This occurs prior to running any of the end of stream algorithm steps.
If the agent was in the middle of a SourceBuffer.append() operation, then updating should be true and INVALID_STATE_ERR should result on an attempt by the app to call endOfStream().
Do I misunderstand the scenario? I&apos;m quoting from the latest editor&apos;s draft (08 July 2014)</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>108922</commentid>
    <comment_count>3</comment_count>
    <who name="Jer Noble">jer.noble</who>
    <bug_when>2014-07-14 21:30:45 +0000</bug_when>
    <thetext>(In reply to Matt Wolenetz from comment #2)
&gt; I&apos;m not sure that I understand the problem.
&gt; As part of endOfStream(), &quot;If the updating attribute equals true on any
&gt; SourceBuffer in sourceBuffers, then throw an INVALID_STATE_ERR exception and
&gt; abort these steps.&quot;
&gt; This occurs prior to running any of the end of stream algorithm steps.
&gt; If the agent was in the middle of a SourceBuffer.append() operation, then
&gt; updating should be true and INVALID_STATE_ERR should result on an attempt by
&gt; the app to call endOfStream().
&gt; Do I misunderstand the scenario? I&apos;m quoting from the latest editor&apos;s draft
&gt; (08 July 2014)

I believe the source of the misunderstanding is that a single append() operation may not append samples for [0, duration).  So after one or more append() operations:

- &quot;updateend&quot; is fired
- /updating/ is unset
- duration is N
- buffered is (0, M), where M &lt; N.

Calling endOfStream() in these circumstances will lead to the events listed in the initial bug report.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>108923</commentid>
    <comment_count>4</comment_count>
    <who name="Jer Noble">jer.noble</who>
    <bug_when>2014-07-14 21:34:09 +0000</bug_when>
    <thetext>(In reply to Jer Noble from comment #3)
&gt; (In reply to Matt Wolenetz from comment #2)
&gt; &gt; I&apos;m not sure that I understand the problem.
&gt; &gt; As part of endOfStream(), &quot;If the updating attribute equals true on any
&gt; &gt; SourceBuffer in sourceBuffers, then throw an INVALID_STATE_ERR exception and
&gt; &gt; abort these steps.&quot;
&gt; &gt; This occurs prior to running any of the end of stream algorithm steps.
&gt; &gt; If the agent was in the middle of a SourceBuffer.append() operation, then
&gt; &gt; updating should be true and INVALID_STATE_ERR should result on an attempt by
&gt; &gt; the app to call endOfStream().
&gt; &gt; Do I misunderstand the scenario? I&apos;m quoting from the latest editor&apos;s draft
&gt; &gt; (08 July 2014)
&gt; 
&gt; I believe the source of the misunderstanding is that a single append()
&gt; operation may not append samples for [0, duration).  So after one or more
&gt; append() operations:
&gt; 
&gt; - &quot;updateend&quot; is fired
&gt; - /updating/ is unset
&gt; - duration is N
&gt; - buffered is (0, M), where M &lt; N.
&gt; 
&gt; Calling endOfStream() in these circumstances will lead to the events listed
&gt; in the initial bug report.

Note, however, that duration must either have been set explicitly from script, or a duration must have been present in the initialization segment, for duration to be greater than the buffered range.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>108929</commentid>
    <comment_count>5</comment_count>
    <who name="Matt Wolenetz">wolenetz</who>
    <bug_when>2014-07-14 23:32:14 +0000</bug_when>
    <thetext>Thank you for clarifying; I was confused by the &quot;partially-appended SourceBuffer&quot; text in the original post.

Note that duration defaults to +Infinity in the initialization segment received algorithm, if duration was previously NaN and is not present in the init segment. This increases the potential for this bug to be hit due to endOfStream()&apos;s execution of the duration change algorithm resulting in remove(new duration, +Infinity or some value &gt; new duration).</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>109254</commentid>
    <comment_count>6</comment_count>
    <who name="Aaron Colwell">acolwell</who>
    <bug_when>2014-07-24 18:03:25 +0000</bug_when>
    <thetext>Jer: I don&apos;t think your proposed fix is quite right since deferring the readyState transition causes SourceBuffer.buffered to return a different value than it currently does.

I can think of 2 possible alternate solutions:
- Replace step 4 in the &quot;duration change algorithm&quot; with steps 7-13 from remove().
- Change step 4 of &quot;duration change algorithm&quot; to only call remove() if any SourceBuffer.buffered contains one or more ranges that intersect with [new duration, old duration). I believe in the case of endOfStream() this should always be false since we are using the end ranges to determine the new duration.

Any preference?</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>109255</commentid>
    <comment_count>7</comment_count>
    <who name="Jer Noble">jer.noble</who>
    <bug_when>2014-07-24 18:31:52 +0000</bug_when>
    <thetext>(In reply to Aaron Colwell from comment #6)
&gt; Jer: I don&apos;t think your proposed fix is quite right since deferring the
&gt; readyState transition causes SourceBuffer.buffered to return a different
&gt; value than it currently does.
&gt; 
&gt; I can think of 2 possible alternate solutions:
&gt; - Replace step 4 in the &quot;duration change algorithm&quot; with steps 7-13 from
&gt; remove().

I think this is the right way to go, because:

&gt; - Change step 4 of &quot;duration change algorithm&quot; to only call remove() if any
&gt; SourceBuffer.buffered contains one or more ranges that intersect with [new
&gt; duration, old duration). I believe in the case of endOfStream() this should
&gt; always be false since we are using the end ranges to determine the new
&gt; duration.

Well, that&apos;s a different issue, which I haven&apos;t written up yet.  But if you use the highest buffered end time across all MediaSource.sourceBuffers, you&apos;ll end up stalling (dropping to HAVE_METADATA) if you try to play to the (new) duration, as the currentTime enters into the unbuffered range of the shorter-buffered SourceBuffer.  And given that the general use case is to have a separate SourceBuffer for each audio and video stream, and given that each will have very slightly different durations, due to the irreconcilable differences in sample durations between the two media types.

Also, the HTMLMediaElement.buffered attribute is also defined to inflate shorter SourceBuffer.buffered values to the highest end time across all MediaSource.sourceBuffers.

So either the &quot;SourceBuffer Monitoring&quot; state algorithm has to be amended to account for these &quot;virtual buffered ranges&quot;, or the endOfStream() behavior has to be changed to truncate the SourceBuffers to the least highest buffered end time.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>109264</commentid>
    <comment_count>8</comment_count>
    <who name="Aaron Colwell">acolwell</who>
    <bug_when>2014-07-24 20:24:42 +0000</bug_when>
    <thetext>(In reply to Jer Noble from comment #7)
&gt; (In reply to Aaron Colwell from comment #6)
&gt; &gt; Jer: I don&apos;t think your proposed fix is quite right since deferring the
&gt; &gt; readyState transition causes SourceBuffer.buffered to return a different
&gt; &gt; value than it currently does.
&gt; &gt; 
&gt; &gt; I can think of 2 possible alternate solutions:
&gt; &gt; - Replace step 4 in the &quot;duration change algorithm&quot; with steps 7-13 from
&gt; &gt; remove().
&gt; 
&gt; I think this is the right way to go, because:

Ok. I&apos;ll do this then.

&gt; 
&gt; &gt; - Change step 4 of &quot;duration change algorithm&quot; to only call remove() if any
&gt; &gt; SourceBuffer.buffered contains one or more ranges that intersect with [new
&gt; &gt; duration, old duration). I believe in the case of endOfStream() this should
&gt; &gt; always be false since we are using the end ranges to determine the new
&gt; &gt; duration.
&gt; 
&gt; Well, that&apos;s a different issue, which I haven&apos;t written up yet.  But if you
&gt; use the highest buffered end time across all MediaSource.sourceBuffers,
&gt; you&apos;ll end up stalling (dropping to HAVE_METADATA) if you try to play to the
&gt; (new) duration, as the currentTime enters into the unbuffered range of the
&gt; shorter-buffered SourceBuffer.  And given that the general use case is to
&gt; have a separate SourceBuffer for each audio and video stream, and given that
&gt; each will have very slightly different durations, due to the irreconcilable
&gt; differences in sample durations between the two media types.
&gt; 
&gt; Also, the HTMLMediaElement.buffered attribute is also defined to inflate
&gt; shorter SourceBuffer.buffered values to the highest end time across all
&gt; MediaSource.sourceBuffers.
&gt; 
&gt; So either the &quot;SourceBuffer Monitoring&quot; state algorithm has to be amended to
&gt; account for these &quot;virtual buffered ranges&quot;, or the endOfStream() behavior
&gt; has to be changed to truncate the SourceBuffers to the least highest
&gt; buffered end time.

I&apos;m pretty sure the &quot;SourceBuffer Monitoring&quot; algorithm needs to be updated. It hasn&apos;t gotten much love in a long time and I wouldn&apos;t be surprised if there are bugs lurking in there. readyState transitions should definitely be driven of HTMLMediaElement.buffered ranges and not individual SourceBuffer.buffered since during non-endOfStream playback the intersection used should make sure that we don&apos;t try to play unbuffered regions. I&apos;ll take a look at the algorithm and file a bugs for any problems with the algorithm I see.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>109267</commentid>
    <comment_count>9</comment_count>
    <who name="Aaron Colwell">acolwell</who>
    <bug_when>2014-07-24 21:05:06 +0000</bug_when>
    <thetext>Change committed.
https://dvcs.w3.org/hg/html-media/rev/b3f086e62b53

Factored the steps out into a &quot;range removal&quot; algorithm and updated remove() and the &quot;duration change&quot; algorithms to use that just to avoid these 2 mechanisms from accidentally diverging.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>109291</commentid>
    <comment_count>10</comment_count>
    <who name="Jer Noble">jer.noble</who>
    <bug_when>2014-07-25 18:14:05 +0000</bug_when>
    <thetext>&gt; I&apos;m pretty sure the &quot;SourceBuffer Monitoring&quot; algorithm needs to be updated.
&gt; It hasn&apos;t gotten much love in a long time and I wouldn&apos;t be surprised if
&gt; there are bugs lurking in there. readyState transitions should definitely be
&gt; driven of HTMLMediaElement.buffered ranges and not individual
&gt; SourceBuffer.buffered since during non-endOfStream playback the intersection
&gt; used should make sure that we don&apos;t try to play unbuffered regions. I&apos;ll
&gt; take a look at the algorithm and file a bugs for any problems with the
&gt; algorithm I see.

Okay great.  I&apos;ve filed Bug #26436 to track this separate issue.</thetext>
  </long_desc>
      
      

    </bug>

</bugzilla>