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
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222

media / filters / frame_processor.h [blame]

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_FILTERS_FRAME_PROCESSOR_H_
#define MEDIA_FILTERS_FRAME_PROCESSOR_H_

#include <map>
#include <memory>

#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "media/base/media_export.h"
#include "media/base/media_log.h"
#include "media/base/stream_parser.h"
#include "media/filters/chunk_demuxer.h"
#include "media/filters/source_buffer_parse_warnings.h"

namespace media {

class MseTrackBuffer;

// Helper class that implements Media Source Extension's coded frame processing
// algorithm.
class MEDIA_EXPORT FrameProcessor {
 public:
  using UpdateDurationCB = base::RepeatingCallback<void(base::TimeDelta)>;

  FrameProcessor(UpdateDurationCB update_duration_cb, MediaLog* media_log);

  FrameProcessor(const FrameProcessor&) = delete;
  FrameProcessor& operator=(const FrameProcessor&) = delete;

  ~FrameProcessor();

  // This must be called exactly once, before doing any track buffer creation or
  // frame processing.
  void SetParseWarningCallback(SourceBufferParseWarningCB parse_warning_cb);

  // Get/set the current append mode, which if true means "sequence" and if
  // false means "segments".
  // See http://www.w3.org/TR/media-source/#widl-SourceBuffer-mode.
  bool sequence_mode() { return sequence_mode_; }
  void SetSequenceMode(bool sequence_mode);

  // Processes buffers in |buffer_queue_map|.
  // Returns true on success or false on failure which indicates decode error.
  // |append_window_start| and |append_window_end| correspond to the MSE spec's
  // similarly named source buffer attributes that are used in coded frame
  // processing.
  // Uses |*timestamp_offset| according to the coded frame processing algorithm,
  // including updating it as required in 'sequence' mode frame processing.
  bool ProcessFrames(const StreamParser::BufferQueueMap& buffer_queue_map,
                     base::TimeDelta append_window_start,
                     base::TimeDelta append_window_end,
                     base::TimeDelta* timestamp_offset);

  // Signals the frame processor to update its group start timestamp to be
  // |timestamp_offset| if it is in sequence append mode.
  void SetGroupStartTimestampIfInSequenceMode(base::TimeDelta timestamp_offset);

  // Adds a new track with unique track ID |id|.
  // If |id| has previously been added, returns false to indicate error.
  // Otherwise, returns true, indicating future ProcessFrames() will emit
  // frames for the track |id| to |stream|.
  bool AddTrack(StreamParser::TrackId id, ChunkDemuxerStream* stream);

  // A map that describes how track ids changed between init segment. Maps the
  // old track id for a new track id for the same track.
  using TrackIdChanges = std::map<StreamParser::TrackId, StreamParser::TrackId>;

  // Updates the internal mapping of TrackIds to track buffers. The input
  // parameter |track_id_changes| maps old track ids to new ones. The track ids
  // not present in the map must be assumed unchanged. Returns false if
  // remapping failed.
  bool UpdateTrackIds(const TrackIdChanges& track_id_changes);

  // Sets the need random access point flag on all track buffers to true.
  void SetAllTrackBuffersNeedRandomAccessPoint();

  // Resets state for the coded frame processing algorithm as described in steps
  // 2-5 of the MSE Reset Parser State algorithm described at
  // http://www.w3.org/TR/media-source/#sourcebuffer-reset-parser-state
  void Reset();

  // Must be called when the audio config is updated.  Used to manage when
  // the preroll buffer is cleared and the allowed "fudge" factor between
  // preroll buffers.
  void OnPossibleAudioConfigUpdate(const AudioDecoderConfig& config);

 private:
  friend class FrameProcessorTest;

  // If |track_buffers_| contains |id|, returns a pointer to the associated
  // MseTrackBuffer. Otherwise, returns NULL.
  MseTrackBuffer* FindTrack(StreamParser::TrackId id);

  // Signals all track buffers' streams that a coded frame group is starting
  // with |start_dts| and |start_pts|.
  void NotifyStartOfCodedFrameGroup(DecodeTimestamp start_dts,
                                    base::TimeDelta start_pts);

  // Helper that signals each track buffer to append any processed, but not yet
  // appended, frames to its stream. Returns true on success, or false if one or
  // more of the appends failed.
  bool FlushProcessedFrames();

  // Handles partial append window trimming of |buffer|.  Returns true if the
  // given |buffer| can be partially trimmed or have preroll added; otherwise,
  // returns false.
  //
  // If |buffer| overlaps |append_window_start|, the portion of |buffer| before
  // |append_window_start| will be marked for post-decode discard.  Further, if
  // |audio_preroll_buffer_| exists and abuts |buffer|, it will be set as
  // preroll on |buffer| and |audio_preroll_buffer_| will be cleared.  If the
  // preroll buffer does not abut |buffer|, it will be discarded unused.
  //
  // Likewise, if |buffer| overlaps |append_window_end|, the portion of |buffer|
  // after |append_window_end| will be marked for post-decode discard.
  //
  // If |buffer| lies entirely before |append_window_start|, and thus would
  // normally be discarded, |audio_preroll_buffer_| will be updated and the
  // method will return false. In this case, the updated preroll will be
  // |buffer| iff |buffer| is a keyframe, otherwise the preroll will be cleared.
  bool HandlePartialAppendWindowTrimming(
      base::TimeDelta append_window_start,
      base::TimeDelta append_window_end,
      scoped_refptr<StreamParserBuffer> buffer);

  // Enables rejection of audio frame streams with nonkeyframe timestamps that
  // do not monotonically increase since the last keyframe. Returns true if
  // |frame| appears to be in order, false if |frame|'s order is not supported.
  // |track_needs_random_access_point| should be the corresponding value for the
  // frame's track buffer. This helper should only be called when
  // |has_dependent_audio_frames_| is true, and only for an audio |frame|. This
  // method also uses and updates
  // |last_audio_pts_for_nonkeyframe_monotonicity_check_|.
  bool CheckAudioPresentationOrder(const StreamParserBuffer& frame,
                                   bool track_needs_random_access_point);

  // Helper that processes one frame with the coded frame processing algorithm.
  // Returns false on error or true on success.
  bool ProcessFrame(scoped_refptr<StreamParserBuffer> frame,
                    base::TimeDelta append_window_start,
                    base::TimeDelta append_window_end,
                    base::TimeDelta* timestamp_offset);

  // TrackId-indexed map of each track's stream.
  using TrackBuffersMap =
      std::map<StreamParser::TrackId, std::unique_ptr<MseTrackBuffer>>;
  TrackBuffersMap track_buffers_;

  // The last audio buffer seen by the frame processor that was removed because
  // it was entirely before the start of the append window.
  scoped_refptr<StreamParserBuffer> audio_preroll_buffer_;

  // The AudioDecoderConfig associated with buffers handed to ProcessFrames().
  // TODO(wolenetz): Associate current audio config and the derived
  // |has_dependent_audio_frames_|, |sample_duration_| and
  // |last_audio_pts_for_nonkeyframe_monotonicity_check_| with MseTrackBuffer
  // instead to enable handling more than 1 audio track in a SourceBuffer
  // simultaneously. See https://crbug.com/1081952.
  AudioDecoderConfig current_audio_config_;
  bool has_dependent_audio_frames_ = false;
  base::TimeDelta sample_duration_;

  // When |has_dependent_audio_frames_| is true, holds the PTS of the last
  // successfully processed audio frame. If the next audio frame is not a
  // keyframe and has lower PTS, the stream is invalid. Currently, the only
  // supported audio streams that could contain nonkeyframes are in-order (PTS
  // increases monotonically since last keyframe), e.g. xHE-AAC.
  base::TimeDelta last_audio_pts_for_nonkeyframe_monotonicity_check_ =
      kNoTimestamp;

  // The AppendMode of the associated SourceBuffer.
  // See SetSequenceMode() for interpretation of |sequence_mode_|.
  // Per http://www.w3.org/TR/media-source/#widl-SourceBuffer-mode:
  // Controls how a sequence of media segments are handled. This is initially
  // set to false ("segments").
  bool sequence_mode_ = false;

  // Tracks whether or not we need to notify all track buffers of a new coded
  // frame group (see https://w3c.github.io/media-source/#coded-frame-group)
  // upon the next successfully processed frame.  Set true initially and upon
  // detection of DTS discontinuity, parser reset during 'segments' mode, or
  // switching from 'sequence' to 'segments' mode.  Individual track buffers can
  // also be notified of an updated coded frame group start in edge cases. See
  // further comments in ProcessFrame().
  bool pending_notify_all_group_start_ = true;

  // Tracks the MSE coded frame processing variable of same name.
  // Initially kNoTimestamp, meaning "unset".
  base::TimeDelta group_start_timestamp_;

  // Tracks the MSE coded frame processing variable of same name. It stores the
  // highest coded frame end timestamp across all coded frames in the current
  // coded frame group. It is set to 0 when the SourceBuffer object is created
  // and gets updated by ProcessFrames().
  base::TimeDelta group_end_timestamp_;

  const UpdateDurationCB update_duration_cb_;

  // MediaLog for reporting messages and properties to debug content and engine.
  raw_ptr<MediaLog> media_log_;

  // Callback for reporting problematic conditions that are not necessarily
  // errors.
  SourceBufferParseWarningCB parse_warning_cb_;

  // Counters that limit spam to |media_log_| for frame processor warnings.
  int num_dropped_preroll_warnings_ = 0;
  int num_audio_non_keyframe_warnings_ = 0;
  int num_muxed_sequence_mode_warnings_ = 0;
  int num_skipped_empty_frame_warnings_ = 0;
  int num_partial_discard_warnings_ = 0;
  int num_dropped_frame_warnings_ = 0;
};

}  // namespace media

#endif  // MEDIA_FILTERS_FRAME_PROCESSOR_H_