Media3 آرتیفکتهای آزمایشی اختصاصی (media3-test-utils و
media3-test-utils-robolectric) را ارائه میدهد تا به شما کمک کند بازپخش رسانه، انتقالهای حالت پخشکننده، مؤلفههای میانای کاربر، و منابع رسانه سفارشی را هم در آزمایشهای JVM محلی و هم در آزمایشهای ابزار دقیق دستگاه آزمایش کنید.
این صفحه روشهای توصیهشده برای آزمایش برنامههایی را که از ExoPlayer استفاده میکنند شرح میدهد
و نحوه استفاده از ابزارهای کمکی آزمایشی Media3 را در هر محیط آزمایشی نشان میدهد.
توصیههای آزمایش
هنگام نوشتن آزمایشهای خودکار برای بازپخش رسانه، این اصول اصلی را دنبال کنید:
- ترجیح دادن مؤلفههای واقعی یا Media3 fakes بر mocks: از mocking
PlayerیاExoPlayerبا چارچوبهایی مثل Mockito اجتناب کنید.Playerدارای ثابتهای حالت و قراردادهای شنونده است که تقلیدها اجرا نمیکنند، که میتواند آزمایشها را شکننده کند یا اشکالها را پنهان کند. درعوض، از یک نمونه واقعیExoPlayer،FakePlayer، یا موارد جعلی مانندFakeMediaSourceوFakeDataSourceاستفاده کنید. درصورت نیاز، برای درستیسنجی فراخوانیهای بازگشت تماس سفارشی، از شبیهسازی استفاده کنید. - Wait deterministically instead of sleeping:
ExoPlayerperforms media loading and playback asynchronously on an internal thread. از تماسهای دلخواهThread.sleepکه باعث کندی و ناپایداری آزمایشها میشود اجتناب کنید. ازTestPlayerRunHelperدر آزمایشهای Robolectric و تعلیق افزونههایPlayerیاPlayerFenceدر آزمایشهای ابزار دقیق استفاده کنید. این کمککنندهها منتظر شرایط صریح پخشکننده میمانند و اگر خطای بازپخش رخ دهد، فوراً ناموفق میشوند. - دسترسی به پخشکننده در رشته برنامه آن: نمونههای
ExoPlayerو کمککنندههای منتظر باید در رشته برنامه پخشکنندهLooperفراخوانی شوند (معمولاً رشته اصلی). برای اطلاعات بیشتر، الزامات رشته ExoPlayer را ببینید.
محیط آزمایشی مناسب را انتخاب کنید
لایههای مختلف ادغام رسانه شما از محیطهای آزمایشی مختلف بهره میبرند:
| هدف آزمایشی | محیط توصیهشده | برنامههای کمکی کلیدی Media3 |
|---|---|---|
مؤلفههای میانای کاربر (PlayerView، Compose UI)، ViewModels، یا بازخوانهای MediaSession |
آزمایشهای Robolectric محلی (src/test) یا آزمایشهای واسط کاربر ابزاری (src/androidTest) |
FakePlayer |
ادغام ExoPlayer، انتقال فهرست پخش، تجزیهوتحلیل، و منطق سفارشی MediaSource یا DataSource |
آزمایشهای Robolectric محلی (src/test) |
TestExoPlayerBuilder، TestPlayerRunHelper، FakeMediaSource |
رمزگشایی MediaCodec، پرداخت Surface، خروجی صوتی، و مدیریت حقوق دیجیتال سختافزار واقعی |
آزمایشهای ابزارگری در دستگاههای فیزیکی (src/androidTest) |
ExoPlayer.Builder، Player.await* افزونه، PlayerFence |
درصورت امکان، آزمایشهای بازپخش ابزار دقیق را بهجای شبیهسازها در دستگاههای فیزیکی اجرا کنید، زیرا پیادهسازیهای پشته رسانه شبیهساز متفاوت است. برای اطلاعات بیشتر درباره پشتیبانی شبیهساز، راهنمای دستگاههای پشتیبانیشده را ببینید.
افزودن وابستگیهای آزمایشی
مصنوعات آزمایشی Media3 را بااستفاده از همان نسخه سایر ماژولهای Media3 به بلوک dependencies فایل build.gradle.kts یا build.gradle ماژول خود اضافه کنید:
کاتلین
testImplementation("androidx.media3:media3-test-utils:1.11.1")
testImplementation("androidx.media3:media3-test-utils-robolectric:1.11.1")
androidTestImplementation("androidx.media3:media3-test-utils:1.11.1")
شیک
testImplementation "androidx.media3:media3-test-utils:1.11.1"
testImplementation "androidx.media3:media3-test-utils-robolectric:1.11.1"
androidTestImplementation "androidx.media3:media3-test-utils:1.11.1"
این دو واحد مسئولیتها را براساس محیط زمان اجرا تقسیم میکنند:
-
media3-test-utils: ابزارهای کمکی بینمحیطی برای هر دو آزمایش محلی و ابزاردار، ازجملهTestExoPlayerBuilder،PlayerFence(و افزونههای KotlinPlayer.await*)،FakePlayer،FakeClock،FakeMediaSource،FakeDataSource،DataSourceContractTest، وMedia3FlagsRule. media3-test-utils-robolectric: برنامههای کمکی برای آزمایشهای Robolectric در JVM محلی، ازجملهTestPlayerRunHelper،RobolectricUtil،ShadowMediaCodecConfig،CapturingRenderersFactory، وPlaybackOutput.
مطمئن شوید که واحد شما شامل وابستگیهای چارچوب آزمون برای محیطهای آزمون شما نیز باشد:
- Robolectric:
org.robolectric:robolectric. هنگام بار کردن داراییهای محلی،android.testOptions.unitTests.isIncludeAndroidResources = trueرا تنظیم کنید. - AndroidX Test:
androidx.test:runner،androidx.test:core، وandroidx.test.ext:junit. - روالهای مشترک Kotlin در رشته اصلی:
org.jetbrains.kotlinx:kotlinx-coroutines-android.
موافقت با میاناهای برنامهسازی کاربردی آزمایشی ناپایدار
کلاسها و روشها در media3-test-utils و media3-test-utils-robolectric
با @UnstableApi حاشیهنویسی شدهاند. اگرچه Media3 قصد دارد
کمککنندههای آزمایشی پرکاربرد را در نسخههای مختلف ارگونومیک نگه دارد، اما ابزارهای آزمایشی از
همان قوانین پایداری سازگاری دودویی مانند میاناهای برنامهسازی کاربردی بازپخش تولید پیروی نمیکنند.
در مجموعههای منبع آزمایشی، کلاسها یا روشهای آزمایشی را با
@androidx.annotation.OptIn حاشیهنویسی کنید، برای بستههای آزمایشیتان
package-info را اضافه کنید، یا lint.xml را برای منابع آزمایشی پیکربندی کنید. برای اطلاعات بیشتر،
یادداشتهای عیبیابی lint میانای برنامهسازی کاربردی ناپایدار را ببینید.
آزمایشهای محلی با Robolectric
آزمایشهای محلی Robolectric در JVM ماشین توسعه نرمافزار شما
بدون دستگاه فیزیکی یا شبیهساز اجرا میشوند. Robolectric پیادهسازیهای MediaCodec را ارائه نمیدهد و Looper اصلی را تا زمانی که بهطور صریح پمپ نشود، موقتاً متوقف نگه میدارد، بنابراین یک نمونه پیشفرض ExoPlayer نمیتواند رسانه را رمزگشایی کند یا بهروزرسانیهای وضعیت را بهتنهایی ارائه دهد.
ساختن پخشکننده آزمایشی و بازپخش پیشرفته
از TestExoPlayerBuilder برای ایجاد ExoPlayer
پیکربندیشده برای آزمایشهای Robolectric استفاده کنید. بهطور پیشفرض، TestExoPlayerBuilder
عناصر زیر را پیکربندی میکند:
- یک
FakeClock(isAutoAdvancing = true) با پیشروی خودکار که پیشرفت زمان را بین رشته بازپخش پسزمینهExoPlayerوLooperاصلی موقتاً متوقفشده Robolectric هماهنگ میکند. - پردازنده ویدیو و صدای جعلی (
FakeVideoRendererوFakeAudioRenderer) که نمونههای رسانه را براساس مُهر زمان آنها بدون نیاز به رمزگشایMediaCodecمصرف میکنند.
برای پیش بردن بازپخش و ادعای نتایج در رشته اصلی (با پخشکنندهای که از
Looper.getMainLooper، پیشفرض در Robolectric، استفاده میکند)، از روشهای ایستا
advance(player) یا play(player) در
TestPlayerRunHelper استفاده کنید (play(player) پیشاز انتظار،
player.play را فراخوانی میکند). برای اکثر شرایط، کمککننده Looper اصلی Robolectric را تا زمانی که شرط هدف برآورده شود پمپ میکند و اگر خطای بازپخش مهلک یا غیرمهلک رخ دهد، استثنایی ایجاد میکند:
کاتلین
@RunWith(AndroidJUnit4::class) @OptIn(UnstableApi::class) class PlaybackRobolectricTest { private lateinit var player: ExoPlayer @After fun tearDown() { if (::player.isInitialized) { player.release() } } @Test fun play_sampleMediaItem_reachesEndedState() { val context = ApplicationProvider.getApplicationContext<Context>() player = TestExoPlayerBuilder(context).build() player.setMediaItem(MediaItem.fromUri("asset:///media/mp4/sample.mp4")) player.prepare() play(player).untilState(Player.STATE_ENDED) assertThat(player.playbackState).isEqualTo(Player.STATE_ENDED) } }
جاوا
@RunWith(AndroidJUnit4.class) @OptIn(markerClass = UnstableApi.class) public static class PlaybackRobolectricTest { private @Nullable ExoPlayer player; @After public void tearDown() { if (player != null) { player.release(); } } @Test public void play_sampleMediaItem_reachesEndedState() throws Exception { Context context = ApplicationProvider.getApplicationContext(); player = new TestExoPlayerBuilder(context).build(); player.setMediaItem(MediaItem.fromUri("asset:///media/mp4/sample.mp4")); player.prepare(); play(player).untilState(Player.STATE_ENDED); assertThat(player.getPlaybackState()).isEqualTo(Player.STATE_ENDED); } }
در این مثال، asset:///media/mp4/sample.mp4 به نمونه فایل رسانهای اشاره دارد
که در فهرست راهنمای src/test/assets/ (یا src/main/assets/) واحد شما قرار داده شده است.
وقتی مرجع ExoPlayer ارسال میشود، advance(player) و play(player)
ExoPlayerRunResult (یا
PlayerRunResult وقتی Player ارسال میشود) را برمیگردانند. این انواع
نتیجه روشهایی را برای انتظار در وضعیتهای بازپخش، موقعیتها، انتقالهای
فهرست پخش، فریمهای رندرشده، فرمانهای معلق، و خطاهای بازپخش موردانتظار، یا
نادیده گرفتن خطاهای غیرمهلک ارائه میدهند. برای اطلاعات بیشتر درباره شرایط انتظار، به
مرجع میانای برنامهسازی کاربردی TestPlayerRunHelper مراجعه کنید.
آزمایش با منابع جعلی و کدکهای شبیهسازیشده
بسیاری از آزمایشهای ادغام، پیمایش فهرست پخش، انتخاب قطعه، بهروزرسانیهای خط زمان، یا رفتار تکرار مجدد را درستیسنجی میکنند و به فایلهای رسانهای واقعی نیاز ندارند. میتوانید
خطوط زمان و دادههای مصنوعی را بااستفاده از
FakeMediaSource، FakeTimeline،
FakeDataSource، یا
WebServerDispatcher (با OkHttp MockWebServer) به پخشکننده TestExoPlayerBuilder وارد کنید:
کاتلین
val player = TestExoPlayerBuilder(context).build() val fakeMediaSource = FakeMediaSource(FakeTimeline(/* windowCount= */ 2)) player.setMediaSource(fakeMediaSource) player.prepare() play(player).untilMediaItemIndex(/* mediaItemIndex= */ 1) assertThat(player.currentMediaItemIndex).isEqualTo(1) player.release()
جاوا
ExoPlayer player = new TestExoPlayerBuilder(context).build(); FakeMediaSource fakeMediaSource = new FakeMediaSource(new FakeTimeline(/* windowCount= */ 2)); player.setMediaSource(fakeMediaSource); player.prepare(); play(player).untilMediaItemIndex(/* mediaItemIndex= */ 1); assertThat(player.getCurrentMediaItemIndex()).isEqualTo(1); player.release();
TestExoPlayerBuilder بهطور پیشفرض از پردازنده گرافیکی جعلی استفاده میکند. اگر آزمایش Robolectric شما
به تعاملات MediaCodec نیاز دارد، کدکهای شبیهسازیشده و ضبط
برونداد را بهصورت زیر پیکربندی کنید:
withAllDefaultSupportedCodecsرا ازShadowMediaCodecConfigبهعنوان JUnit@Ruleاضافه کنید تا کدکهای شبیهسازیشده Robolectric ثبت شود.CapturingRenderersFactoryساختهشده باClock(CapturingRenderersFactory(context, builder.getClock())) سازنده را بهbuilder.setRenderersFactoryارسال کنید.- هنگام آزمایش قطعههای ویدیو، یک
Surface(مانندSurface(SurfaceTexture(1))) را باplayer.setVideoSurfaceپیوست کنید تاMediaCodecVideoRendererفریمهای رمزگشاییشده را بهجای رد کردن، پردازش کند. - برای درستیسنجی برونداد پردازششده، قبلاز شروع بازپخش،
register(player, capturingRenderersFactory)را درPlaybackOutputفراخوانی کنید. - پساز پایان بازپخش، پخشکننده و سطح را رها کنید و سپس خروجی قالببندیشده از
Dumper(Dumper().add(playbackOutput).toString()) را بررسی کنید.
آزمایشهای ابزارگری در دستگاهها
آزمایشهای Robolectric محلی رمزگشاهای پلاتفرم واقعی، قطعههای صوتی، جلسههای DRM، یا رندر کردن Surface را اجرا نمیکنند. برای درستیسنجی رفتار بازپخش سرتاسر در
دستگاههایی که Android را اجرا میکنند، آزمایشهای ابزاردار را در
src/androidTest بااستفاده از ExoPlayer واقعی ساختهشده با ExoPlayer.Builder بنویسید.
در آزمایشهای ابزار JUnit، روشهای @Test بهجای رشته اصلی برنامه، در رشته ابزار اجرا میشوند. همیشه ExoPlayer را در رشته برنامه Looper آن ایجاد، کنترل، و منتشر کنید، برای مثال بااستفاده از runBlocking(Dispatchers.Main) در Kotlin یا InstrumentationRegistry.getInstrumentation().runOnMainSync در Java.
منتظر ماندن برای وضعیتهای بازیکن با PlayerFence و روالهای همزمان
در آزمایشهای ابزار دقیق، Looper اصلی بهطور همزمان در دستگاه اجرا میشود.
Robolectric یا media3-test-utils-robolectric را در
وابستگیهای androidTestImplementation اضافه نکنید. برای منتظر ماندن برای وضعیتها و رویدادهای بازپخش،
media3-test-utils کمککنندههای زیر را ارائه میدهد:
- Kotlin: تعلیق توابع افزونه در
Player(مانندawaitPlaybackState،awaitIsPlaying،awaitFirstFrameRendered، وawaitContentPositionAtLeast). - Java: روش ثابت
futureWhen(player)درPlayerFence، که یکPlayerFenceبرمیگرداند که روشهای شرط آن (مانندentersPlaybackStateوisPlaying) باید در رشتهLooperپخشکننده فراخوانی شوند و یکListenableFutureبرمیگرداند که رشته آزمایش میتواند باgetدر آن مسدود شود.
هر دو میانای برنامهسازی کاربردی بر بازیکن درحال انتظار نظارت میکنند. اگر خطای مهلک در بازپخش (یا بهطور پیشفرض، خطای غیرمهلک) رخ دهد، یا اگر زمان انتظار از مهلت زمانی فراتر رود (بهطور پیشفرض ۱۰ ثانیه، یا ۳۰ ثانیه در شبیهساز)، تماس بهجای قطع شدن، فوراً ناموفق میشود:
کاتلین
@RunWith(AndroidJUnit4::class) @OptIn(UnstableApi::class) class PlaybackInstrumentationTest { private lateinit var player: ExoPlayer @After fun tearDown() = runBlocking(Dispatchers.Main) { if (::player.isInitialized) { player.release() } } @Test fun play_sampleMediaItem_advancesAndEnds() = runBlocking(Dispatchers.Main) { val context = ApplicationProvider.getApplicationContext<Context>() player = ExoPlayer.Builder(context).build() player.setMediaItem(MediaItem.fromUri("asset:///media/mp4/sample.mp4")) player.prepare() player.play() player.awaitIsPlaying(true) player.awaitContentPositionAtLeast(500) player.awaitPlaybackState(Player.STATE_ENDED) assertThat(player.playbackState).isEqualTo(Player.STATE_ENDED) } }
جاوا
@RunWith(AndroidJUnit4.class) @OptIn(markerClass = UnstableApi.class) public static class PlaybackInstrumentationTest { private @Nullable ExoPlayer player; @After public void tearDown() { InstrumentationRegistry.getInstrumentation() .runOnMainSync(() -> { if (player != null) { player.release(); } }); } @Test public void play_sampleMediaItem_advancesAndEnds() throws Exception { SettableFuture<Void> playbackStarted = SettableFuture.create(); SettableFuture<Void> positionPassed = SettableFuture.create(); SettableFuture<Void> playbackEnded = SettableFuture.create(); InstrumentationRegistry.getInstrumentation() .runOnMainSync(() -> { Context context = ApplicationProvider.getApplicationContext(); player = new ExoPlayer.Builder(context).build(); player.setMediaItem(MediaItem.fromUri("asset:///media/mp4/sample.mp4")); playbackStarted.setFuture(futureWhen(player).isPlaying(true)); positionPassed.setFuture(futureWhen(player).passesContentPosition(500)); playbackEnded.setFuture(futureWhen(player).entersPlaybackState(Player.STATE_ENDED)); player.prepare(); player.play(); }); playbackStarted.get(); positionPassed.get(); playbackEnded.get(); InstrumentationRegistry.getInstrumentation() .runOnMainSync(() -> assertThat(player.getPlaybackState()).isEqualTo(Player.STATE_ENDED)); } }
When building ExoPlayer with ApplicationProvider.getApplicationContext in
an app module (com.android.application), asset:/// URIs resolve against the
target app's assets (such as src/debug/assets/ or src/main/assets/, rather
than src/androidTest/assets/).
رویدادهای گذرا و نظارت سفارشی
علاوهبر بررسی وضعیت فعلی بازیکن (که در آن اگر شرط ازقبل برآورده شده باشد، انتظارها فوراً برمیگردند)، کمککنندهها در media3-test-utils از رویدادهای یکباره و شرایط شنونده سفارشی پشتیبانی میکنند:
- رویدادهای گذرا مانند اولین قاب پردازششده: خروجی ویدیویی
(مانند
SurfaceیاPlayerView) را به پخشکننده متصل کنید و قبلاز فراخوانیplayer.prepare، باawaitFirstFrameRenderedیاrendersFirstFrameمنتظر بمانید تا رویداد قاب یکباره اولیه ازدست نرود. - شرایط سفارشی: شنونده سفارشی یا انتظار روتین همزمان را در
awaitSupervisedدرplayerدر Kotlin قرار دهید، یاListenableFutureسفارشی را بهfutureWhen(player).withSupervision(future)در Java ارسال کنید. هنگام آزمایش بازیابی از خطاهای غیرکشنده،failOnNonFatalErrors = false(Kotlin) را ارسال کنید یاignoringNonFatalErrors(Java) را فراخوانی کنید، و زمانهای اتمام را باtimeoutیاwithTimeoutMs(timeoutMs)سفارشیسازی کنید.
مثال زیر منتظر یک رویداد فریم اول گذرا میماند و بر یک شنونده فراداده سفارشی نظارت میکند:
کاتلین
// Start awaiting transient events (with a video surface attached) before prepare(). coroutineScope { val firstFrameDeferred = async(start = CoroutineStart.UNDISPATCHED) { player.awaitFirstFrameRendered() } player.setMediaItem(mediaItem) player.prepare() player.play() firstFrameDeferred.await() } // Supervise a custom listener condition while still failing fast on player errors. val metadataTitle = player.awaitSupervised { player.mediaMetadata.title?.let { return@awaitSupervised it } val titleDeferred = CompletableDeferred<CharSequence>() val listener = object : Player.Listener { override fun onMediaMetadataChanged(mediaMetadata: MediaMetadata) { mediaMetadata.title?.let { titleDeferred.complete(it) } } } player.addListener(listener) try { titleDeferred.await() } finally { player.removeListener(listener) } }
جاوا
SettableFuture<Void> firstFrameFuture = SettableFuture.create(); SettableFuture<CharSequence> metadataTitleFuture = SettableFuture.create(); SettableFuture<CharSequence> supervisedTitleFuture = SettableFuture.create(); Player.Listener metadataListener = new Player.Listener() { @Override public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { if (mediaMetadata.title != null) { metadataTitleFuture.set(mediaMetadata.title); } } }; try { InstrumentationRegistry.getInstrumentation() .runOnMainSync(() -> { // Register transient event fences (with a video surface attached) before prepare(). firstFrameFuture.setFuture(futureWhen(player).rendersFirstFrame()); // Supervise a custom ListenableFuture so player errors fail fast. player.addListener(metadataListener); supervisedTitleFuture.setFuture( futureWhen(player).withSupervision(metadataTitleFuture)); player.setMediaItem(mediaItem); if (player.getMediaMetadata().title != null) { metadataTitleFuture.set(player.getMediaMetadata().title); } player.prepare(); player.play(); }); firstFrameFuture.get(); CharSequence title = supervisedTitleFuture.get(); } finally { InstrumentationRegistry.getInstrumentation() .runOnMainSync(() -> { player.removeListener(metadataListener); firstFrameFuture.cancel(/* mayInterruptIfRunning= */ true); supervisedTitleFuture.cancel(/* mayInterruptIfRunning= */ true); }); }
آزمایش کردن میانای کاربر و مؤلفههای برنامه با FakePlayer
هنگام آزمایش کردن عناصر میانای کاربر مثل PlayerView یا media3-ui-compose
composables، ViewModels، یا MediaSession callbacks، ایجاد کردن ExoPlayer کامل
اغلب غیرضروری است. FakePlayer در
media3-test-utils، SimpleBasePlayer را گسترش میدهد تا
فهرستهای پخش، وضعیتهای بازپخش، موقعیتها، اندازههای ویدیو، نشانهها، و خطاهای موجود در
Looper رشته فراخوان را شبیهسازی کند. این Looper بهطور خودکار در
آزمایشهای Robolectric با @RunWith(AndroidJUnit4::class) و در رشته اصلی
در آزمایشهای ابزاردار دردسترس است.
بهطور پیشفرض، FakePlayer تأخیر میانگیری ۱۰۰ میلیثانیهای را در prepare و
seekTo شبیهسازی میکند؛ bufferingDelayMs = 0 را در Kotlin ارسال کنید یا وضعیتها را مستقیماً با
setPlaybackState تنظیم کنید:
کاتلین
val fakePlayer = FakePlayer(bufferingDelayMs = 0) fakePlayer.setMediaItem(MediaItem.fromUri("https://example.com/media.mp4")) fakePlayer.prepare() fakePlayer.play() assertThat(fakePlayer.isPlaying).isTrue() // Drive state transitions directly to test UI or session listeners. fakePlayer.setPosition(30_000) fakePlayer.setPlaybackState(Player.STATE_ENDED) fakePlayer.release()
جاوا
FakePlayer fakePlayer = new FakePlayer(); fakePlayer.setMediaItem(MediaItem.fromUri("https://example.com/media.mp4")); // Drive state transitions directly (prepare() simulates a 100 ms buffering delay by default). fakePlayer.setPlaybackState(Player.STATE_READY); fakePlayer.play(); assertThat(fakePlayer.isPlaying()).isTrue(); fakePlayer.setPosition(30_000); fakePlayer.setPlaybackState(Player.STATE_ENDED); fakePlayer.release();
ابزارهای آزمایش بیشتر
media3-test-utils شامل کمککنندههای تخصصی برای سناریوهای آزمایشی دیگر است:
- پرچمهای ویژگی و حالت Canary: از
Media3FlagsRuleهمراه با@BindFlagو@BindCanaryModeبرای بازنشانی لغو پرچم پساز هر آزمایش و اجرای آزمایشهای پارامتریشده در سراسر وضعیتهای پرچم استفاده کنید. برای اطلاعات بیشتر، به آزمایش با پرچمهای ویژگی مراجعه کنید. - آزمایشهای قرارداد
DataSourceسفارشی: زیرکلاسDataSourceContractTestبرای تأیید اینکه پیادهسازیهایDataSourceیاHttpDataSourceسفارشی با انتظارات قرارداد Media3 در درخواستهای محدوده، رسیدگی به خطا، و شنوندگان انتقال مطابقت دارند. برای اطلاعات بیشتر، ادغام کردن پشتههای شبکه دیگر را ببینید.