Android MediaCodec with MediaProjection - android-mediacodec

i would like to record screen with MediaCodec and MediaProjection. After MediaCodec starts, i expect to see the display image. but i got wrong display image in onOutputBufferAvailable. also i am using Galaxy S20 for testing. what is wrong with my code?
private fun startRecording(){
val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val defaultDisplay: Display = displayManager.getDisplay(Display.DEFAULT_DISPLAY)
val metrics = resources.displayMetrics
prepareMediaCodec(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi)
}
private fun prepareMediaCodec(screenWidth: Int, screenHeight: Int, screenDensity : Int) {
val mediaFormat =
MediaFormat.createVideoFormat("video/avc", screenWidth, screenHeight).apply {
setInteger(MediaFormat.KEY_BIT_RATE, 6000000)
setInteger(MediaFormat.KEY_FRAME_RATE, 30)
setInteger(MediaFormat.KEY_CAPTURE_RATE, 30)
setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / 30)
setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1)
setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1)
setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)
}
mediaCodec = MediaCodec.createEncoderByType("video/avc")
mediaCodec.setCallback(object : MediaCodec.Callback() {
override fun onOutputBufferAvailable( p0: MediaCodec,outputBufferId: Int, info: MediaCodec.BufferInfo) {
val encodedData: ByteBuffer = mediaCodec.getOutputBuffer(outputBufferId)
encodedData.position(info.offset)
encodedData.limit(info.offset + info.size)
val data = ByteArray(encodedData.remaining())
encodedData.get(data)
val baos =ByteArrayOutputStream()
val yuvImage = YuvImage(data, ImageFormat.NV21, screenWidth, screenHeight, null)
yuvImage.compressToJpeg(Rect(0, 0, screenWidth, screenHeight), 80, baos)
val response = baos.toByteArray()
val bitmap: Bitmap? = BitmapFactory.decodeByteArray(response, 0, response.size)
Log.e(TAG, bitmap.toString()) // check bitmap
mediaCodec.releaseOutputBuffer(outputBufferId, false)
}
...
})
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
mediaCodec.start()
mediaProjection.createVirtualDisplay(
"Record", screenWidth, screenHeight, screenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mediaCodec.createInputSurface(), null)
}

The output buffer that you received from onOutputBufferAvailable of an encoder is in h264 format and is not NV21 buffer so you couldn't not save it to bitmap as it is.
What you need to do is use MediaMuxer to save it as a playable video format such as mp4 files.

Related

Having problem in making api calls in android studio using kotlin

I want to make api calls using json object request in android studio using kotlin.
This is the api.
How to access the 'name' in the array articles ?. I can access all the other things except 'id' and 'name'.
This is my code
val url = "https://news-api-don.herokuapp.com/api/v1?apiKey=20d14506791144cc8b424549c42068c0"
val jsonObjectRequest = JsonObjectRequest(
Request.Method.GET, url, null,
{
val newsJsonArray = it.getJSONArray("articles")
val newsArray = ArrayList<News>()
for(i in 0 until newsJsonArray.length()) {
val newsJsonObject = newsJsonArray.getJSONObject(i)
val news = News(
newsJsonObject.getString("title"),
newsJsonObject.getString("author"),
newsJsonObject.getString("url"),
newsJsonObject.getString("urlToImage")
)
newsArray.add(news)
}
mAdapter.updateNews(newsArray)
swipeRefreshLayout.isRefreshing = false
progressBar.visibility = View.GONE
},
{
Toast.makeText(this,"Something went wrong", Toast.LENGTH_LONG).show()
swipeRefreshLayout.isRefreshing = false
progressBar.visibility = View.GONE
}
)
MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest)
}

How to convert data class to map

I tried convert to data class to map. (Under the code is just example code)
data class User (
val name : String = "",
val age : Int = 0,
val deviceGroup: MutableSet<DeviceGroup> = mutableSetOf()
)
data class DeviceGroup (
val name : String = "",
val deviceLink : MutableSet<DeviceLink> = mutableSetOf()
)
data class DeviceLink (
val id : Int = 0,
val device : Device
)
data class Device (
val devId : Int = 0,
val name : String = ""
)
fun main (request : HttpServletRequest) {
val currentUser = request.session.getAttribute("user") as User
val data = userRepository.findByName(currentUser.name)
// return currentUser
// result is {name="test", age=17, deviceGroup = [{name="group1"}, {name="group2"}]}
// I want deserialization data class to Map
val response = data.deviceGroup.toMap()
response.deivceGroup.forEach {
// And add new key, pair
it.add(Map<String, MutableSet<Device>>("devices", mutableSetOf()))
// Lastly, I want put in the value
deviceGroupRepository.findByName(it.name).deviceLink.forEach {
it.devices.add(this)
}
}
return response
}
if just return the data value, that's result is "{name="test", age=17, deviceGroup = [{name="group1"}, {name="group2"}]}"
How to convert to data class to Map object and add new key pair?
Use associate to turn a collection into a Map
The Kotlin standard library provides a function called associate which will take a collection of objects and transform them into a map. It takes one argument, which is a function specifying what the keys and values of the map should be.
For example, in your case, you would call it like this:
val response = data.deviceGroup.associate { it.name to it.deviceLink }
It will return a Map<String, MutableSet<DeviceLink>> where the key is the name of the device group and the value is the deviceLink set.
The easiest way to add new values is simply to append them with the + operator.
val response = data.deviceGroup.associate {
it.name to it.deviceLink
} + mapOf("device" to emptySet())
If you need more control than that, you could use .toMutableMap() so new entries can be added using put.
val response = data.deviceGroup.associate {
it.name to it.deviceLink
}.toMutableMap()
response.put("device", emptySet())

Generating Bitmaps for Google MapOverlays with Kotlin Coroutines

I'm having some issues building out a section of my code which edits server sent thumbnails and renders them as a Google GroundOverlay.
The issue seems to stem from Kotlin Coroutines. First, the documentation from Google says that the ground overlays must be created on the Main Thread. Running their creation out of the main thread causes a fatal error. So I have been sure to make these GroundOverlays on the main thread. When trying to create the bitmap on a thread out of Main, however, I seem to get no overlay at all.
class BarreMapFragment : Fragment(),
GoogleMap.OnCameraIdleListener,
GoogleMap.OnCameraMoveCanceledListener,
GoogleMap.OnCameraMoveListener,
GoogleMap.OnCameraMoveStartedListener,
OnMapReadyCallback {
//Main handler for google map/item styling
googleMapHandler = GoogleMapHandler(gMap!!, activity!!.applicationContext, DefaultTheme, lifecycle.coroutineScope)
. . .
open class GoogleMapHandler(val gMap: GoogleMap,
val context: Context,
val mapThemeInstructions: MapThemeInstructions,
val coroutineScope: CoroutineScope
) {
fun updateActiveUserAvatarPosition(position: LatLng) {
if (mActiveUserAvatar == null) {
coroutineScope.launch {
mActiveUserAvatar = mapObjectFactory.factory(
MapObject(
latitude = position.latitude,
longitude = position.longitude,
objectId = "SELF_AVATAR",
objectType = MapObjectType.USER_AVATAR,
timestamp = System.currentTimeMillis(),
weight = 20.toFloat()
), getOverlayWidthByZoom(dpScreenWidth, gMap.cameraPosition.target, gMap.cameraPosition.zoom)) as RenderedUserAvatarItem
}
}
mActiveUserAvatar?.updatePosition(position)
}
suspend fun factory(mapObject: MapObject, diameter: Float) : RenderedMapItem {
overlayDiameter = diameter
val item = RenderedUserAvatarItem(
mapObject,
buildOverlay(mapObject)
)
return item
}
#MainThread
private suspend fun buildOverlay(mapObject: MapObject) : GroundOverlay {
Log.d("UserOverlay", "I was called.")
//Get the bitmap from the resources
//TODO: We can do more with this later... Like custom avatars
//val bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.ic_user_avatar)
val bitmap = withContext(Dispatchers.Default) {
async {
val d: Drawable = context.getDrawable(R.drawable.ic_user_avatar)!!
val bitmap : Bitmap = drawableToBitmap(d)!!
bitmap
}
}.await()
//val d: Drawable = context.getDrawable(R.drawable.ic_user_avatar)!!
//val bitmap : Bitmap = drawableToBitmap(d)!!
Log.d(TAG, "bitmap = " + bitmap.toString())
//Make bitmap descriptor
val descriptor = BitmapDescriptorFactory.fromBitmap(bitmap)
val overlayOptions = GroundOverlayOptions().image(descriptor)
//Position and size of groundoverlay
overlayOptions.position(LatLng(mapObject.latitude, mapObject.latitude) , overlayDiameter)
//Add the overlay to the map, get a handle and save it to public Overlay list
val mOverlay = gMap.addGroundOverlay(overlayOptions)
//Store the moment information in the overlay tag
mOverlay.tag = mapObject.objectId
return mOverlay
}
The suspend function is called from the main thread. Now,
val bitmap = withContext(Dispatchers.Unconfined) {
async {
val d: Drawable = context.getDrawable(R.drawable.ic_user_avatar)!!
val bitmap : Bitmap = drawableToBitmap(d)!!
bitmap
}
}.await()
and the commented out section above (without using async)
val d: Drawable = context.getDrawable(R.drawable.ic_user_avatar)!!
val bitmap : Bitmap = drawableToBitmap(d)!!
Will both yield a GroundOverlay with no problems. The problem happens when I change Dispatchers.Unconfined to anything else. Even Dispatchers.Main causes the GroundOverlay to not show up on the map. The GroundOverlays are made, I have checked them with log statements. Their transparency is expected as is their visibility. The issue seems to be with the Bitmap. I suspect I am not understanding the way the await() works. I figured that it would pause the suspend function until the bitmap is returned and ready to go.
This is simplified code to isolate the error. I do need this to be done on Dispatchers.Default because each thumbnail is adjusted in style depending on the location and time of day. This bit of processing would be hard on the UI if done on the main thread.

How to split an UInt() into a Vec of UInt to do subword extraction and assignment?

I have a 16bits register declared like that :
val counterReg = RegInit(0.U(16.W))
And I want to do indexed dibit assignment on module output like that :
//..
val io = IO(new Bundle {
val dibit = Output(UInt(2.W))
})
//..
var indexReg = RegInit(0.U(4.W))
//..
io.dibit = vectorizedCounter(indexReg)
But I have some difficulties to know how to declare vectorizedCounter().
I found some examples using Bundles, but for Vector I don't know. And I can't manage to do that with UInt():
val counterReg = RegInit(UInt(16.W))
//...
io.dibit := counterReg(indexReg*2.U + 1.U, indexReg*2.U)
You could dynamically shift and bit extract the result:
io.dibit := (counterReg >> indexReg)(1, 0)

json data parsing using retrofit and rxjava2 data display in Textview and TableLayout

JSON Data Parsing Using Retofit2 and Rxjava2. This Data get In ArrayList successfully. its ArrayList Size is Nine but its display only two Record in Table. After Two Record its Kotlin.NullPointerException.
JSON Data:
{"success":1,"salesGst":[{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"1","ChallanDate":"2019-03-15 00:00:00","ChallanAmount":"2778.75","TaxTotal":"2778.75","InvoiceType":"Retail Invoice","CGSTTotal":"0.0","PartyGST":"CDE","SGSTTotal":"0.0","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"1","ChallanDate":"2019-03-13 00:00:00","ChallanAmount":"2203.0","TaxTotal":"2118.5","InvoiceType":"Tax Invoice","CGSTTotal":"52.96","PartyGST":"CDE","SGSTTotal":"52.96","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"VIKAS","ChallanNo":"2","ChallanDate":"2019-03-16 00:00:00","ChallanAmount":"6975.0","TaxTotal":"6975.0","InvoiceType":"Retail Invoice","CGSTTotal":"0.0","PartyGST":null,"SGSTTotal":"0.0","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES MH","ChallanNo":"2","ChallanDate":"2019-03-13 00:00:00","ChallanAmount":"420.0","TaxTotal":"403.75","InvoiceType":"Tax Invoice","CGSTTotal":"0.0","PartyGST":"ABC","SGSTTotal":"0.0","IGSTTotal":"20.19"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"3","ChallanDate":"2019-03-14 00:00:00","ChallanAmount":"4788.0","TaxTotal":"4560.0","InvoiceType":"Tax Invoice","CGSTTotal":"114.0","PartyGST":"CDE","SGSTTotal":"114.0","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"4","ChallanDate":"2019-03-15 00:00:00","ChallanAmount":"241.9","TaxTotal":"230.38","InvoiceType":"Tax Invoice","CGSTTotal":"5.76","PartyGST":"CDE","SGSTTotal":"5.76","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"5","ChallanDate":"2019-03-15 00:00:00","ChallanAmount":"5563.68","TaxTotal":"5101.5","InvoiceType":"Tax Invoice","CGSTTotal":"231.28","PartyGST":"CDE","SGSTTotal":"231.28","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"6","ChallanDate":"2019-03-16 00:00:00","ChallanAmount":"13238.0","TaxTotal":"12459.25","InvoiceType":"Tax Invoice","CGSTTotal":"389.29","PartyGST":"CDE","SGSTTotal":"389.29","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES MH","ChallanNo":"7","ChallanDate":"2019-03-16 00:00:00","ChallanAmount":"2074.0","TaxTotal":"1975.0","InvoiceType":"Tax Invoice","CGSTTotal":"0.0","PartyGST":"ABC","SGSTTotal":"0.0","IGSTTotal":"98.75"}]}
Please Guide Me,After Getting How to Show in TableLayout.
In ArrayList Nine Record but in Table show only Two Record another seven record is not display. in third record taxtotal give kotlin.nullpointerException. what missing?
private fun displaySalesGSTData(salesGSt : List<SalesGST>) {
salesGST = SalesGST()
tvSalesCompanyName.setText(salesGSt.get(1).Cmp_Name)
tvGSTIN.setText(salesGSt.get(1).GSTIN)
val rowHeader = TableRow(this#Sales)
rowHeader.setBackgroundColor(Color.parseColor("#c0c0c0"))
rowHeader.setLayoutParams(TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT))
val headerText = arrayOf<String>("Sr.No.", "Invoice Type", "Bill No.", "Bill Date", "Firm Name", "GST NO","TAX Total","CGST","SGST","IGST","Net Amount")
for (c in headerText)
{
val tv = TextView(this#Sales)
tv.setLayoutParams(TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT))
tv.setGravity(Gravity.CENTER)
// tv.setBackgroundResource(R.drawable.table_header)
tv.setTextColor(Color.parseColor("#3F51B5"))
tv.setTextSize(18F)
tv.setPadding(5, 5, 5, 5)
tv.setText(c)
rowHeader.addView(tv)
}
tableMarks.addView(rowHeader)
for (j in 0 until salesGSt.size)
{
/*val jsonObject1 = jsonArray.getJSONObject(j)
val date = jsonObject1.getString("ExamDate")
val inputFormatter1 = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val date1 = inputFormatter1.parse(date)
val outputFormatter1 = SimpleDateFormat("dd-MMM-yyyy")
ExamDate = outputFormatter1.format(date1)*/
/* String replaceDate = date.replace("/Date(", "").replace(")/", "");
Long getDate = Long.valueOf(replaceDate);
ExamDate = dateFormat.format(getDate);*/
/*Subject = jsonObject1.getString("subject")
ExamName = jsonObject1.getString("ExamName")
TotalMark = jsonObject1.getLong("TotalMarks")
PassingMark = jsonObject1.getLong("PassingMarks")
Mark = jsonObject1.getLong("Marks")*/
var fName : String = salesGSt.get(j).FirmName!!
var invoice : String = salesGSt.get(j).InvoiceType!!
var bill_no : String = salesGSt.get(j).ChallanNo!!
var bill_date : String = salesGSt.get(j).ChallanDate!!
var gst_no : String = salesGSt.get(j).PartyGST!!
var tax_total : Double = salesGSt.get(j).TaxTotal!!.toDouble()
var cgst : String = salesGSt.get(j).CGSTTotal!!
var igst : String = salesGSt.get(j).IGSTTotal!!
var sgst : String = salesGSt.get(j).SGSTTotal!!
var net_amount : String = salesGSt.get(j).ChallanAmount!!
var sr : Int = j + 1
// dara rows
val row = TableRow(this#Sales)
row.setLayoutParams(TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT))
val colText = arrayOf<String>(sr.toString(),(invoice), bill_no, bill_date, fName, gst_no, tax_total.toString(),cgst,sgst,igst,net_amount)
for (text in colText)
{
val tv = TextView(this#Sales)
tv.setLayoutParams(TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT))
tv.setGravity(Gravity.CENTER)
// tv.setBackgroundResource(R.drawable.table_shape)
tv.setTextSize(18F)
tv.setTextColor(Color.parseColor("#3F51B5"))
tv.setPadding(5, 5, 5, 5)
tv.setText(text)
row.addView(tv)
}
tableMarks.addView(row)
}
}
The 3rd item, salesGst[2], is "PartyGST": null. Your json deserializer library won't handle non-null fields as it's written in Java. I assume you have a data class where PartGST is defined as non-null, yet the deserializer will still parse it as null. Therefore, when you access PartyGST then you will receive a NullPointerException because Kotlin is expecting it to be non-null. This is a good article, which explains in more detail:
I've trusted you! You promised no null pointer exceptions!
A solution to this would be to have two models. The DTO (for JSON response) where all fields are optional and your internal model (used by your app), where you define which fields you want to be optional. Then you can have a mapper to handle the case where a field is null in the DTO, but non-null in your internal model:
// Models for your API response
data class SalesGstsDTO(val gsts: List<GstDTO>)
data class GstDTO(val name: String?, val surname: String?)
// Internal models used by your app
data class SalesGsts(val gsts: List<Gst>)
data class Gst(val name: String, val surname: String?)
class SalesGstDTOMapper {
fun mapToSalesGsts(salesGstsDTO: SalesGstsDTO): SalesGsts {
val gsts = mutableListOf<Gst>()
salesGstsDTO.gsts.map {
val name = it.name ?: return#map // Skips this item. You could handle this how you wish
val surname = it.surname
val gst = Gst(name, surname)
gsts.add(gst)
}
return SalesGsts(gsts)
}
}
This also allows you to decouple your app from the JSON response.