WIP: Play a simple media.ccc stream from the video fragment
This barely works, if you click on any video it should start playing a media.ccc.de stream, but it does not display anything in the video view yet.
This commit is contained in:
parent
4604a89137
commit
d9d6a5609c
|
@ -243,6 +243,9 @@ dependencies {
|
|||
implementation "androidx.work:work-rxjava3:${androidxWorkVersion}"
|
||||
implementation 'com.google.dagger:hilt-android:2.52'
|
||||
kapt 'com.google.dagger:hilt-compiler:2.52'
|
||||
implementation "androidx.media3:media3-common:1.3.1"
|
||||
implementation "androidx.media3:media3-exoplayer:1.3.1"
|
||||
implementation "androidx.media3:media3-ui:1.3.1"
|
||||
implementation 'com.google.android.material:material:1.11.0'
|
||||
|
||||
/** Third-party libraries **/
|
||||
|
|
|
@ -21,8 +21,14 @@
|
|||
package net.newpipe.newplayer.testapp
|
||||
|
||||
import android.app.Application
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.media3.common.MediaMetadata
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
@ -30,11 +36,19 @@ import dagger.hilt.components.SingletonComponent
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.newpipe.newplayer.NewPlayer
|
||||
import net.newpipe.newplayer.NewPlayerImpl
|
||||
import net.newpipe.newplayer.data.AudioStreamTrack
|
||||
import net.newpipe.newplayer.data.Chapter
|
||||
import net.newpipe.newplayer.data.Stream
|
||||
import net.newpipe.newplayer.data.Subtitle
|
||||
import net.newpipe.newplayer.data.VideoStreamTrack
|
||||
import net.newpipe.newplayer.repository.CachingRepository
|
||||
import net.newpipe.newplayer.repository.PlaceHolderRepository
|
||||
import net.newpipe.newplayer.repository.MediaRepository
|
||||
import net.newpipe.newplayer.repository.PrefetchingRepository
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.schabi.newpipe.App
|
||||
import org.schabi.newpipe.MainActivity
|
||||
import javax.inject.Singleton
|
||||
|
@ -47,7 +61,7 @@ object NewPlayerComponent {
|
|||
fun provideNewPlayer(app: Application): NewPlayer {
|
||||
val player = NewPlayerImpl(
|
||||
app = app,
|
||||
repository = PrefetchingRepository(CachingRepository(PlaceHolderRepository())),
|
||||
repository = PrefetchingRepository(CachingRepository(TestMediaRepository())),
|
||||
notificationIcon = IconCompat.createWithResource(app, net.newpipe.newplayer.R.drawable.new_player_tiny_icon),
|
||||
playerActivityClass = MainActivity::class.java,
|
||||
// rescueStreamFault = …
|
||||
|
@ -64,3 +78,83 @@ object NewPlayerComponent {
|
|||
return player
|
||||
}
|
||||
}
|
||||
|
||||
class TestMediaRepository() : MediaRepository {
|
||||
private val client = OkHttpClient()
|
||||
|
||||
override fun getRepoInfo() =
|
||||
MediaRepository.RepoMetaInfo(canHandleTimestampedLinks = true, pullsDataFromNetwork = true)
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
override suspend fun getMetaInfo(item: String): MediaMetadata =
|
||||
MediaMetadata.Builder()
|
||||
.setTitle("BGP and the rule of bla")
|
||||
.setArtist("mr BGP")
|
||||
.setArtworkUri(Uri.parse("https://static.media.ccc.de/media/congress/2017/9072-hd.jpg"))
|
||||
.setDurationMs(
|
||||
1871L * 1000L
|
||||
)
|
||||
.build()
|
||||
|
||||
override suspend fun getStreams(item: String): List<Stream> {
|
||||
return listOf(
|
||||
Stream(
|
||||
item = "bgp",
|
||||
streamUri = Uri.parse("https://cdn.media.ccc.de/congress/2017/h264-hd/34c3-9072-eng-BGP_and_the_Rule_of_Custom.mp4"),
|
||||
mimeType = null,
|
||||
streamTracks = listOf(
|
||||
AudioStreamTrack(
|
||||
bitrate = 480000,
|
||||
fileFormat = "MPEG4",
|
||||
language = "en"
|
||||
),
|
||||
VideoStreamTrack(
|
||||
width = 1920,
|
||||
height = 1080,
|
||||
frameRate = 25,
|
||||
fileFormat = "MPEG4"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getSubtitles(item: String) =
|
||||
emptyList<Subtitle>()
|
||||
|
||||
override suspend fun getPreviewThumbnail(item: String, timestampInMs: Long): Bitmap? {
|
||||
|
||||
val templateUrl = "https://static.media.ccc.de/media/congress/2017/9072-hd.jpg"
|
||||
|
||||
val thumbnailId = (timestampInMs / (10 * 1000)) + 1
|
||||
|
||||
if (getPreviewThumbnailsInfo(item).count < thumbnailId) {
|
||||
return null
|
||||
}
|
||||
|
||||
val thumbUrl = String.format(templateUrl, thumbnailId)
|
||||
|
||||
val bitmap = withContext(Dispatchers.IO) {
|
||||
val request = Request.Builder().url(thumbUrl).build()
|
||||
val response = client.newCall(request).execute()
|
||||
try {
|
||||
val responseBody = response.body
|
||||
val bitmap = BitmapFactory.decodeStream(responseBody?.byteStream())
|
||||
return@withContext bitmap
|
||||
} catch (e: Exception) {
|
||||
return@withContext null
|
||||
}
|
||||
}
|
||||
|
||||
return bitmap
|
||||
}
|
||||
|
||||
override suspend fun getPreviewThumbnailsInfo(item: String) =
|
||||
MediaRepository.PreviewThumbnailsInfo(0, 0)
|
||||
|
||||
override suspend fun getChapters(item: String) =
|
||||
listOf<Chapter>()
|
||||
|
||||
override suspend fun getTimestampLink(item: String, timestampInSeconds: Long) =
|
||||
""
|
||||
}
|
||||
|
|
|
@ -63,6 +63,9 @@ import com.google.android.material.appbar.AppBarLayout;
|
|||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import net.newpipe.newplayer.NewPlayer;
|
||||
import net.newpipe.newplayer.data.PlayMode;
|
||||
|
||||
import org.schabi.newpipe.App;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||
|
@ -128,11 +131,15 @@ import java.util.Optional;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public final class VideoDetailFragment
|
||||
extends BaseStateFragment<StreamInfo>
|
||||
implements BackPressable,
|
||||
|
@ -230,6 +237,8 @@ public final class VideoDetailFragment
|
|||
@Nullable
|
||||
private PlayerService playerService;
|
||||
private Player player;
|
||||
@Inject
|
||||
NewPlayer newPlayer;
|
||||
private final PlayerHolder playerHolder = PlayerHolder.getInstance();
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1155,10 +1164,13 @@ public final class VideoDetailFragment
|
|||
|
||||
final PlayQueue queue = setupPlayQueueForIntent(false);
|
||||
tryAddVideoPlayerView();
|
||||
newPlayer.playStream("bgp", PlayMode.EMBEDDED_VIDEO);
|
||||
newPlayer.setPlayWhenReady(true);
|
||||
newPlayer.prepare();
|
||||
|
||||
final Intent playerIntent = NavigationHelper.getPlayerIntent(requireContext(),
|
||||
PlayerService.class, queue, true, autoPlayEnabled);
|
||||
ContextCompat.startForegroundService(activity, playerIntent);
|
||||
// final Intent playerIntent = NavigationHelper.getPlayerIntent(requireContext(),
|
||||
// PlayerService.class, queue, true, autoPlayEnabled);
|
||||
// ContextCompat.startForegroundService(activity, playerIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1251,15 +1263,18 @@ public final class VideoDetailFragment
|
|||
// setup the surface view height, so that it fits the video correctly
|
||||
setHeightThumbnail();
|
||||
|
||||
player.UIs().get(MainPlayerUi.class).ifPresent(playerUi -> {
|
||||
// sometimes binding would be null here, even though getView() != null above u.u
|
||||
if (binding != null) {
|
||||
// prevent from re-adding a view multiple times
|
||||
playerUi.removeViewFromParent();
|
||||
binding.playerPlaceholder.addView(playerUi.getBinding().getRoot());
|
||||
playerUi.setupVideoSurfaceIfNeeded();
|
||||
}
|
||||
});
|
||||
getLayoutInflater().inflate(
|
||||
R.layout.fragment_newplayer_view, binding.playerPlaceholder);
|
||||
|
||||
// player.UIs().get(MainPlayerUi.class).ifPresent(playerUi -> {
|
||||
// // sometimes binding would be null here, even though getView() != null above u.u
|
||||
// if (binding != null) {
|
||||
// // prevent from re-adding a view multiple times
|
||||
// playerUi.removeViewFromParent();
|
||||
// binding.playerPlaceholder.addView(playerUi.getBinding().getRoot());
|
||||
// playerUi.setupVideoSurfaceIfNeeded();
|
||||
// }
|
||||
// });
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<net.newpipe.newplayer.ui.NewPlayerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/embedded_player_newplayer"
|
||||
android:name="net.newpipe.newplayer.VideoPlayerFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="50dp" />
|
Loading…
Reference in New Issue