PhotoModule.java
void saveFinalPhoto(final byte[] jpegData, NamedEntity name,
final ExifInterface exif, CameraProxy camera, boolean burstMode) {
...
if (needInitData() && !burstMode) {
if (!mPaused)//SPRD:fix bug1539079
initData(jpegData, title, date, width, height,
orientation, exif, mLocation,
mOnMediaSavedListener, mIsSmallPicture);
...
}
点击拍照之后,待缩略图拍照完成,调用 initData 初始化初步数据
AudioPictureModule.java
@Override
protected void initData(byte[] jpegData, String title, long date,
int width, int height, int orientation, ExifInterface exif,
Location location,
MediaSaver.OnMediaSavedListener onMediaSavedListener, boolean smallPicture) {
...
if (mDataModule.getBoolean(Keys.KEY_CAMERA_SHUTTER_SOUND)) {
mHandler.sendEmptyMessageDelayed(PhotoVoiceMessage.MSG_RECORD_AUDIO, 680);
} else {
mHandler.sendEmptyMessageDelayed(PhotoVoiceMessage.MSG_RECORD_AUDIO, 300);
}
...
}
PhotoModule.java
case PhotoVoiceMessage.MSG_RECORD_AUDIO: {
module.startAudioRecord();
break;
AudioPictureModule.java
@Override
protected void startAudioRecord() { // 开始录音
boolean result = mPhotoVoiceRecorder.startAudioRecord();
mShutterClicked = result;//SRPD:fix bug957941
if (isGestureRecongnitionRunning()) {
mUI.hideGestureRecognitionPalmImage();
mUI.showGestureRecognitionTip(false);
}
if (result) {
isAudioRecording = true;
if (isHdr() || isHdrPicture()) {
mUI.showHdrTips(false);
}
mFocusManager.stopFocusAnima();
((AudioPictureUI)mUI).showAudioNoteProgress(); // 显示录音进度
mActivity.getCameraAppUI().updateRecordVoiceUI(View.INVISIBLE);
freezePreview();
} else {
mPhotoVoiceRecorder.savePhoto(null);
onAudioRecordStopped();//SPRD:fix bug1274467
mAPMAppController.getCameraAppUI().setBottomPanelLeftRightClickable(true);
}
if (mDataModuleCurrent.getBoolean(Keys.KEY_ADD_LEVEL)) {
mUI.setLevelVisiable(false);
}
}
PhotoVoiceRecorder.java
public boolean startAudioRecord() {
if (mMediaRecorder != null) {
return false;
}
String directory = null;
StorageUtil util = StorageUtil.getInstance();
directory = util.getPhotoVoiceDirectory();
try {
File f = new File(directory);
f.mkdirs();
} catch (Exception e) {
return false;
}
mRecordAudioFile = directory + "/" + mRecordTitle + ".aac"; // 录音文件保存的路径
if (mMediaRecorder == null) {
initlizeMediaRecorder();
}
mMediaRecorder.setOutputFile(mRecordAudioFile);
try {
if (!isAudioFocusGain) {
if (requestAudioFocus() == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
mMediaRecorder = null;
mHandler.sendEmptyMessage(PhotoVoiceMessage.MSG_RECORD_STOPPED);
return false;
}
}
mMediaRecorder.prepare();
mMediaRecorder.start(); // 开始录音
} catch (IllegalStateException exception) {
if (mMediaRecorder != null) {
abandonAudioFocus();
try {
mMediaRecorder.release();
} catch (RuntimeException e) {
}
mMediaRecorder = null;
mHandler.sendEmptyMessage(PhotoVoiceMessage.MSG_RECORD_STOPPED);
}
mRecordExif.setTagValue(ExifInterface.TAG_CAMERATYPE_IFD, FilmstripItemData.TYPE_NORMAL);
return false;
} catch (Exception e) {
mMediaRecorder = null;
mRecordExif.setTagValue(ExifInterface.TAG_CAMERATYPE_IFD, FilmstripItemData.TYPE_NORMAL);
return false;
}
return true;
}
private void initlizeMediaRecorder() { // 初始化录音机
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mMediaRecorder.setMaxDuration(PhotoVoiceRecordProgress.LIMIT_TIME * 1000);
mMediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {
@Override
public void onError(MediaRecorder mr, int what, int extra) {
if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) {
stopAudioRecord();
}
}
});
mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
@Override
public void onInfo(MediaRecorder mr, int what, int extra) {
if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) { // 最长录音时间限制
stopAudioRecord();
} else if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) { // 最大录音文件限制
stopAudioRecord();
}
}
});
}
public synchronized void stopAudioRecord() {
if (mMediaRecorder != null) {
abandonAudioFocus();
try {
mMediaRecorder.stop();//SPRD:fix bug598422
} catch (RuntimeException e) {
mRecordAudioFile = null;//SPRD:fix bug1009124
mRecordExif.setTagValue(ExifInterface.TAG_CAMERATYPE_IFD, FilmstripItemData.TYPE_NORMAL);//SPRD:fix bug1389520
}
mMediaRecorder.release();
mMediaRecorder = null;
savePhoto(mRecordAudioFile);
mHandler.sendEmptyMessage(PhotoVoiceMessage.MSG_RECORD_STOPPED);
}
}
public void savePhoto(String recordAudioFile) {
mMediaSaver.addImage(mRecordJpeg, mRecordTitle,
mRecordDate, mRecordLocation, mRecordWidth, mRecordHeight,
mRecordOrientation, mRecordExif, mOnMediaSavedListener,
recordAudioFile);
}
MediaSaverImpl.java
addImage() {
...
ImageSaveTask t = new ImageSaveTask(data, title, date,
(loc == null) ? null : new Location(loc),
width, height, orientation, mimeType, exif, mContentResolver, l, photoVoicePath, builder);
...
}
ImageSaveTask.class
protected Uri doInBackground(Void... v) {
...
Storage.addImage(resolver, title, date, loc, orientation, exif, data, width, height, mimeType, photoVoicePath, builder);
...
}
Storage.java
public static Uri addImage(ContentResolver resolver, String title, long date,
Location location, int orientation, ExifInterface exif, byte[] data, int width,
int height, String mimeType, String photoVoicePath, XmpBuilder builder) throws IOException {
mContentResolver = resolver;
StorageUtil storageUtil = StorageUtil.getInstance();
String path = null;
Integer val = exif.getTagIntValue(ExifInterface.TAG_CAMERATYPE_IFD);
val = getCameraTypeIFD(val,exif,photoVoicePath); // val = 53, TYPE_AUDIO_PIC
...
if (val != null) {
fileTag = val.intValue();
if (builder == null) {
builder = setSpecialTypeId(fileTag); // xmpBuilder.setSpecialTypeId("AUDIO_PHOTO_TYPE");
}
}
...
Uri insertUri = addImageToMediaStore(resolver, title, date, location, orientation, 0,
path, width, height, mimeType, photoVoicePath, fileTag, 1); // 插入 MediaProvider 数据库
...
dealPictureVoiceAudioInfo(fileInfo.fileLength, path, exif, photoVoicePath, fileInfo.pendingPath); // 把录音文件写入到 JPEG 之后删除
if (insertUri != null) {
updateImageToMediaStore(resolver, insertUri, title, date, location, orientation, fileInfo.fileLength,
path, width, height, mimeType, photoVoicePath, fileTag, 0); // 更新 MediaProvider 数据库
}
...
}
private static Integer getCameraTypeIFD(Integer val,ExifInterface exif,String photoVoicePath) {
if (photoVoicePath == null) {
if (val != null) {
val = (val == FilmstripItemData.TYPE_AUDIO_PIC ? FilmstripItemData.TYPE_NORMAL : val);
}
exif.setTagValue(ExifInterface.TAG_CAMERATYPE_IFD, val);
}
return val;
}
10-12 00:15:15.067 26507 27515 E zq8888 : Time:2023_10_12_18_59_36-->com.android.camera.Storage-->addImage()-->174 val:53
从 log 中看 val = 53, 这是从哪里设置的呢?
PhotoModule.java
private void modifyAudioPictureType(final ExifInterface exif) {
if (mIsSmallPicture){
exif.setTagValue(ExifInterface.TAG_CAMERATYPE_IFD, new Integer(TYPE_AUDIO_PHOTO_THUMB));
} else {
exif.setTagValue(ExifInterface.TAG_CAMERATYPE_IFD, new Integer(TYPE_AUDIO_PIC)); // 这里设置了 TAG_CAMERATYPE_IFD = TYPE_AUDIO_PIC
}
}
private void modifyCameraPicType(final ExifInterface exif , boolean burstMode , String title) {
if (mIsSmallPicture) {
exif.setTag(exif.buildTag(ExifInterface.TAG_CAMERATYPE_IFD, new Integer(TYPE_THUMBNAIL)));
}
if (burstMode && !mIsSmallPicture) {
exif.setTagValue(ExifInterface.TAG_CAMERATYPE_IFD, new Integer(TYPE_BURST));
} else if (getModuleTpye() == DreamModule.AUDIOPICTURE_MODULE) {
modifyAudioPictureType(exif); // 这里
} else if (mIsSmallPicture && (getModuleTpye() == DreamModule.REFOCUS_MODULE)){
exif.setTagValue(ExifInterface.TAG_CAMERATYPE_IFD,new Integer(TYPE_BOKEH_THUMB));
}
}
void saveFinalPhoto(final byte[] jpegData, NamedEntity name, final ExifInterface exif, CameraProxy camera, boolean burstMode) {
...
modifyCameraPicType(exif, burstMode , title); // 这里?
...
}
// 处理录音文件
private static void dealPictureVoiceAudioInfo(long fileLength,String path, ExifInterface exif,String photoVoicePath,String pendingPath) throws IOException {
...
} else if (photoVoicePath != null){
ExifTag[] jpegLenTag = new ExifTag[] {exif.buildTag(ExifInterface.TAG_JPEG_LENGTH, new Long(fileLength))};
Collection<ExifTag> reWritePara = Arrays.asList(jpegLenTag);
exif.rewriteExif(pendingPath, reWritePara);
FileInputStream fin = null;
RandomAccessFile randomAccessFile = null;
try {
fin = new FileInputStream(photoVoicePath);
int length = fin.available();
byte[] buffer = new byte[length];
fin.read(buffer);
randomAccessFile = new RandomAccessFile(pendingPath,"rw");
randomAccessFile.seek(new File(pendingPath).length());
randomAccessFile.write(buffer); // 把录音文件写入到 pendingPath 这个临时 jpeg 文件的最后,DCIM/Camera/.pending-1697647734-IMG_20231012_004849.jpg
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fin == null) {
return;
}
fin.close();
randomAccessFile.close();
File d = new File(photoVoicePath);
if (d.exists() && d.isFile() && !d.isHidden()) {
d.delete(); // 最后删除临时录音文件
}
}
}
}