Compare commits

...

5 Commits

Author SHA1 Message Date
Raymond Yang 4a8fb55fc8 拿掉LOG 2023-05-22 13:37:27 +08:00
Raymond Yang fdb88fc47a 更新Gradle Plugin 2023-05-22 13:36:56 +08:00
Raymond Yang ba12fd3856 1.優化for loop
2.優化VideoView get/set
2023-05-22 10:56:24 +08:00
Raymond Yang 8449706b64 1.加入TextureView支援
2.將取得畫面寬高的計算方式改成不使用baseView.post
2023-05-22 09:43:53 +08:00
Raymond Yang 02a26445be 同步HiSharpDX的程式碼 2023-05-19 14:02:47 +08:00
18 changed files with 330 additions and 207 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="16" />
<bytecodeTargetLevel target="17" />
</component>
</project>
+1 -1
View File
@@ -7,7 +7,7 @@
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="semeru-16" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
+1 -2
View File
@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
@@ -16,5 +15,5 @@
</option>
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_16_PREVIEW" project-jdk-name="11" project-jdk-type="JavaSDK" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK" />
</project>
+9 -9
View File
@@ -6,6 +6,7 @@ plugins {
}
android {
namespace 'com.ray650128.gstreamer_demo_app'
ndkVersion "21.3.6528147"
compileSdk 33
@@ -26,29 +27,28 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = '17'
}
lint {
abortOnError false
checkReleaseBuilds false
}
viewBinding.enabled = true
namespace 'com.ray650128.gstreamer_demo_app'
}
dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.gridlayout:gridlayout:1.0.0'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.core:core-ktx:1.10.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
@@ -56,6 +56,6 @@ dependencies {
implementation project(path: ':gstreamer_player')
// Android Jetpack lib
implementation("androidx.fragment:fragment-ktx:1.5.5")
implementation("androidx.activity:activity-ktx:1.6.1")
implementation("androidx.fragment:fragment-ktx:1.5.7")
implementation("androidx.activity:activity-ktx:1.7.1")
}
@@ -1,5 +1,12 @@
package com.ray650128.gstreamer_demo_app
object Constants {
//region Split mode
const val SPLIT_MODE_SIXTEEN = 16
const val SPLIT_MODE_NINE = 9
const val SPLIT_MODE_FOUR = 4
const val SPLIT_MODE_SINGLE = 1
//endregion
const val CONF_DELAY_BASE_MILLIS = 1000L
}
@@ -0,0 +1,94 @@
package com.ray650128.gstreamer_demo_app
import android.content.Context
import android.os.Build
import android.util.DisplayMetrics
import android.view.WindowManager
/**
* 螢幕參數工具類別
* @author Raymond Yang
*/
class DisplayUtils(private var context: Context) {
private val TAG = DisplayUtils::class.java.simpleName
/**
* 取得螢幕寬度
* @return 螢幕寬度值
*/
fun getScreenWidth(): Int {
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val windowMetrics = windowManager.currentWindowMetrics
windowMetrics.bounds.width()
} else {
val metric = DisplayMetrics()
@Suppress("DEPRECATION")
windowManager.defaultDisplay.getMetrics(metric)
metric.widthPixels
}
}
/**
* 取得螢幕高度
* @return 螢幕高度值
*/
fun getScreenHeight(): Int {
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val windowMetrics = windowManager.currentWindowMetrics
windowMetrics.bounds.height()
} else {
val metric = DisplayMetrics()
@Suppress("DEPRECATION")
windowManager.defaultDisplay.getMetrics(metric)
metric.heightPixels
}
}
/**
* 取得狀態列高度
* @return 狀態列高度值
*/
fun getStatusBarHeight(): Int {
var result = 0
val resourceId: Int = context.resources.getIdentifier(
"status_bar_height",
"dimen",
"android"
)
if (resourceId > 0) {
result = context.resources.getDimensionPixelSize(resourceId)
}
return result
}
/**
* 取得導航列高度
* @return 導航列高度值
*/
fun getNavigationBarHeight(): Int {
val resources = context.resources
val resourceId: Int = resources.getIdentifier("navigation_bar_height", "dimen", "android")
return if (resourceId > 0) {
resources.getDimensionPixelSize(resourceId)
} else 0
}
/**
* 取得視窗範圍高度
* @return 視窗範圍高度值
*/
fun getWindowHeight(): Int {
val screen = getScreenHeight()
val statusBar = getStatusBarHeight()
val navBar = getNavigationBarHeight()
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
screen - statusBar - navBar
} else {
screen
}
}
}
@@ -19,6 +19,6 @@ object PreferenceUtil {
set(value) = sharedPreferences.edit().putBoolean(IS_FIRST_OPEN_KEY, value).apply()
var lastSplitMode: Int
get() = sharedPreferences.getInt(LAST_SPLIT_MODE, MainViewModel.PAGE_MODE_ONE)
get() = sharedPreferences.getInt(LAST_SPLIT_MODE, Constants.SPLIT_MODE_SINGLE)
set(value) = sharedPreferences.edit().putInt(LAST_SPLIT_MODE, value).apply()
}
@@ -57,27 +57,27 @@ class MainActivity : AppCompatActivity() {
private fun initContentView() = binding.apply {
//region Content area
button.setOnClickListener {
if (splitMode == MainViewModel.PAGE_MODE_ONE) return@setOnClickListener
viewModel.setSplitMode(MainViewModel.PAGE_MODE_ONE)
if (splitMode == Constants.SPLIT_MODE_SINGLE) return@setOnClickListener
viewModel.setSplitMode(Constants.SPLIT_MODE_SINGLE)
Log.e(TAG, "+++ split style: 1")
}
button2.setOnClickListener {
if (splitMode == MainViewModel.PAGE_MODE_FOUR) return@setOnClickListener
viewModel.setSplitMode(MainViewModel.PAGE_MODE_FOUR)
if (splitMode == Constants.SPLIT_MODE_FOUR) return@setOnClickListener
viewModel.setSplitMode(Constants.SPLIT_MODE_FOUR)
Log.e(TAG, "+++ split style: 4")
}
button3.setOnClickListener {
if (splitMode == MainViewModel.PAGE_MODE_NINE) return@setOnClickListener
viewModel.setSplitMode(MainViewModel.PAGE_MODE_NINE)
if (splitMode == Constants.SPLIT_MODE_NINE) return@setOnClickListener
viewModel.setSplitMode(Constants.SPLIT_MODE_NINE)
Log.e(TAG, "+++ split style: 9")
}
button4.setOnClickListener {
if (splitMode == MainViewModel.PAGE_MODE_SIXTEEN) return@setOnClickListener
viewModel.setSplitMode(MainViewModel.PAGE_MODE_SIXTEEN)
Log.e(TAG, "+++ split style: 9")
if (splitMode == Constants.SPLIT_MODE_SIXTEEN) return@setOnClickListener
viewModel.setSplitMode(Constants.SPLIT_MODE_SIXTEEN)
Log.e(TAG, "+++ split style: 16")
}
//endregion
}
@@ -94,8 +94,7 @@ class MainActivity : AppCompatActivity() {
}
}
private fun reloadVideoViews(list: List<List<Device>>?) = MainScope().launch {
val oldListCount = videoPageList.size
private fun reloadVideoViews(list: List<List<Device>>?) {
for (videoPage in videoPageList) {
supportFragmentManager.commit {
remove(videoPage)
@@ -104,9 +103,6 @@ class MainActivity : AppCompatActivity() {
binding.viewPager.removeAllViews()
videoPageList.clear()
if (oldListCount > 0) {
delay(500L)
}
// 如果群組內沒有裝置,則顯示底圖
if (list.isNullOrEmpty()) {
binding.viewPager.setBackgroundResource(R.drawable.bg_not_in_playing)
@@ -19,7 +19,7 @@ class MainViewModel: ViewModel() {
stream1 = "/media/video1",
stream2 = "/media/video2",
),
Device(
/*Device(
deviceName = "192.168.0.73",
ip = "192.168.0.73",
rtspPort = "554",
@@ -27,8 +27,8 @@ class MainViewModel: ViewModel() {
password = "hs22601576",
stream1 = "/media/video1",
stream2 = "/media/video2",
),
Device(
),*/
/*Device(
deviceName = "192.168.0.79",
ip = "192.168.0.79",
rtspPort = "554",
@@ -36,7 +36,7 @@ class MainViewModel: ViewModel() {
password = "1q2w3e4r!",
stream1 = "/media/video1",
stream2 = "/media/video2",
),
),*/
Device(
deviceName = "192.168.0.88",
ip = "211.23.78.226",
@@ -64,7 +64,7 @@ class MainViewModel: ViewModel() {
stream1 = "/v01",
stream2 = "/v02",
),
Device(
/*Device(
deviceName = "192.168.0.76",
ip = "211.23.78.226",
rtspPort = "8576",
@@ -72,7 +72,7 @@ class MainViewModel: ViewModel() {
password = "123456",
stream1 = "/profile1",
stream2 = "/profile2",
),
),*/
Device(
deviceName = "192.168.0.82",
ip = "192.168.0.82",
@@ -91,7 +91,7 @@ class MainViewModel: ViewModel() {
stream1 = "/profile1",
stream2 = "/profile2",
),
Device(
/*Device(
deviceName = "192.168.0.95",
ip = "192.168.0.95",
rtspPort = "554",
@@ -99,7 +99,7 @@ class MainViewModel: ViewModel() {
password = "123456",
stream1 = "/profile1",
stream2 = "/profile2",
)
)*/
)
}
@@ -137,11 +137,4 @@ class MainViewModel: ViewModel() {
}
return tmpData
}
companion object {
const val PAGE_MODE_ONE = 1
const val PAGE_MODE_FOUR = 4
const val PAGE_MODE_NINE = 9
const val PAGE_MODE_SIXTEEN = 16
}
}
@@ -6,10 +6,12 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.gridlayout.widget.GridLayout
import com.ray650128.gstreamer_demo_app.Constants
import com.ray650128.gstreamer_demo_app.DisplayUtils
import com.ray650128.gstreamer_demo_app.R
import com.ray650128.gstreamer_demo_app.databinding.FragmentSplitViewBinding
import com.ray650128.gstreamer_demo_app.dp
import com.ray650128.gstreamer_demo_app.model.Device
@@ -22,10 +24,8 @@ import kotlin.math.sqrt
class SplitViewFragment : Fragment() {
val viewModel: SplitViewModel by activityViewModels()
private var mPageNum: Int = 0
private var splitMode = MainViewModel.PAGE_MODE_ONE
private var splitMode = Constants.SPLIT_MODE_SINGLE
private var streamType = VideoView.SUB_STREAM
private var isClickable = true
@@ -56,18 +56,6 @@ class SplitViewFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
initView()
/*viewModel.activePage.observe(viewLifecycleOwner) {
if (it == null) return@observe
if (it == this.mPageNum) {
MainScope().launch {
//delay(1000)
//playAll()
}
} else {
//stopAll()
}
}*/
}
override fun onPause() {
@@ -90,6 +78,7 @@ class SplitViewFragment : Fragment() {
}
private fun initView() {
val displayUtil = DisplayUtils(requireContext())
// 生成 VideoView 分割畫面
binding.apply {
val maxRow = sqrt(splitMode.toFloat()).toInt()
@@ -98,9 +87,20 @@ class SplitViewFragment : Fragment() {
baseView.rowCount = maxRow
baseView.columnCount = maxCol
Log.e("${TAG}_$mPageNum", "baseView.rowCount: ${baseView.rowCount}, baseView.columnCount: ${baseView.columnCount}")
baseView.post {
val cellWidth: Int
val cellHeight: Int
when (splitMode) {
Constants.SPLIT_MODE_SINGLE -> {
cellWidth = (displayUtil.getScreenWidth() / maxRow)
cellHeight = (cellWidth * 0.5625).toInt()
}
else -> {
cellWidth = (displayUtil.getScreenWidth() / maxRow) - maxRow.dp
cellHeight = (cellWidth * 0.5625).toInt() - maxCol.dp
}
}
for (col in 0 until maxCol) {
for (row in 0 until maxRow) {
val videoView = VideoView(requireContext())
@@ -110,13 +110,12 @@ class SplitViewFragment : Fragment() {
marginEnd = 0.dp
marginStart = 0.dp
width = cellWidth
height = cellHeight
// 調整間距
when (splitMode) {
MainViewModel.PAGE_MODE_ONE -> {
width = (baseView.width / maxRow)
height = (baseView.height / maxCol)
}
MainViewModel.PAGE_MODE_FOUR -> {
Constants.SPLIT_MODE_FOUR -> {
when (col) {
0 -> bottomMargin = 2.dp
1 -> topMargin = 2.dp
@@ -125,10 +124,8 @@ class SplitViewFragment : Fragment() {
0 -> marginEnd = 2.dp
1 -> marginStart = 2.dp
}
width = (baseView.width / maxRow) - maxRow.dp
height = (baseView.height / maxCol) - maxCol.dp
}
MainViewModel.PAGE_MODE_NINE -> {
Constants.SPLIT_MODE_NINE -> {
if (col == 1) {
topMargin = 4.dp
bottomMargin = 4.dp
@@ -137,10 +134,8 @@ class SplitViewFragment : Fragment() {
marginEnd = 4.dp
marginStart = 4.dp
}
width = (baseView.width / maxRow) - maxRow.dp
height = (baseView.height / maxCol) - maxCol.dp
}
MainViewModel.PAGE_MODE_SIXTEEN -> {
Constants.SPLIT_MODE_SIXTEEN -> {
if (col == 1) {
topMargin = 4.dp
bottomMargin = 2.dp
@@ -157,8 +152,6 @@ class SplitViewFragment : Fragment() {
marginStart = 2.dp
marginEnd = 4.dp
}
width = (baseView.width / maxRow) - maxRow.dp
height = (baseView.height / maxCol) - maxCol.dp
}
}
}
@@ -166,20 +159,16 @@ class SplitViewFragment : Fragment() {
videoViews.add(videoView)
}
}
setAllUrl()
}
if (isClickable) {
for (position in videoViews.indices) {
videoViews[position].setOnClickListener {
if (position >= data.size) return@setOnClickListener
if (!videoViews[position].isPlaying) {
videoViews.forEach { videoView ->
videoView.setOnClickListener {
if (!videoView.isPlaying) {
return@setOnClickListener
}
MainScope().launch {
stopAll()
delay(splitMode * Constants.CONF_DELAY_BASE_MILLIS)
val item = data[position]
val item = videoView.data
val bundle = Bundle().apply {
//putInt(MonitoringActivity.BUNDLE_DEVICE_ID, item.id)
//putInt(MonitoringActivity.BUNDLE_CHANNEL_ID, item.channelId)
@@ -193,48 +182,43 @@ class SplitViewFragment : Fragment() {
}
}
}
}
}
}
setAllUrl()
}
private fun setAllUrl() {
for (index in data.indices) {
videoViews[index].setData(data[index])
videoViews[index].data = data[index]
videoViews[index].setTextVisible(
(splitMode != MainViewModel.PAGE_MODE_NINE && splitMode != MainViewModel.PAGE_MODE_SIXTEEN)
(splitMode != Constants.SPLIT_MODE_NINE && splitMode != Constants.SPLIT_MODE_SIXTEEN)
)
}
}
fun playAll() = MainScope().launch(Dispatchers.Main) {
if (videoViews.isEmpty()) return@launch
delay(splitMode * Constants.CONF_DELAY_BASE_MILLIS)
for (index in data.indices) {
if (!videoViews[index].isReady) continue
videoViews[index].resetRetryCount()
videoViews[index].play()
//delay(300)
fun playAll() {
videoViews.forEach { videoView ->
if (videoView.isReady) {
videoView.resetRetryCount()
videoView.play()
}
}
}
}.start()
fun stopAll() = MainScope().launch(Dispatchers.Main) {
if (videoViews.isEmpty()) return@launch
for (index in data.indices) {
videoViews[index].stopRetryCount()
if (!videoViews[index].isPlaying || videoViews[index].isLoading) continue
videoViews[index].pause()
//delay(300)
fun stopAll() {
videoViews.forEach { videoView ->
videoView.stopRetryCount()
if (videoView.isPlaying || !videoView.isLoading) {
videoView.pause()
}
}
}
}.start()
fun destroyAll() = MainScope().launch(Dispatchers.Main) {
if (videoViews.isEmpty()) return@launch
for (index in data.indices) {
//videoViews[index].destroy()
videoViews[index].destroySurface()
fun destroyAll() {
videoViews.forEach { videoView ->
//videoView.destroy()
videoView.destroySurface()
}
}
}.start()
companion object {
private val TAG = SplitViewFragment::class.java.simpleName
@@ -9,6 +9,7 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.TextureView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import com.hisharp.gstreamer_player.GstCallback
@@ -34,7 +35,25 @@ class VideoView : ConstraintLayout, GstCallback {
private lateinit var view: ItemVideoViewBinding
private var data: Device? = null
var streamType: Int = MAIN_STREAM
var data: Device? = null
set(value) {
field = value
if (field == null) {
view.textDeviceName.isVisible = false
isPlaying = false
isLoading = false
return
}
this.tag = field?.deviceName
view.textDeviceName.text = field?.deviceName
view.textDeviceName.isVisible = true
val rtspUrl = this.data?.getStreamPath(streamType) ?: return // 如果 null 就不指派給 Gstreamer 了
gstLibrary.setTag(this.data!!.deviceName)
gstLibrary.setRtspUrl(rtspUrl)
Log.d("${TAG}_$tag", "Set device to: $field, rtspUrl = $rtspUrl")
}
var isReady: Boolean = false
@@ -62,7 +81,7 @@ class VideoView : ConstraintLayout, GstCallback {
}
}
private val videoView: SurfaceView by lazy { view.videoView }
private val videoView: TextureView by lazy { view.videoView }
private lateinit var gstLibrary: GstLibrary
@@ -75,11 +94,13 @@ class VideoView : ConstraintLayout, GstCallback {
view = ItemVideoViewBinding.inflate(layoutInflater, this, true)
view.baseView.clipToOutline = true
//videoView.holder.addCallback(this)
gstLibrary = GstLibrary(context)
gstLibrary.setSurfaceHolder(videoView.holder)
gstLibrary.setTextureView(videoView)
gstLibrary.setOnStatusChangeListener(this)
//videoView.holder.addCallback(this)
//gstLibrary.setSurfaceHolder(videoView.holder)
// View 預設狀態
view.textDeviceName.isVisible = false
isPlaying = false
@@ -92,23 +113,6 @@ class VideoView : ConstraintLayout, GstCallback {
}
}
fun setData(device: Device?, streamType: Int = SUB_STREAM) {
if (device == null) {
view.textDeviceName.isVisible = false
isPlaying = false
isLoading = false
return
}
this.data = device
this.tag = device.deviceName
view.textDeviceName.text = device.deviceName
view.textDeviceName.isVisible = true
val rtspUrl = this.data?.getStreamPath(streamType) ?: return // 如果 null 就不指派給 Gstreamer 了
gstLibrary.setTag(this.data!!.deviceName)
gstLibrary.setRtspUrl(rtspUrl)
Log.d("${TAG}_$tag", "Set device to: $device, rtspUrl = $rtspUrl")
}
fun setTextVisible(isVisible: Boolean) {
view.textDeviceName.isVisible = isVisible
}
+1 -1
View File
@@ -7,7 +7,7 @@
android:background="@drawable/bg_video_view"
android:outlineProvider="background">
<SurfaceView
<TextureView
android:id="@+id/videoView"
android:layout_width="match_parent"
android:layout_height="match_parent"
+2 -2
View File
@@ -7,8 +7,8 @@ buildscript {
}
plugins {
id 'com.android.application' version '7.4.2' apply false
id 'com.android.library' version '7.4.2' apply false
id 'com.android.application' version '8.0.1' apply false
id 'com.android.library' version '8.0.1' apply false
id 'org.jetbrains.kotlin.android' version '1.8.10' apply false
id 'org.jetbrains.kotlin.plugin.parcelize' version '1.7.0' apply false
}
+3
View File
@@ -19,3 +19,6 @@ android.useAndroidX=true
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false
+1 -1
View File
@@ -1,6 +1,6 @@
#Thu Mar 24 14:41:01 CST 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
+14 -2
View File
@@ -2,9 +2,10 @@ apply plugin: 'com.android.library'
apply plugin: 'org.jetbrains.kotlin.android'
android {
namespace 'com.hisharp.gstreamer_player'
ndkVersion "21.3.6528147"
compileSdkVersion 33
compileSdk 33
defaultConfig {
minSdkVersion 15
@@ -58,6 +59,17 @@ android {
path 'jni/Android.mk'
}
}
buildFeatures {
renderScript true
aidl true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
}
afterEvaluate {
@@ -69,7 +81,7 @@ afterEvaluate {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.core:core-ktx:1.10.1'
testImplementation 'junit:junit:4.13.2'
//implementation 'androidx.appcompat:appcompat:1.6.0'
}
+1 -1
View File
@@ -250,7 +250,7 @@ static void *app_function(void *userdata) {
g_main_context_push_thread_default(data->context);
/* Build pipeline */
data->pipeline = gst_parse_launch("playbin", &error);
data->pipeline = gst_parse_launch("playbin3", &error);
if (error) {
gchar *message = g_strdup_printf("Unable to build pipeline: %s", error->message);
g_clear_error(&error);
@@ -1,13 +1,16 @@
package com.hisharp.gstreamer_player
import android.content.Context
import android.graphics.SurfaceTexture
import android.util.Log
import android.view.Surface
import android.view.SurfaceHolder
import android.view.TextureView
import org.freedesktop.gstreamer.GStreamer
import java.io.Closeable
import java.io.IOException
class GstLibrary(context: Context) : Closeable, SurfaceHolder.Callback {
class GstLibrary(context: Context) : Closeable, SurfaceHolder.Callback, TextureView.SurfaceTextureListener {
private val mAppContext: Context
private var gstCallback: GstCallback? = null
@@ -15,6 +18,8 @@ class GstLibrary(context: Context) : Closeable, SurfaceHolder.Callback {
private var tag = ""
private var isInit = false
private var surface: Surface? = null
private external fun nativeInit() // Initialize native code, build pipeline, etc
private external fun nativeFinalize() // Destroy pipeline and shutdown native code
private external fun nativeSetUri(uri: String) // Set the URI of the media to play
@@ -59,6 +64,10 @@ class GstLibrary(context: Context) : Closeable, SurfaceHolder.Callback {
holder.addCallback(this)
}
fun setTextureView(textureView: TextureView) {
textureView.surfaceTextureListener = this
}
// Called from native code. This sets the content of the TextView from the UI thread.
private fun setMessage(message: String) {
if (gstCallback == null) return
@@ -128,19 +137,41 @@ class GstLibrary(context: Context) : Closeable, SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
Log.d("$TAG+$tag", "Surface created: " + holder.surface)
nativeSurfaceInit(holder.surface)
this.surface = holder.surface
this.surface?.let { nativeSurfaceInit(it) }
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
Log.d("$TAG+$tag", String.format("Surface changed, format: %d, width: %d, height: %d", format, width, height))
//Log.d("$TAG+$tag", String.format("Surface changed, format: %d, width: %d, height: %d", format, width, height))
/*setSurfaceHolder(holder);*/
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
Log.d("$TAG+$tag", "Surface destroyed")
isInit = false
//pause()
//releaseSurface()
//Log.d("$TAG+$tag", "Surface destroyed")
this.isInit = false
this.surface = null
releaseSurface()
}
override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
this.surface = Surface(surface)
this.surface?.let { nativeSurfaceInit(it) }
//Log.d("$TAG+$tag", "Surface onSurfaceTextureAvailable: $surface")
}
override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
//Log.d("$TAG+$tag", "Surface onSurfaceTextureSizeChanged: $surface")
}
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
//Log.d("$TAG+$tag", "Surface onSurfaceTextureDestroyed: $surface")
this.isInit = false
this.surface = null
return isInit
}
override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
//Log.d("$TAG+$tag", "Surface onSurfaceTextureUpdated: $surface")
}
companion object {