https://bugs.webkit.org/show_bug.cgi?id=135164
Reviewed by Eric Carlson.
Source/WebCore:
Test: media/media-source/media-source-abort-resets-parser.html
Use the -[AVStreamDataParser appendStreamData:withFlags:] to implement "resetting" the parser. In this case,
the parser isn't explicitly reset during resetParserState(), but rather a flag is set so that the next append
signals a data discontinuity, and the parser is reset at that point.
Because a previous append operation may be in-flight during this abort(), care must be taken to invalidate any
operations which may have already started on a background thread. So SourceBufferPrivateAVFObjC will use a
separate WeakPtrFactory for its append operations, will invalidate any outstanding WeakPtrs during an abort(),
and will block until the previous append() operation completes.
This will require the WebAVStreamDataParserListener object to occasionally have it's WeakPtr pointing back to the
SourceBufferPrivateAVFObjC to be reset after an abort(), so make that ivar an @property. Rather than passing a
RetainPtr to itself in all the callbacks it handles, the WebAVStreamDataParserListener can just pass in a copy
of its own WeakPtr (which may be invalidated during an abort()).
Break the distinct operations of "abort()" and "resetParserState()" into their own methods in SourceBufferPrivate
and all its subclasses.
* Modules/mediasource/SourceBuffer.cpp:
(WebCore::SourceBuffer::resetParserState):
(WebCore::SourceBuffer::abortIfUpdating):
* platform/graphics/SourceBufferPrivate.h:
* platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
* platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
(-[WebAVStreamDataParserListener streamDataParser:didParseStreamDataAsAsset:]):
(-[WebAVStreamDataParserListener streamDataParser:didParseStreamDataAsAsset:withDiscontinuity:]):
(-[WebAVStreamDataParserListener streamDataParser:didFailToParseStreamDataWithError:]):
(-[WebAVStreamDataParserListener streamDataParser:didProvideMediaData:forTrackID:mediaType:flags:]):
(-[WebAVStreamDataParserListener streamDataParser:didReachEndOfTrackWithTrackID:mediaType:]):
(-[WebAVStreamDataParserListener streamDataParserWillProvideContentKeyRequestInitializationData:forTrackID:]):
(-[WebAVStreamDataParserListener streamDataParser:didProvideContentKeyRequestInitializationData:forTrackID:]):
(WebCore::SourceBufferPrivateAVFObjC::SourceBufferPrivateAVFObjC):
(WebCore::SourceBufferPrivateAVFObjC::append):
(WebCore::SourceBufferPrivateAVFObjC::abort):
(WebCore::SourceBufferPrivateAVFObjC::resetParserState):
(-[WebAVStreamDataParserListener initWithParser:parent:]): Deleted.
* platform/graphics/gstreamer/SourceBufferPrivateGStreamer.cpp:
(WebCore::SourceBufferPrivateGStreamer::resetParserState):
* platform/graphics/gstreamer/SourceBufferPrivateGStreamer.h:
* platform/mock/mediasource/MockSourceBufferPrivate.cpp:
(WebCore::MockSourceBufferPrivate::resetParserState):
* platform/mock/mediasource/MockSourceBufferPrivate.h:
* platform/spi/mac/AVFoundationSPI.h:
LayoutTests:
* media/media-source/media-source-abort-resets-parser-expected.txt: Added.
* media/media-source/media-source-abort-resets-parser.html: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@206518
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2016-09-28 Jer Noble <jer.noble@apple.com>
+
+ [MSE][Mac] In SourceBufferPrivateAVFObjC::abort(), support reseting parser to the last appended initialization segment.
+ https://bugs.webkit.org/show_bug.cgi?id=135164
+
+ Reviewed by Eric Carlson.
+
+ * media/media-source/media-source-abort-resets-parser-expected.txt: Added.
+ * media/media-source/media-source-abort-resets-parser.html: Added.
+
2016-09-28 Alejandro G. Castro <alex@igalia.com>
Add WebIDL special operation support: serializer
--- /dev/null
+This tests the ability of the SourceBuffer to reset the parser after an abort(). A SourceBuffer in this state should be able to accept a new initialization segment or a new media segment.
+
+RUN(video.src = URL.createObjectURL(source))
+EVENT(sourceopen)
+RUN(source.duration = loader.duration())
+RUN(sourceBuffer = source.addSourceBuffer(loader.type()))
+RUN(sourceBuffer.appendBuffer(loader.initSegment()))
+EVENT(update)
+Append a partial media segment.
+RUN(sourceBuffer.appendBuffer(loader.mediaSegment(0).slice(0, loader.mediaSegment(0).byteLength / 2)))
+EVENT(update)
+Abort and append a new initialization segment.
+RUN(sourceBuffer.abort())
+RUN(sourceBuffer.appendBuffer(loader.initSegment()))
+EVENT(update)
+Append a partial media segment.
+RUN(sourceBuffer.appendBuffer(loader.mediaSegment(0).slice(0, loader.mediaSegment(0).byteLength / 2)))
+EVENT(update)
+Abort and append a new media segment.
+RUN(sourceBuffer.abort())
+RUN(sourceBuffer.appendBuffer(loader.mediaSegment(1)))
+EVENT(update)
+END OF TEST
+
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+ <title>media-source-abort-resets-parser</title>
+ <script src="media-source-loader.js"></script>
+ <script src="../video-test.js"></script>
+ <script>
+ var loader;
+ var source;
+ var sourceBuffer;
+
+ function runTest() {
+ findMediaElement();
+
+ loader = new MediaSourceLoader('content/test-fragmented-manifest.json');
+ loader.onload = mediaDataLoaded;
+ loader.onerror = mediaDataLoadingFailed;
+ }
+
+ function mediaDataLoadingFailed() {
+ failTest('Media data loading failed');
+ }
+
+ function mediaDataLoaded() {
+ source = new MediaSource();
+ waitForEvent('sourceopen', sourceOpen, false, false, source);
+ waitForEventAndFail('error');
+ run('video.src = URL.createObjectURL(source)');
+ }
+
+ function sourceOpen() {
+ run('source.duration = loader.duration()');
+ run('sourceBuffer = source.addSourceBuffer(loader.type())');
+ waitForEventOn(sourceBuffer, 'update', sourceInitialized, false, true);
+ run('sourceBuffer.appendBuffer(loader.initSegment())');
+ }
+
+ function sourceInitialized() {
+ waitForEventOn(sourceBuffer, 'update', partialMediaSegmentAppended1, false, true);
+ consoleWrite('Append a partial media segment.')
+ run('sourceBuffer.appendBuffer(loader.mediaSegment(0).slice(0, loader.mediaSegment(0).byteLength / 2))');
+ }
+
+ function partialMediaSegmentAppended1() {
+ consoleWrite('Abort and append a new initialization segment.')
+ run('sourceBuffer.abort()');
+ run('sourceBuffer.appendBuffer(loader.initSegment())');
+ waitForEventOn(sourceBuffer, 'update', initSegmentAppended, false, true);
+ }
+
+ function initSegmentAppended() {
+ waitForEventOn(sourceBuffer, 'update', partialMediaSegmentAppended2, false, true);
+ consoleWrite('Append a partial media segment.')
+ run('sourceBuffer.appendBuffer(loader.mediaSegment(0).slice(0, loader.mediaSegment(0).byteLength / 2))');
+ }
+
+ function partialMediaSegmentAppended2() {
+ consoleWrite('Abort and append a new media segment.')
+ run('sourceBuffer.abort()');
+ run('sourceBuffer.appendBuffer(loader.mediaSegment(1))');
+ waitForEventOn(sourceBuffer, 'update', endTest, false, true);
+ }
+ </script>
+</head>
+<body onload="runTest()">
+ <div>
+ This tests the ability of the SourceBuffer to reset the parser after an abort(). A SourceBuffer in this state should be able to accept
+ a new initialization segment or a new media segment.
+ </div>
+ <video controls></video>
+</body>
+</html>
\ No newline at end of file
+2016-09-28 Jer Noble <jer.noble@apple.com>
+
+ [MSE][Mac] In SourceBufferPrivateAVFObjC::abort(), support reseting parser to the last appended initialization segment.
+ https://bugs.webkit.org/show_bug.cgi?id=135164
+
+ Reviewed by Eric Carlson.
+
+ Test: media/media-source/media-source-abort-resets-parser.html
+
+ Use the -[AVStreamDataParser appendStreamData:withFlags:] to implement "resetting" the parser. In this case,
+ the parser isn't explicitly reset during resetParserState(), but rather a flag is set so that the next append
+ signals a data discontinuity, and the parser is reset at that point.
+
+ Because a previous append operation may be in-flight during this abort(), care must be taken to invalidate any
+ operations which may have already started on a background thread. So SourceBufferPrivateAVFObjC will use a
+ separate WeakPtrFactory for its append operations, will invalidate any outstanding WeakPtrs during an abort(),
+ and will block until the previous append() operation completes.
+
+ This will require the WebAVStreamDataParserListener object to occasionally have it's WeakPtr pointing back to the
+ SourceBufferPrivateAVFObjC to be reset after an abort(), so make that ivar an @property. Rather than passing a
+ RetainPtr to itself in all the callbacks it handles, the WebAVStreamDataParserListener can just pass in a copy
+ of its own WeakPtr (which may be invalidated during an abort()).
+
+ Break the distinct operations of "abort()" and "resetParserState()" into their own methods in SourceBufferPrivate
+ and all its subclasses.
+
+ * Modules/mediasource/SourceBuffer.cpp:
+ (WebCore::SourceBuffer::resetParserState):
+ (WebCore::SourceBuffer::abortIfUpdating):
+ * platform/graphics/SourceBufferPrivate.h:
+ * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
+ * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
+ (-[WebAVStreamDataParserListener streamDataParser:didParseStreamDataAsAsset:]):
+ (-[WebAVStreamDataParserListener streamDataParser:didParseStreamDataAsAsset:withDiscontinuity:]):
+ (-[WebAVStreamDataParserListener streamDataParser:didFailToParseStreamDataWithError:]):
+ (-[WebAVStreamDataParserListener streamDataParser:didProvideMediaData:forTrackID:mediaType:flags:]):
+ (-[WebAVStreamDataParserListener streamDataParser:didReachEndOfTrackWithTrackID:mediaType:]):
+ (-[WebAVStreamDataParserListener streamDataParserWillProvideContentKeyRequestInitializationData:forTrackID:]):
+ (-[WebAVStreamDataParserListener streamDataParser:didProvideContentKeyRequestInitializationData:forTrackID:]):
+ (WebCore::SourceBufferPrivateAVFObjC::SourceBufferPrivateAVFObjC):
+ (WebCore::SourceBufferPrivateAVFObjC::append):
+ (WebCore::SourceBufferPrivateAVFObjC::abort):
+ (WebCore::SourceBufferPrivateAVFObjC::resetParserState):
+ (-[WebAVStreamDataParserListener initWithParser:parent:]): Deleted.
+ * platform/graphics/gstreamer/SourceBufferPrivateGStreamer.cpp:
+ (WebCore::SourceBufferPrivateGStreamer::resetParserState):
+ * platform/graphics/gstreamer/SourceBufferPrivateGStreamer.h:
+ * platform/mock/mediasource/MockSourceBufferPrivate.cpp:
+ (WebCore::MockSourceBufferPrivate::resetParserState):
+ * platform/mock/mediasource/MockSourceBufferPrivate.h:
+ * platform/spi/mac/AVFoundationSPI.h:
+
2016-09-28 Michael Catanzaro <mcatanzaro@igalia.com>
[GTK] User agent should always claim to be Intel
// 7. Set append state to WAITING_FOR_SEGMENT.
m_appendState = WaitingForSegment;
- m_private->abort();
+ m_private->resetParserState();
}
void SourceBuffer::abort(ExceptionCode& ec)
// 4.1. Abort the buffer append algorithm if it is running.
m_appendBufferTimer.stop();
m_pendingAppendData.clear();
+ m_private->abort();
// 4.2. Set the updating attribute to false.
m_updating = false;
virtual void append(const unsigned char* data, unsigned length) = 0;
virtual void abort() = 0;
+ virtual void resetParserState() = 0;
virtual void removedFromMediaSource() = 0;
virtual MediaPlayer::ReadyState readyState() const = 0;
#if ENABLE(MEDIA_SOURCE) && USE(AVFOUNDATION)
#include "SourceBufferPrivate.h"
+#include <dispatch/group.h>
#include <dispatch/semaphore.h>
#include <wtf/Deque.h>
#include <wtf/HashMap.h>
void setClient(SourceBufferPrivateClient*) override;
void append(const unsigned char* data, unsigned length) override;
void abort() override;
+ void resetParserState() override;
void removedFromMediaSource() override;
MediaPlayer::ReadyState readyState() const override;
void setReadyState(MediaPlayer::ReadyState) override;
Vector<SourceBufferPrivateAVFObjCErrorClient*> m_errorClients;
WeakPtrFactory<SourceBufferPrivateAVFObjC> m_weakFactory;
+ WeakPtrFactory<SourceBufferPrivateAVFObjC> m_appendWeakFactory;
RetainPtr<AVStreamDataParser> m_parser;
RetainPtr<AVAsset> m_asset;
RetainPtr<WebAVSampleBufferErrorListener> m_errorListener;
RetainPtr<NSError> m_hdcpError;
OSObjectPtr<dispatch_semaphore_t> m_hasSessionSemaphore;
+ OSObjectPtr<dispatch_group_t> m_isAppendingGroup;
MediaSourcePrivateAVFObjC* m_mediaSource;
SourceBufferPrivateClient* m_client;
FloatSize m_cachedSize;
FloatSize m_currentSize;
bool m_parsingSucceeded;
+ bool m_parserStateWasReset { false };
int m_enabledVideoTrackID;
int m_protectedTrackID;
};
WeakPtr<WebCore::SourceBufferPrivateAVFObjC> _parent;
AVStreamDataParser* _parser;
}
+@property (assign) WeakPtr<WebCore::SourceBufferPrivateAVFObjC> parent;
- (id)initWithParser:(AVStreamDataParser*)parser parent:(WeakPtr<WebCore::SourceBufferPrivateAVFObjC>)parent;
@end
return self;
}
+@synthesize parent=_parent;
+
- (void)dealloc
{
[_parser setDelegate:nil];
UNUSED_PARAM(streamDataParser);
#endif
ASSERT(streamDataParser == _parser);
- RetainPtr<WebAVStreamDataParserListener> protectedSelf = self;
RetainPtr<AVAsset*> protectedAsset = asset;
- callOnMainThread([protectedSelf = WTFMove(protectedSelf), protectedAsset = WTFMove(protectedAsset)] {
- if (protectedSelf->_parent)
- protectedSelf->_parent->didParseStreamDataAsAsset(protectedAsset.get());
+ callOnMainThread([parent = _parent, protectedAsset = WTFMove(protectedAsset)] {
+ if (parent)
+ parent->didParseStreamDataAsAsset(protectedAsset.get());
});
}
UNUSED_PARAM(streamDataParser);
#endif
ASSERT(streamDataParser == _parser);
- RetainPtr<WebAVStreamDataParserListener> protectedSelf = self;
RetainPtr<AVAsset*> protectedAsset = asset;
- callOnMainThread([protectedSelf = WTFMove(protectedSelf), protectedAsset = WTFMove(protectedAsset)] {
- if (protectedSelf->_parent)
- protectedSelf->_parent->didParseStreamDataAsAsset(protectedAsset.get());
+ callOnMainThread([parent = _parent, protectedAsset = WTFMove(protectedAsset)] {
+ if (parent)
+ parent->didParseStreamDataAsAsset(protectedAsset.get());
});
}
UNUSED_PARAM(streamDataParser);
#endif
ASSERT(streamDataParser == _parser);
- RetainPtr<WebAVStreamDataParserListener> protectedSelf = self;
RetainPtr<NSError> protectedError = error;
- callOnMainThread([protectedSelf = WTFMove(protectedSelf), protectedError = WTFMove(protectedError)] {
- if (protectedSelf->_parent)
- protectedSelf->_parent->didFailToParseStreamDataWithError(protectedError.get());
+ callOnMainThread([parent = _parent, protectedError = WTFMove(protectedError)] {
+ if (parent)
+ parent->didFailToParseStreamDataWithError(protectedError.get());
});
}
UNUSED_PARAM(streamDataParser);
#endif
ASSERT(streamDataParser == _parser);
- RetainPtr<WebAVStreamDataParserListener> protectedSelf = self;
RetainPtr<CMSampleBufferRef> protectedSample = sample;
String mediaType = nsMediaType;
- callOnMainThread([protectedSelf = WTFMove(protectedSelf), protectedSample = WTFMove(protectedSample), trackID, mediaType, flags] {
- if (protectedSelf->_parent)
- protectedSelf->_parent->didProvideMediaDataForTrackID(trackID, protectedSample.get(), mediaType, flags);
+ callOnMainThread([parent = _parent, protectedSample = WTFMove(protectedSample), trackID, mediaType, flags] {
+ if (parent)
+ parent->didProvideMediaDataForTrackID(trackID, protectedSample.get(), mediaType, flags);
});
}
UNUSED_PARAM(streamDataParser);
#endif
ASSERT(streamDataParser == _parser);
- RetainPtr<WebAVStreamDataParserListener> protectedSelf = self;
String mediaType = nsMediaType;
- callOnMainThread([protectedSelf = WTFMove(protectedSelf), trackID, mediaType] {
- if (protectedSelf->_parent)
- protectedSelf->_parent->didReachEndOfTrackWithTrackID(trackID, mediaType);
+ callOnMainThread([parent = _parent, trackID, mediaType] {
+ if (parent)
+ parent->didReachEndOfTrackWithTrackID(trackID, mediaType);
});
}
// We must call synchronously to the main thread, as the AVStreamSession must be associated
// with the streamDataParser before the delegate method returns.
- RetainPtr<WebAVStreamDataParserListener> strongSelf = self;
- dispatch_sync(dispatch_get_main_queue(), [strongSelf, trackID]() {
- if (strongSelf->_parent)
- strongSelf->_parent->willProvideContentKeyRequestInitializationDataForTrackID(trackID);
+ dispatch_sync(dispatch_get_main_queue(), [parent = _parent, trackID]() {
+ if (parent)
+ parent->willProvideContentKeyRequestInitializationDataForTrackID(trackID);
});
}
UNUSED_PARAM(streamDataParser);
#endif
ASSERT(streamDataParser == _parser);
- RetainPtr<WebAVStreamDataParserListener> protectedSelf = self;
OSObjectPtr<dispatch_semaphore_t> hasSessionSemaphore = adoptOSObject(dispatch_semaphore_create(0));
- callOnMainThread([protectedSelf = WTFMove(protectedSelf), protectedInitData = RetainPtr<NSData>(initData), trackID, hasSessionSemaphore] {
- if (protectedSelf->_parent)
- protectedSelf->_parent->didProvideContentKeyRequestInitializationDataForTrackID(protectedInitData.get(), trackID, hasSessionSemaphore);
+ callOnMainThread([parent = _parent, protectedInitData = RetainPtr<NSData>(initData), trackID, hasSessionSemaphore] {
+ if (parent)
+ parent->didProvideContentKeyRequestInitializationDataForTrackID(protectedInitData.get(), trackID, hasSessionSemaphore);
});
dispatch_semaphore_wait(hasSessionSemaphore.get(), DISPATCH_TIME_FOREVER);
}
SourceBufferPrivateAVFObjC::SourceBufferPrivateAVFObjC(MediaSourcePrivateAVFObjC* parent)
: m_weakFactory(this)
+ , m_appendWeakFactory(this)
, m_parser(adoptNS([allocAVStreamDataParserInstance() init]))
, m_delegate(adoptNS([[WebAVStreamDataParserListener alloc] initWithParser:m_parser.get() parent:createWeakPtr()]))
, m_errorListener(adoptNS([[WebAVSampleBufferErrorListener alloc] initWithParent:this]))
+ , m_isAppendingGroup(adoptOSObject(dispatch_group_create()))
, m_mediaSource(parent)
, m_client(0)
, m_parsingSucceeded(true)
LOG(MediaSource, "SourceBufferPrivateAVFObjC::append(%p) - data:%p, length:%d", this, data, length);
RetainPtr<NSData> nsData = adoptNS([[NSData alloc] initWithBytes:data length:length]);
- WeakPtr<SourceBufferPrivateAVFObjC> weakThis = createWeakPtr();
+ WeakPtr<SourceBufferPrivateAVFObjC> weakThis = m_appendWeakFactory.createWeakPtr();
RetainPtr<AVStreamDataParser> parser = m_parser;
RetainPtr<WebAVStreamDataParserListener> delegate = m_delegate;
m_parsingSucceeded = true;
+ dispatch_group_enter(m_isAppendingGroup.get());
- dispatch_async(globalDataParserQueue(), [nsData, weakThis, parser, delegate] {
-
- [parser appendStreamData:nsData.get()];
+ dispatch_async(globalDataParserQueue(), [nsData, weakThis, parser, delegate, isAppendingGroup = m_isAppendingGroup, parserStateWasReset = m_parserStateWasReset] {
+ if (parserStateWasReset)
+ [parser appendStreamData:nsData.get() withFlags:AVStreamDataParserStreamDataDiscontinuity];
+ else
+ [parser appendStreamData:nsData.get()];
callOnMainThread([weakThis] {
if (weakThis)
weakThis->appendCompleted();
});
+ dispatch_group_leave(isAppendingGroup.get());
});
+ m_parserStateWasReset = false;
}
void SourceBufferPrivateAVFObjC::appendCompleted()
void SourceBufferPrivateAVFObjC::abort()
{
- // The parser does not have a mechanism for resetting to a clean state, so destroy and re-create it.
- // FIXME(135164): Support resetting parser to the last appended initialization segment.
- destroyParser();
+ // The parsing queue may be blocked waiting for the main thread to provide it a AVStreamSession. We
+ // were asked to abort, and that cancels all outstanding append operations. Without cancelling this
+ // semaphore, the m_isAppendingGroup wait operation will deadlock.
+ if (m_hasSessionSemaphore)
+ dispatch_semaphore_signal(m_hasSessionSemaphore.get());
+ dispatch_group_wait(m_isAppendingGroup.get(), DISPATCH_TIME_FOREVER);
+ m_appendWeakFactory.revokeAll();
+ m_delegate.get().parent = m_appendWeakFactory.createWeakPtr();
+}
- m_parser = adoptNS([allocAVStreamDataParserInstance() init]);
- m_delegate = adoptNS([[WebAVStreamDataParserListener alloc] initWithParser:m_parser.get() parent:createWeakPtr()]);
+void SourceBufferPrivateAVFObjC::resetParserState()
+{
+ m_parserStateWasReset = true;
}
void SourceBufferPrivateAVFObjC::destroyParser()
notImplemented();
}
+void SourceBufferPrivateGStreamer::resetParserState()
+{
+ notImplemented();
+}
+
void SourceBufferPrivateGStreamer::removedFromMediaSource()
{
m_client->removedFromMediaSource(this);
virtual void append(const unsigned char* data, unsigned length);
virtual void abort();
+ virtual void resetParserState();
virtual void removedFromMediaSource();
virtual MediaPlayer::ReadyState readyState() const;
{
}
+void MockSourceBufferPrivate::resetParserState()
+{
+}
+
void MockSourceBufferPrivate::removedFromMediaSource()
{
if (m_mediaSource)
void setClient(SourceBufferPrivateClient*) override;
void append(const unsigned char* data, unsigned length) override;
void abort() override;
+ void resetParserState() override;
void removedFromMediaSource() override;
MediaPlayer::ReadyState readyState() const override;
void setReadyState(MediaPlayer::ReadyState) override;
AVStreamDataParserOutputMediaDataReserved = 1 << 0
};
+typedef NS_ENUM(NSUInteger, AVStreamDataParserStreamDataFlags) {
+ AVStreamDataParserStreamDataDiscontinuity = 1 << 0,
+};
+
@interface AVStreamDataParser : NSObject
- (void)setDelegate:(nullable id<AVStreamDataParserOutputHandling>)delegate;
- (void)appendStreamData:(NSData *)data;
+- (void)appendStreamData:(NSData *)data withFlags:(AVStreamDataParserStreamDataFlags)flags;
- (void)setShouldProvideMediaData:(BOOL)shouldProvideMediaData forTrackID:(CMPersistentTrackID)trackID;
- (BOOL)shouldProvideMediaDataForTrackID:(CMPersistentTrackID)trackID;
- (void)providePendingMediaData;