Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(385)

Side by Side Diff: media/base/pipeline.h

Issue 1658303002: Create abstract interface for media::Pipeline. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix FakeMediaSource includes. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/base/media_log.cc ('k') | media/base/pipeline.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #ifndef MEDIA_BASE_PIPELINE_H_ 5 #ifndef MEDIA_BASE_PIPELINE_H_
6 #define MEDIA_BASE_PIPELINE_H_ 6 #define MEDIA_BASE_PIPELINE_H_
7 7
8 #include "base/gtest_prod_util.h"
9 #include "base/macros.h"
10 #include "base/memory/ref_counted.h" 8 #include "base/memory/ref_counted.h"
11 #include "base/memory/weak_ptr.h" 9 #include "base/time/time.h"
12 #include "base/synchronization/lock.h"
13 #include "base/threading/thread_checker.h"
14 #include "base/time/default_tick_clock.h"
15 #include "media/base/buffering_state.h" 10 #include "media/base/buffering_state.h"
16 #include "media/base/cdm_context.h" 11 #include "media/base/cdm_context.h"
17 #include "media/base/demuxer.h"
18 #include "media/base/media_export.h" 12 #include "media/base/media_export.h"
19 #include "media/base/pipeline_status.h" 13 #include "media/base/pipeline_status.h"
20 #include "media/base/ranges.h" 14 #include "media/base/ranges.h"
21 #include "media/base/serial_runner.h"
22 #include "media/base/text_track.h" 15 #include "media/base/text_track.h"
23 #include "media/base/video_rotation.h" 16 #include "media/base/video_rotation.h"
24 #include "ui/gfx/geometry/size.h" 17 #include "ui/gfx/geometry/size.h"
25 18
26 namespace base {
27 class SingleThreadTaskRunner;
28 class TimeDelta;
29 }
30
31 namespace media { 19 namespace media {
32 20
33 class MediaLog; 21 class Demuxer;
34 class Renderer; 22 class Renderer;
35 class TextRenderer;
36 class TextTrackConfig;
37 class TimeDeltaInterpolator;
38 class VideoFrame; 23 class VideoFrame;
39 24
40 // Metadata describing a pipeline once it has been initialized. 25 // Metadata describing a pipeline once it has been initialized.
41 struct PipelineMetadata { 26 struct PipelineMetadata {
42 PipelineMetadata() 27 PipelineMetadata()
43 : has_audio(false), has_video(false), video_rotation(VIDEO_ROTATION_0) {} 28 : has_audio(false), has_video(false), video_rotation(VIDEO_ROTATION_0) {}
44 29
45 bool has_audio; 30 bool has_audio;
46 bool has_video; 31 bool has_video;
47 gfx::Size natural_size; 32 gfx::Size natural_size;
48 VideoRotation video_rotation; 33 VideoRotation video_rotation;
49 base::Time timeline_offset; 34 base::Time timeline_offset;
50 }; 35 };
51 36
52 typedef base::Callback<void(PipelineMetadata)> PipelineMetadataCB; 37 typedef base::Callback<void(PipelineMetadata)> PipelineMetadataCB;
53 38
54 // Pipeline runs the media pipeline. Filters are created and called on the 39 class MEDIA_EXPORT Pipeline {
55 // task runner injected into this object. Pipeline works like a state
56 // machine to perform asynchronous initialization, pausing, seeking and playing.
57 //
58 // Here's a state diagram that describes the lifetime of this object.
59 //
60 // [ *Created ] [ Any State ]
61 // | Start() | Stop() / SetError()
62 // V V
63 // [ InitXXX (for each filter) ] [ Stopping ]
64 // | |
65 // V V
66 // [ Playing ] <---------. [ Stopped ]
67 // | | Seek() |
68 // | V |
69 // | [ Seeking ] ----'
70 // | ^
71 // | Suspend() |
72 // V |
73 // [ Suspending ] |
74 // | |
75 // V |
76 // [ Suspended ] |
77 // | Resume() |
78 // V |
79 // [ Resuming ] ---------'
80 //
81 // Initialization is a series of state transitions from "Created" through each
82 // filter initialization state. When all filter initialization states have
83 // completed, we simulate a Seek() to the beginning of the media to give filters
84 // a chance to preroll. From then on the normal Seek() transitions are carried
85 // out and we start playing the media.
86 //
87 // If any error ever happens, this object will transition to the "Error" state
88 // from any state. If Stop() is ever called, this object will transition to
89 // "Stopped" state.
90 //
91 // TODO(sandersd): It should be possible to pass through Suspended when going
92 // from InitDemuxer to InitRenderer, thereby eliminating the Resuming state.
93 // Some annoying differences between the two paths need to be removed first.
94 class MEDIA_EXPORT Pipeline : public DemuxerHost {
95 public: 40 public:
96 // Used to paint VideoFrame. 41 // Used to paint VideoFrame.
97 typedef base::Callback<void(const scoped_refptr<VideoFrame>&)> PaintCB; 42 typedef base::Callback<void(const scoped_refptr<VideoFrame>&)> PaintCB;
98 43
99 // Constructs a media pipeline that will execute on |task_runner|.
100 Pipeline(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
101 MediaLog* media_log);
102 ~Pipeline() override;
103
104 // Build a pipeline to using the given |demuxer| and |renderer| to construct 44 // Build a pipeline to using the given |demuxer| and |renderer| to construct
105 // a filter chain, executing |seek_cb| when the initial seek has completed. 45 // a filter chain, executing |seek_cb| when the initial seek has completed.
106 // 46 //
107 // The following permanent callbacks will be executed as follows up until 47 // The following permanent callbacks will be executed as follows up until
108 // Stop() has completed: 48 // Stop() has completed:
109 // |ended_cb| will be executed whenever the media reaches the end. 49 // |ended_cb| will be executed whenever the media reaches the end.
110 // |error_cb| will be executed whenever an error occurs but hasn't been 50 // |error_cb| will be executed whenever an error occurs but hasn't been
111 // reported already through another callback. 51 // reported already through another callback.
112 // |metadata_cb| will be executed when the content duration, container video 52 // |metadata_cb| will be executed when the content duration, container video
113 // size, start time, and whether the content has audio and/or 53 // size, start time, and whether the content has audio and/or
114 // video in supported formats are known. 54 // video in supported formats are known.
115 // |buffering_state_cb| will be executed whenever there are changes in the 55 // |buffering_state_cb| will be executed whenever there are changes in the
116 // overall buffering state of the pipeline. 56 // overall buffering state of the pipeline.
117 // |duration_change_cb| optional callback that will be executed whenever the 57 // |duration_change_cb| optional callback that will be executed whenever the
118 // presentation duration changes. 58 // presentation duration changes.
119 // |add_text_track_cb| will be executed whenever a text track is added. 59 // |add_text_track_cb| will be executed whenever a text track is added.
120 // |waiting_for_decryption_key_cb| will be executed whenever the key needed 60 // |waiting_for_decryption_key_cb| will be executed whenever the key needed
121 // to decrypt the stream is not available. 61 // to decrypt the stream is not available.
122 // It is an error to call this method after the pipeline has already started. 62 // It is an error to call this method after the pipeline has already started.
123 void Start(Demuxer* demuxer, 63 virtual void Start(Demuxer* demuxer,
124 scoped_ptr<Renderer> renderer, 64 scoped_ptr<Renderer> renderer,
125 const base::Closure& ended_cb, 65 const base::Closure& ended_cb,
126 const PipelineStatusCB& error_cb, 66 const PipelineStatusCB& error_cb,
127 const PipelineStatusCB& seek_cb, 67 const PipelineStatusCB& seek_cb,
128 const PipelineMetadataCB& metadata_cb, 68 const PipelineMetadataCB& metadata_cb,
129 const BufferingStateCB& buffering_state_cb, 69 const BufferingStateCB& buffering_state_cb,
130 const base::Closure& duration_change_cb, 70 const base::Closure& duration_change_cb,
131 const AddTextTrackCB& add_text_track_cb, 71 const AddTextTrackCB& add_text_track_cb,
132 const base::Closure& waiting_for_decryption_key_cb); 72 const base::Closure& waiting_for_decryption_key_cb) = 0;
133 73
134 // Asynchronously stops the pipeline, executing |stop_cb| when the pipeline 74 // Asynchronously stops the pipeline, executing |stop_cb| when the pipeline
135 // teardown has completed. 75 // teardown has completed.
136 // 76 //
137 // Stop() must complete before destroying the pipeline. It it permissible to 77 // Stop() must complete before destroying the pipeline. It it permissible to
138 // call Stop() at any point during the lifetime of the pipeline. 78 // call Stop() at any point during the lifetime of the pipeline.
139 // 79 //
140 // It is safe to delete the pipeline during the execution of |stop_cb|. 80 // It is safe to delete the pipeline during the execution of |stop_cb|.
141 void Stop(const base::Closure& stop_cb); 81 virtual void Stop(const base::Closure& stop_cb) = 0;
142 82
143 // Attempt to seek to the position specified by time. |seek_cb| will be 83 // Attempt to seek to the position specified by time. |seek_cb| will be
144 // executed when the all filters in the pipeline have processed the seek. 84 // executed when the all filters in the pipeline have processed the seek.
145 // 85 //
146 // Clients are expected to call GetMediaTime() to check whether the seek 86 // Clients are expected to call GetMediaTime() to check whether the seek
147 // succeeded. 87 // succeeded.
148 // 88 //
149 // It is an error to call this method if the pipeline has not started or 89 // It is an error to call this method if the pipeline has not started or
150 // is suspended. 90 // has been suspended.
151 void Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb); 91 virtual void Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) = 0;
92
93 // Suspends the pipeline, discarding the current renderer.
94 //
95 // While suspended, GetMediaTime() returns the presentation timestamp of the
96 // last rendered frame.
97 //
98 // It is an error to call this method if the pipeline has not started or is
99 // seeking.
100 virtual void Suspend(const PipelineStatusCB& suspend_cb) = 0;
101
102 // Resume the pipeline with a new renderer, and initialize it with a seek.
103 //
104 // It is an error to call this method if the pipeline has not finished
105 // suspending.
106 virtual void Resume(scoped_ptr<Renderer> renderer,
107 base::TimeDelta timestamp,
108 const PipelineStatusCB& seek_cb) = 0;
152 109
153 // Returns true if the pipeline has been started via Start(). If IsRunning() 110 // Returns true if the pipeline has been started via Start(). If IsRunning()
154 // returns true, it is expected that Stop() will be called before destroying 111 // returns true, it is expected that Stop() will be called before destroying
155 // the pipeline. 112 // the pipeline.
156 bool IsRunning() const; 113 virtual bool IsRunning() const = 0;
157 114
158 // Gets the current playback rate of the pipeline. When the pipeline is 115 // Gets the current playback rate of the pipeline. When the pipeline is
159 // started, the playback rate will be 0.0. A rate of 1.0 indicates 116 // started, the playback rate will be 0.0. A rate of 1.0 indicates
160 // that the pipeline is rendering the media at the standard rate. Valid 117 // that the pipeline is rendering the media at the standard rate. Valid
161 // values for playback rate are >= 0.0. 118 // values for playback rate are >= 0.0.
162 double GetPlaybackRate() const; 119 virtual double GetPlaybackRate() const = 0;
163 120
164 // Attempt to adjust the playback rate. Setting a playback rate of 0.0 pauses 121 // Attempt to adjust the playback rate. Setting a playback rate of 0.0 pauses
165 // all rendering of the media. A rate of 1.0 indicates a normal playback 122 // all rendering of the media. A rate of 1.0 indicates a normal playback
166 // rate. Values for the playback rate must be greater than or equal to 0.0. 123 // rate. Values for the playback rate must be greater than or equal to 0.0.
167 // 124 //
168 // TODO(scherkus): What about maximum rate? Does HTML5 specify a max? 125 // TODO(scherkus): What about maximum rate? Does HTML5 specify a max?
169 void SetPlaybackRate(double playback_rate); 126 virtual void SetPlaybackRate(double playback_rate) = 0;
170
171 // Suspend the pipeline, discarding the current renderer.
172 //
173 // While suspended, GetMediaTime() returns the presentation timestamp of the
174 // last rendered frame.
175 //
176 // It is an error to call this method if the pipeline has not started or is
177 // seeking.
178 void Suspend(const PipelineStatusCB& suspend_cb);
179
180 // Resume the pipeline with a new renderer, and initialize it with a seek.
181 void Resume(scoped_ptr<Renderer> renderer,
182 base::TimeDelta timestamp,
183 const PipelineStatusCB& seek_cb);
184 127
185 // Gets the current volume setting being used by the audio renderer. When 128 // Gets the current volume setting being used by the audio renderer. When
186 // the pipeline is started, this value will be 1.0f. Valid values range 129 // the pipeline is started, this value will be 1.0f. Valid values range
187 // from 0.0f to 1.0f. 130 // from 0.0f to 1.0f.
188 float GetVolume() const; 131 virtual float GetVolume() const = 0;
189 132
190 // Attempt to set the volume of the audio renderer. Valid values for volume 133 // Attempt to set the volume of the audio renderer. Valid values for volume
191 // range from 0.0f (muted) to 1.0f (full volume). This value affects all 134 // range from 0.0f (muted) to 1.0f (full volume). This value affects all
192 // channels proportionately for multi-channel audio streams. 135 // channels proportionately for multi-channel audio streams.
193 void SetVolume(float volume); 136 virtual void SetVolume(float volume) = 0;
194 137
195 // Returns the current media playback time, which progresses from 0 until 138 // Returns the current media playback time, which progresses from 0 until
196 // GetMediaDuration(). 139 // GetMediaDuration().
197 base::TimeDelta GetMediaTime() const; 140 virtual base::TimeDelta GetMediaTime() const = 0;
198 141
199 // Get approximate time ranges of buffered media. 142 // Get approximate time ranges of buffered media.
200 Ranges<base::TimeDelta> GetBufferedTimeRanges() const; 143 virtual Ranges<base::TimeDelta> GetBufferedTimeRanges() const = 0;
201 144
202 // Get the duration of the media in microseconds. If the duration has not 145 // Get the duration of the media in microseconds. If the duration has not
203 // been determined yet, then returns 0. 146 // been determined yet, then returns 0.
204 base::TimeDelta GetMediaDuration() const; 147 virtual base::TimeDelta GetMediaDuration() const = 0;
205 148
206 // Return true if loading progress has been made since the last time this 149 // Return true if loading progress has been made since the last time this
207 // method was called. 150 // method was called.
208 bool DidLoadingProgress(); 151 virtual bool DidLoadingProgress() = 0;
209 152
210 // Gets the current pipeline statistics. 153 // Gets the current pipeline statistics.
211 PipelineStatistics GetStatistics() const; 154 virtual PipelineStatistics GetStatistics() const = 0;
212 155
213 void SetCdm(CdmContext* cdm_context, const CdmAttachedCB& cdm_attached_cb); 156 virtual void SetCdm(CdmContext* cdm_context,
214 157 const CdmAttachedCB& cdm_attached_cb) = 0;
215 void SetErrorForTesting(PipelineStatus status);
216 bool HasWeakPtrsForTesting() const;
217
218 private:
219 FRIEND_TEST_ALL_PREFIXES(PipelineTest, GetBufferedTimeRanges);
220 FRIEND_TEST_ALL_PREFIXES(PipelineTest, EndedCallback);
221 FRIEND_TEST_ALL_PREFIXES(PipelineTest, AudioStreamShorterThanVideo);
222 friend class MediaLog;
223
224 // Pipeline states, as described above.
225 enum State {
226 kCreated,
227 kInitDemuxer,
228 kInitRenderer,
229 kSeeking,
230 kPlaying,
231 kStopping,
232 kStopped,
233 kSuspending,
234 kSuspended,
235 kResuming,
236 };
237
238 // Updates |state_|. All state transitions should use this call.
239 void SetState(State next_state);
240
241 static const char* GetStateString(State state);
242 State GetNextState() const;
243
244 // Helper method that runs & resets |seek_cb_| and resets |seek_timestamp_|
245 // and |seek_pending_|.
246 void FinishSeek();
247
248 // DemuxerHost implementaion.
249 void OnBufferedTimeRangesChanged(
250 const Ranges<base::TimeDelta>& ranges) override;
251 void SetDuration(base::TimeDelta duration) override;
252 void OnDemuxerError(PipelineStatus error) override;
253 void AddTextStream(DemuxerStream* text_stream,
254 const TextTrackConfig& config) override;
255 void RemoveTextStream(DemuxerStream* text_stream) override;
256
257 // Callback executed when a rendering error happened, initiating the teardown
258 // sequence.
259 void OnError(PipelineStatus error);
260
261 // Callback executed by filters to update statistics.
262 void OnUpdateStatistics(const PipelineStatistics& stats_delta);
263
264 // The following "task" methods correspond to the public methods, but these
265 // methods are run as the result of posting a task to the Pipeline's
266 // task runner.
267 void StartTask();
268
269 // Suspends the pipeline, discarding the current renderer.
270 void SuspendTask(const PipelineStatusCB& suspend_cb);
271
272 // Resumes the pipeline with a new renderer, and initializes it with a seek.
273 void ResumeTask(scoped_ptr<Renderer> renderer,
274 base::TimeDelta timestamp,
275 const PipelineStatusCB& seek_sb);
276
277 // Stops and destroys all filters, placing the pipeline in the kStopped state.
278 void StopTask(const base::Closure& stop_cb);
279
280 // Carries out stopping and destroying all filters, placing the pipeline in
281 // the kStopped state.
282 void ErrorChangedTask(PipelineStatus error);
283
284 // Carries out notifying filters that the playback rate has changed.
285 void PlaybackRateChangedTask(double playback_rate);
286
287 // Carries out notifying filters that the volume has changed.
288 void VolumeChangedTask(float volume);
289
290 // Carries out notifying filters that we are seeking to a new timestamp.
291 void SeekTask(base::TimeDelta time, const PipelineStatusCB& seek_cb);
292
293 // Carries out setting the |cdm_context| in |renderer_|, and then fires
294 // |cdm_attached_cb| with the result. If |renderer_| is null,
295 // |cdm_attached_cb| will be fired immediately with true, and |cdm_context|
296 // will be set in |renderer_| later when |renderer_| is created.
297 void SetCdmTask(CdmContext* cdm_context,
298 const CdmAttachedCB& cdm_attached_cb);
299
300 // Callbacks executed when a renderer has ended.
301 void OnRendererEnded();
302 void OnTextRendererEnded();
303 void RunEndedCallbackIfNeeded();
304
305 scoped_ptr<TextRenderer> CreateTextRenderer();
306
307 // Carries out adding a new text stream to the text renderer.
308 void AddTextStreamTask(DemuxerStream* text_stream,
309 const TextTrackConfig& config);
310
311 // Carries out removing a text stream from the text renderer.
312 void RemoveTextStreamTask(DemuxerStream* text_stream);
313
314 // Callbacks executed when a text track is added.
315 void OnAddTextTrack(const TextTrackConfig& config,
316 const AddTextTrackDoneCB& done_cb);
317
318 // Kicks off initialization for each media object, executing |done_cb| with
319 // the result when completed.
320 void InitializeDemuxer(const PipelineStatusCB& done_cb);
321 void InitializeRenderer(const PipelineStatusCB& done_cb);
322
323 void StateTransitionTask(PipelineStatus status);
324
325 // Initiates an asynchronous pause-flush-seek-preroll call sequence
326 // executing |done_cb| with the final status when completed.
327 void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb);
328
329 // Initiates an asynchronous pause-flush-stop call sequence executing
330 // |done_cb| when completed.
331 void DoStop(const PipelineStatusCB& done_cb);
332 void OnStopCompleted(PipelineStatus status);
333
334 void ReportMetadata();
335
336 void BufferingStateChanged(BufferingState new_buffering_state);
337
338 // Task runner used to execute pipeline tasks.
339 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
340
341 // MediaLog to which to log events.
342 scoped_refptr<MediaLog> media_log_;
343
344 // Lock used to serialize access for the following data members.
345 mutable base::Lock lock_;
346
347 // Whether or not the pipeline is running.
348 bool running_;
349
350 // Amount of available buffered data as reported by |demuxer_|.
351 Ranges<base::TimeDelta> buffered_time_ranges_;
352
353 // True when OnBufferedTimeRangesChanged() has been called more recently than
354 // DidLoadingProgress().
355 bool did_loading_progress_;
356
357 // Current volume level (from 0.0f to 1.0f). This value is set immediately
358 // via SetVolume() and a task is dispatched on the task runner to notify the
359 // filters.
360 float volume_;
361
362 // Current playback rate (>= 0.0). This value is set immediately via
363 // SetPlaybackRate() and a task is dispatched on the task runner to notify
364 // the filters.
365 double playback_rate_;
366
367 // Current duration as reported by |demuxer_|.
368 base::TimeDelta duration_;
369
370 // Status of the pipeline. Initialized to PIPELINE_OK which indicates that
371 // the pipeline is operating correctly. Any other value indicates that the
372 // pipeline is stopped or is stopping. Clients can call the Stop() method to
373 // reset the pipeline state, and restore this to PIPELINE_OK.
374 PipelineStatus status_;
375
376 // The following data members are only accessed by tasks posted to
377 // |task_runner_|.
378
379 // Member that tracks the current state.
380 State state_;
381
382 // The timestamp to start playback from after starting/seeking/resuming has
383 // completed.
384 base::TimeDelta start_timestamp_;
385
386 // The media timestamp to return while the pipeline is suspended. Otherwise
387 // set to kNoTimestamp().
388 base::TimeDelta suspend_timestamp_;
389
390 // Whether we've received the audio/video/text ended events.
391 bool renderer_ended_;
392 bool text_renderer_ended_;
393
394 // Temporary callback used for Start(), Seek(), and Resume().
395 PipelineStatusCB seek_cb_;
396
397 // Temporary callback used for Stop().
398 base::Closure stop_cb_;
399
400 // Temporary callback used for Suspend().
401 PipelineStatusCB suspend_cb_;
402
403 // Permanent callbacks passed in via Start().
404 base::Closure ended_cb_;
405 PipelineStatusCB error_cb_;
406 PipelineMetadataCB metadata_cb_;
407 BufferingStateCB buffering_state_cb_;
408 base::Closure duration_change_cb_;
409 AddTextTrackCB add_text_track_cb_;
410 base::Closure waiting_for_decryption_key_cb_;
411
412 // Holds the initialized demuxer. Used for seeking. Owned by client.
413 Demuxer* demuxer_;
414
415 // Holds the initialized renderers. Used for setting the volume,
416 // playback rate, and determining when playback has finished.
417 scoped_ptr<Renderer> renderer_;
418 scoped_ptr<TextRenderer> text_renderer_;
419
420 PipelineStatistics statistics_;
421
422 scoped_ptr<SerialRunner> pending_callbacks_;
423
424 // CdmContext to be used to decrypt (and decode) encrypted stream in this
425 // pipeline. Non-null only when SetCdm() is called and the pipeline has not
426 // been started. Then during Start(), this value will be set on |renderer_|.
427 CdmContext* pending_cdm_context_;
428
429 base::ThreadChecker thread_checker_;
430
431 // NOTE: Weak pointers must be invalidated before all other member variables.
432 base::WeakPtrFactory<Pipeline> weak_factory_;
433
434 DISALLOW_COPY_AND_ASSIGN(Pipeline);
435 }; 158 };
436 159
437 } // namespace media 160 } // namespace media
438 161
439 #endif // MEDIA_BASE_PIPELINE_H_ 162 #endif // MEDIA_BASE_PIPELINE_H_
OLDNEW
« no previous file with comments | « media/base/media_log.cc ('k') | media/base/pipeline.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698