检测视频文件何时真正写入?

2024-01-09

cwac-camera 库具有在保存照片之前调用的事件挂钩:

@Override public void saveImage(PictureTransaction xact, byte[] image) {}

我是否只是忽略了这一点,或者是否没有接收器告诉我视频文件何时被保存?

更多背景信息:有两种方式结束视频。一种是通过调用

.stopRecording();

另一种是通过配置记录器的最大持续时间/最大文件大小(在这种情况下,无需显式调用 stop Recording() 即可停止记录:

recorder.setMaxDuration()

在我看来,在这两种情况下,Android 都需要大约 100 到 200 毫秒才能完成文件。由于我刚使用时视频文件经常损坏

mediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
        @Override public void onInfo(MediaRecorder mr, int what, int extra) {}
}

检测它何时完成或尝试在调用停止 Recording() 后立即读取它。我尝试使用 FileObserver 来检测媒体记录器何时实际关闭文件,效果很好。

我想讨论一下这个问题,如果有人有同样的经历,以及是否有一种方法可以让 CWAC-Camera 检测并发出录制文件的信号。

--

我刚刚在 Android 文档中跳过了这个http://developer.android.com/reference/android/media/MediaRecorder.html#setMaxDuration(int) http://developer.android.com/reference/android/media/MediaRecorder.html#setMaxDuration(int)

“停止是异步发生的,不能保证录音机在通知侦听器时已经停止。”

这解释了为什么文件有时没有正确关闭,并且隐含地意味着使用 FileObserver 可能是在写入文件后访问文件的唯一安全且有效的方法。

所以我们需要这样的东西来检测事件。

@Override protected File getVideoPath() {
    File file = super.getVideoPath(); // assemble the video path
    fileObserverPath = file.getAbsolutePath();

    fileObserver = new FileObserver(fileObserverPath, FileObserver.CLOSE_WRITE) {
        @Override public void onEvent(int event, String videoPath) {

            System.out.println("**** CameraHost: write closed");

            if (fileObserver != null) { // defensive
                fileObserver.stopWatching();
                fileObserver = null;
            }

            // process the file here
        }
    };
    fileObserver.startWatching();

    return file;
}

--

我继续将以下代码添加到我的 CameraHost 中以获取计时:

@Override public void configureRecorderOutput(int cameraId, MediaRecorder recorder) {

    System.out.println("**** CameraHost: configureRecorderOutput");

    recorder.setMaxDuration(Constants.FRAMEPLAYER_MAX_RECORDING_LENGTH_PER_FRAME_MSEC);
    recorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
        @Override public void onInfo(MediaRecorder mr, int what, int extra) {
            switch (what) {
                case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED: {

                    System.out.println("**** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED");

                    break;
                }
            }
        }
    });

    super.configureRecorderOutput(cameraId, recorder);
}

Timings:

11-20 07:33:28.974  32602-32602/cc.closeup.android I/System.out﹕ **** CameraHost: configureRecorderOutput
11-20 07:33:31.064  32602-32602/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
11-20 07:33:31.084  32602-32602/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
11-20 07:33:36.914  32602-32655/cc.closeup.android I/System.out﹕ **** CameraHost: write closed
11-20 07:33:36.914  32602-32655/cc.closeup.android I/System.out﹕ **** CameraHost: write closed

有两件事很奇怪:

  1. MEDIA_RECORDER_INFO_MAX_DURATION_REACHED 被引发,但录制不会停止。与 Android 文档中指定的相反,我必须显式调用 stop。
  2. 尽管 configureRecorderOutput 仅引发一次,但我的侦听器被调用两次。我尝试了相同的外部 CWAC 摄像头,但听众只被调用一次。

不确定我是否在某个地方犯了错误,或者 CWAC 相机出了什么问题。

有人来评论一下吗?

--

关于2.,无论是什么,我的日志显示触发事件的媒体记录器是相同的,并且在what=800(MEDIA_RECORDER_INFO_MAX_DURATION_REACHED)之后不久,引发了未记录的what=536871912。第二次引发该事件时,未记录的 What=268436456。

[来自华硕 Memo Pad 7:]

11-20 09:41:25.748    3398-3398/cc.closeup.android I/System.out﹕ **** CameraHost: onInfo: mr = android.media.MediaRecorder@41c9da88, what = 800, extra 0
11-20 09:41:25.748    3398-3398/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
11-20 09:41:25.748    3398-3398/cc.closeup.android I/System.out﹕ **** CameraHost: onInfo: mr = android.media.MediaRecorder@41c9da88, what = 536871912, extra 0

11-20 09:41:25.778    3398-3398/cc.closeup.android I/System.out﹕ **** CameraHost: onInfo: mr = android.media.MediaRecorder@41c9da88, what = 800, extra 0
11-20 09:41:25.778    3398-3398/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
11-20 09:41:25.778    3398-3398/cc.closeup.android I/System.out﹕ **** CameraHost: onInfo: mr = android.media.MediaRecorder@41c9da88, what = 268436456, extra 0

我在另一台设备上运行了这个,其中 MEDIA_RECORDER_INFO_MAX_DURATION_REACHED 事件也引发了两次,但是未记录的什么=?根本没有提高。供应商具体实施?

[来自三星 Galaxy Note 2:]

11-20 09:46:42.711  25699-25699/cc.closeup.android I/System.out﹕ **** CameraHost: onInfo: mr = android.media.MediaRecorder@42831c30, what = 800, extra 0
11-20 09:46:42.711  25699-25699/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED

11-20 09:46:42.776  25699-25699/cc.closeup.android I/System.out﹕ **** CameraHost: onInfo: mr = android.media.MediaRecorder@42831c30, what = 800, extra 0
11-20 09:46:42.776  25699-25699/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED

我继续发帖...

--

关于1.,录制实际上已停止(录制的文件长一秒),但FileObserver()显然只是在很晚之后才被调用,即在我显式调用cameraView.stop()之后。

我尝试直接在事件中传递的 MediaRecorder 上调用 stop() ,这在 Android 中有效,但在 CWAC 中无效。

--

关于1.,我进一步深入研究了这一点,看起来视频文件一直被写入,直到我显式调用cameraView.stop() - 然后它才会被截断为一秒并关闭。

// startRecording() called here

11-20 11:29:49.072    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:49.072    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 32, videoPath = media0.mp4
11-20 11:29:49.082    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:49.082    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:49.082    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:49.082    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:49.082    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:49.082    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.132    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.132    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.132    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.132    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4

11-20 11:29:51.132    4987-4987/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED

11-20 11:29:51.142    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.142    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.142    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.142    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.142    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
(...)
11-20 11:29:51.182    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.182    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.182    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4

11-20 11:29:51.192    4987-4987/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED

11-20 11:29:51.192    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.192    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.192    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.192    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.192    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
(...)
11-20 11:29:51.252    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.262    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.262    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.262    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.262    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:51.262    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4

// stopRecording() called here

11-20 11:29:52.492    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.492    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.492    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.492    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
(...)
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 8, videoPath = media0.mp4
11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: write closed: videoPath = media0.mp4

FileObserver 可用于检测文件何时最终写入。它可以挂接到 CameraHost 中的 getVideoDirectory() 重写中:

@Override protected File getVideoDirectory() {
    fileObserver = new FileObserver(videoDirectory.getAbsolutePath(), FileObserver.CLOSE_WRITE) {
        @Override public void onEvent(int event, String videoPath) {
            System.out.println("**** CameraHost: FileObserver: onEvent: event = " + event + ", videoPath = " + videoPath);

            if (fileObserver != null && videoPath != null && event == FileObserver.CLOSE_WRITE) { // do not process directory
                System.out.println("**** CameraHost: write closed: videoPath = " + videoPath);

                // fileObserver.stopWatching(); // TODO this needs to be closed somewhere
                // fileObserver = null;

                raiseVideo(videoPath);
            }
        }
    };
    fileObserver.startWatching();

    return videoDirectory;
}

FileObserver 有一个限制,它只有在显式调用 stopRecording() 时才能准确工作。如果由于达到预定义的文件大小或持续时间而终止录制,则引发得太晚(另请参阅https://github.com/commonsguy/cwac-camera/issues/242 https://github.com/commonsguy/cwac-camera/issues/242 ).

使用 recorder.setOnInfoListener() 不起作用,因为它可能会在文件实际写入磁盘之前报告文件的完成情况。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

检测视频文件何时真正写入? 的相关文章

随机推荐

  • 替换嵌套 NSDictionary 中出现的 NSNull

    这个问题类似于这个问题 https stackoverflow com questions 8075147 replace all nsnull objects in an nsdictionary 但是此方法仅适用于字典的根级别 我正在寻
  • 将 Maildir 转换为 mbox

    我正在更改主机 并且需要将旧服务器上的 maildir 帐户转换为新服务器上的 mbox 关于最好的方法有什么想法吗 我找到了这个 for i in new cur do formail gt mbox done 但我真的不太明白 我有 L
  • IIS 10 错误 500.19 (0x80070021) - 锁定冲突

    我有一个应用程序 我正在尝试从 Server 2012 IIS 8 迁移到 Server 2016 IIS 10 当我最初移动所有内容时 我收到了附加的错误消息 我已经阅读了几篇堆栈交换帖子 它基本上说该部分需要解锁 我已通过命令提示符命令
  • 在 SharedPreferences 中存储数组列表对象

    该方法将新对象添加到ArrayList get text from textview time date getText toString entry d entry getText toString dayName day getText
  • Angular Material 2 中的 md-table

    我正在与角材料表 https material angular io components table overview在Html代码中 有
  • 将项目升级到 MVC 5

    我一直在寻找一种解决方案来升级当前的 MVC 4 应用程序 我必须使用新的 MVC 5 二进制文件 我在任何地方都找不到解决方案 有人有主意吗 Visual Studio 2013 只需允许 Nuget 包管理器运行更新即可自动升级项目 S
  • 调用方法一次以在 Django Rest Framework 序列化器中设置多个字段

    如何使用 Django Rest Framework 序列化程序调用同一方法一次来设置多个字段 这就是我现在所做的 但这显然调用了该方法两次 如何限制它只能被调用一次 class MyModel models Model def GetTw
  • 我应该实现 onRetainNonConfigurationInstance 吗?

    我刚刚读到有关维护 Android 应用程序状态的内容 并偶然发现了 onRetainNonConfigurationInstance 但在阅读时文档 http developer android com reference android
  • Flutter:如何将 URI 转换为文件?

    我想将路径 content media external images media 138501 转换为文件并在图像中设置 Code File imageFile File content media external images med
  • Lucene索引中“-”字符的问题

    我在使用 Lucene 索引时遇到问题 该索引已索引包含 字符的单词 它适用于某些包含 的单词 但不适用于所有单词 我找不到它不起作用的原因 我正在搜索的字段经过分析并包含带有和不带有 字符的单词版本 我正在使用分析器 org apache
  • Graphviz dot - HTML 表格中的斜体文本不是斜体

    我在用着dot版本2 26 3 下列 dot内容 digraph html results shape none margin 0 label lt table border 0 cellspacing 0 cellpadding 4 tr
  • 从 CGPoints 数组中获取最大值和最小值

    我们有一组 CGPoints let points 1234 0 1053 0 1241 0 1111 0 1152 0 1043 0 我想做的是获取数组中 x 值最高的 CGPoint 和 y 值最高的 CGPoint 我将使用这些点来创
  • 您不是该客户端的沙箱用户。错误代码igniter-Instagram api

    In my codeigniter我正在尝试使用的项目Instagram API用于登录 This https github com ianckc CodeIgniter Instagram Library是我正在使用的库 但是当进入登录页
  • Android Studio 中 make 和 build 的区别

    Android Studio Build 菜单的选项包括 Make Project Rebuild Project 我应该什么时候使用它们 大多数时候你应该使用制作项目 有时 在添加库并对项目进行重大更改后 您应该使用重建项目 如果你看一下
  • 当我将项目从一个列表框传输到另一个列表框时,如何防止页面回发

    我的应用程序中有两个列表框 单击按钮后我将项目从一个列表框推送到另一个列表框 代码工作正常 但它会导致回发 当我将项目从一个列表框移动到另一个时 整个页面正在再次加载 如何我可以阻止这一切 这将是我的 aspx 页面上的代码 div cla
  • UISearchBar 文本颜色

    浏览文档 我找不到任何可以更改 UISearchBar 颜色的内容 有人知道如何更改吗 没有任何 textColor 属性 Thx 适用于 iOS 7 及更高版本 UITextField appearanceWhenContainedIn
  • SIMD 将 12 位字段解包为 16 位

    我需要从每个 24 位输入中解压缩两个 16 位值 3 字节 gt 4 字节 我已经以幼稚的方式做到了 但我对表现不满意 例如 InBuffer 是 m128i value1 uint16 t InBuffer 0 11 bit range
  • 如何让 ListView 部分标题固定下来

    我的按钮显示在屏幕顶部 使用反应本机可滚动选项卡视图 在按钮下面 我有ListView上面有节标题 当我滚动时 有没有办法让标题粘在选项卡视图的底部边缘 我很难得到ListView的节标题粘在 Facebook TabBar 的底部 默认情
  • CSS 旋转 IE7 和 8

    我还要问另一个问题 所以 CSS Rotate 在 ie9 中可以工作 但在以前的版本中让旋转工作将是我的死 filter progid DXImageTransform Microsoft BasicImage rotation 1 这行
  • 检测视频文件何时真正写入?

    cwac camera 库具有在保存照片之前调用的事件挂钩 Override public void saveImage PictureTransaction xact byte image 我是否只是忽略了这一点 或者是否没有接收器告诉我