目次: Linux
半年経ったら完全に忘れるのでメモします。最近JPEGのデコードエンコードが必要になって色々調べていました。Jetson特有のAPI群がありまして、API名はJetson Linux API(のなかのMultimedia APIs)だそうです(Jetson Linux APIドキュメント)。ハードウェアJPEGデコーダ/エンコーダ(NVJPG)を自動的に使用してくれます。
今回はエンコードのAPIをご紹介します。APIの使い方は簡単です。こんな感じでした。
NvJPEGEncoder *jpgenc = nullptr;
NvBuffer *inbuf = nullptr;
uint8_t *buffer = nullptr;
size_t bufsize = 0;
int r;
// Create
jpgenc = NvJPEGEncoder::createJPEGEncoder("jpgenc");
// Align
#define ALIGN_2N(a, b) (((a) + (b) - 1) & ~((b) - 1))
NvBuffer::NvBufferPlaneFormat fmts[3];
fmts[0].width = width;
fmts[0].height = height;
fmts[0].bytesperpixel = 1;
fmts[0].stride = ALIGN_2N(width, 256);
fmts[0].sizeimage = fmts[0].stride * fmts[0].height;
fmts[1].width = width / 2;
fmts[1].height = height / 2;
fmts[1].bytesperpixel = 1;
fmts[1].stride = ALIGN_2N(width / 2, 256);
fmts[1].sizeimage = fmts[1].stride * fmts[1].height;
fmts[2].width = width / 2;
fmts[2].height = height / 2;
fmts[2].bytesperpixel = 1;
fmts[2].stride = ALIGN_2N(width / 2, 256);
fmts[2].sizeimage = fmts[2].stride * fmts[2].height;
inbuf = new NvBuffer(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, V4L2_MEMORY_USERPTR, 3, fmts, 0);
if (!inbuf) {
printf("error in %s:%d\n", __func__, __LINE__);
return -1;
}
r = inbuf->allocateMemory();
if (r) {
printf("error in %s:%d\n", __func__, __LINE__);
return -1;
}
bufsize = width * height * 3 / 2;
buffer = (uint8_t *)malloc(bufsize);
// Encoding
uint8_t *jpegbuf = buffer;
size_t jpegsize = bufsize;
int quality = 80;
r = jpgenc->encodeFromBuffer(*inbuf, JCS_YCbCr, &jpegbuf, jpegsize, quality);
// Destroy
free(buffer);
delete inbuf;
delete jpgenc;
若干NvBufferの確保がややこしいです(参考: NvBufferPlaneのドキュメント、その隣のNvBufferPlaneFormatも参考になります)けど、基本的にはencodeFromBuffer()を呼ぶだけです。
ソースコードを置いておきます。
使い方はコードの先頭にコメントで書いている通りですが、ここでも説明しておきます。引数はありません。ファイル名test_420.yuvのRaw YUV420ファイルを読み込んで、ファイル名jetson_420.jpgのJPEGファイルを書き出します。
$ g++ -g -O2 -g -Wall \
-I jetson_multimedia_api/include/ \
-I jetson_multimedia_api/include/libjpeg-8b/ \
jetson_multimedia_api/samples/common/classes/NvJpegDecoder.cpp \
jetson_multimedia_api/samples/common/classes/NvJpegEncoder.cpp \
jetson_multimedia_api/samples/common/classes/NvBuffer.cpp \
jetson_multimedia_api/samples/common/classes/NvElement.cpp \
jetson_multimedia_api/samples/common/classes/NvElementProfiler.cpp \
jetson_multimedia_api/samples/common/classes/NvLogging.cpp \
jetson_enc.cpp \
-L /usr/lib/aarch64-linux-gnu/nvidia/ \
-lnvjpeg
$ ./a.out
$ ffplay -i jetson_420.jpg
エンコード結果はJPEGです。ffplayでも普段お使いの画像ビューアでも、何を使って確認しても構いません。
この記事にコメントする
目次: Linux
半年経ったら完全に忘れるのでメモします。最近JPEGのデコードエンコードが必要になって色々調べていました。Jetson特有のAPI群がありまして、API名はJetson Linux API(のなかのMultimedia APIs)だそうです(Jetson Linux APIドキュメント)。ハードウェアJPEGデコーダ/エンコーダ(NVJPG)を自動的に使用してくれます。
今回はデコードのAPIをご紹介します。APIの使い方は簡単でこんな感じでした。
NvJPEGDecoder *jpgdec = nullptr;
NvBuffer *outbuf = nullptr;
int r;
// Create
jpgdec = NvJPEGDecoder::createJPEGDecoder("jpgdec");
// Decoding
uint32_t jpeg_pixfmt;
uint32_t jpeg_width;
uint32_t jpeg_height;
r = jpgdec->decodeToBuffer(&outbuf, jpegbuf, jpegsize, &jpeg_pixfmt, &jpeg_width, &jpeg_height);
// Destroy
delete outbuf;
delete jpgdec;
注意すべき点は2つあります。1つ目はdecodeToBuffer()が勝手にoutbufを確保して返してくるので、delete outbufしなければならないことです。Turbo JPEGと異なり、予めバッファを確保しておけばメモリ確保処理を回避するような仕組みはなさそうでした。イマイチです。
2つ目はProgressive JPEGがデコードできないことです。デコードしようとするとdecodeToBuffer()でハングします。どんなJPEGファイルが来るかわからない状況で使う場合、JPEGファイルの中身をチェックしてBaseline JPEGはハードウェアデコード、Progressive JPEGはソフトウェアデコードする仕組みが必要です。
しかし前に話したとおりJetson APIの裏にいるlibnvjpeg.soが謎にIJG JPEGのAPIを実装しているため、本家IJG JPEGのlibjpeg.soがリンクできません。この状態でどうやってProgressive JPEGをソフトウェアデコードすれば良いのでしょう。Jetson APIの裏にいるlibnvjpeg.soをIJG JPEGだと思って呼べば動作するんでしょうか?
ソースコードを置いておきます。
使い方はコードの先頭にコメントで書いている通りですが、ここでも説明しておきます。引数はありません。ファイル名test_420.yuvのRaw YUV420ファイルを読み込んで、ファイル名jetson_420.jpgのJPEGファイルを書き出します。
$ g++ -g -O2 -g -Wall \
-I jetson_multimedia_api/include/ \
-I jetson_multimedia_api/include/libjpeg-8b/ \
jetson_multimedia_api/samples/common/classes/NvJpegDecoder.cpp \
jetson_multimedia_api/samples/common/classes/NvJpegEncoder.cpp \
jetson_multimedia_api/samples/common/classes/NvBuffer.cpp \
jetson_multimedia_api/samples/common/classes/NvElement.cpp \
jetson_multimedia_api/samples/common/classes/NvElementProfiler.cpp \
jetson_multimedia_api/samples/common/classes/NvLogging.cpp \
jetson_dec.cpp \
-L /usr/lib/aarch64-linux-gnu/nvidia/ \
-lnvjpeg
$ ./a.out
$ ffplay -f rawvideo -video_size 1920x1440 -pixel_format yuv420p -i jetson_420.yuv
デコード結果のRawvideoを確認するときはffplayを使うと便利です。
この記事にコメントする
目次: Linux
半年経ったら完全に忘れるのでメモします。最近JPEGのデコードエンコードが必要になって色々調べていました。Jetson特有のAPI群がありまして、API名はJetson Linux API(のなかのMultimedia APIs)だそうです(Jetson Linux APIドキュメント)。
Jetsonを持っていないと使えません。最新機種はJetson AGX OrinかJetson Orin Nanoですが、AGX Orinは40万円、Orin Nanoは10万円近くするボッタクリ価格です。Xavierまではそこそこ安かったのにねえ……。
Jetson Linux APIがもしシステムにインストールされていなければapt-get install nvidia-l4t-jetson-multimedia-apiで使えるようになるはずです。Jetson用のパッケージは一覧があります(一覧へのリンク)ので、手動でダウンロードしたいときにはありがたいですね。
Jetson Linux APIは実装も使い方もかなり独特というか……はっきり言って変です。
変な点その1、APIの提供方法です。API実装は/usr/src/jetson_multimedia_api/samples/common/classesにソースコードで置かれています。ライセンスはBSDライセンスです。なぜ*.soや*.aで提供しないのでしょう?
後ほど紹介するJPEGデコード、エンコードのコンパイルにはNvJpegDecoder.cppとNvJpegEncoder.cppとお供のツール類が必要ですから、あえてディレクトリ名を省かずに書くと、下記のようなコマンドでコンパイルできます。面倒ならばjetson_multimedia_api/samples/common/classes/*.cppで良いです、NVIDIA提供のサンプルもワイルドカードを使っていました。
g++ -O2 -g -Wall \ -I jetson_multimedia_api/include/ \ -I jetson_multimedia_api/include/libjpeg-8b/ \ jetson_multimedia_api/samples/common/classes/NvJpegDecoder.cpp \ jetson_multimedia_api/samples/common/classes/NvJpegEncoder.cpp \ jetson_multimedia_api/samples/common/classes/NvBuffer.cpp \ jetson_multimedia_api/samples/common/classes/NvElement.cpp \ jetson_multimedia_api/samples/common/classes/NvElementProfiler.cpp \ jetson_multimedia_api/samples/common/classes/NvLogging.cpp \ (自分のソースコード) \ -L /usr/lib/aarch64-linux-gnu/nvidia/ \ -lnvjpeg
変な点その2、JPEG機能のヘッダ名です。jetson_multimedia_api/include/NvJpegDecoder.hを見ると、JPEG関連の機能はjetson_multimedia_api/include/libjpeg-8b/jpeglib.hにあるjpeglib.hヘッダにて定義されています。そう、このヘッダ名はIJG JPEGライブラリのヘッダと同じ名前です。なぜそんな場所にヘッダを置くのか?IJG JPEGと互換性がない(APIが増えている)のになぜ同じ名前?
変な点その3、JPEG機能のライブラリ名です。jpeglib.hだからlibjpeg.soかと思うじゃないですか、それは罠でリンクすべきはlibnvjpeg.soです。良くないことにlibjpeg.soも存在していて、さらに良くないことにデコード機能だけ使っている場合は間違ってlibjpeg.soを指定してもリンクできてしまい、
$ ./a.out JPEG parameter struct mismatch: library thinks size is 656, caller expects 776
実行時にこのようなエラーが出るだけでうんともすんとも動きません。IJG JPEGと同じAPI名にするならTurbo JPEGのようにlibjpeg.soの方も置き換えれば問題は起きなかったのに、なぜ中途半端な実装に?
変な点その4、Jetson APIとNVJPEGの衝突です。以前紹介したNVJPEGはJetsonでも動きますがインストールはオススメしないです。
この2つが両方ともインストールされ、同じ-lnvjpegでリンクしてもライブラリパスによってリンクエラーになったり実行時のダイナミックリンクエラーになります。全く違う機能とAPI体系なのに、なぜ同じライブラリ名になっているの?
JPEGデコードの話は次にしたいと思います。Jetsonは適当なのか突貫なのか知りませんが、すっごい変だしちょっと使っただけなのに無限に疑問が出てきます。みんな良くこんなの使ってるなあ……。
この記事にコメントする
| < | 2024 | > | ||||
| << | < | 12 | > | >> | ||
| 日 | 月 | 火 | 水 | 木 | 金 | 土 |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 | - | - | - | - |
23年4月10日
25年10月22日
23年6月1日
05年11月23日
05年11月22日
15年5月8日
15年3月9日
25年10月29日
25年10月31日
25年10月27日
25年10月15日
25年10月18日
22年5月5日
25年10月19日
23年4月11日
06年4月22日
25年10月17日
25年10月6日
25年10月13日
20年10月23日
wiki
Linux JM
Java API
2002年
2003年
2004年
2005年
2006年
2007年
2008年
2009年
2010年
2011年
2012年
2013年
2014年
2015年
2016年
2017年
2018年
2019年
2020年
2021年
2022年
2023年
2024年
2025年
過去日記について
アクセス統計
サーバ一覧
サイトの情報合計:
本日: