Skip to content

Commit

Permalink
complete yuv converter
Browse files Browse the repository at this point in the history
  • Loading branch information
HowQuitVim committed Sep 13, 2021
0 parents commit 671ae5b
Show file tree
Hide file tree
Showing 85 changed files with 12,988 additions and 0 deletions.
81 changes: 81 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Created by .ignore support plugin (hsz.mobi)
### Android template
# Built application files
*.apk
*.aar
*.ap_
*.aab

# Files for the ART/Dalvik VM
*.dex

# Java class files
*.class

# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/

# Gradle files
.gradle/
build/

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
proguard/

# Log Files
*.log

# Android Studio Navigation editor temp files
.navigation/

# Android Studio captures folder
captures/

# IntelliJ
*.iml
.idea/*

# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore

# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/

# Google Services (e.g. APIs or Firebase)
# google-services.json

# Freeline
freeline.py
freeline/
freeline_project_description.json

# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md

# Version control
vcs.xml

# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/


*.gpg

21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 IAmCodingCoding

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
120 changes: 120 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# yuv_convert
是一个基于libyuv的图像色彩空间转换框架.支持NV12,NV21,YUV420,RGBA四种格式的相互转化

# 支持的abi:
armeabi-v7a,arm64-v8a

# 集成:
implementation 'io.github.IAmCodingCoding:yuv_convert:$last_version'
具体版本请查看 [release](https://github.com/IAmCodingCoding/yuv_convert/releases)

# 支持不同形式的输入输出:
1. 输入:
- ByteBufferInput: 以DirectByteBuffer作为数据载体.所有不同的色彩通道放到不同的ByteBuffer中.比如: RGBA格式需要输入:\[bufferR,bufferG,bufferB,bufferA\]
- PackedByteBufferInput: 以DirectByteBuffer作为数据载体.所有色彩通道放到同一个ByteBuffer中
- ByteArrayInput: 以ByteArray作为数据载体.所有不同的色彩通道放到不同的ByteArray中.比如:RGBA格式需要输入\[arrayR,arrayG,arrayB,arrayA\]
- PackedByteArrayInput:以ByteArray作为数据载体.所有色彩通道放到同一个ByteArray中
3. 输出:
- ByteBufferOutput: 以DirectByteBuffer作为数据载体.所有不同的色彩通道放到不同的ByteBuffer中.比如: RGBA格式输出为:\[bufferR,bufferG,bufferB,bufferA\]
- PackedByteBufferOutput:以DirectByteBuffer作为数据载体.所有色彩通道放到同一个ByteBuffer中
- ByteArrayOutput: 以ByteArray作为数据载体.所有不同的色彩通道放到不同的ByteArray中.比如:RGBA格式输出{arrayR,arrayG,arrayB,arrayA}
- PackedByteArrayOutput:以ByteArray作为数据载体.所有不同的色彩通道放到同一个ByteArray中

# Sample:
1. nv12数据转换成rgba数据.并装载到bitmap中:
```kotlin
//创建一个输入端口.输入数据为打包的bytebuffer(即所有色彩通道都在一个DirectByteBuffer中)
val input = PackedByteBufferInput()
//创建一个输出端口.输出数据为打包的bytebuffer(即所有色彩通道都在一个DirectByteBuffer中)
val output = PackedByteBufferOutput(Format.RGBA)
input.provide(nv12Src, Format.NV12, width, height, intArrayOf(width, width))
Converter(input).convert(output)

val result:ByteBuffer=output.getOutput()
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
bitmap.copyPixelsFromBuffer(result)
showBitmap(bitmap)//显示bitmap
```

2. 使用ImageReader读取到RGAB数据后需要转换为NV12:
```kotlin
val imageReader = ImageReader.newInstance(videoWidth, videoHeight, android.graphics.PixelFormat.RGBA_8888, 3)
private val input by lazy { PackedByteBufferInput() }
private val converter by lazy { Converter(input) }
private val nv12Output by lazy { ByteBufferOutput(Format.NV12) }//输出格式为Array<ByteBuffer>
imageReader.setOnImageAvailableListener(object :ImageReader.OnImageAvailableListener{
override fun onImageAvailable(reader: ImageReader?) {
if (reader == null) return
val image = reader.acquireNextImage()
input.provide(
image.planes[0].buffer, //ImageReader读取rgba数据时.所有的通道都放在planes[0]中.所以这里选择了PackedByteBufferInput
Format.RGBA,//指定输入数据的格式.此处为RGBA
image.width,//输入图像的宽
image.height,//输入图像的高
intArrayOf(image.planes[0].rowStride)//输入图像每个通道每行的步长
)
converter.convert(nv12Output)//转换为nv12格式.转换后的数据存放在nv12Output中.同一个converter可以对接多个不同的Output
image.close()//归还image
val output:Array<ByteBuffer> = output.getOutput()//从Output中获取转换后的数据,可以重复调用
val componentY=output[0] //存放y通道的ByteBuffer
val componentUV=output[1] //存放uv通道的ByteBuffer
val componentSize:Int=output.format.getComponentCount()//NV12和NV21格式固定返回2.YUV420返回3.RGBA返回4
val strides=output.format.getStandardStride(image.width,image.height)//获取特定格式每行的步长
val yCapacity=output.format.getComponentCapacity(0,image.width,image.height)//y通道的size
val uvCapacity=output.format.getComponentCapacity(1,image.width,image.height)//uv通道的size
val minSize=output.format.getMinFrameSize(image.width,image.height)//获取存储该格式一帧画面最小所需要的空间.nv12、nv21、yu420所需空间为width*height*1.5 rgba所需空间为width*height*4
//todo use the nv12 data ...
}
},workHandler)
```
3. 并发场景:
- 使用synchronized
```kotlin
private val input by lazy { PackedByteBufferInput() }
private val output by lazy { ByteBufferOutput(Format.YUV420) }
private val converter by lazy { Converter(input) }

imageProducer.parallelProduce{rgbaImage->//伪代码,此处的ImageProducer会并发的生产rgba格式的数据
synchronized(imageProducer){
input.provide(
image.planes[0].buffer, //ImageReader读取rgba数据时.所有的通道都放在planes[0]中.所以这里选择了PackedByteBufferInput
Format.RGBA,//指定输入数据的格式.此处为RGBA
image.width,//输入图像的宽
image.height,//输入图像的高
intArrayOf(image.planes[0].rowStride)//输入图像每个通道每行的步长
)
converter.convert(output)
val result=output.getOutput()
}
}
```
- 使用ThreadLocal
```kotlin
val input = object : ThreadLocal<PackedByteBufferInput>() {
override fun initialValue(): PackedByteBufferInput {
return PackedByteBufferInput()
}
}
val output = object : ThreadLocal<PackedByteBufferOutput>() {
override fun initialValue(): PackedByteBufferOutput {
return PackedByteBufferOutput(Format.YUV420)
}
}
val converter = object : ThreadLocal<Converter<ByteBuffer>>() {
override fun initialValue(): Converter<ByteBuffer> {
return Converter(input.get())
}
}
imageProducer.parallelProduce{rgbaImage->//伪代码,此处的ImageProducer会并发的生产rgba格式的数据
input.get()?.provide(
image.planes[0].buffer, //ImageReader读取rgba数据时.所有的通道都放在planes[0]中.所以这里选择了PackedByteBufferInput
Format.RGBA,//指定输入数据的格式.此处为RGBA
image.width,//输入图像的宽
image.height,//输入图像的高
intArrayOf(image.planes[0].rowStride)//输入图像每个通道每行的步长
)
converter.get()?.convert(output.get()!!)
val result=output.get()?.getOutput()
}
```

1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
46 changes: 46 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
}

android {
compileSdkVersion 30
buildToolsVersion "30.0.3"

defaultConfig {
applicationId "com.zmy.yuv_convert.example"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}

dependencies {

implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation project(path: ':yuv_convert')
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
21 changes: 21 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.zmy.yuv_convert.example

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.zmy.yuv_convert.example", appContext.packageName)
}
}
21 changes: 21 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zmy.yuv_convert.example">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Yuv_convert">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
1 change: 1 addition & 0 deletions app/src/main/assets/720_1280.yuv420

Large diffs are not rendered by default.

Loading

0 comments on commit 671ae5b

Please sign in to comment.