OSDN Git Service

スモール画像を取得するシーケンスを複数化。
authorMRSa <mrsa@myad.jp>
Fri, 1 Jan 2021 13:22:16 +0000 (22:22 +0900)
committerMRSa <mrsa@myad.jp>
Fri, 1 Jan 2021 13:22:16 +0000 (22:22 +0900)
26 files changed:
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/NikonInterfaceProvider.java
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/PtpIpInterfaceProvider.java
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/command/IPtpIpCommand.java
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/command/IPtpIpCommandPublisher.java
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/command/PtpIpCommandPublisher.kt [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/command/PtpIpCommandPublisher0.java [moved from app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/command/PtpIpCommandPublisher.java with 96% similarity]
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/command/messages/PtpIpCommandBase.java
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/playback/CanonPlaybackControl.java
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/playback/CanonReducedImageReceiver.kt [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/playback/CanonSmallImageReceiver.java
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/playback/ICanonSmallImageReceiver.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/pkremote/preference/IPreferencePropertyAccessor.java
app/src/main/java/net/osdn/gokigen/pkremote/preference/canon/CanonPreferenceFragment.java
app/src/main/java/net/osdn/gokigen/pkremote/preference/fujix/FujiXPreferenceFragment.java
app/src/main/java/net/osdn/gokigen/pkremote/preference/nikon/NikonPreferenceFragment.java
app/src/main/java/net/osdn/gokigen/pkremote/preference/olympus/OpcPreferenceFragment.java
app/src/main/java/net/osdn/gokigen/pkremote/preference/olympuspen/OlympusPenPreferenceFragment.java
app/src/main/java/net/osdn/gokigen/pkremote/preference/panasonic/PanasonicPreferenceFragment.java
app/src/main/java/net/osdn/gokigen/pkremote/preference/pixpro/PixproPreferenceFragment.java
app/src/main/java/net/osdn/gokigen/pkremote/preference/ricohgr2/RicohGr2PreferenceFragment.java
app/src/main/java/net/osdn/gokigen/pkremote/preference/sony/SonyPreferenceFragment.java
app/src/main/java/net/osdn/gokigen/pkremote/preference/theta/ThetaPreferenceFragment.java
app/src/main/res/values-ja/strings.xml
app/src/main/res/values/arrays.xml
app/src/main/res/values/strings.xml
app/src/main/res/xml/preferences_canon.xml

index aba6c5d..d4ecb6a 100644 (file)
@@ -38,7 +38,7 @@ import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpComma
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpCommandPublisher;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpCommunication;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.PtpIpAsyncResponseReceiver;
-import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.PtpIpCommandPublisher;
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.PtpIpCommandPublisher0;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.connection.NikonConnection;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.liveview.PtpIpLiveViewControl;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.status.IPtpIpRunModeHolder;
@@ -61,7 +61,7 @@ public class NikonInterfaceProvider implements INikonInterfaceProvider, IDisplay
     private final PtpIpHardwareStatus hardwareStatus;
     private PtpIpButtonControl ptpIpButtonControl;
     private NikonConnection nikonConnection;
-    private PtpIpCommandPublisher commandPublisher;
+    private PtpIpCommandPublisher0 commandPublisher;
     private PtpIpLiveViewControl liveViewControl;
     private PtpIpAsyncResponseReceiver asyncReceiver;
     private PtpIpZoomControl zoomControl;
@@ -85,7 +85,7 @@ public class NikonInterfaceProvider implements INikonInterfaceProvider, IDisplay
         {
             e.printStackTrace();
         }
-        commandPublisher = new PtpIpCommandPublisher(ipAddress, CONTROL_PORT);
+        commandPublisher = new PtpIpCommandPublisher0(ipAddress, CONTROL_PORT);
         liveViewControl = new PtpIpLiveViewControl(context, ipAddress, STREAM_PORT);
         asyncReceiver = new PtpIpAsyncResponseReceiver(ipAddress, ASYNC_RESPONSE_PORT);
         statusChecker = new NikonStatusChecker(activity, commandPublisher, ipAddress, EVENT_PORT);
index bc08cf8..539e639 100644 (file)
@@ -34,6 +34,7 @@ import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpComma
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpCommunication;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.PtpIpAsyncResponseReceiver;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.PtpIpCommandPublisher;
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.PtpIpCommandPublisher0;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.connection.CanonConnection;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.liveview.PtpIpLiveViewControl;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.playback.CanonPlaybackControl;
@@ -57,6 +58,7 @@ public class PtpIpInterfaceProvider implements IPtpIpInterfaceProvider, IDisplay
     private final PtpIpHardwareStatus hardwareStatus;
     private final PtpIpButtonControl ptpIpButtonControl;
     private final CanonConnection canonConnection;
+    //private final PtpIpCommandPublisher0 commandPublisher;
     private final PtpIpCommandPublisher commandPublisher;
     private final PtpIpLiveViewControl liveViewControl;
     private final PtpIpAsyncResponseReceiver asyncReceiver;
@@ -100,7 +102,8 @@ public class PtpIpInterfaceProvider implements IPtpIpInterfaceProvider, IDisplay
             ipAddress = "192.168.0.1";
         }
 
-        commandPublisher = new PtpIpCommandPublisher(ipAddress, CONTROL_PORT);
+        commandPublisher = new PtpIpCommandPublisher(ipAddress, CONTROL_PORT, false, false);
+        //commandPublisher = new PtpIpCommandPublisher0(ipAddress, CONTROL_PORT);
         liveViewControl = new PtpIpLiveViewControl(context, ipAddress, STREAM_PORT);
         asyncReceiver = new PtpIpAsyncResponseReceiver(ipAddress, ASYNC_RESPONSE_PORT);
         statusChecker = new PtpIpStatusChecker(context, commandPublisher, ipAddress, EVENT_PORT);
index e2f6383..86761c2 100644 (file)
@@ -52,4 +52,17 @@ public interface IPtpIpCommand
 
     // デバッグ用: ログ(logcat)に通信結果を残すかどうか
     boolean dumpLog();
+
+    // リトライオーバー発生時、コマンドを再送するか?
+    boolean isRetrySend();
+
+    // 最後に1回余計に受信をするか?
+    boolean isLastReceiveRetry();
+
+    // 受信待ち再試行回数
+    int maxRetryCount();
+
+    // リトライオーバーで再送するとき、SeqNoをインクリメントするか
+    boolean isIncrementSequenceNumberToRetry();
+
 }
index dcf33b7..bd01a13 100644 (file)
@@ -9,6 +9,10 @@ public interface IPtpIpCommandPublisher
 
     boolean flushHoldQueue();
 
+    int isExistCommandMessageQueue(int id);
+    int getCurrentQueueSize();
+    boolean flushQueue();
+
     void start();
     void stop();
 }
diff --git a/app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/command/PtpIpCommandPublisher.kt b/app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/command/PtpIpCommandPublisher.kt
new file mode 100644 (file)
index 0000000..b181035
--- /dev/null
@@ -0,0 +1,762 @@
+package net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command
+
+import android.util.Log
+import net.osdn.gokigen.pkremote.camera.utils.SimpleLogDumper
+import java.io.BufferedReader
+import java.io.ByteArrayOutputStream
+import java.io.DataOutputStream
+import java.io.InputStream
+import java.net.InetSocketAddress
+import java.net.Socket
+import java.util.*
+
+class PtpIpCommandPublisher(private val ipAddress : String, private val portNumber : Int, private val tcpNoDelay : Boolean, private val waitForever: Boolean) : IPtpIpCommandPublisher, IPtpIpCommunication
+{
+    private var isConnected = false
+    private var isStart = false
+    private var isHold = false
+    private var holdId = 0
+
+    private var socket : Socket? = null
+    private var dos: DataOutputStream? = null
+    private var bufferedReader: BufferedReader? = null
+
+    private var sequenceNumber = SEQUENCE_START_NUMBER
+    private var commandQueue: Queue<IPtpIpCommand> = ArrayDeque()
+    private var holdCommandQueue: Queue<IPtpIpCommand> = ArrayDeque()
+
+    init
+    {
+        commandQueue.clear()
+        holdCommandQueue.clear()
+    }
+
+    override fun isConnected(): Boolean
+    {
+        return (isConnected)
+    }
+
+    override fun connect(): Boolean
+    {
+        try
+        {
+            Log.v(TAG, " connect()")
+            socket = Socket()
+            socket?.tcpNoDelay = tcpNoDelay
+            if (tcpNoDelay)
+            {
+                //socket?.tcpNoDelay = true
+                //socket?.keepAlive = false
+                socket?.keepAlive = false
+                //socket?.setPerformancePreferences(0, 1, 2)
+                socket?.setPerformancePreferences(0, 1, 2)
+                //socket?.setPerformancePreferences(0, 1, 2)
+                //socket?.setPerformancePreferences(1, 0, 0)
+                //socket?.setPerformancePreferences(0, 0, 2)
+                socket?.oobInline = true
+                //socket?.reuseAddress = false
+                socket?.trafficClass = 0x80 // 0x80
+                socket?.soTimeout = 0
+                //socket?.soTimeout = 0
+                //socket?.receiveBufferSize = 8192  // 10240 // 16384 // 6144// 8192 // 49152 // 65536 // 32768
+                //socket?.sendBufferSize = 1024 // 8192 // 4096 // 2048 // 10240
+                socket?.setSoLinger(true, 500)
+                //socket?.setReceiveBufferSize(2097152);
+                //socket?.setSendBufferSize(524288);
+
+                Log.v(TAG, " SOCKET (SEND:${socket?.sendBufferSize}, RECV:${socket?.receiveBufferSize}) oob:${socket?.oobInline} SO_TIMEOUT:${socket?.soTimeout}ms trafficClass:${socket?.trafficClass}")
+            }
+            socket?.connect(InetSocketAddress(ipAddress, portNumber), 0)
+            isConnected = true
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+            isConnected = false
+            socket = null
+        }
+        return (isConnected)
+    }
+
+    override fun disconnect()
+    {
+        try
+        {
+            dos?.close()
+            bufferedReader?.close()
+            socket?.close()
+            commandQueue.clear()
+        }
+        catch (e : Exception)
+        {
+            e.printStackTrace()
+        }
+        System.gc()
+
+        sequenceNumber = SEQUENCE_START_NUMBER
+        isConnected = false
+        isStart = false
+        dos = null
+        bufferedReader = null
+        socket = null
+    }
+
+    override fun start()
+    {
+        if (isStart)
+        {
+            // すでにコマンドのスレッド動作中なので抜ける
+            return
+        }
+        if (socket == null)
+        {
+            isStart = false
+            Log.v(TAG, " SOCKET IS NULL. (cannot start)")
+            return
+        }
+
+        isStart = true
+        Log.v(TAG, " start()")
+        val thread = Thread {
+            try
+            {
+                dos = DataOutputStream(socket?.getOutputStream())
+                while (isStart)
+                {
+                    try
+                    {
+                        val command = commandQueue.poll()
+                        command?.let { issueCommand(it) }
+                        Thread.sleep(COMMAND_POLL_QUEUE_MS.toLong())
+                    }
+                    catch (e: Exception)
+                    {
+                        e.printStackTrace()
+                    }
+                }
+            }
+            catch (e: Exception)
+            {
+                Log.v(TAG, "<<<<< IP : $ipAddress port : $portNumber >>>>>")
+                e.printStackTrace()
+            }
+        }
+        try
+        {
+            thread.start()
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    override fun stop()
+    {
+        isStart = false
+        commandQueue.clear()
+    }
+
+    override fun enqueueCommand(command: IPtpIpCommand): Boolean
+    {
+        try
+        {
+            if (isHold)
+            {
+                return (if (holdId == command.holdId) {
+                    if (command.isRelease)
+                    {
+                        // コマンドをキューに積んだ後、リリースする
+                        val ret = commandQueue.offer(command)
+                        isHold = false
+
+                        //  溜まっているキューを積みなおす
+                        while (holdCommandQueue.size != 0)
+                        {
+                            val queuedCommand = holdCommandQueue.poll()
+                            commandQueue.offer(queuedCommand)
+                            if (queuedCommand != null && queuedCommand.isHold)
+                            {
+                                // 特定シーケンスに入った場合は、そこで積みなおすのをやめる
+                                isHold = true
+                                holdId = queuedCommand.holdId
+                                break
+                            }
+                        }
+                        return ret
+                    }
+                    commandQueue.offer(command)
+                }
+                else
+                {
+                    // 特定シーケンスではなかったので HOLD
+                    holdCommandQueue.offer(command)
+                })
+            }
+            if (command.isHold)
+            {
+                isHold = true
+                holdId = command.holdId
+            }
+            if (commandQueue.size > 1)
+            {
+                // たまっているときだけログを吐く
+                Log.v(TAG, "Enqueue [ID: " + command.id + "] size: " + commandQueue.size)
+            }
+            return (commandQueue.offer(command))
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+        return (false)
+    }
+
+    override fun getCurrentQueueSize(): Int
+    {
+        return commandQueue.size
+    }
+
+    override fun flushQueue(): Boolean
+    {
+        Log.v(TAG, "  flushQueue()  size: ${commandQueue.size}")
+        // TODO: たまっているキューをダンプする
+        commandQueue.clear()
+        System.gc()
+        return (true)
+    }
+
+    override fun isExistCommandMessageQueue(id: Int): Int
+    {
+        var count = 0
+        for (cmd in commandQueue)
+        {
+            if (cmd.id == id)
+            {
+                count++
+            }
+        }
+       return count
+    }
+
+    override fun flushHoldQueue(): Boolean
+    {
+        Log.v(TAG, "  flushHoldQueue()")
+        holdCommandQueue.clear()
+        System.gc()
+        return (true)
+    }
+
+    private fun issueCommand(command: IPtpIpCommand)
+    {
+        try
+        {
+            var retryOver = true
+            while (retryOver)
+            {
+                //Log.v(TAG, "issueCommand : " + command.getId());
+                val commandBody = command.commandBody()
+                if (commandBody != null)
+                {
+                    // コマンドボディが入っていた場合には、コマンド送信(入っていない場合は受信待ち)
+                    sendToCamera(command.dumpLog(), commandBody, command.useSequenceNumber(), command.embeddedSequenceNumberIndex())
+                    val commandBody2 = command.commandBody2()
+                    if (commandBody2 != null)
+                    {
+                        // コマンドボディの2つめが入っていた場合には、コマンドを連続送信する
+                        sendToCamera(command.dumpLog(), commandBody2, command.useSequenceNumber(), command.embeddedSequenceNumberIndex2())
+                    }
+                    val commandBody3 = command.commandBody3()
+                    if (commandBody3 != null)
+                    {
+                        // コマンドボディの3つめが入っていた場合には、コマンドを連続送信する
+                        sendToCamera(command.dumpLog(), commandBody3, command.useSequenceNumber(), command.embeddedSequenceNumberIndex3())
+                    }
+                    if (command.isIncrementSeqNumber)
+                    {
+                        // シーケンス番号を更新する
+                        sequenceNumber++
+                    }
+                }
+                retryOver = receiveFromCamera(command)
+                if ((retryOver)&&(commandBody != null))
+                {
+                    if (!command.isRetrySend)
+                    {
+                        while (retryOver)
+                        {
+                            //  コマンドを再送信しない場合はここで応答を待つ...
+                            retryOver = receiveFromCamera(command)
+                        }
+                        break
+                    }
+                    if (!command.isIncrementSequenceNumberToRetry)
+                    {
+                        // 再送信...のために、シーケンス番号を戻す...
+                        sequenceNumber--
+                    }
+                }
+            }
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    /**
+     * カメラにコマンドを送信する(メイン部分)
+     *
+     */
+    private fun sendToCamera(isDumpReceiveLog: Boolean, byte_array: ByteArray, useSequenceNumber: Boolean, embeddedSequenceIndex: Int)
+    {
+        try
+        {
+            if (dos == null)
+            {
+                Log.v(TAG, " DataOutputStream is null.")
+                return
+            }
+
+            // メッセージボディを加工: 最初に4バイトのレングス長をつける
+            val sendData = ByteArray(byte_array.size + 4)
+            sendData[0] = (byte_array.size + 4).toByte()
+            sendData[1] = 0x00
+            sendData[2] = 0x00
+            sendData[3] = 0x00
+            System.arraycopy(byte_array, 0, sendData, 4, byte_array.size)
+            if (useSequenceNumber)
+            {
+                // Sequence Number を反映させる
+                sendData[embeddedSequenceIndex    ] = (0x000000ff and sequenceNumber).toByte()
+                sendData[embeddedSequenceIndex + 1] = (0x0000ff00 and sequenceNumber ushr 8 and 0x000000ff).toByte()
+                sendData[embeddedSequenceIndex + 2] = (0x00ff0000 and sequenceNumber ushr 16 and 0x000000ff).toByte()
+                sendData[embeddedSequenceIndex + 3] = (-0x1000000 and sequenceNumber ushr 24 and 0x000000ff).toByte()
+                if (isDumpReceiveLog)
+                {
+                    Log.v(TAG, "----- SEQ No. : $sequenceNumber -----")
+                }
+            }
+            if (isDumpReceiveLog)
+            {
+                // ログに送信メッセージを出力する
+                SimpleLogDumper.dump_bytes("SEND[" + sendData.size + "] ", sendData)
+            }
+
+            // (データを)送信
+            dos?.write(sendData)
+            dos?.flush()
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    private fun sleep(delayMs: Int)
+    {
+        try
+        {
+            Thread.sleep(delayMs.toLong())
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+    }
+
+    /**
+     * カメラからにコマンドの結果を受信する(メイン部分)
+     *
+     */
+    private fun receiveFromCamera(command: IPtpIpCommand): Boolean
+    {
+        val callback = command.responseCallback()
+        var delayMs = command.receiveDelayMs()
+        if (delayMs < 0 || delayMs > COMMAND_SEND_RECEIVE_DURATION_MAX)
+        {
+            delayMs = COMMAND_SEND_RECEIVE_DURATION_MS
+        }
+
+        return (if (callback != null && callback.isReceiveMulti)
+        {
+            // 受信したら逐次「受信したよ」と応答するパターン
+            //Log.v(TAG, " receiveMulti() : $delayMs [id:${command.id}] SEQ: $sequenceNumber")
+            receiveMulti(command, delayMs)
+        }
+        else
+        {
+            //Log.v(TAG, " receiveSingle() : $delayMs [id:${command.id}] SEQ: $sequenceNumber")
+            receiveSingle(command, delayMs)
+        })
+        //  受信した後、すべてをまとめて「受信したよ」と応答するパターン
+    }
+
+    private fun receiveSingle(command: IPtpIpCommand, delayMs: Int): Boolean
+    {
+        val isDumpReceiveLog = command.dumpLog()
+        val id = command.id
+        val callback = command.responseCallback()
+        try
+        {
+            val receiveMessageBufferSize = BUFFER_SIZE
+            val byteArray = ByteArray(receiveMessageBufferSize)
+            val inputStream = socket?.getInputStream()
+            if (inputStream == null)
+            {
+                Log.v(TAG, " InputStream is NULL... RECEIVE ABORTED.")
+                receivedAllMessage(isDumpReceiveLog, id, null, callback)
+                return (false)
+            }
+
+            // 初回データが受信バッファにデータが溜まるまで待つ...
+            var readBytes = waitForReceive(inputStream, delayMs, command.maxRetryCount())
+            if (readBytes <= 0)
+            {
+                // リトライオーバー...
+                Log.v(TAG, " RECEIVE : RETRY OVER...... : $delayMs ms x ${command.maxRetryCount()}  SEQ: $sequenceNumber isRetry: ${command.isRetrySend}")
+                if (!command.isRetrySend)
+                {
+                    // 再送しない場合には、応答がないことを通知する
+                    receivedAllMessage(isDumpReceiveLog, id, null, callback)
+                    return (false)
+                }
+                return (true)
+            }
+
+            // 受信したデータをバッファに突っ込む
+            var receivedLength = 0
+            val byteStream = ByteArrayOutputStream()
+            while (readBytes > 0)
+            {
+                readBytes = inputStream.read(byteArray, 0, receiveMessageBufferSize)
+                if (readBytes <= 0)
+                {
+                    Log.v(TAG, " RECEIVED MESSAGE FINISHED ($readBytes)")
+                    break
+                }
+                byteStream.write(byteArray, 0, readBytes)
+                sleep(delayMs)
+                receivedLength += readBytes
+                readBytes = inputStream.available()
+            }
+
+            Log.v(TAG, " receivedLength : $receivedLength")
+            if (receivedLength >= 4)
+            {
+                val outputStream = cutHeader(byteStream)
+                receivedAllMessage(isDumpReceiveLog, id, outputStream.toByteArray(), callback)
+            }
+            else
+            {
+                receivedAllMessage(isDumpReceiveLog, id, byteStream.toByteArray(), callback)
+            }
+            System.gc()
+        }
+        catch (e: Throwable)
+        {
+            e.printStackTrace()
+            System.gc()
+        }
+        return false
+    }
+
+    private fun receivedAllMessage(isDumpReceiveLog: Boolean, id: Int, body: ByteArray?, callback: IPtpIpCommandCallback?)
+    {
+        Log.v(TAG, "receivedAllMessage() : " + (body?.size ?: 0) + " bytes.")
+        if ((isDumpReceiveLog)&&(body != null))
+        {
+            // ログに受信メッセージを出力する
+            SimpleLogDumper.dump_bytes("RECV[" + body.size + "] ", body)
+        }
+        callback?.receivedMessage(id, body)
+    }
+
+    private fun receiveMulti(command: IPtpIpCommand, delayMs: Int): Boolean
+    {
+        val isDumpLog = command.dumpLog()
+        var maxRetryCount = command.maxRetryCount()
+        val id = command.id
+        val callback = command.responseCallback()
+        try
+        {
+            // Log.v(TAG, " ===== receive_multi() =====")
+            val receiveMessageBufferSize = BUFFER_SIZE
+            val byteArray = ByteArray(receiveMessageBufferSize)
+            val inputStream = socket?.getInputStream()
+            if (inputStream == null)
+            {
+                Log.v(TAG, " InputStream is NULL... RECEIVE ABORTED.")
+                return (false)
+            }
+
+            // 初回データが受信バッファにデータが溜まるまで待つ...
+            var readBytes = waitForReceive(inputStream, delayMs, command.maxRetryCount())
+            if (readBytes <= 0)
+            {
+                // リトライオーバー...
+                Log.v(TAG, " RECEIVE : RETRY OVER...... : $delayMs ms x ${command.maxRetryCount()}  SEQ: $sequenceNumber ")
+                if (command.isRetrySend)
+                {
+                    // 要求を再送する場合、、、ダメな場合は受信待ちとする
+                    Log.v(TAG, " --- SEND RETRY ---")
+                    return (true)
+                }
+                callback?.receivedMessage(id, null)
+                return (false)
+            }
+
+            // 初回データの読み込み...
+            var targetLength = parseDataLength(byteArray, readBytes)
+            readBytes = inputStream.read(byteArray, 0, receiveMessageBufferSize)
+            var receivedLength = readBytes
+            if (targetLength <= 0)
+            {
+                // もう一回データを読み直す...
+                targetLength = parseDataLength(byteArray, readBytes)
+            }
+            if ((targetLength == 0)&&(readBytes > 0))
+            {
+                // 知らないデータがついている...ダンプしてみる
+                // Log.v(TAG, " RECEIVE UNKNOWN BYTES : ${readBytes}")
+                if (isDumpLog)
+                {
+                    // ログに送信メッセージを出力する
+                    SimpleLogDumper.dump_bytes("RECV.UNKNOWN[${readBytes}] ", byteArray)
+                }
+                callback?.receivedMessage(id, null)
+                return (false)
+            }
+
+            if ((targetLength <= 0)||(readBytes <= 0))
+            {
+                // 受信サイズ異常の場合...
+                if (isDumpLog)
+                {
+                    if (receivedLength > 0)
+                    {
+                        SimpleLogDumper.dump_bytes("WRONG DATA : ", byteArray.copyOfRange(0, Math.min(receivedLength, 64)))
+                    }
+                    Log.v(TAG, " WRONG LENGTH. : $targetLength READ : $receivedLength bytes.")
+                }
+                callback?.receivedMessage(id, null)
+
+                // 受信したデータが不足しているので、もう一度受信待ち
+                Log.v(TAG, " 1st receive : AGAIN. [$readBytes][$targetLength]")
+                return (true)
+            }
+
+            //  初回データの受信を報告する。
+            if (isDumpLog)
+            {
+                Log.v(TAG, "  -=-=-=- 1st CALL : read_bytes : " + readBytes + "(" + receivedLength + ") : target_length : " + targetLength + "  buffer SIZE : " + byteArray.size)
+            }
+            callback?.onReceiveProgress(receivedLength, targetLength, byteArray.copyOfRange(fromIndex = 0, toIndex = receivedLength))
+
+            var isWaitLogging = true
+            while ((maxRetryCount > 0)&&(readBytes >= 0)&&(receivedLength < targetLength))
+            {
+                sleep(delayMs)
+                readBytes = inputStream.available()
+                if (readBytes <= 0)
+                {
+                    if (isWaitLogging)
+                    {
+                        Log.v(TAG, " WAIT is.available() ... [length: $receivedLength, target: $targetLength] $readBytes bytes, retry : $maxRetryCount  SEQ: $sequenceNumber")
+                        isWaitLogging = false
+                    }
+                    maxRetryCount--
+                    continue
+                }
+                if (!isWaitLogging)
+                {
+                    Log.v(TAG, " WAIT FOR RECEIVE COUNT: $maxRetryCount (SEQ: $sequenceNumber)")
+                }
+
+                readBytes = inputStream.read(byteArray, 0, receiveMessageBufferSize)
+                if (readBytes <= 0)
+                {
+                    if (isDumpLog)
+                    {
+                        Log.v(TAG, "  RECEIVED MESSAGE FINISHED ($readBytes) [receivedLength: $receivedLength, targetLength: $targetLength]")
+                    }
+                    break
+                }
+                receivedLength += readBytes
+                callback?.onReceiveProgress(receivedLength, targetLength, byteArray.copyOfRange(0, readBytes))
+                maxRetryCount = command.maxRetryCount()
+            }
+
+            // 最後のデータを受信した後にもう一度受信が必要な場合の処理...
+            if (command.isLastReceiveRetry)
+            {
+                var responseReceive = true
+                try
+                {
+                    while (responseReceive)
+                    {
+                        Log.v(TAG, "   --- isLastReceiveRetry is true ---  SEQ: $sequenceNumber")
+                        sleep(delayMs)
+                        if (inputStream.available() > 0)
+                        {
+                            readBytes = inputStream.read(byteArray, 0, receiveMessageBufferSize)
+                            if (readBytes > 0)
+                            {
+                                receivedLength += readBytes
+                                callback?.onReceiveProgress(receivedLength, targetLength, byteArray.copyOfRange(0, readBytes))
+                                Log.v(TAG, "   --- isLastReceiveRetry: onReceiveProgress() $readBytes bytes. ---  SEQ: $sequenceNumber ")
+                            }
+                            responseReceive = false
+                        }
+                        else
+                        {
+                            Log.v(TAG, "   --- inputStream.available() is <= 0 --- : ${inputStream.available()}  SEQ: $sequenceNumber")
+                            responseReceive = false
+                        }
+                    }
+                }
+                catch (ex: Exception)
+                {
+                    ex.printStackTrace()
+                }
+            }
+
+            //  終了報告...
+            if (isDumpLog)
+            {
+                Log.v(TAG, "  --- receive_multi : $id  ($readBytes) [$maxRetryCount] $receiveMessageBufferSize ($receivedLength) SEQ: $sequenceNumber")
+            }
+            val copyBytes = if (byteArray.size > receivedLength) { receivedLength } else { byteArray.size }
+            callback?.receivedMessage(id, byteArray.copyOfRange(0, copyBytes))
+        }
+        catch (e: Throwable)
+        {
+            e.printStackTrace()
+        }
+        return (false)
+    }
+
+    private fun parseDataLength(byte_array: ByteArray, read_bytes: Int): Int
+    {
+        var offset = 0
+        var lenlen = 0
+        try
+        {
+            if (read_bytes > 20)
+            {
+                if (byte_array[offset + 4].toUByte().toInt() == 0x07)
+                {
+                    // 前の応答が入っていると考える... レングスバイト分読み飛ばす
+                    offset = byte_array[offset].toUByte().toInt()
+                }
+                if (byte_array[offset + 4].toUByte().toInt() == 0x09)
+                {
+                    // データバイト...
+                    lenlen = (byte_array[offset + 15].toUByte().toInt() and 0xff shl 24) + (byte_array[offset + 14].toUByte().toInt() and 0xff shl 16) + (byte_array[offset + 13].toUByte().toInt() and 0xff shl 8) + (byte_array[offset + 12].toUByte().toInt() and 0xff)
+                }
+            }
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+        return (lenlen)
+    }
+
+    private fun cutHeader(receivedBuffer: ByteArrayOutputStream): ByteArrayOutputStream
+    {
+        try
+        {
+            val byteArray = receivedBuffer.toByteArray()
+            val limit = byteArray.size
+            var lenlen = 0
+            val len = (byteArray[3].toUByte().toInt() and 0xff shl 24) + (byteArray[2].toUByte().toInt() and 0xff shl 16) + (byteArray[1].toUByte().toInt() and 0xff shl 8) + (byteArray[0].toUByte().toInt() and 0xff)
+            val packetType = byteArray[4].toUByte().toInt() and 0xff
+            if ((limit == len)||(limit < 16384))
+            {
+                // 応答は1つしか入っていない。もしくは受信データサイズが16kBの場合は、そのまま返す。
+                return (receivedBuffer)
+            }
+
+            if (packetType == 0x09)
+            {
+                lenlen = (byteArray[15].toUByte().toInt() and 0xff shl 24) + (byteArray[14].toUByte().toInt() and 0xff shl 16) + (byteArray[13].toUByte().toInt() and 0xff shl 8) + (byteArray[12].toUByte().toInt() and 0xff)
+            }
+
+            if (lenlen == 0)
+            {
+                // データとしては変なので、なにもしない
+                return receivedBuffer
+            }
+            val outputStream = ByteArrayOutputStream()
+            var position = 20 // ヘッダ込の先頭
+            while (position < limit)
+            {
+                lenlen = (byteArray[position + 3].toUByte().toInt() and 0xff shl 24) + (byteArray[position + 2].toUByte().toInt() and 0xff shl 16) + (byteArray[position + 1].toUByte().toInt() and 0xff shl 8) + (byteArray[position].toUByte().toInt() and 0xff)
+
+                val copyByte = Math.min(limit - (position + 12), lenlen - 12)
+                outputStream.write(byteArray, position + 12, copyByte)
+                position += lenlen
+            }
+            return (outputStream)
+        }
+        catch (e: Throwable)
+        {
+            e.printStackTrace()
+            System.gc()
+        }
+        return (receivedBuffer)
+    }
+
+    private fun waitForReceive(inputStream : InputStream, delayMs: Int, retryCnt : Int): Int
+    {
+        var retryCount = retryCnt
+        var isLogOutput = true
+        var readBytes = 0
+        try
+        {
+            while ((retryCount >= 0) && (readBytes <= 0))
+            {
+                sleep(delayMs)
+                readBytes = inputStream.available()
+                if (readBytes <= 0)   // if (readBytes <= 0)
+                {
+                    if (isLogOutput)
+                    {
+                        Log.v(TAG, "waitForReceive:: is.available() WAIT... : $delayMs ms (Count : $retryCount/$retryCnt) SEQ: $sequenceNumber")
+                        isLogOutput = false
+                    }
+                    if (!waitForever)
+                    {
+                        retryCount--
+                    }
+                    else
+                    {
+                        Log.v(TAG, "waitForReceive: wait forever ")
+                        isLogOutput = false
+                    }
+                }
+            }
+            if (!isLogOutput)
+            {
+                Log.v(TAG, " --- waitForReceive : $readBytes bytes. (RetryCount : $retryCount/$retryCnt)")
+            }
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+        }
+        return (readBytes)
+    }
+
+    companion object
+    {
+        private val TAG = PtpIpCommandPublisher::class.java.simpleName
+
+        private const val SEQUENCE_START_NUMBER = 1
+        private const val BUFFER_SIZE = 1024 * 1024 + 16 // 受信バッファは 1MB
+        private const val COMMAND_SEND_RECEIVE_DURATION_MS = 5
+        private const val COMMAND_SEND_RECEIVE_DURATION_MAX = 3000
+        private const val COMMAND_POLL_QUEUE_MS = 5 // 5
+    }
+}
@@ -15,9 +15,9 @@ import java.util.Queue;
 
 import static net.osdn.gokigen.pkremote.camera.utils.SimpleLogDumper.dump_bytes;
 
-public class PtpIpCommandPublisher implements IPtpIpCommandPublisher, IPtpIpCommunication
+public class PtpIpCommandPublisher0 implements IPtpIpCommandPublisher, IPtpIpCommunication
 {
-    private static final String TAG = PtpIpCommandPublisher.class.getSimpleName();
+    private static final String TAG = PtpIpCommandPublisher0.class.getSimpleName();
 
     private static final int SEQUENCE_START_NUMBER = 1;
     private static final int BUFFER_SIZE = 1024 * 1024 + 16;  // 受信バッファは 256kB
@@ -35,10 +35,10 @@ public class PtpIpCommandPublisher implements IPtpIpCommandPublisher, IPtpIpComm
     private DataOutputStream dos = null;
     private BufferedReader bufferedReader = null;
     private int sequenceNumber = SEQUENCE_START_NUMBER;
-    private Queue<IPtpIpCommand> commandQueue;
-    private Queue<IPtpIpCommand> holdCommandQueue;
+    private final Queue<IPtpIpCommand> commandQueue;
+    private final Queue<IPtpIpCommand> holdCommandQueue;
 
-    public PtpIpCommandPublisher(@NonNull String ip, int portNumber)
+    public PtpIpCommandPublisher0(@NonNull String ip, int portNumber)
     {
         this.ipAddress = ip;
         this.portNumber = portNumber;
@@ -233,6 +233,34 @@ public class PtpIpCommandPublisher implements IPtpIpCommandPublisher, IPtpIpComm
         return (true);
     }
 
+    @Override
+    public int isExistCommandMessageQueue(int id)
+    {
+        int count = 0;
+        for (IPtpIpCommand cmd : commandQueue)
+        {
+            if (cmd.getId() == id)
+            {
+                count++;
+            }
+        }
+        return (count);
+    }
+
+    @Override
+    public int getCurrentQueueSize()
+    {
+        return (commandQueue.size());
+    }
+
+    @Override
+    public boolean flushQueue()
+    {
+        commandQueue.clear();
+        System.gc();
+        return (true);
+    }
+
     private void issueCommand(@NonNull IPtpIpCommand command)
     {
         try
index a25c881..feac3f0 100644 (file)
@@ -34,7 +34,7 @@ public class PtpIpCommandBase implements IPtpIpCommand, IPtpIpMessages
     @Override
     public int receiveDelayMs()
     {
-        return (15);
+        return (30);
     }
 
     @Override
@@ -108,4 +108,29 @@ public class PtpIpCommandBase implements IPtpIpCommand, IPtpIpMessages
     {
         return (true);
     }
+
+    @Override
+    public boolean isRetrySend()
+    {
+        return (false);
+    }
+
+    @Override
+    public boolean isLastReceiveRetry()
+    {
+        return (false);
+    }
+
+    @Override
+    public int maxRetryCount()
+    {
+        return (3500);
+    }
+
+    @Override
+    public boolean isIncrementSequenceNumberToRetry()
+    {
+        return (false);
+    }
+
 }
index 774710b..f19658a 100644 (file)
@@ -32,20 +32,20 @@ public class CanonPlaybackControl implements IPlaybackControl
     private final Activity activity;
     private final PtpIpInterfaceProvider provider;
     private final CanonFullImageReceiver fullImageReceiver;
-    private final CanonSmallImageReceiver smallImageReciever;
-    //private int delayMs = 20;
+    private final ICanonSmallImageReceiver smallImageReciever;
     private String raw_suffix = "CR2";
-    private boolean use_screennail_image = false;
-    private CanonImageObjectReceiver canonImageObjectReceiver;
+    private boolean useScreennailImage = false;
+    private final CanonImageObjectReceiver canonImageObjectReceiver;
 
     public CanonPlaybackControl(Activity activity, PtpIpInterfaceProvider provider)
     {
+        int smallImageSequence = 0;
         int delayMs = 20;
         try
         {
             SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
             raw_suffix = preferences.getString(IPreferencePropertyAccessor.CANON_RAW_SUFFIX, IPreferencePropertyAccessor.CANON_RAW_SUFFIX_DEFAULT_VALUE);
-            use_screennail_image = preferences.getBoolean(IPreferencePropertyAccessor.CANON_USE_SCREENNAIL_AS_SMALL, false);
+            useScreennailImage = preferences.getBoolean(IPreferencePropertyAccessor.CANON_USE_SCREENNAIL_AS_SMALL, false);
             try
             {
                 delayMs = Integer.parseInt(preferences.getString(IPreferencePropertyAccessor.CANON_RECEIVE_WAIT, IPreferencePropertyAccessor.CANON_RECEIVE_WAIT_DEFAULT_VALUE));
@@ -58,6 +58,14 @@ public class CanonPlaybackControl implements IPlaybackControl
             {
                 delayMs = 10;  // 最短は 10msにする
             }
+            try
+            {
+                smallImageSequence = Integer.parseInt(preferences.getString(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE, IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE_DEFAULT_VALUE));
+            }
+            catch (Exception ee)
+            {
+                ee.printStackTrace();
+            }
         }
         catch (Exception e)
         {
@@ -66,9 +74,15 @@ public class CanonPlaybackControl implements IPlaybackControl
         this.activity = activity;
         this.provider = provider;
         this.fullImageReceiver = new CanonFullImageReceiver(activity, provider.getCommandPublisher());
-        this.smallImageReciever = new CanonSmallImageReceiver(activity, provider.getCommandPublisher());
+        if (smallImageSequence == 1)
+        {
+            this.smallImageReciever = new CanonReducedImageReceiver(activity, provider.getCommandPublisher());
+        }
+        else
+        {
+            this.smallImageReciever = new CanonSmallImageReceiver(activity, provider.getCommandPublisher());
+        }
         canonImageObjectReceiver = new CanonImageObjectReceiver(provider, delayMs);
-
     }
 
     @Override
@@ -101,7 +115,7 @@ public class CanonPlaybackControl implements IPlaybackControl
     {
         Log.v(TAG, " downloadContentScreennail() " + path);
 
-        if (!use_screennail_image)
+        if (!useScreennailImage)
         {
             // Thumbnail と同じ画像を表示する
             downloadContentThumbnail(path, callback);
@@ -152,17 +166,13 @@ public class CanonPlaybackControl implements IPlaybackControl
             {
                 start = 1;
             }
-            //String indexStr = path.substring(start, path.indexOf("."));
-            final String indexStr = path.substring(start);
-            //Log.v(TAG, "downloadContentThumbnail() : [" + path + "] " + indexStr);
 
+            final String indexStr = path.substring(start);
             CanonImageContentInfo content = canonImageObjectReceiver.getContentObject(indexStr);
             if (content != null)
             {
-                IPtpIpCommandPublisher publisher = provider.getCommandPublisher();
-                //int storageId = content.getStorageId();
                 int objectId = content.getId();
-                // Log.v(TAG, "downloadContentThumbnail() " + indexStr + " [" + objectId + "] (" + storageId + ")");
+                IPtpIpCommandPublisher publisher = provider.getCommandPublisher();
                 publisher.enqueueCommand(new PtpIpCommandGeneric(new PtpIpThumbnailImageReceiver(activity, callback), objectId, false, 0, 0x910a, 8, objectId, 0x00032000));
             }
         }
@@ -211,7 +221,6 @@ public class CanonPlaybackControl implements IPlaybackControl
         {
             return;
         }
-
         try
         {
             Thread thread = new Thread(new Runnable() {
@@ -235,7 +244,6 @@ public class CanonPlaybackControl implements IPlaybackControl
         try
         {
             Log.v(TAG, "   showPictureStarted() ");
-
             IPtpIpCommandPublisher publisher = provider.getCommandPublisher();
             publisher.flushHoldQueue();
             System.gc();
@@ -252,7 +260,6 @@ public class CanonPlaybackControl implements IPlaybackControl
         try
         {
             Log.v(TAG, "   showPictureFinished() ");
-
             IPtpIpCommandPublisher publisher = provider.getCommandPublisher();
             publisher.flushHoldQueue();
             System.gc();
@@ -262,5 +269,4 @@ public class CanonPlaybackControl implements IPlaybackControl
             e.printStackTrace();
         }
     }
-
 }
diff --git a/app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/playback/CanonReducedImageReceiver.kt b/app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/playback/CanonReducedImageReceiver.kt
new file mode 100644 (file)
index 0000000..7b848e8
--- /dev/null
@@ -0,0 +1,217 @@
+package net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.playback
+
+import android.app.Activity
+import android.util.Log
+import net.osdn.gokigen.pkremote.camera.interfaces.playback.IDownloadContentCallback
+import net.osdn.gokigen.pkremote.camera.interfaces.playback.IProgressEvent
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpCommandCallback
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpCommandPublisher
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.messages.PtpIpCommandCanonGetPartialObject
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.messages.PtpIpCommandGeneric
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.messages.specific.CanonRequestInnerDevelopEnd
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.messages.specific.CanonRequestInnerDevelopStart
+import java.io.ByteArrayOutputStream
+
+class CanonReducedImageReceiver(private val activity: Activity, private val publisher: IPtpIpCommandPublisher) : IPtpIpCommandCallback, ICanonSmallImageReceiver
+{
+    private val mine = this
+
+    private var callback: IDownloadContentCallback? = null
+    private var objectId = 0
+    private var isReceiveMulti = false
+    private var receivedFirstData = false
+
+    private var receivedTotalBytes = 0
+    private var receivedRemainBytes = 0
+
+
+    override fun issueCommand(objectId: Int, callback: IDownloadContentCallback?)
+    {
+        Log.v(TAG, " issueCommand() : ${objectId}")
+        if (this.objectId != 0)
+        {
+            // already issued
+            Log.v(TAG, " COMMAND IS ALREADY ISSUED. : $objectId")
+            return
+        }
+        this.callback = callback
+        this.objectId = objectId
+        publisher.enqueueCommand(CanonRequestInnerDevelopStart(object : IPtpIpCommandCallback {
+            override fun receivedMessage(id: Int, rx_body: ByteArray?) {
+                Log.v(TAG, " getRequestStatusEvent  : $objectId " + (rx_body?.size ?: 0))
+                publisher.enqueueCommand(PtpIpCommandGeneric(mine, objectId + 5, false, objectId, 0x9116))
+            }
+            override fun onReceiveProgress(currentBytes: Int, totalBytes: Int, rx_body: ByteArray?) { }
+            override fun isReceiveMulti(): Boolean { return (false) }
+        }, objectId, false, objectId, objectId))  // 0x9141 : RequestInnerDevelopStart
+    }
+
+    override fun receivedMessage(id: Int, rx_body: ByteArray?)
+    {
+        try
+        {
+            when (id)
+            {
+                (objectId + 1) -> {
+                    sendTransferComplete(rx_body)
+                }
+                (objectId + 2) -> {
+                    Log.v(TAG, " requestInnerDevelopEnd() : $objectId")
+                    publisher.enqueueCommand(CanonRequestInnerDevelopEnd(this, objectId + 3, false, objectId)) // 0x9143 : RequestInnerDevelopEnd
+                }
+                (objectId + 3) -> {
+                    //Log.v(TAG, "  --- COMMAND RESET : " + objectId + " --- ");
+
+                    // リセットコマンドを送ってみる
+                    publisher.enqueueCommand(PtpIpCommandGeneric(this, objectId + 4, false, objectId, 0x902f))
+                }
+                (objectId + 4) -> {
+                    // 画像取得終了
+                    Log.v(TAG, " ----- SMALL IMAGE RECEIVE SEQUENCE FINISHED  : $objectId")
+                    callback!!.onCompleted()
+                    objectId = 0
+                    callback = null
+                    receivedTotalBytes = 0
+                    receivedRemainBytes = 0
+                    receivedFirstData = false
+                    System.gc()
+                }
+                (objectId + 5) -> {
+                    requestGetPartialObject(rx_body)
+                }
+                else -> {
+                    Log.v(TAG, " RECEIVED UNKNOWN ID : $id")
+                }
+            }
+        }
+        catch (e: Exception)
+        {
+            e.printStackTrace()
+            callback?.onErrorOccurred(e)
+        }
+    }
+
+    override fun onReceiveProgress(currentBytes: Int, totalBytes: Int, rx_body: ByteArray?)
+    {
+        val body = cutHeader(rx_body)
+        val length = body?.size ?: 0
+        Log.v(TAG, " onReceiveProgress() $currentBytes/$totalBytes ($length bytes.)")
+        callback?.onProgress(body, length, object : IProgressEvent
+        {
+            override fun getProgress(): Float { return (currentBytes.toFloat() / totalBytes.toFloat()) }
+            override fun isCancellable(): Boolean { return (false) }
+            override fun requestCancellation() { }
+        })
+    }
+
+    private fun cutHeader(rx_body: ByteArray?): ByteArray?
+    {
+        if (rx_body == null)
+        {
+            return (null)
+        }
+        val length = rx_body.size
+        var dataPosition = 0
+        val byteStream = ByteArrayOutputStream()
+        if (!receivedFirstData)
+        {
+            // 初回データを読み込んだ
+            receivedFirstData = true
+
+            // データを最初に読んだとき。ヘッダ部分を読み飛ばす
+            dataPosition = rx_body[0].toInt() and 0xff
+        }
+        else if (receivedRemainBytes > 0)
+        {
+            //Log.v(TAG, "  >>> [ remain_bytes : " + received_remain_bytes + "] ( length : " + length + ") " + data_position);
+            //SimpleLogDumper.dump_bytes("[zzz]", Arrays.copyOfRange(rx_body, data_position, (data_position + 160)));
+
+            // データの読み込みが途中だった場合...
+            if (length < receivedRemainBytes)
+            {
+                // 全部コピーする、足りないバイト数は残す
+                receivedRemainBytes = receivedRemainBytes - length
+                receivedTotalBytes = receivedTotalBytes + rx_body.size
+                return rx_body
+            }
+            else
+            {
+                byteStream.write(rx_body, dataPosition, receivedRemainBytes)
+                dataPosition = receivedRemainBytes
+                receivedRemainBytes = 0
+            }
+        }
+        while (dataPosition <= length - 12)
+        {
+            val bodySize: Int = (rx_body[dataPosition].toUByte().toInt() and 0xff) + (rx_body[dataPosition + 1].toUByte().toInt() and 0xff shl 8) + (rx_body[dataPosition + 2].toUByte().toInt() and 0xff shl 16) + (rx_body[dataPosition + 3].toUByte().toInt() and 0xff shl 24)
+            if (bodySize <= 12)
+            {
+                Log.v(TAG, "  BODY SIZE IS SMALL : " + dataPosition + " (" + bodySize + ") [" + receivedRemainBytes + "] " + rx_body.size + "  ")
+                //int startpos = (data_position > 48) ? (data_position - 48) : 0;
+                //SimpleLogDumper.dump_bytes("[xxx]", Arrays.copyOfRange(rx_body, startpos, (data_position + 48)));
+                break
+            }
+
+            // Log.v(TAG, " RX DATA : " + data_position + " (" + body_size + ") [" + received_remain_bytes + "] (" + received_total_bytes + ")");
+            //SimpleLogDumper.dump_bytes("[yyy] " + data_position + ": ", Arrays.copyOfRange(rx_body, data_position, (data_position + 64)));
+            if (dataPosition + bodySize > length)
+            {
+                // データがすべてバッファ内になかったときは、バッファすべてコピーして残ったサイズを記憶しておく。
+                val copysize = length - (dataPosition + 12)
+                byteStream.write(rx_body, dataPosition + 12, copysize)
+                receivedRemainBytes = bodySize - copysize - 12 // マイナス12は、ヘッダ分
+                receivedTotalBytes = receivedTotalBytes + copysize
+                // Log.v(TAG, " --- copy : " + (data_position + 12) + " " + copysize + " remain : " + received_remain_bytes + "  body size : " + body_size);
+                break
+            }
+            try
+            {
+                byteStream.write(rx_body, dataPosition + 12, bodySize - 12)
+                dataPosition = dataPosition + bodySize
+                receivedTotalBytes = receivedTotalBytes + 12
+                //Log.v(TAG, " --- COPY : " + (data_position + 12) + " " + (body_size - 12) + " remain : " + received_remain_bytes);
+            }
+            catch (e: Exception)
+            {
+                Log.v(TAG, "  pos : $dataPosition  size : $bodySize length : $length")
+                e.printStackTrace()
+            }
+        }
+        return byteStream.toByteArray()
+    }
+
+    override fun isReceiveMulti(): Boolean
+    {
+        return (isReceiveMulti)
+    }
+
+    private fun requestGetPartialObject(rx_body: ByteArray?) {
+        Log.v(TAG, " requestGetPartialObject() : $objectId")
+        isReceiveMulti = true
+        receivedFirstData = false
+
+        // 0x9107 : GetPartialObject  (元は 0x00020000)
+        val pictureLength: Int
+        if (rx_body != null && rx_body.size > 52) {
+            val dataIndex = 48
+            pictureLength = (rx_body[dataIndex].toUByte().toInt() and 0xff) + (rx_body[dataIndex + 1].toUByte().toInt() and 0xff shl 8) + (rx_body[dataIndex + 2].toUByte().toInt() and 0xff shl 16) + (rx_body[dataIndex + 3].toUByte().toInt() and 0xff shl 24)
+        }
+        else
+        {
+            pictureLength = 0x020000
+        }
+        publisher.enqueueCommand(PtpIpCommandCanonGetPartialObject(this, objectId + 1, false, objectId, 0x01, 0x00, pictureLength, pictureLength))
+    }
+
+    private fun sendTransferComplete(rx_body: ByteArray?)
+    {
+        Log.v(TAG, " sendTransferComplete(), id : $objectId size: " + (rx_body?.size ?: 0))
+        publisher.enqueueCommand(PtpIpCommandGeneric(this, objectId + 2, false, objectId, 0x9117, 4, 0x01)) // 0x9117 : TransferComplete
+        isReceiveMulti = false
+    }
+
+    companion object
+    {
+        private val TAG = CanonReducedImageReceiver::class.java.simpleName
+    }
+}
index caac0c0..827d7f0 100644 (file)
@@ -18,7 +18,7 @@ import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.messages.sp
 import java.io.ByteArrayOutputStream;
 
 
-public class CanonSmallImageReceiver implements IPtpIpCommandCallback
+public class CanonSmallImageReceiver implements IPtpIpCommandCallback, ICanonSmallImageReceiver
 {
     private static final String TAG = CanonSmallImageReceiver.class.getSimpleName();
 
@@ -40,6 +40,7 @@ public class CanonSmallImageReceiver implements IPtpIpCommandCallback
         this.mine = this;
     }
 
+    @Override
     public void issueCommand(final int objectId, IDownloadContentCallback callback)
     {
         if (this.objectId != 0)
diff --git a/app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/playback/ICanonSmallImageReceiver.java b/app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/playback/ICanonSmallImageReceiver.java
new file mode 100644 (file)
index 0000000..1cf837f
--- /dev/null
@@ -0,0 +1,8 @@
+package net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.playback;
+
+import net.osdn.gokigen.pkremote.camera.interfaces.playback.IDownloadContentCallback;
+
+public interface ICanonSmallImageReceiver
+{
+    void issueCommand(final int objectId, IDownloadContentCallback callback);
+}
index ccaaaf0..751371e 100644 (file)
@@ -137,6 +137,9 @@ public interface IPreferencePropertyAccessor
     String CANON_CONNECTION_SEQUENCE = "canon_connection_mode";
     String CANON_CONNECTION_SEQUENCE_DEFAULT_VALUE = "0";
 
+    String CANON_SMALL_PICTURE_TYPE = "canon_small_picture_type";
+    String CANON_SMALL_PICTURE_TYPE_DEFAULT_VALUE = "0";
+
 /*
     //String GR2_DISPLAY_MODE = "gr2_display_mode";
     //String GR2_DISPLAY_MODE_DEFAULT_VALUE = "0";
index a42e2ee..8a3e480 100644 (file)
@@ -179,6 +179,9 @@ public class CanonPreferenceFragment  extends PreferenceFragmentCompat implement
             if (!items.containsKey(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE)) {
                 editor.putString(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE, IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE_DEFAULT_VALUE);
             }
+            if (!items.containsKey(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE)) {
+                editor.putString(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE, IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE_DEFAULT_VALUE);
+            }
             editor.apply();
         }
         catch (Exception e)
index 36028f4..9c5c320 100644 (file)
@@ -179,6 +179,9 @@ public class FujiXPreferenceFragment  extends PreferenceFragmentCompat implement
             if (!items.containsKey(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE)) {
                 editor.putString(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE, IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE_DEFAULT_VALUE);
             }
+            if (!items.containsKey(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE)) {
+                editor.putString(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE, IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE_DEFAULT_VALUE);
+            }
             editor.apply();
         }
         catch (Exception e)
index 07967e8..19233a3 100644 (file)
@@ -176,6 +176,9 @@ public class NikonPreferenceFragment  extends PreferenceFragmentCompat implement
             if (!items.containsKey(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE)) {
                 editor.putString(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE, IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE_DEFAULT_VALUE);
             }
+            if (!items.containsKey(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE)) {
+                editor.putString(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE, IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE_DEFAULT_VALUE);
+            }
             editor.apply();
         }
         catch (Exception e)
index 97b8e67..4d86cf4 100644 (file)
@@ -205,6 +205,9 @@ public class OpcPreferenceFragment extends PreferenceFragmentCompat implements S
         if (!items.containsKey(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE)) {
             editor.putString(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE, IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE_DEFAULT_VALUE);
         }
+        if (!items.containsKey(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE)) {
+            editor.putString(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE, IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE_DEFAULT_VALUE);
+        }
         editor.apply();
     }
 
index 122b3bc..74ffa82 100644 (file)
@@ -170,6 +170,9 @@ public class OlympusPenPreferenceFragment  extends PreferenceFragmentCompat impl
             if (!items.containsKey(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE)) {
                 editor.putString(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE, IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE_DEFAULT_VALUE);
             }
+            if (!items.containsKey(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE)) {
+                editor.putString(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE, IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE_DEFAULT_VALUE);
+            }
             editor.apply();
         }
         catch (Exception e)
index 4dc8f98..aac176c 100644 (file)
@@ -168,6 +168,9 @@ public class PanasonicPreferenceFragment  extends PreferenceFragmentCompat imple
             if (!items.containsKey(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE)) {
                 editor.putString(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE, IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE_DEFAULT_VALUE);
             }
+            if (!items.containsKey(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE)) {
+                editor.putString(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE, IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE_DEFAULT_VALUE);
+            }
             editor.apply();
         }
         catch (Exception e)
index 63d80bf..f6e872c 100644 (file)
@@ -167,6 +167,9 @@ public class PixproPreferenceFragment  extends PreferenceFragmentCompat implemen
             if (!items.containsKey(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE)) {
                 editor.putString(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE, IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE_DEFAULT_VALUE);
             }
+            if (!items.containsKey(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE)) {
+                editor.putString(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE, IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE_DEFAULT_VALUE);
+            }
             editor.apply();
         }
         catch (Exception e)
index d0d9392..623a6af 100644 (file)
@@ -183,6 +183,9 @@ public class RicohGr2PreferenceFragment  extends PreferenceFragmentCompat implem
             if (!items.containsKey(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE)) {
                 editor.putString(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE, IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE_DEFAULT_VALUE);
             }
+            if (!items.containsKey(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE)) {
+                editor.putString(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE, IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE_DEFAULT_VALUE);
+            }
             editor.apply();
         }
         catch (Exception e)
index 53fdcea..278dc5e 100644 (file)
@@ -165,6 +165,9 @@ public class SonyPreferenceFragment  extends PreferenceFragmentCompat implements
             if (!items.containsKey(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE)) {
                 editor.putString(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE, IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE_DEFAULT_VALUE);
             }
+            if (!items.containsKey(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE)) {
+                editor.putString(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE, IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE_DEFAULT_VALUE);
+            }
             editor.apply();
         }
         catch (Exception e)
index 777f1f4..4c2b943 100644 (file)
@@ -167,6 +167,9 @@ public class ThetaPreferenceFragment  extends PreferenceFragmentCompat implement
             if (!items.containsKey(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE)) {
                 editor.putString(IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE, IPreferencePropertyAccessor.CANON_CONNECTION_SEQUENCE_DEFAULT_VALUE);
             }
+            if (!items.containsKey(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE)) {
+                editor.putString(IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE, IPreferencePropertyAccessor.CANON_SMALL_PICTURE_TYPE_DEFAULT_VALUE);
+            }
             editor.apply();
         }
         catch (Exception e)
index fce3479..4c607b8 100644 (file)
     <string name="pref_canon_connection_mode">接続シーケンス</string>
     <string name="pref_summary_canon_connection_mode">通常、変更は不要です (初期値: TYPE0)</string>
 
+    <string name="pref_canon_small_picture_type">スモール画像取得シーケンス</string>
+    <string name="pref_summary_canon_small_picture_type">(初期値: TYPE0)</string>
+
 </resources>
index e08ec12..d639701 100644 (file)
         <item>2</item>
     </string-array>
 
+    <string-array name="canon_small_picture_type">
+        <item>TYPE0</item>
+        <item>TYPE1</item>
+    </string-array>
+
+    <string-array name="canon_small_picture_type_value">
+        <item>0</item>
+        <item>1</item>
+    </string-array>
+
 </resources>
index 1276303..0dd75af 100644 (file)
     <string name="pref_canon_connection_mode">Connection Sequence</string>
     <string name="pref_summary_canon_connection_mode">default: TYPE0 </string>
 
+    <string name="pref_canon_small_picture_type">Small Picture Getting Sequence</string>
+    <string name="pref_summary_canon_small_picture_type">default: TYPE0 </string>
+
 </resources>
index 7a2f31c..b14e7bb 100644 (file)
             android:summary="@string/pref_summary_canon_host_ip" />
 
         <ListPreference
+            android:title="@string/pref_canon_small_picture_type"
+            android:summary="@string/pref_summary_canon_small_picture_type"
+            android:entryValues="@array/canon_small_picture_type_value"
+            android:entries="@array/canon_small_picture_type"
+            android:key="canon_small_picture_type"
+            android:defaultValue="0"/>
+
+        <ListPreference
             android:title="@string/pref_canon_connection_mode"
             android:summary="@string/pref_summary_canon_connection_mode"
             android:entryValues="@array/canon_connection_mode_value"