[SPRD]Android展锐有声拍照功能研究

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();                                             // 最后删除临时录音文件
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值