JPG还是PNG?
JPG和PNG是两种常见的图片文件格式,在压缩方式、图像质量、透明效果和可编辑性等方面存在显著差异。
- 压缩方式:JPG是一种有损压缩格式,通过丢弃图像数据来减小文件大小,因此可能会损失一些图像细节和质量。而PNG使用的是无损压缩格式,它不会丢失任何原始图像数据,从而保持了图像的完整性和质量。
- 图像质量:由于压缩方式的不同,JPG在压缩后会牺牲一部分图像数据,因此在图像质量上可能存在损失,例如可能会出现锯齿状边缘或颜色失真。相比之下,PNG的无损压缩可以保证原图像数据的完整性,其256个透明层次的设定可以使图片边缘平滑融合,从而消除图片锯齿边缘。
- 透明效果:PNG支持透明度,可以用作背景透明的图片,而JPG则不支持透明效果。因此,如果你需要制作半透明的图像或者需要背景透明的图片,PNG是一个更好的选择。
- 可编辑性:JPG是一种不可编辑的图片格式,一旦被保存为JPG格式,就无法进行修改。而PNG是一种可编辑的图片格式,可以通过图像编辑软件(如Photoshop)进行修改、编辑和重新保存。例如,你可以改变PNG图片中的文字样式、线条等元素。
Android推流端的截图设计
大牛直播SDK早期在做Android平台RTMP推流和轻量级RTSP服务模块的时候,截图考虑到PNG的特性,直接保存png图片,随着GB28181-2022规范的实施,规范里面有明确要求,需要支持JPG编码,为此我们针对截图这块,做了如下的调整(对应:实时快照):
原接口:
/**
* 请使用新的CaptureImage接口, 这个接口只能保存PNG图片, 不推荐使用
* Save current image during publishing stream(实时快照)
*
* @param imageName: image name, which including fully path, "/sdcard/daniuliveimage/daniu.png", etc.
*
* @return {0} if successful
*/
public native int SmartPublisherSaveCurImage(long handle, String imageName);
值得注意的是,原接口如果需要截图,还需要调用SmartPublisherSaveImageFlag()。
新的接口,我们设计如下:
/**
* 新的截图接口, 支持JPEG和PNG两种格式
* @param compress_format: 压缩格式, 0:JPEG格式, 1:PNG格式, 其他返回错误
* @param quality: 取值范围:[0, 100], 值越大图像质量越好, 仅对JPEG格式有效, 若是PNG格式,请填100
* @param file_name: 图像文件名, 例如:/dirxxx/test20231113100739.jpeg, /dirxxx/test20231113100739.png
* @param user_data_string: 用户自定义字符串
* @return {0} if successful
*/
public native int CaptureImage(long handle, int compress_format, int quality, String file_name, String user_data_string);
如何调用?
废话不多说,直接上代码:
private SimpleDateFormat capture_image_date_format_;
class ButtonCaptureImageListener implements OnClickListener {
@SuppressLint("SimpleDateFormat")
public void onClick(View v) {
if(isPushingRtmp || isRecording || isRTSPPublisherRunning || isPushingRtsp)
{
if (null == capture_image_date_format_)
capture_image_date_format_ = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String timeStamp = capture_image_date_format_.format(new Date());
String imageFileName = timeStamp; //创建以时间命名的文件名称
String imagePath = imageSavePath + "/" + imageFileName;
int quality;
boolean is_jpeg = true;
if (is_jpeg) {
imagePath += ".jpeg";
quality = 100;
}
else {
imagePath += ".png";
quality = 100;
}
int capture_ret = libPublisher.CaptureImage(publisherHandle,is_jpeg?0:1, quality, imagePath, "test_user_data");
Log.i(TAG, "capture_image ret:" + capture_ret + ", file:" + imagePath);
}
else
{
Log.e(TAG, "快照失败,请确保在推送、录像或内置RTSP服务发布状态..");
}
}
}
截图成功,对应的event回调如下:
class EventHandeV2 implements NTSmartEventCallbackV2 {
@Override
public void onNTSmartEventCallbackV2(long handle, int id, long param1, long param2, String param3, String param4, Object param5) {
Log.i(TAG, "EventHandeV2: handle=" + handle + " id:" + id);
String publisher_event = "";
switch (id) {
.....
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CAPTURE_IMAGE:
publisher_event = "快照: " + param1 + " 路径:" + param3;
if (0 == param1) {
rename_image_file_name(param3, param2);
publisher_event = publisher_event + "截取快照成功.." + ", 用户数据:" + param4;
} else
publisher_event = publisher_event + "截取快照失败..";
break;
....
}
}
如果需要对截图后的文件重命名(比如gb28181,我们会把截图时间返上来),便于统一管理,参考代码如下:
private void rename_image_file_name(String file_name, long file_date_time_ms) {
if (null == file_name || file_name.isEmpty()
|| file_date_time_ms < 1 || null == capture_image_date_format_)
return;
try {
java.io.File file = new File(file_name);
if (!file.exists() || !file.isFile() || !file.canRead() || file.length() < 1)
return;
String file_name_extension = null;
int index = file_name.lastIndexOf('.');
if (index > -1)
file_name_extension = file_name.substring(index + 1);
Date file_date = new Date(file_date_time_ms);
String new_file_name = capture_image_date_format_.format(file_date);
if (file_name_extension != null && !file_name_extension.isEmpty())
new_file_name += "." + file_name_extension;
java.io.File new_file = new java.io.File(file.getParent(), new_file_name);
if (file.renameTo(new_file))
Log.i(TAG, "rename image file name ok, file_name:" + file_name + ", new:"+ new_file_name);
else
Log.e(TAG, "rename image file name failed, file_name:" + file_name);
} catch (Exception e) {
Log.e(TAG, "rename_image_file_name Exception:", e);
}
}
总结
Android平台RTMP推送、轻量级RTSP还是GB28181设备对接模块,选择哪种图片格式主要取决于具体的使用需求。如果你需要压缩图像文件并且不关心原始图像的完整性,JPG可能是一个更好的选择。而如果你需要保持原始图像的完整性和质量,或者需要制作背景透明的图片,那么PNG可能是更好的选择。