3. JPEG编码基础
任务说明
本案例目标:使用Android的YuvImage类,将NV21格式的YUV数据编码为JPEG字节流。你将实现一个工具方法,输入NV21字节数组及图像宽高,输出JPEG压缩后的字节数组,并验证编码结果可被正确解码为Bitmap。
核心实现
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.YuvImage;
import java.io.ByteArrayOutputStream;
public class JpegEncoder {
/**
* 将NV21数据编码为JPEG字节数组
*
* @param nv21Data NV21格式的YUV数据
* @param width 图像宽度
* @param height 图像高度
* @param quality JPEG压缩质量(0-100)
* @return JPEG编码后的字节数组;若输入无效返回null
*/
public static byte[] encodeNv21ToJpeg(byte[] nv21Data, int width, int height, int quality) {
if (nv21Data == null || width <= 0 || height <= 0) {
return null;
}
// NV21数据长度必须等于 1.5 * width * height
int expectedLength = width * height * 3 / 2;
if (nv21Data.length != expectedLength) {
return null;
}
// 创建YuvImage对象,NV21对应ImageFormat.NV21
YuvImage yuvImage = new YuvImage(nv21Data, ImageFormat.NV21, width, height, null);
// 将YUV数据压缩为JPEG
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
boolean success = yuvImage.compressToJpeg(new Rect(0, 0, width, height), quality, outStream);
if (!success) {
return null;
}
return outStream.toByteArray();
}
/**
* 验证JPEG数据能否被正确解码为Bitmap
*
* @param jpegData JPEG字节数组
* @return 解码成功返回Bitmap,失败返回null
*/
public static Bitmap decodeJpegToBitmap(byte[] jpegData) {
if (jpegData == null || jpegData.length == 0) {
return null;
}
return BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
}
}
使用示例
// 模拟生成一张纯色NV21图像(仅用于演示编码流程)
int width = 320;
int height = 240;
byte[] nv21 = generateSolidNv21(width, height, (byte) 128, (byte) 128);
// 编码为JPEG,质量设为85
byte[] jpegBytes = JpegEncoder.encodeNv21ToJpeg(nv21, width, height, 85);
if (jpegBytes != null) {
// 验证:解码JPEG并检查尺寸
Bitmap bitmap = JpegEncoder.decodeJpegToBitmap(jpegBytes);
if (bitmap != null) {
// 预期输出:bitmap.getWidth() == 320, bitmap.getHeight() == 240
// 预期输出:jpegBytes.length > 0(具体大小取决于图像内容与质量参数)
}
}
// 生成纯色NV21数据的辅助方法(仅测试用)
private static byte[] generateSolidNv21(int width, int height, byte yValue, byte uvValue) {
int ySize = width * height;
int uvSize = ySize / 2;
byte[] nv21 = new byte[ySize + uvSize];
// Y平面填充
for (int i = 0; i < ySize; i++) {
nv21[i] = yValue;
}
// UV平面填充(NV21为VU交替存储)
for (int i = 0; i < uvSize; i += 2) {
nv21[ySize + i] = uvValue; // V
nv21[ySize + i + 1] = uvValue; // U
}
return nv21;
}
落地与验证
运行环境:Android API 12+(YuvImage自API 1起可用,此处无特殊限制)。
验证步骤:
- 将
JpegEncoder类复制到你的Android工程中。 - 在任意
Activity或单元测试中调用encodeNv21ToJpeg(),传入有效的NV21数据。 - 调用
decodeJpegToBitmap()解码返回的JPEG字节。 - 判定标准:
decodeJpegToBitmap()返回的Bitmap非空,且其宽高与输入宽高一致(即320×240)。若输入数据无效(长度不匹配或宽高为0),方法返回null,此时应检查输入数据来源。
预期输出说明:以上代码中generateSolidNv21()生成的是纯色图像,编码后JPEG字节数取决于图像内容与质量参数,无法预先给出精确数值;但解码得到的Bitmap宽高必然等于输入宽高,这是由JPEG编码格式保证的确定性结果。