Dana
2025-11-30 0ff46cc3489bf028802c9ef46f54bf2b77c23fb4
1.h264文件以rtp sendPacket
1个文件已添加
511 ■■■■■ 已修改文件
app/src/main/java/com/anyun/h264/H264FileTransmitter.java 511 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/anyun/h264/H264FileTransmitter.java
New file
@@ -0,0 +1,511 @@
package com.anyun.h264;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
/**
 * H264文件传输器
 * 从H264文件读取数据,按照JT/T 1076-2016协议通过TCP/UDP传输
 *
 * 使用示例:
 * <pre>
 * // 创建传输器
 * H264FileTransmitter transmitter = new H264FileTransmitter();
 *
 * // 设置服务器地址和协议类型
 * transmitter.setServerAddress("192.168.1.100", 8888);
 * transmitter.setProtocolType(JT1076ProtocolHelper.PROTOCOL_TYPE_TCP); // 或 PROTOCOL_TYPE_UDP
 *
 * // 设置协议参数
 * transmitter.setProtocolParams("013120122580", (byte)1);
 *
 * // 设置帧率(用于计算时间戳间隔)
 * transmitter.setFrameRate(25);
 *
 * // 初始化Socket
 * if (transmitter.initialize()) {
 *     // 开始传输文件
 *     transmitter.transmitFile("/path/to/video.h264");
 * }
 *
 * // 停止传输
 * transmitter.stop();
 * </pre>
 */
public class H264FileTransmitter {
    private static final String TAG = "H264FileTransmitter";
    // H264 NAL起始码
    private static final byte[] START_CODE_3 = {0x00, 0x00, 0x01};
    private static final byte[] START_CODE_4 = {0x00, 0x00, 0x00, 0x01};
    // JT/T 1076-2016 协议工具类
    private JT1076ProtocolHelper protocolHelper;
    // 传输控制
    private AtomicBoolean isRunning = new AtomicBoolean(false);
    private Thread transmitThread;
    // 参数配置
    private int frameRate = 25; // 帧率,用于计算时间戳间隔
    private long frameInterval = 1000 / 25; // 帧间隔(毫秒)
    // 时间戳管理
    private long lastIFrameTime = 0; // 上一个I帧时间
    private long lastFrameTime = 0; // 上一帧时间
    private long baseTimestamp = 0; // 基准时间戳
    /**
     * 传输进度回调接口
     */
    public interface OnTransmitProgressCallback {
        /**
         * 传输进度回调
         * @param currentFrame 当前帧序号(从1开始)
         * @param totalFrames 总帧数(如果未知则为-1)
         */
        void onProgress(int currentFrame, int totalFrames);
        /**
         * 传输完成回调
         */
        void onComplete();
        /**
         * 传输错误回调
         * @param error 错误信息
         */
        void onError(String error);
    }
    private OnTransmitProgressCallback progressCallback;
    public H264FileTransmitter() {
        this.protocolHelper = new JT1076ProtocolHelper();
        // 默认使用TCP协议
        protocolHelper.setProtocolType(JT1076ProtocolHelper.PROTOCOL_TYPE_TCP);
    }
    /**
     * 设置服务器地址
     */
    public void setServerAddress(String ip, int port) {
        protocolHelper.setServerAddress(ip, port);
    }
    /**
     * 设置传输协议类型(UDP或TCP)
     * @param protocolType PROTOCOL_TYPE_UDP 或 PROTOCOL_TYPE_TCP
     */
    public void setProtocolType(int protocolType) {
        protocolHelper.setProtocolType(protocolType);
    }
    /**
     * 设置SIM卡号和逻辑通道号
     */
    public void setProtocolParams(String simCardNumber, byte logicalChannelNumber) {
        protocolHelper.setProtocolParams(simCardNumber, logicalChannelNumber);
    }
    /**
     * 设置帧率(用于计算时间戳间隔)
     * @param frameRate 帧率(fps)
     */
    public void setFrameRate(int frameRate) {
        this.frameRate = frameRate > 0 ? frameRate : 25;
        this.frameInterval = 1000 / this.frameRate;
        Log.d(TAG, "Frame rate set to: " + this.frameRate + " fps, interval: " + this.frameInterval + " ms");
    }
    /**
     * 设置传输进度回调
     */
    public void setOnTransmitProgressCallback(OnTransmitProgressCallback callback) {
        this.progressCallback = callback;
    }
    /**
     * 初始化Socket连接
     * @return 是否成功
     */
    public boolean initialize() {
        if (isRunning.get()) {
            Log.w(TAG, "Transmitter is already running");
            return false;
        }
        if (!protocolHelper.initializeSocket()) {
            Log.e(TAG, "Failed to initialize socket");
            return false;
        }
        // 重置序号
        protocolHelper.resetSequenceNumber();
        // 初始化时间戳
        baseTimestamp = System.currentTimeMillis();
        lastIFrameTime = 0;
        lastFrameTime = 0;
        Log.d(TAG, "Socket initialized successfully");
        return true;
    }
    /**
     * 开始传输H264文件
     * @param filePath H264文件路径
     */
    public void transmitFile(String filePath) {
        if (isRunning.get()) {
            Log.w(TAG, "Transmitter is already running");
            return;
        }
        File file = new File(filePath);
        if (!file.exists() || !file.isFile()) {
            Log.e(TAG, "File does not exist: " + filePath);
            if (progressCallback != null) {
                progressCallback.onError("File does not exist: " + filePath);
            }
            return;
        }
        isRunning.set(true);
        // 启动传输线程
        transmitThread = new Thread(new Runnable() {
            @Override
            public void run() {
                transmitFileInternal(file);
            }
        });
        transmitThread.start();
        Log.d(TAG, "Started transmitting file: " + filePath);
    }
    /**
     * 传输文件的内部实现
     */
    private void transmitFileInternal(File file) {
        FileInputStream fis = null;
        int frameCount = 0;
        try {
            fis = new FileInputStream(file);
            // 读取整个文件到内存
            // 注意:对于大文件,可以改为流式读取,但为简化实现,这里使用一次性读取
            long fileSize = file.length();
            if (fileSize > Integer.MAX_VALUE) {
                throw new IOException("File too large: " + fileSize + " bytes");
            }
            byte[] fileData = new byte[(int) fileSize];
            int bytesRead = 0;
            int totalRead = 0;
            while (totalRead < fileData.length && (bytesRead = fis.read(fileData, totalRead, fileData.length - totalRead)) > 0) {
                totalRead += bytesRead;
            }
            if (totalRead != fileData.length) {
                Log.w(TAG, "File read incomplete, expected: " + fileData.length + ", actual: " + totalRead);
            }
            Log.d(TAG, "File read complete, size: " + fileData.length + " bytes");
            // 按帧解析并传输(一个帧包含从一个起始码到下一个起始码之间的所有数据,包括起始码)
            int offset = 0;
            while (offset < fileData.length && isRunning.get()) {
                // 查找下一个帧的起始位置
                int nextFrameStart = findNextFrameStart(fileData, offset);
                if (nextFrameStart < 0) {
                    // 没有找到下一个起始码,当前offset到文件末尾是一个完整的帧
                    if (offset < fileData.length) {
                        byte[] frameData = Arrays.copyOfRange(fileData, offset, fileData.length);
                        transmitFrame(frameData, frameCount);
                        frameCount++;
                        // 通知进度
                        if (progressCallback != null) {
                            progressCallback.onProgress(frameCount, -1);
                        }
                    }
                    break;
                }
                // 提取当前帧数据(包含起始码)
                if (nextFrameStart > offset) {
                    byte[] frameData = Arrays.copyOfRange(fileData, offset, nextFrameStart);
                    transmitFrame(frameData, frameCount);
                    frameCount++;
                    // 通知进度
                    if (progressCallback != null) {
                        progressCallback.onProgress(frameCount, -1);
                    }
                }
                // 移动到下一个帧的起始位置
                offset = nextFrameStart;
            }
            Log.d(TAG, "Transmission complete, total frames: " + frameCount);
            if (progressCallback != null) {
                progressCallback.onComplete();
            }
        } catch (IOException e) {
            Log.e(TAG, "Error transmitting file", e);
            if (progressCallback != null) {
                progressCallback.onError("IO Error: " + e.getMessage());
            }
        } catch (Exception e) {
            Log.e(TAG, "Unexpected error during transmission", e);
            if (progressCallback != null) {
                progressCallback.onError("Error: " + e.getMessage());
            }
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    Log.e(TAG, "Error closing file", e);
                }
            }
            isRunning.set(false);
        }
    }
    /**
     * 查找下一个帧的起始位置(下一个起始码的位置)
     * @param data 文件数据
     * @param currentFrameStart 当前帧的起始位置(起始码位置)
     * @return 下一个帧的起始码位置,如果未找到返回-1
     */
    private int findNextFrameStart(byte[] data, int currentFrameStart) {
        if (currentFrameStart >= data.length) {
            return -1;
        }
        // 跳过当前帧的起始码
        int offset = currentFrameStart;
        if (isStartCodeAt(data, offset)) {
            offset += getStartCodeLength(data, offset);
        }
        // 查找下一个起始码
        for (int i = offset; i < data.length - 3; i++) {
            if (isStartCodeAt(data, i)) {
                return i;
            }
        }
        return -1;
    }
    /**
     * 检查指定位置是否为起始码
     */
    private boolean isStartCodeAt(byte[] data, int offset) {
        if (offset + 3 > data.length) {
            return false;
        }
        // 检查4字节起始码
        if (offset + 4 <= data.length) {
            if (data[offset] == 0x00 && data[offset + 1] == 0x00 &&
                data[offset + 2] == 0x00 && data[offset + 3] == 0x01) {
                return true;
            }
        }
        // 检查3字节起始码(确保前面不是0x00)
        if (offset == 0 || data[offset - 1] != 0x00) {
            if (data[offset] == 0x00 && data[offset + 1] == 0x00 &&
                data[offset + 2] == 0x01) {
                return true;
            }
        }
        return false;
    }
    /**
     * 获取起始码长度
     */
    private int getStartCodeLength(byte[] data, int offset) {
        if (offset + 4 <= data.length &&
            data[offset] == 0x00 && data[offset + 1] == 0x00 &&
            data[offset + 2] == 0x00 && data[offset + 3] == 0x01) {
            return 4;
        }
        return 3;
    }
    /**
     * 判断帧数据是否为I帧(IDR)
     * @param frameData 帧数据(包含起始码)
     * @return 是否为I帧
     */
    private boolean isIFrame(byte[] frameData) {
        if (frameData == null || frameData.length < 5) {
            return false;
        }
        // 查找帧中所有的NAL单元,检查是否包含IDR帧
        int offset = 0;
        boolean hasSpsPps = false;
        while (offset < frameData.length - 3) {
            // 检查当前位置是否为起始码
            if (isStartCodeAt(frameData, offset)) {
                // 跳过起始码
                int startCodeLen = getStartCodeLength(frameData, offset);
                int nalStart = offset + startCodeLen;
                if (nalStart < frameData.length) {
                    // 获取NAL类型(第一个字节的低5位)
                    int nalType = frameData[nalStart] & 0x1F;
                    // NAL类型5 = IDR (Instantaneous Decoder Refresh) 关键帧
                    if (nalType == 5) {
                        return true; // 找到IDR帧,确定是I帧
                    }
                    // NAL类型7 = SPS, 类型8 = PPS
                    if (nalType == 7 || nalType == 8) {
                        hasSpsPps = true;
                    }
                }
                // 移动到下一个可能的位置继续查找
                offset = nalStart + 1;
            } else {
                offset++;
            }
        }
        // 如果没有找到IDR,但包含SPS/PPS,也认为是关键帧相关的数据
        // (有些编码器可能将SPS/PPS单独作为一个"帧"发送)
        return hasSpsPps;
    }
    /**
     * 传输一个完整的帧数据(类似H264Encoder的方式)
     * @param frameData 帧数据(包含起始码,与H264Encoder输出的格式一致)
     * @param frameIndex 帧序号
     */
    private void transmitFrame(byte[] frameData, int frameIndex) {
        if (frameData == null || frameData.length == 0) {
            return;
        }
        try {
            // 判断帧类型
            boolean isKeyFrame = isIFrame(frameData);
            // 计算时间戳
            long timestamp = baseTimestamp + (frameIndex * frameInterval);
            // 计算时间间隔
            long lastIFrameInterval = (lastIFrameTime > 0) ? (timestamp - lastIFrameTime) : 0;
            long lastFrameInterval = (lastFrameTime > 0) ? (timestamp - lastFrameTime) : frameInterval;
            if (isKeyFrame) {
                lastIFrameTime = timestamp;
            }
            lastFrameTime = timestamp;
            // 判断帧类型(用于协议)
            int dataType = isKeyFrame ? JT1076ProtocolHelper.DATA_TYPE_I_FRAME :
                    JT1076ProtocolHelper.DATA_TYPE_P_FRAME;
            // 分包发送(如果数据超过最大包大小)
            int offset = 0;
            int totalPackets = (int) Math.ceil((double) frameData.length / JT1076ProtocolHelper.MAX_PACKET_SIZE);
            for (int i = 0; i < totalPackets; i++) {
                int packetDataSize = Math.min(JT1076ProtocolHelper.MAX_PACKET_SIZE, frameData.length - offset);
                byte[] packetData = Arrays.copyOfRange(frameData, offset, offset + packetDataSize);
                // 确定分包标记
                int packetMark;
                if (totalPackets == 1) {
                    packetMark = JT1076ProtocolHelper.PACKET_MARK_ATOMIC;
                } else if (i == 0) {
                    packetMark = JT1076ProtocolHelper.PACKET_MARK_FIRST;
                } else if (i == totalPackets - 1) {
                    packetMark = JT1076ProtocolHelper.PACKET_MARK_LAST;
                } else {
                    packetMark = JT1076ProtocolHelper.PACKET_MARK_MIDDLE;
                }
                // 创建RTP包
                byte[] rtpPacket = protocolHelper.createVideoRtpPacket(
                        packetData, timestamp, dataType, packetMark,
                        lastIFrameInterval, lastFrameInterval);
                // 发送RTP包(UDP或TCP,根据协议类型自动选择)
                protocolHelper.sendPacket(rtpPacket);
                offset += packetDataSize;
            }
            // 控制发送速率(模拟帧率)
            if (frameInterval > 0) {
                try {
                    Thread.sleep(frameInterval);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    Log.d(TAG, "Transmission interrupted");
                    return;
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Error transmitting frame", e);
        }
    }
    /**
     * 停止传输
     */
    public void stop() {
        if (!isRunning.get()) {
            return;
        }
        isRunning.set(false);
        // 等待传输线程结束
        if (transmitThread != null) {
            try {
                transmitThread.join(2000);
            } catch (InterruptedException e) {
                Log.e(TAG, "Wait transmit thread error", e);
            }
        }
        // 关闭Socket
        if (protocolHelper != null) {
            protocolHelper.closeSocket();
        }
        Log.d(TAG, "H264 file transmitter stopped");
    }
    /**
     * 释放资源
     */
    public void release() {
        stop();
    }
}