Dana
2025-11-30 98ecefcc2309d1b5078a9cf3d27f5f78465b5310
1.h264 完整 可以跑 ;提交相关文件
7个文件已添加
791 ■■■■■ 已修改文件
README_H264_CHECK.md 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
check_h264.py 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
gradlew 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
gradlew.bat 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
keystore.properties 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test.h264 补丁 | 查看 | 原始文档 | blame | 历史
如何检查test.h264文件.md 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README_H264_CHECK.md
New file
@@ -0,0 +1,120 @@
# H264文件检查工具使用说明
## å¿«é€Ÿæ£€æŸ¥
### æ–¹æ³•1: ç›´æŽ¥ä»Žè®¾å¤‡è·¯å¾„检查(如果文件在本地)
```bash
python check_h264.py test.h264
```
### æ–¹æ³•2: ä»ŽAndroid设备下载文件后检查
1. **连接Android设备并启用USB调试**
2. **查找文件路径**
   Android设备上的文件路径通常是:
   ```
   /storage/emulated/0/Android/data/com.anyun.h264/files/test.h264
   ```
   æˆ–者使用adb查找:
   ```bash
   adb shell "find /sdcard -name test.h264 2>/dev/null"
   ```
3. **下载文件到本地**
   ```bash
   adb pull /storage/emulated/0/Android/data/com.anyun.h264/files/test.h264 ./test.h264
   ```
4. **运行检查工具**
   ```bash
   python check_h264.py test.h264
   ```
## æ£€æŸ¥å†…容
工具会检查以下内容:
1. âœ… **文件是否存在**
2. âœ… **文件大小**(应该大于100字节)
3. âœ… **NALU单元数量**(至少应该有SPS/PPS + 1个关键帧)
4. âœ… **是否包含SPS**(序列参数集,必需)
5. âœ… **是否包含PPS**(图像参数集,必需)
6. âœ… **是否包含IDR关键帧**(必需)
7. âœ… **是否有多个帧**
## é¢„期结果
一个可以播放的H264文件应该包含:
- âœ… è‡³å°‘1个SPS (类型7)
- âœ… è‡³å°‘1个PPS (类型8)
- âœ… è‡³å°‘1个IDR关键帧 (类型5)
- âœ… å¤šä¸ªNALU单元(建议>10个)
## å¸¸è§é—®é¢˜
### Q: æ–‡ä»¶åªæœ‰1帧怎么办?
**A:** è¿™æ˜¯ä¹‹å‰çš„问题。现在的代码已经修复:
- âœ… æ­£ç¡®å¤„理SPS/PPS配置
- âœ… åœ¨å…³é”®å¸§æ—¶åˆå¹¶SPS/PPS
- âœ… æ”¹è¿›ç¼–码循环,确保处理所有输出
- âœ… æ·»åŠ æ¸…ç©ºç¼–ç å™¨åŠŸèƒ½
**建议:**
1. é‡æ–°ç¼–译并运行应用
2. å½•制至少3-5秒视频
3. åœæ­¢ç¼–码后检查文件
### Q: æ–‡ä»¶æ— æ³•播放怎么办?
**可能原因:**
1. âŒ ç¼ºå°‘SPS/PPS - æ£€æŸ¥æ˜¯å¦è¾“出了配置数据
2. âŒ æ–‡ä»¶æ ¼å¼é”™è¯¯ - æ£€æŸ¥æ˜¯å¦æ˜¯Annex-B格式
3. âŒ åªæœ‰1帧 - æ£€æŸ¥ç¼–码循环是否正常工作
**解决方案:**
1. æŸ¥çœ‹Logcat日志,搜索 "H264Encoder"
2. æ£€æŸ¥æ˜¯å¦æœ‰ "SPS/PPS included" æ—¥å¿—
3. æ£€æŸ¥æ˜¯å¦æœ‰ "Frame encoded" æ—¥å¿—
4. ä½¿ç”¨æ­¤å·¥å…·æ£€æŸ¥æ–‡ä»¶ç»“æž„
### Q: å¦‚何播放H264文件?
可以使用以下播放器:
- **VLC Media Player**(推荐)
- **ffplay** (FFmpeg自带)
- **MPC-HC**
- **PotPlayer**
直接双击 `.h264` æ–‡ä»¶æˆ–拖放到播放器窗口即可。
## è°ƒè¯•建议
如果文件无法播放,请检查:
1. **查看Logcat日志**
   ```bash
   adb logcat -s H264Encoder:D MainActivity:D
   ```
2. **检查关键日志**
   - "SPS/PPS included in key frame data" - è¯´æ˜ŽSPS/PPS已合并
   - "Frame encoded: ..." - è¯´æ˜Žæœ‰å¸§è¾“出
   - "Encoder output EOS" - è¯´æ˜Žæ­£å¸¸ç»“束
3. **验证文件结构**
   ```bash
   python check_h264.py test.h264
   ```
4. **使用hexdump查看文件头**
   ```bash
   hexdump -C test.h264 | head -20
   ```
   åº”该看到:`00 00 00 01` æˆ– `00 00 01`(Annex-B起始码)
check_h264.py
New file
@@ -0,0 +1,209 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
H264文件检查工具
检查生成的test.h264文件是否符合播放要求
"""
import os
import sys
def check_h264_file(file_path):
    """检查H264文件是否符合播放要求"""
    print(f"检查文件: {file_path}\n")
    # 1. æ£€æŸ¥æ–‡ä»¶æ˜¯å¦å­˜åœ¨
    if not os.path.exists(file_path):
        print("❌ æ–‡ä»¶ä¸å­˜åœ¨ï¼")
        print(f"   é¢„期路径: {file_path}")
        print("\n提示:")
        print("   - å¦‚果是Android设备,需要先通过adb pull下载文件")
        print(f"   - æˆ–者将文件复制到当前目录")
        return False
    # 2. æ£€æŸ¥æ–‡ä»¶å¤§å°
    file_size = os.path.getsize(file_path)
    print(f"✓ æ–‡ä»¶å¤§å°: {file_size} å­—节 ({file_size / 1024:.2f} KB)")
    if file_size == 0:
        print("❌ æ–‡ä»¶ä¸ºç©ºï¼")
        return False
    if file_size < 100:
        print("⚠️  è­¦å‘Š: æ–‡ä»¶å¤ªå°ï¼Œå¯èƒ½åªæœ‰SPS/PPS或1帧数据")
    # 3. è¯»å–文件内容并分析
    try:
        with open(file_path, 'rb') as f:
            data = f.read()
    except Exception as e:
        print(f"❌ è¯»å–文件失败: {e}")
        return False
    # æ£€æŸ¥NALU单元
    nal_start_code_3 = bytes([0x00, 0x00, 0x01])
    nal_start_code_4 = bytes([0x00, 0x00, 0x00, 0x01])
    nalu_list = []
    i = 0
    # æŸ¥æ‰¾æ‰€æœ‰NALU单元
    while i < len(data):
        # æŸ¥æ‰¾èµ·å§‹ç 
        found = False
        # æŸ¥æ‰¾4字节起始码
        if i + 4 <= len(data) and data[i:i+4] == nal_start_code_4:
            start_pos = i + 4
            found = True
        # æŸ¥æ‰¾3字节起始码
        elif i + 3 <= len(data) and data[i:i+3] == nal_start_code_3:
            # ç¡®ä¿å‰é¢ä¸æ˜¯0x00 (避免误判)
            if i == 0 or data[i-1] != 0x00:
                start_pos = i + 3
                found = True
        if found:
            # æŸ¥æ‰¾ä¸‹ä¸€ä¸ªèµ·å§‹ç 
            next_start = -1
            # å…ˆæ‰¾4字节起始码
            for j in range(start_pos, len(data) - 3):
                if j + 4 <= len(data) and data[j:j+4] == nal_start_code_4:
                    next_start = j
                    break
            # å¦‚果没找到4字节的,找3字节的
            if next_start == -1:
                for j in range(start_pos, len(data) - 2):
                    if j + 3 <= len(data) and data[j:j+3] == nal_start_code_3:
                        # ç¡®ä¿å‰é¢ä¸æ˜¯0x00
                        if j == 0 or data[j-1] != 0x00:
                            next_start = j
                            break
            # æå–NALU数据
            if next_start == -1:
                nalu_data = data[start_pos:]
            else:
                nalu_data = data[start_pos:next_start]
            if len(nalu_data) > 0:
                # èŽ·å–NALU类型 (第一个字节的低5位)
                nal_type = nalu_data[0] & 0x1F
                nalu_list.append({
                    'type': nal_type,
                    'size': len(nalu_data),
                    'name': get_nalu_type_name(nal_type)
                })
            i = next_start if next_start != -1 else len(data)
        else:
            i += 1
    # 4. åˆ†æžNALU单元
    print(f"\n✓ æ‰¾åˆ° {len(nalu_list)} ä¸ªNALU单元\n")
    if len(nalu_list) == 0:
        print("❌ æœªæ‰¾åˆ°ä»»ä½•NALU单元!文件格式可能不正确")
        return False
    # ç»Ÿè®¡ä¸åŒç±»åž‹çš„NALU
    nal_type_count = {}
    for nalu in nalu_list:
        nal_type = nalu['type']
        if nal_type not in nal_type_count:
            nal_type_count[nal_type] = 0
        nal_type_count[nal_type] += 1
    print("NALU类型统计:")
    for nal_type in sorted(nal_type_count.keys()):
        count = nal_type_count[nal_type]
        name = get_nalu_type_name(nal_type)
        print(f"  {name} (类型{nal_type}): {count} ä¸ª")
    # 5. æ£€æŸ¥å…³é”®è¦æ±‚
    print("\n检查项:")
    has_sps = 7 in nal_type_count
    has_pps = 8 in nal_type_count
    has_idr = 5 in nal_type_count
    has_non_idr = 1 in nal_type_count
    # SPS/PPS检查
    if has_sps:
        print(f"  âœ“ åŒ…含SPS (序列参数集) - {nal_type_count[7]} ä¸ª")
    else:
        print("  âŒ ç¼ºå°‘SPS (序列参数集) - å¿…需!")
    if has_pps:
        print(f"  âœ“ åŒ…含PPS (图像参数集) - {nal_type_count[8]} ä¸ª")
    else:
        print("  âŒ ç¼ºå°‘PPS (图像参数集) - å¿…需!")
    # å…³é”®å¸§æ£€æŸ¥
    if has_idr:
        print(f"  âœ“ åŒ…含IDR关键帧 - {nal_type_count[5]} ä¸ª")
    else:
        print("  âŒ ç¼ºå°‘IDR关键帧")
    # éžå…³é”®å¸§æ£€æŸ¥
    if has_non_idr:
        print(f"  âœ“ åŒ…含非IDR帧 - {nal_type_count[1]} ä¸ª")
    else:
        print("  âš ï¸  æ²¡æœ‰éžIDR帧(只有关键帧)")
    # 6. æ€»ä½“评估
    print("\n" + "="*50)
    can_play = has_sps and has_pps and has_idr
    if can_play:
        if len(nalu_list) >= 3:
            print("✅ æ–‡ä»¶åº”该可以播放!")
            print(f"   åŒ…含完整的SPS/PPS和 {len(nalu_list)} ä¸ªNALU单元")
        else:
            print("⚠️  æ–‡ä»¶ç»“构完整,但帧数较少")
            print("   å»ºè®®å½•制更长时间以获得更多帧")
    else:
        print("❌ æ–‡ä»¶å¯èƒ½æ— æ³•播放")
        if not has_sps or not has_pps:
            print("   åŽŸå› : ç¼ºå°‘SPS/PPS参数集")
        if not has_idr:
            print("   åŽŸå› : ç¼ºå°‘IDR关键帧")
    print("="*50)
    return can_play
def get_nalu_type_name(nal_type):
    """获取NALU类型名称"""
    nal_names = {
        1: "非IDR编码片",
        2: "编码片数据分区A",
        3: "编码片数据分区B",
        4: "编码片数据分区C",
        5: "IDR图像编码片",
        6: "SEI (补充增强信息)",
        7: "SPS (序列参数集)",
        8: "PPS (图像参数集)",
        9: "访问单元分隔符",
        10: "序列结束",
        11: "流结束",
        12: "填充数据"
    }
    return nal_names.get(nal_type, f"未知类型{nal_type}")
if __name__ == "__main__":
    # é»˜è®¤æ–‡ä»¶è·¯å¾„
    default_path = "test.h264"
    # å¦‚果提供了命令行参数,使用参数作为文件路径
    file_path = sys.argv[1] if len(sys.argv) > 1 else default_path
    print("H264文件检查工具")
    print("="*50)
    success = check_h264_file(file_path)
    sys.exit(0 if success else 1)
gradlew
New file
@@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
    echo "$*"
}
die () {
    echo
    echo "$*"
    echo
    exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
  NONSTOP* )
    nonstop=true
    ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
    JAVACMD=`cygpath --unix "$JAVACMD"`
    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=`expr $i + 1`
    done
    case $i in
        0) set -- ;;
        1) set -- "$args0" ;;
        2) set -- "$args0" "$args1" ;;
        3) set -- "$args0" "$args1" "$args2" ;;
        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi
# Escape application args
save () {
    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
    echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
gradlew.bat
New file
@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem      https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
keystore.properties
New file
@@ -0,0 +1,4 @@
storePassword = 123456
keyPassword = 123456
keyAlias = key0
storeFile = ../key/key.jks
test.h264
Binary files differ
ÈçºÎ¼ì²étest.h264Îļþ.md
New file
@@ -0,0 +1,184 @@
# å¦‚何检查test.h264文件能否播放
## å¿«é€Ÿåˆ¤æ–­æ–¹æ³•
### æ–¹æ³•1: ç›´æŽ¥ç”¨æ’­æ”¾å™¨æµ‹è¯•(最简单)
1. **从Android设备下载文件**
   ```bash
   adb pull /storage/emulated/0/Android/data/com.anyun.h264/files/test.h264 ./test.h264
   ```
2. **用VLC播放器打开**
   - ä¸‹è½½VLC: https://www.videolan.org/vlc/
   - ç›´æŽ¥åŒå‡» `test.h264` æ–‡ä»¶
   - æˆ–者拖放到VLC窗口
3. **如果能播放** âœ…
   - è¯´æ˜Žæ–‡ä»¶æ ¼å¼æ­£ç¡®
   - å¯ä»¥çœ‹åˆ°è§†é¢‘内容
4. **如果不能播放** âŒ
   - å¯èƒ½ç¼ºå°‘SPS/PPS
   - å¯èƒ½åªæœ‰1帧
   - å¯èƒ½æ ¼å¼é”™è¯¯
### æ–¹æ³•2: ä½¿ç”¨Python工具检查(详细分析)
1. **确保安装了Python** (Python 3.6+)
2. **下载文件到本地**
   ```bash
   adb pull /storage/emulated/0/Android/data/com.anyun.h264/files/test.h264 ./test.h264
   ```
3. **运行检查工具**
   ```bash
   python check_h264.py test.h264
   ```
4. **查看检查结果**
   âœ… **可以播放的标志:**
   - âœ“ åŒ…含SPS (序列参数集)
   - âœ“ åŒ…含PPS (图像参数集)
   - âœ“ åŒ…含IDR关键帧
   - âœ“ æœ‰å¤šä¸ªNALU单元(建议>10个)
   âŒ **不能播放的标志:**
   - ç¼ºå°‘SPS或PPS
   - åªæœ‰1-2个NALU单元
   - æ–‡ä»¶å¤ªå°ï¼ˆ<100字节)
### æ–¹æ³•3: ä½¿ç”¨ffprobe检查(如果安装了FFmpeg)
```bash
ffprobe test.h264
```
如果能正确显示视频信息(分辨率、帧率等),说明文件可以播放。
### æ–¹æ³•4: æŸ¥çœ‹æ–‡ä»¶å¤§å°ï¼ˆç²—略判断)
```bash
# åœ¨Android设备上
adb shell "ls -lh /storage/emulated/0/Android/data/com.anyun.h264/files/test.h264"
```
**参考标准:**
- âœ… **可以播放**:通常 > 10KB(640x480@25fps,3秒约30-50KB)
- âš ï¸ **可能只有1帧**:< 5KB
- âŒ **文件异常**:= 0KB
## æ£€æŸ¥æ–‡ä»¶å†…容(高级)
### ä½¿ç”¨hexdump查看文件头
```bash
hexdump -C test.h264 | head -20
```
**正常文件应该看到:**
```
00000000  00 00 00 01 67 64 00 1f  ac 72 84 44 26 84 00 00  |....gd...r.D&...|
00000010  00 01 00 00 00 01 68 ee  3c b0 44 00 00 00 01 06  |......h.<.D.....|
```
- `00 00 00 01` = Annex-B起始码(4字节)
- `67` = SPS的NALU类型(0x67 & 0x1F = 7)
- `68` = PPS的NALU类型(0x68 & 0x1F = 8)
### ä½¿ç”¨adb在设备上直接检查
```bash
# æ£€æŸ¥æ–‡ä»¶å¤§å°
adb shell "stat -c '%s' /storage/emulated/0/Android/data/com.anyun.h264/files/test.h264"
# æŸ¥çœ‹æ–‡ä»¶å‰50字节
adb shell "hexdump -C /storage/emulated/0/Android/data/com.anyun.h264/files/test.h264 | head -5"
```
## å¸¸è§é—®é¢˜è¯Šæ–­
### âŒ é—®é¢˜1: æ–‡ä»¶åªæœ‰1帧、0秒
**原因:**
- ç¼–码器输出处理不完整
- SPS/PPS没有正确写入
- ç¼–码循环提前退出
**解决方案:**
✅ å·²ç»ä¿®å¤ï¼æ–°ä»£ç åŒ…含:
- æ­£ç¡®å¤„理SPS/PPS配置数据
- åœ¨å…³é”®å¸§æ—¶åˆå¹¶SPS/PPS
- æ”¹è¿›ç¼–码循环,处理所有输出
- æ·»åŠ æ¸…ç©ºç¼–ç å™¨åŠŸèƒ½
**建议:**
1. é‡æ–°ç¼–译运行应用
2. å½•制至少3-5秒
3. æ­£å¸¸åœæ­¢ç¼–码(不要强制退出)
### âŒ é—®é¢˜2: VLC无法播放
**检查步骤:**
1. **查看文件大小**
   ```bash
   adb shell "ls -lh /storage/emulated/0/Android/data/com.anyun.h264/files/test.h264"
   ```
   å¦‚果太小(<1KB),说明可能只有配置数据
2. **查看Logcat日志**
   ```bash
   adb logcat -s H264Encoder:D | grep -E "Frame encoded|SPS/PPS|NALU"
   ```
   åº”该看到:
   - "SPS/PPS included in key frame data"
   - "Frame encoded: ..." ï¼ˆå¤šæ¬¡ï¼‰
3. **检查文件格式**
   ```bash
   adb shell "hexdump -C /storage/emulated/0/Android/data/com.anyun.h264/files/test.h264 | head -3"
   ```
   åº”该看到 `00 00 00 01` æˆ– `00 00 01`
### âŒ é—®é¢˜3: æ–‡ä»¶å­˜åœ¨ä½†æ’­æ”¾å™¨æŠ¥é”™
**可能原因:**
1. æ–‡ä»¶æ ¼å¼ä¸æ˜¯çº¯Annex-B
2. ç¼ºå°‘SPS/PPS
3. æ•°æ®æŸå
**解决方法:**
1. ä½¿ç”¨ `check_h264.py` è¯¦ç»†æ£€æŸ¥
2. æŸ¥çœ‹Logcat确认编码过程正常
3. å°è¯•用ffmpeg转换:
   ```bash
   ffmpeg -i test.h264 -c copy test_fixed.h264
   ```
## éªŒè¯ä¿®å¤æ•ˆæžœ
修复后的代码应该能够:
✅ **生成可播放的H264文件**
- åŒ…含完整的SPS/PPS
- åŒ…含多个帧(IDR + éžIDR)
- æ­£ç¡®çš„Annex-B格式
✅ **文件特征:**
- æ–‡ä»¶å¤§å° > 10KB(3秒录制)
- NALU单元数量 > 10个
- åŒ…含SPS (类型7)
- åŒ…含PPS (类型8)
- åŒ…含IDR关键帧 (类型5)
- åŒ…含非IDR帧 (类型1)
## ä¸‹ä¸€æ­¥
1. **重新运行应用**,录制3-5秒视频
2. **下载文件**到本地
3. **用VLC播放**验证
4. **如果还有问题**,运行检查工具获取详细诊断