open media state added

This commit is contained in:
2021-05-10 22:19:10 +03:00
parent 05c50e7acd
commit e5c0287f51
9 changed files with 81 additions and 38 deletions

View File

@@ -200,21 +200,25 @@ class HelloSurface : SurfaceView, SurfaceHolder.Callback {
} }
} }
fun render() { fun render() {
persistentSurface?.let { persistentSurface?.let {surface ->
val pCanvas = it.lockCanvas(null) val pCanvas = surface.lockCanvas(null)
clearCanvas(pCanvas) pCanvas?.let {
synchronized(it) { clearCanvas(it)
drawHello(pCanvas, _frameNumber) synchronized(surface) {
drawHello(it, _frameNumber)
}
surface.unlockCanvasAndPost(pCanvas)
} }
it.unlockCanvasAndPost(pCanvas)
} }
} }
fun preview() { fun preview() {
val canvas = holder.lockCanvas() val canvas = holder.lockCanvas()
clearCanvas(canvas) canvas?.let {
drawHello(canvas, _frameNumber) clearCanvas(it)
holder.unlockCanvasAndPost(canvas) drawHello(it, _frameNumber)
holder.unlockCanvasAndPost(it)
}
} }
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {

View File

@@ -12,10 +12,9 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.Inspiry"> android:theme="@style/Theme.Inspiry">
<activity android:name=".MainActivity"> <activity android:name=".ui.MainActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>

View File

@@ -0,0 +1,4 @@
package su.rst10h.inspiry
const val MEDIA_FOLDER = "test"
const val MEDIA_FILE_NAME = "TestVideo.mp4"

View File

@@ -2,5 +2,7 @@ package su.rst10h.inspiry.data
enum class ActivityState { enum class ActivityState {
WAIT, WAIT,
RENDER RENDERING,
RENDERED,
OPEN_MEDIA
} }

View File

@@ -7,6 +7,7 @@ import android.view.Surface
import su.rst10h.loopedworld.HelloSurface import su.rst10h.loopedworld.HelloSurface
import java.io.File import java.io.File
@Deprecated("Это тоже работает, но кривовато - нет синхронизации фреймов. Лучше использовать VideoEncoder")
class CanvasRecorder(private val helloSurface: HelloSurface, appPath: File) { class CanvasRecorder(private val helloSurface: HelloSurface, appPath: File) {
private var recorder: MediaRecorder private var recorder: MediaRecorder

View File

@@ -8,6 +8,8 @@ import android.util.Log
import android.view.Surface import android.view.Surface
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import su.rst10h.inspiry.MEDIA_FILE_NAME
import su.rst10h.inspiry.MEDIA_FOLDER
import su.rst10h.loopedworld.HelloSurface import su.rst10h.loopedworld.HelloSurface
import java.io.File import java.io.File
@@ -15,8 +17,10 @@ class VideoEncoder(private val baseFileDir: File, private val helloSurface: Hell
private val bufferInfo = MediaCodec.BufferInfo() private val bufferInfo = MediaCodec.BufferInfo()
private val mediaEncoder: MediaCodec private val mediaEncoder: MediaCodec
private val inputSurface: Surface private lateinit var inputSurface: Surface
var onStopRecord = {} var onStopRecord = {}
init { init {
val format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 1080, 1920) val format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 1080, 1920)
format.setInteger( format.setInteger(
@@ -31,11 +35,14 @@ class VideoEncoder(private val baseFileDir: File, private val helloSurface: Hell
inputSurface = mediaEncoder.createInputSurface() inputSurface = mediaEncoder.createInputSurface()
} }
private lateinit var mediaMuxer: MediaMuxer private lateinit var mediaMuxer: MediaMuxer
private var muxerTrackIndex = -1 private var muxerTrackIndex = -1
@Volatile var isRunning = false @Volatile var isRunning = false
fun startRender() { fun startRender() {
muxerTrackIndex = -1
val file = File(prepareFilePath(baseFileDir)) val file = File(prepareFilePath(baseFileDir))
if (!file.exists()) file.delete() if (!file.exists()) file.delete()
mediaMuxer = MediaMuxer(file.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) mediaMuxer = MediaMuxer(file.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
@@ -60,8 +67,7 @@ class VideoEncoder(private val baseFileDir: File, private val helloSurface: Hell
while (isRunning) { while (isRunning) {
val encoderStatus = mediaEncoder.dequeueOutputBuffer(bufferInfo, 10000L) val encoderStatus = mediaEncoder.dequeueOutputBuffer(bufferInfo, 10000L)
when(encoderStatus) { when(encoderStatus) {
MediaCodec.INFO_TRY_AGAIN_LATER -> { MediaCodec.INFO_TRY_AGAIN_LATER -> { }
}
MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> { MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> {
Log.d("encoder", "start muxer $muxerTrackIndex") Log.d("encoder", "start muxer $muxerTrackIndex")
val newFormat: MediaFormat = mediaEncoder.outputFormat val newFormat: MediaFormat = mediaEncoder.outputFormat
@@ -83,17 +89,20 @@ class VideoEncoder(private val baseFileDir: File, private val helloSurface: Hell
} }
} }
} }
Log.d("encoder", "File closed") Log.d("encoder","stop encoding")
mediaMuxer.stop() mediaMuxer.stop()
mediaMuxer.release()
mediaEncoder.stop()
} }
private fun prepareFilePath(appPath: File): String { private fun prepareFilePath(appPath: File): String {
val fullPath = appPath.absolutePath+"/test/" val fullPath = appPath.absolutePath+"/$MEDIA_FOLDER/"
if (!File(fullPath).exists()) { if (!File(fullPath).exists()) {
if (!File(fullPath).mkdir()) { if (!File(fullPath).mkdir()) {
Log.d("main", "directory test not created") Log.d("main", "directory test not created")
throw Exception("The test directory does not exist and has not been created:\n$fullPath$\n") throw Exception("The test directory does not exist and has not been created:\n$fullPath$\n")
} }
} }
return fullPath+"TestVideoEncoder2.mp4" return fullPath+MEDIA_FILE_NAME
} }
} }

View File

@@ -1,38 +1,50 @@
package su.rst10h.inspiry package su.rst10h.inspiry.ui
import androidx.appcompat.app.AppCompatActivity import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.widget.Button import android.widget.Button
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import su.rst10h.inspiry.MEDIA_FILE_NAME
import su.rst10h.inspiry.MEDIA_FOLDER
import su.rst10h.inspiry.R
import su.rst10h.inspiry.data.ActivityState import su.rst10h.inspiry.data.ActivityState
import su.rst10h.loopedworld.HelloSurface import su.rst10h.loopedworld.HelloSurface
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
val viewModel : MainViewModel by viewModels() val viewModel : MainViewModel by viewModels()
val btn = findViewById<Button>(R.id.button) val btn = findViewById<Button>(R.id.button)
val surfaceView = findViewById<HelloSurface>(R.id.testView) val surfaceView = findViewById<HelloSurface>(R.id.testView)
viewModel.state.observe(this,{ viewModel.state.observe(this, { state ->
when(it) { when (state) {
ActivityState.WAIT -> { ActivityState.WAIT -> {
Log.d("main", "record stopped")
btn.text = "Render Template" btn.text = "Render Template"
btn.isEnabled = true btn.isEnabled = true
} }
ActivityState.RENDER -> { ActivityState.RENDERING -> {
Log.d("main", "record started") btn.text = "RENDERING.."
btn.text = "Rendering.."
btn.isEnabled = false btn.isEnabled = false
} }
ActivityState.RENDERED -> {
btn.isEnabled = true
btn.text = "OPEN MEDIA"
}
ActivityState.OPEN_MEDIA -> {
openMedia()
viewModel.reset_state()
}
} }
}) })
@@ -41,7 +53,15 @@ class MainActivity : AppCompatActivity() {
} }
btn.setOnClickListener { btn.setOnClickListener {
viewModel.renderTemplate() viewModel.action()
} }
} }
private fun openMedia() {
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(Uri.parse(
getExternalFilesDir(null)?.absolutePath+
"/$MEDIA_FOLDER/$MEDIA_FILE_NAME"),
"video/mp4")
startActivity(intent)
}
} }

View File

@@ -1,11 +1,9 @@
package su.rst10h.inspiry package su.rst10h.inspiry.ui
import android.graphics.Canvas
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import su.rst10h.inspiry.data.* import su.rst10h.inspiry.data.*
import su.rst10h.inspiry.data.ActivityState.* import su.rst10h.inspiry.data.ActivityState.*
import su.rst10h.inspiry.repository.CanvasRecorder
import su.rst10h.inspiry.repository.VideoEncoder import su.rst10h.inspiry.repository.VideoEncoder
import su.rst10h.loopedworld.HelloSurface import su.rst10h.loopedworld.HelloSurface
import java.io.File import java.io.File
@@ -23,18 +21,24 @@ class MainViewModel: ViewModel() {
videoEncoder = VideoEncoder(appPath, surface) videoEncoder = VideoEncoder(appPath, surface)
} }
fun renderTemplate() { fun reset_state() {
state.postValue(WAIT)
}
fun action() {
when (state.value) { when (state.value) {
WAIT -> { WAIT -> {
videoEncoder.onStopRecord = { videoEncoder.onStopRecord = {
state.postValue(WAIT) state.postValue(RENDERED)
} }
videoEncoder.startRender() videoEncoder.startRender()
state.postValue(RENDER) state.postValue(RENDERING)
} }
RENDER -> { RENDERED -> {
state.postValue(WAIT) state.postValue(OPEN_MEDIA)
} }
} }
} }

View File

@@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".MainActivity"> tools:context=".ui.MainActivity">
<su.rst10h.loopedworld.HelloSurface <su.rst10h.loopedworld.HelloSurface
android:id="@+id/testView" android:id="@+id/testView"