DSGViewer を作るためにh.264を扱う必要がありました。h.264なんてMediaCodecで簡単!と思っていたら全然うまくいかず挫折しました。h.264でもmp4なら簡単にできたのですが、h264のrawデータ(というのか?最初が00 00 00 01で始まるやつ)がうまくいかない。
悩んでいても仕方がないので、javaだけでデコードできるライブラリを探したところ、JCodecを見つけて試行錯誤しながら使うことができました。使って分かったことを記録します。JCodecはjavaですが、kotlinでいきます。エラー処理やnullチェックは抜かしています。
まず、使うためにここのGetting startedを参照し、build.gradleを編集します。
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' //追加 compileをimplementationに置き換え implementation 'org.jcodec:jcodec:0.2.3' implementation 'org.jcodec:jcodec-android:0.2.3' }
追加の部分は、そのままコピーするとcompileの部分で警告が出るので implementation に置き換えました。
あと、Bitmapの確認用にMainActivityにはImageViewを置いておきます。
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <ImageView android:layout_width="0dp" android:layout_height="0dp" app:srcCompat="@android:color/holo_blue_dark" android:id="@+id/imageView" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent"/> </android.support.constraint.ConstraintLayout>
h264から最初のフレームを取り出して表示してみます。
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) imageView.setImageBitmap(getFirstFrame()) } fun getFirstFrame() : Bitmap{ val file = File("/sdcard/000.h264") val buf = NIOUtils.fetchFromFile(file) val es = BufferH264ES(buf) val nextFrame = es!!.nextFrame() val decoder = H264Decoder() val pic = Picture.create(1280, 1024, ColorSpace.YUV420) val op = decoder.decodeFrame(nextFrame.data, pic.data).cropped() return AndroidUtil.toBitmap(op) } }
pic = Picture.create(1280, 1024, ColorSpace.YUV420)は、デコードする画像のバッファーが必要ということで、画面サイズとかは仮です。 ColorSpace.YUV420 は、RGBにしたらダメでした。デコードした後にcropped()とやると画像サイズをちゃんと合わせてくれるみたいです。
ひとまずこんな感じ。ディスクから読むのでManifestのパーミッションの設定は忘れずに。次は任意のフレーム取得へ続く。