ijkplayer 代码走读之 h264 解封装器应用详解

彭海阳
2023-12-01

本连续记录 IJKPLAYER 代码走读文章,其主要的目的是在 IPKPLAYER 中增加私有网络通讯协议
和私有解封装器。 本篇文章是成果输出记录,通过实例再次阐述 IJKPLAYER 的解协议、解封装的
实现逻辑。
并在本文中把实现过程做详细记录,供大家参考,以防个人备忘。

第一 H264 解封装ffmpeg实现过程分析

///> 源码文件路径 libavformat/h264dec.c
/*
        1、找到nalu定位符0x00 0x00 0x01
        2、解析nalu头
        3、检查borbidden bit
        4、判断ref idc是否和nalu type相符
        5、预解析nalu
                1、若是slice,则判断slice头的pps id是否已经存在
                2、若是sps,则记录sps id
                3、若是pps,则判断其sps id是否研究存在,记录pps id
        6、若有sps、pps、slice(I/P/B/IDR),则表明是一个H.264/AVC的数据流
*/

static int h264_probe(AVProbeData *p)
{
    uint32_t code = -1;
    int sps = 0, pps = 0, idr = 0, res = 0, sli = 0;
    int i, ret;
    int pps_ids[MAX_PPS_COUNT+1] = {0};
    int sps_ids[MAX_SPS_COUNT+1] = {0};
    unsigned pps_id, sps_id;
    GetBitContext gb;

    for (i = 0; i + 2 < p->buf_size; i++) {
        code = (code << 8) + p->buf[i];
        if ((code & 0xffffff00) == 0x100) {
            int ref_idc = (code >> 5) & 3;      //解析nalu头
            int type    = code & 0x1F;
            static const int8_t ref_zero[] = {
                 2,  0,  0,  0,  0, -1,  1, -1,
                -1,  1,  1,  1,  1, -1,  2,  2,
                 2,  2,  2,  0,  2,  2,  2,  2,
                 2,  2,  2,  2,  2,  2,  2,  2
            };

            if (code & 0x80) // forbidden_bit
                return 0;

            if (ref_zero[type] == 1 && ref_idc)
                return 0;
            if (ref_zero[type] == -1 && !ref_idc)
                return 0;
            if (ref_zero[type] == 2) {
                if (!(code == 0x100 && !p->buf[i + 1] && !p->buf[i + 2]))
                    res++;
            }

            ret = init_get_bits8(&gb, p->buf + i + 1, p->buf_size - i - 1);
            if (ret < 0)
                return 0;

            switch (type) {
            case 1:
            case 5:
                get_ue_golomb_long(&gb);
                if (get_ue_golomb_long(&gb) > 9U)
                    return 0;
                pps_id = get_ue_golomb_long(&gb);
                if (pps_id > MAX_PPS_COUNT)
                    return 0;
                if (!pps_ids[pps_id])
                    break;

                if (type == 1)
                    sli++;
                else
                    idr++;
                break;
            case 7:
                skip_bits(&gb, 14);
                if (get_bits(&gb, 2))
                    return 0;
                skip_bits(&gb, 8);
                sps_id = get_ue_golomb_long(&gb);
                if (sps_id > MAX_SPS_COUNT)
                    return 0;
                sps_ids[sps_id] = 1;
                sps++;
                break;
            case 8:
                pps_id = get_ue_golomb_long(&gb);
                if (pps_id > MAX_PPS_COUNT)
                    return 0;
                sps_id = get_ue_golomb_long(&gb);
                if (sps_id > MAX_SPS_COUNT)
                    return 0;
                if (!sps_ids[sps_id])
                    break;
                pps_ids[pps_id] = 1;
                pps++;
                break;
            }
        }
    }
    ff_tlog(NULL, "sps:%d pps:%d idr:%d sli:%d res:%d\n", sps, pps, idr, sli, res);

    if (sps && pps && (idr || sli > 3) && res < (sps + pps + idr))
        return AVPROBE_SCORE_EXTENSION + 1;  // 1 more than .mpg

    return 0;
}

FF_DEF_RAWVIDEO_DEMUXER(h264, "raw H.264 video", h264_probe, "h26l,h264,264,avc", AV_CODEC_ID_H264)

//> 1. 此宏定义在源码在 libavformat/rawdec.h 中,
#define FF_DEF_RAWVIDEO_DEMUXER(shortname, longname, probe, ext, id)\
FF_DEF_RAWVIDEO_DEMUXER2(shortname, longname, probe, ext, id, AVFMT_GENERIC_INDEX)

#define FF_DEF_RAWVIDEO_DEMUXER2(shortname, longname, probe, ext, id, flag)\
FF_RAWVIDEO_DEMUXER_CLASS(shortname)\
AVInputFormat ff_ ## shortname ## _demuxer = {\
    .name           = #shortname,\
    .long_name      = NULL_IF_CONFIG_SMALL(longname),\
    .read_probe     = probe,\
    .read_header    = ff_raw_video_read_header,\
    .read_packet    = ff_raw_read_partial_packet,\
    .extensions     = ext,\
    .flags          = flag,\
    .raw_codec_id   = id,\
    .priv_data_size = sizeof(FFRawVideoDemuxerContext),\
    .priv_class     = &shortname ## _demuxer_class,\
};
//> 2. 此宏展开生成的源码如下:
AVInputFormat ff_h264_demuxer = {
    .name           = "h264",
    .long_name      = "raw H.264 video",
    .read_probe     = h264_probe,
    .read_header    = ff_raw_data_read_header,
    .read_packet    = ff_raw_read_partial_packet,
    .extensions     = "h26l,h264,264,avc",
    .flags          = AVFMT_GENERIC_INDEX,
    .raw_codec_id   = AV_CODEC_ID_H264,
    .priv_data_size = 0,
    .priv_class     = &h264_demuxer_class,
};

#define FF_RAWVIDEO_DEMUXER_CLASS(name)\
static const AVClass name ## _demuxer_class = {\
    .class_name = #name " demuxer",\
    .item_name  = av_default_item_name,\
    .option     = ff_rawvideo_options,\
    .version    = LIBAVUTIL_VERSION_INT,\
};
//> 3. 此宏展开生成的源码如下:
static const AVClass h264_demuxer_class = {
    .class_name = "h264 demuxer",
    .item_name  = av_default_item_name,
    .option     = ff_rawvideo_options,
    .version    = LIBAVUTIL_VERSION_INT,
};
//> 此部分代码在 libavformat/rawdec.c 中
#define OFFSET(x) offsetof(FFRawVideoDemuxerContext, x)
#define DEC AV_OPT_FLAG_DECODING_PARAM
const AVOption ff_rawvideo_options[] = {
    { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, DEC},
    { NULL },
};

通过此段源码的分析,我们得到 h264 裸数据解封装入口函数、和数据包读取函数的实现方法。

第二步 ijkplayer 中增加 H.264 解封装器的支持,因为默认没有打开.

IJKPLAYER 添加编译 h264 裸数据解封装方法:

脚本 compile_ffmpeg.sh 调用 tools/do-complie-ffmpeg.sh,在该文件中添加下面内容:

///> do-complie-ffmpeg.sh 文件内容
#--------------------
# with openssl
if [ -f "${FF_DEP_OPENSSL_LIB}/libssl.a" ]; then
    echo "OpenSSL detected"
# FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-nonfree"
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-openssl"

    FF_CFLAGS="$FF_CFLAGS -I${FF_DEP_OPENSSL_INC}"
    FF_DEP_LIBS="$FF_DEP_LIBS -L${FF_DEP_OPENSSL_LIB} -lssl -lcrypto"
fi

if [ -f "${FF_DEP_LIBSOXR_LIB}/libsoxr.a" ]; then
    echo "libsoxr detected"
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-libsoxr"

    FF_CFLAGS="$FF_CFLAGS -I${FF_DEP_LIBSOXR_INC}"
    FF_DEP_LIBS="$FF_DEP_LIBS -L${FF_DEP_LIBSOXR_LIB} -lsoxr"
fi

FF_CFG_FLAGS="$FF_CFG_FLAGS $COMMON_FF_CFG_FLAGS"

#--------------------
# Standard options:
FF_CFG_FLAGS="$FF_CFG_FLAGS --prefix=$FF_PREFIX"

# Advanced options (experts only):
FF_CFG_FLAGS="$FF_CFG_FLAGS --cross-prefix=${FF_CROSS_PREFIX}-"
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-cross-compile"
FF_CFG_FLAGS="$FF_CFG_FLAGS --target-os=linux"
FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-pic"
# FF_CFG_FLAGS="$FF_CFG_FLAGS --disable-symver"
## <start>   
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-demuxer=h264"
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-protocol=udp"
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-decoder=h264"
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-parser=h264"

    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-hwaccel=h264_vaapi"
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-hwaccel=h264_vaapi"
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-hwaccel=h264_dxva2"
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-demuxer=mjpeg"
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-demuxer=rtsp"
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-demuxer=rtp"
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-encoder=h264"
## <end>
if [ "$FF_ARCH" = "x86" ]; then
    FF_CFG_FLAGS="$FF_CFG_FLAGS --disable-asm"
else
    # Optimization options (experts only):
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-asm"
    FF_CFG_FLAGS="$FF_CFG_FLAGS --enable-inline-asm"
fi

第三部分 增加私有协议

3.1> 在 对应硬件架构下编写私有协议

  本次采用 ffmpeg-arm64\libavformat\tcp_ext.c 文件,文件是在tcp.c 文件基础上修改。

修改中需要注意的内容如下。

#define FAST_OPEN_FLAG 0x20000000
#define OFFSET(x) offsetof(TCPEXTContext, x)
#define D AV_OPT_FLAG_DECODING_PARAM
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
    { "listen",          "Listen for incoming connections",  OFFSET(listen),         AV_OPT_TYPE_INT, { .i64 = 0 },     0,       2,       .flags = D|E },
    { "timeout",     "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout),     AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
    { "connect_timeout",  "set connect timeout (in microseconds) of socket", OFFSET(open_timeout),     AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
    { "listen_timeout",  "Connection awaiting timeout (in milliseconds)",      OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
    { "send_buffer_size", "Socket send buffer size (in bytes)",                OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
    { "recv_buffer_size", "Socket receive buffer size (in bytes)",             OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
    { "ijkapplication",   "AVApplicationContext",                              OFFSET(app_ctx_intptr),   AV_OPT_TYPE_INT64, { .i64 = 0 }, INT64_MIN, INT64_MAX, .flags = D },

    { "addrinfo_one_by_one",  "parse addrinfo one by one in getaddrinfo()",    OFFSET(addrinfo_one_by_one), AV_OPT_TYPE_INT, { .i64 = 0 },         0, 1, .flags = D|E },
    { "addrinfo_timeout", "set timeout (in microseconds) for getaddrinfo()",   OFFSET(addrinfo_timeout), AV_OPT_TYPE_INT, { .i64 = -1 },       -1, INT_MAX, .flags = D|E },
    { "dns_cache_timeout", "dns cache TTL (in microseconds)",   OFFSET(dns_cache_timeout), AV_OPT_TYPE_INT, { .i64 = -1 },       -1, INT64_MAX, .flags = D|E },
    { "dns_cache_clear", "clear dns cache",   OFFSET(dns_cache_clear), AV_OPT_TYPE_INT, { .i64 = 0},       -1, INT_MAX, .flags = D|E },
    { "fastopen", "enable fastopen",          OFFSET(fastopen), AV_OPT_TYPE_INT, { .i64 = 0},       0, INT_MAX, .flags = D|E },
    { NULL }
};

static const AVClass tcpext_class = {
    .class_name = "tcpext",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

... 省略实现的代码。

const URLProtocol ff_tcpext_protocol = {
    .name                = "tcpext",
    .url_open            = tcp_open,
    .url_accept          = tcp_accept,
    .url_read            = tcp_read,
    .url_write           = tcp_write,
    .url_close           = tcp_close,
    .url_get_file_handle = tcp_get_file_handle,
    .url_get_short_seek  = tcp_get_window_size,
    .url_shutdown        = tcp_shutdown,
    .priv_data_size      = sizeof(TCPEXTContext),
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
    .priv_data_class     = &tcpext_class,
};

3.2 ff_tcpext_protocol 静态结构体内容增加至\libavformat\protocols.c 文件中

extern const URLProtocol ff_rtmp_protocol;
extern const URLProtocol ff_rtmpe_protocol;
extern const URLProtocol ff_rtmps_protocol;
extern const URLProtocol ff_rtmpt_protocol;
extern const URLProtocol ff_rtmpte_protocol;
extern const URLProtocol ff_rtmpts_protocol;
extern const URLProtocol ff_rtp_protocol;
extern const URLProtocol ff_sctp_protocol;
extern const URLProtocol ff_srtp_protocol;
extern const URLProtocol ff_subfile_protocol;
extern const URLProtocol ff_tee_protocol;
extern const URLProtocol ff_tcp_protocol;
extern const URLProtocol ff_tls_gnutls_protocol;
extern const URLProtocol ff_tls_schannel_protocol;
extern const URLProtocol ff_tls_securetransport_protocol;
extern const URLProtocol ff_tls_openssl_protocol;
extern const URLProtocol ff_udp_protocol;
extern const URLProtocol ff_udplite_protocol;
extern const URLProtocol ff_unix_protocol;
extern const URLProtocol ff_librtmp_protocol;
extern const URLProtocol ff_librtmpe_protocol;
extern const URLProtocol ff_librtmps_protocol;
extern const URLProtocol ff_librtmpt_protocol;
extern const URLProtocol ff_librtmpte_protocol;
extern const URLProtocol ff_libssh_protocol;
extern const URLProtocol ff_libsmbclient_protocol;
extern const URLProtocol ff_tcpext_protocol;                        ///> 在此处添加

#include "libavformat/protocol_list.c"

const AVClass *ff_urlcontext_child_class_next(const AVClass *prev)
{
    int i;

    /* find the protocol that corresponds to prev */
    for (i = 0; prev && url_protocols[i]; i++) {
        if (url_protocols[i]->priv_data_class == prev) {
            i++;
            break;
        }
    }

    /* find next protocol with priv options */
    for (; url_protocols[i]; i++)
        if (url_protocols[i]->priv_data_class)
            return url_protocols[i]->priv_data_class;
    return NULL;
}

3.3 修改 Makefile 文件内容,编译 tcp_ext.c 文件

OBJS-$(CONFIG_RTP_PROTOCOL)              += rtpproto.o
OBJS-$(CONFIG_SCTP_PROTOCOL)             += sctp.o
OBJS-$(CONFIG_SRTP_PROTOCOL)             += srtpproto.o srtp.o
OBJS-$(CONFIG_SUBFILE_PROTOCOL)          += subfile.o
OBJS-$(CONFIG_TEE_PROTOCOL)              += teeproto.o tee_common.o
OBJS-$(CONFIG_TCP_PROTOCOL)              += tcp.o
OBJS-$(CONFIG_TCPEXT_PROTOCOL)           += tcp_ext.o                   ///> 参考 tcp 协议方式添加
OBJS-$(CONFIG_TLS_GNUTLS_PROTOCOL)       += tls_gnutls.o tls.o
OBJS-$(CONFIG_TLS_OPENSSL_PROTOCOL)      += tls_openssl.o tls.o
OBJS-$(CONFIG_TLS_SCHANNEL_PROTOCOL)     += tls_schannel.o tls.o
OBJS-$(CONFIG_TLS_SECURETRANSPORT_PROTOCOL) += tls_securetransport.o tls.o
OBJS-$(CONFIG_UDP_PROTOCOL)              += udp.o
OBJS-$(CONFIG_UDPLITE_PROTOCOL)          += udp.o
OBJS-$(CONFIG_UNIX_PROTOCOL)             += unix.o

3.4 重要提示:

你的源码如果 git clone 下来代码,需要使用 git add 把修改的文件添加到版本中.
否则 ./compile-ffmpeg.sh clean 时后,代码就被清楚了,因为脚本是使用 git clean
处理的文件。
需要把 ijkplayer 中支持的硬件平台 libavformat 文件夹下的 Makefile 文件都修改,并
添加 tcp_ext.c 文件,否则 ./compile-ffmpeg.sh all 编译报错,因为是编译所以硬件架构so库.

3.5 重新编译 ffmpeg 和 ijkplayer 代码

///> 实现clean前面编译生成的内容.
robot@ubuntu:~/ljbPlayer/ijkplayer/android/contrib$ ./compile-ffmpeg.sh clean
///> 1. 编译 ffmpeg
robot@ubuntu:~/ljbPlayer/ijkplayer/android/contrib$ ./compile-ffmpeg.sh all
====================
[*] check archs
====================
FF_ALL_ARCHS = armv5 armv7a arm64 x86 x86_64
FF_ACT_ARCHS = armv5 armv7a arm64 x86 x86_64

====================
[*] check env armv5
====================
FF_ARCH=armv5
FF_BUILD_OPT=

--------------------
[*] make NDK standalone toolchain
--------------------
build on Linux x86_64
ANDROID_NDK=/home/robot/Android/Sdk/ndk/android-ndk-r14b/
IJK_NDK_REL=14.1.3816874
NDKr14.1.3816874 detected

--------------------
[*] check ffmpeg env
--------------------

--------------------
[*] configurate ffmpeg
--------------------
reuse configure

--------------------
[*] compile ffmpeg
--------------------

--------------------
[*] create files for shared ffmpeg
--------------------

--------------------
[*] Finished
--------------------
# to continue to build ijkplayer, run script below,
sh compile-ijk.sh 
robot@ubuntu:~/ljbPlayer/ijkplayer/android/contrib$ 

///> 2. 编译 ijkplayer 
robot@ubuntu:~/ljbPlayer/ijkplayer/android$ ./compile-ijk.sh all
profiler build: NO

[armeabi] Prebuilt       : libijkffmpeg.so <= /home/robot/ljbPlayer/ijkplayer/android/contrib/build/ffmpeg-armv5/output/
[armeabi] Compile thumb  : ijkplayer <= ff_cmdutils.c
[armeabi] Compile thumb  : ijkplayer <= ff_ffplay.c
[armeabi] Compile thumb  : ijkplayer <= ff_ffpipeline.c
[armeabi] Compile thumb  : ijkplayer <= ijkmeta.c
[armeabi] Compile thumb  : ijkplayer <= ijkplayer.c
[armeabi] Compile thumb  : ijkplayer <= ffpipeline_ffplay.c
[armeabi] Compile thumb  : ijkplayer <= ffpipenode_ffplay_vdec.c
[armeabi] Compile thumb  : ijkplayer <= ffmpeg_api_jni.c
[armeabi] Compile thumb  : ijkplayer <= ijkplayer_android.c
[armeabi] Compile thumb  : ijkplayer <= ijkplayer_jni.c
[armeabi] Compile thumb  : ijkplayer <= ffpipeline_android.c
[armeabi] Compile thumb  : ijkplayer <= ffpipenode_android_mediacodec_vdec.c
[armeabi] Compile thumb  : ijkplayer <= allformats.c
[armeabi] Compile thumb  : ijkplayer <= ijklivehook.c
[armeabi] Compile thumb  : ijkplayer <= ijkmediadatasource.c
...
[x86_64] Compile        : ijksdl <= renderer.c
[x86_64] Compile        : ijksdl <= renderer_rgb.c
[x86_64] Compile        : ijksdl <= renderer_yuv420p.c
[x86_64] Compile        : ijksdl <= renderer_yuv444p10le.c
[x86_64] Compile        : ijksdl <= shader.c
[x86_64] Compile        : ijksdl <= rgb.fsh.c
[x86_64] Compile        : ijksdl <= yuv420p.fsh.c
[x86_64] Compile        : ijksdl <= yuv444p10le.fsh.c
[x86_64] Compile        : ijksdl <= mvp.vsh.c
[x86_64] Compile        : ijksdl <= ijksdl_vout_dummy.c
[x86_64] Compile        : ijksdl <= ijksdl_vout_overlay_ffmpeg.c
[x86_64] Compile        : ijksdl <= image_convert.c
[x86_64] Compile        : ijksdl <= android_nativewindow.c
[x86_64] Compile        : ijksdl <= ijksdl_vout_android_surface.c
[x86_64] Compile        : ijksdl <= ijksdl_vout_android_nativewindow.c
[x86_64] Compile        : ijksdl <= ijksdl_vout_overlay_android_mediacodec.c
[x86_64] Install        : libijkffmpeg.so => libs/x86_64/libijkffmpeg.so
[x86_64] SharedLibrary  : libijksdl.so
[x86_64] Install        : libijksdl.so => libs/x86_64/libijksdl.so
[x86_64] SharedLibrary  : libijkplayer.so
[x86_64] Install        : libijkplayer.so => libs/x86_64/libijkplayer.so
/home/robot/ljbPlayer/ijkplayer/android
///> 3.更换 ANDROID 工程中的库文件
robot@ubuntu:~/ljbPlayer/ijkplayer/android$ cp -r ijkplayer/ijkplayer-arm64/src/main/libs/arm64-v8a/libijk* /home/robot/Videos/IjkPlayerProject_2/app/src/main/jniLibs/arm64-v8a/

第 四 步 验证中应用的文件

首先通过 ffmpeg 生成 h264 格式文件。

FFMPEG 录制屏幕输出 mp4 文件的方法:

ffmpeg -video_size 1024x768 -framerate 25 -f x11grab -i :0.0+100,200 v-out.mp4
指的是从屏幕的左上角(x=100, y=200)的位置,录制分辨率为1024×768的视频。

从 mp4提取 h264 格式数据流的方法:

ffmpeg -i 20130312_133313.mp4 -codec copy -bsf h264_mp4toannexb -f h264 20130312_133313.264
说明:
 -i 20130312_133313.mp4 :是输入的MP4文件
-codec copy:从MP4封装中进行拷贝
-bsf: h264_mp4toannexb:从MP4拷贝到annexB封装
-f h264:采用h.264格式
20130312_133313.264:输出的文件名称

第 五 步 验证结果日志

D/IJKMEDIA: IjkMediaPlayer_setDataSourceAndHeaders
V/IJKMEDIA: setDataSource: path tcpext://192.168.1.55:7777/v-out.h264
D/IJKMEDIA: ijkmp_set_data_source(url="tcpext://192.168.1.55:7777/v-out.h264")
    ijkmp_set_data_source(url="tcpext://192.168.1.55:7777/v-out.h264")=0
I/IjkVideoView: type 2
D/IJKMEDIA: IjkMediaPlayer_setVideoSurface
    ijkmp_set_android_surface(surface=0x7fd6646a78)
    ffpipeline_set_surface()
    ijkmp_set_android_surface(surface=0x7fd6646a78)=void
    IjkMediaPlayer_prepareAsync
    ijkmp_prepare_async()
I/IJKMEDIA: ===== versions =====
    ijkplayer    : k0.8.8-7-gf10b8d1a
    FFmpeg       : ff3.4--ijk0.8.7--20180103--001-1-gf921a673ba6
    libavutil    : 55.78.100
    libavcodec   : 57.107.100
    libavformat  : 57.83.100
    libswscale   : 4.8.100
    libswresample: 2.9.100
    ===== options =====
    player-opts : mediacodec                   = 0
    player-opts : opensles                     = 0
    player-opts : overlay-format               = 842225234
    player-opts : framedrop                    = 1
    player-opts : start-on-prepared            = 0
I/IJKMEDIA: format-opts : ijkapplication               = 525242808320
    format-opts : ijkiomanager                 = 525555707008
    format-opts : http-detect-range-support    = 0
    codec-opts  : skip_loop_filter             = 48
    ===================
D/IJKMEDIA: ijkmp_prepare_async()=0
I/IJKMEDIA: SDL_RunThread: [20145] ff_read
    DEBUG read_thread, LINE:3116 ,ffp->iformat_name:(null)
     libavformat/utils.c / avformat_open_input , LINE:537 
     libavformat/format.c / av_probe_input_format2 , LINE:254  
     libavformat/format.c / av_probe_input_format3 , LINE:202  
     libavformat/format.c / av_probe_input_format3 , LINE:208 ,EXT name:(null) 
     libavformat/format.c / av_probe_input_format3, LINE:211, probeName:ijklivehook 
     libavformat/format.c / av_probe_input_format3 , LINE:247  
     libavformat/utils.c / init_input , LINE:415  
D/IJKMEDIA: Opening 'tcpext://192.168.1.55:7777/v-out.h264' for reading
    No default whitelist set
I/IJKMEDIA:  libavformat/tcp.c / tcp_open , LINE:358 ,tcp open url:tcpext://192.168.1.55:7777/v-out.h264 
I/tv.danmaku.ijk.media.player.IjkMediaPlayer: onNativeInvoke 131073
I/IJKMEDIA: SDL_RunThread: [20144] ff_vout
I/IJKMEDIA: SDL_RunThread: [20143] ff_msg_loop
D/IJKMEDIA: message_loop
    FFP_MSG_FLUSH:
D/HwGalleryCacheManagerImpl: mIsEffect:false
I/tv.danmaku.ijk.media.player.IjkMediaPlayer: onNativeInvoke 131074
I/IJKMEDIA:  libavformat/utils.c / init_input , LINE:421  
     libavformat/aviobuf.c / avio_read , LINE:637 ,size:2048 
     libavformat/aviobuf.c / avio_read , LINE:641 ,size:2048 
     libavformat/aviobuf.c / avio_read , LINE:666 
     libavformat/aviobuf.c / fill_buffer() , LINE:539 ,len:32768 
     libavformat/aviobuf.c / fill_buffer() , LINE:554 
     libavformat/aviobuf.c / fill_buffer() , LINE:567 
I/zygote64: Do partial code cache collection, code=29KB, data=26KB
    After code cache collection, code=29KB, data=26KB
    Increasing code cache capacity to 128KB
D/OpenGLRenderer:   HWUI Binary is  enabled
W/InputMethodManager: startInputReason = 1
W/libEGL: EGLNativeWindowType 0x7a4ae48010 disconnect failed
D/OpenGLRenderer: endAllActiveAnimators on 0x7a4ae70400 (RippleDrawable) with handle 0x7a5d9ecf60
I/IJKMEDIA:  libavformat/tcpext.c / tcp_read , LINE:756  recive len:1792               ///> 读取网络数据日志
I/IJKMEDIA:  libavformat/aviobuf.c / fill_buffer() , LINE:584 ,bytes_read:1792 
     libavformat/aviobuf.c / avio_read , LINE:641 ,size:2048 
     libavformat/aviobuf.c / avio_read , LINE:641 ,size:256 
     libavformat/aviobuf.c / avio_read , LINE:666 
     libavformat/aviobuf.c / fill_buffer() , LINE:539 ,len:32768 
     libavformat/aviobuf.c / fill_buffer() , LINE:554 
     libavformat/aviobuf.c / fill_buffer() , LINE:567 
I/IJKMEDIA:  libavformat/tcpext.c / tcp_read , LINE:756  recive len:256 
I/IJKMEDIA:  libavformat/aviobuf.c / fill_buffer() , LINE:584 ,bytes_read:2048 
     libavformat/aviobuf.c / avio_read , LINE:641 ,size:256 
     libavformat/aviobuf.c / avio_read , LINE:679 ,size:0 
     libavformat/format.c / av_probe_input_buffer2 , LINE:331  
     libavformat/format.c / av_probe_input_format2 , LINE:254  
     libavformat/format.c / av_probe_input_format3 , LINE:202  
     libavformat/format.c / av_probe_input_format3 , LINE:208 ,EXT name:aac 
     libavformat/format.c / av_probe_input_format3, LINE:211, probeName:aac 
     libavformat/format.c / av_probe_input_format3 , LINE:208 ,EXT name:(null) 
     libavformat/format.c / av_probe_input_format3, LINE:211, probeName:concat 
     libavformat/format.c / av_probe_input_format3 , LINE:208 ,EXT name:(null) 
     libavformat/format.c / av_probe_input_format3 , LINE:208 ,EXT name:flac 
     libavformat/format.c / av_probe_input_format3, LINE:211, probeName:flac 
     libavformat/format.c / av_probe_input_format3 , LINE:208 ,EXT name:flv 
     libavformat/format.c / av_probe_input_format3, LINE:211, probeName:flv 
     libavformat/format.c / av_probe_input_format3 , LINE:208 ,EXT name:flv 
     libavformat/format.c / av_probe_input_format3, LINE:211, probeName:live_flv 

     libavformat/format.c / av_probe_input_format3 , LINE:208 ,EXT name:h26l,h264,264,avc   ///> 此部分是解封装器 h264 内容。
     libavformat/h264dec.c / h264_probe , LINE:40, buf_size:00000800 
     libavformat/h264dec.c  code:ffffff00, index:0, buf:00 
     libavformat/h264dec.c  code:ffff0000, index:1, buf:00 
I/IJKMEDIA:  libavformat/h264dec.c  code:ff000000, index:2, buf:00 
        
        省略部分日志

     libavformat/aviobuf.c / fill_buffer() , LINE:584 ,bytes_read:104704                    /// 下面是解封装器输出结果
D/IJKMEDIA: user data:"x264 - core 155 r2917 0a84d98 - H.264/MPEG-4 AVC codec - Copyleft 2003-2018 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x1:0x111 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=4 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00"
I/IJKMEDIA:  libavformat/utils.c / avformat_find_stream_info() , LINE:3736 
     libavformat/utils.c / avformat_find_stream_info() , LINE:3747 ,read_size:104462 
D/IJKMEDIA: nal_unit_type: 6, nal_ref_idc: 0
    nal_unit_type: 7, nal_ref_idc: 3
D/IJKMEDIA: nal_unit_type: 8, nal_ref_idc: 3
D/IJKMEDIA: nal_unit_type: 5, nal_ref_idc: 3
I/IJKMEDIA:  libavformat/utils.c / avformat_find_stream_info() , LINE:3839 
D/IJKMEDIA: nal_unit_type: 6, nal_ref_idc: 0
    nal_unit_type: 7, nal_ref_idc: 3
D/IJKMEDIA: nal_unit_type: 8, nal_ref_idc: 3
D/IJKMEDIA: nal_unit_type: 5, nal_ref_idc: 3
    user data:"x264 - core 155 r2917 0a84d98 - H.264/MPEG-4 AVC codec - Copyleft 2003-2018 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x1:0x111 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=4 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00"
I/IJKMEDIA: Reinit context to 1024x768, pix_fmt: yuv444p
D/IJKMEDIA: no picture 
I/IJKMEDIA:  libavformat/utils.c / avformat_find_stream_info() , LINE:3639 
D/IJKMEDIA: Probe buffer size limit of 5000 bytes reached
W/IJKMEDIA: Stream #0: not enough frames to estimate rate; consider increasing probesize
I/IJKMEDIA:  libavformat/utils.c / avformat_find_stream_info() , LINE:3858 
     libavformat/utils.c / avformat_find_stream_info() , LINE:3884 
I/IJKMEDIA:  libavformat/utils.c / avformat_find_stream_info() , LINE:3909 
I/IJKMEDIA:  libavformat/utils.c / avformat_find_stream_info() , LINE:3917 
     libavformat/utils.c / avformat_find_stream_info() , LINE:4009, probesize:5000 
     libavformat/utils.c / avformat_find_stream_info() , LINE:4042 
     libavformat/utils.c / avformat_find_stream_info() , LINE:4100 
D/IJKMEDIA: After avformat_find_stream_info() pos: 104704 bytes read:104704 seeks:0 frames:1
I/IJKMEDIA:  /home/robot/ljbPlayer/ijkplayer/android/ijkplayer/ijkplayer-arm64/src/main/jni/ijkmedia/ijkplayer/ff_ffplay.c / read_thread , LINE:3168 
D/IJKMEDIA: FFP_MSG_FIND_STREAM_INFO:
I/IJKMEDIA: max_frame_duration: 10.000
    Input #0, h264, from 'tcpext://192.168.1.55:7777/v-out.h264':
      Duration: 
    N/A
    , bitrate: 
    N/A
I/IJKMEDIA:     Stream #0:0
D/IJKMEDIA: , 1, 1/1200000
I/IJKMEDIA: : Video: h264, 1 reference frame, yuv444p(progressive, left), 1024x768, 0/1
    , 
    25 tbr, 
    1200k tbn, 
    50 tbc
     /home/robot/ljbPlayer/ijkplayer/android/ijkplayer/ijkplayer-arm64/src/main/jni/ijkmedia/ijkplayer/ff_ffplay.c / read_thread , LINE:3216 
    DEBUG /home/robot/ljbPlayer/ijkplayer/android/ijkplayer/ijkplayer-arm64/src/main/jni/ijkmedia/ijkplayer/ff_ffplay.c, LINE:3239 ,CODEC_ID:28
     /home/robot/ljbPlayer/ijkplayer/android/ijkplayer/ijkplayer-arm64/src/main/jni/ijkmedia/ijkplayer/ff_ffplay.c / read_thread , LINE:3241 
D/IJKMEDIA: detected 8 logical cores
    nal_unit_type: 7, nal_ref_idc: 3
    nal_unit_type: 8, nal_ref_idc: 3
I/IJKMEDIA: VideoCodec: avcodec, h264
W/IJKMEDIA: fps: 25.000000 (normal)
D/IJKMEDIA: FFP_MSG_COMPONENT_OPEN:
I/IJKMEDIA: SDL_RunThread: [20161] ff_video_dec
D/IJKMEDIA: FFP_MSG_VIDEO_SIZE_CHANGED: 1024, 768
    FFP_MSG_SAR_CHANGED: 0, 1
    ijkmp_get_msg: FFP_MSG_PREPARED
    FFP_MSG_PREPARED:
D/IJKMEDIA: FFP_MSG_VIDEO_ROTATION_CHANGED: 0
D/IJKMEDIA: ffp_toggle_buffering_l: start
D/IJKMEDIA: FFP_MSG_BUFFERING_START:
D/IjkVideoView: mLoadCost:9091
D/IjkVideoView: MEDIA_INFO_VIDEO_ROTATION_CHANGED: 0
E/: SurfaceView doesn't support rotation (0)!
D/IjkVideoView: MEDIA_INFO_BUFFERING_START:
D/IJKMEDIA: IjkMediaPlayer_start
    ijkmp_start()
    ijkmp_start()=0
D/IJKMEDIA: ijkmp_get_msg: FFP_REQ_START                            ///> 播放器启动播放输出日志
    ijkmp_get_msg: FFP_REQ_START: start on fly
I/IJKMEDIA:  /home/robot/ljbPlayer/ijkplayer/android/ijkplayer/ijkplayer-arm64/src/main/jni/ijkmedia/ijkplayer/ff_ffplay.c / read_thread , LINE:3349 
     /home/robot/ljbPlayer/ijkplayer/android/ijkplayer/ijkplayer-arm64/src/main/jni/ijkmedia/ijkplayer/ff_ffplay.c / read_thread , LINE:3521 
D/IJKMEDIA: ijkmp_get_msg: FFP_REQ_START
    ijkmp_get_msg: FFP_REQ_START: start on fly
I/IJKMEDIA:  /home/robot/ljbPlayer/ijkplayer/android/ijkplayer/ijkplayer-arm64/src/main/jni/ijkmedia/ijkplayer/ff_ffplay.c / read_thread , LINE:3607 
     /home/robot/ljbPlayer/ijkplayer/android/ijkplayer/ijkplayer-arm64/src/main/jni/ijkmedia/ijkplayer/ff_ffplay.c / read_thread , LINE:3615 

至此,在 ijkplayer 中添加私有协议、私有解封装器目标已经实现,存在的问题是
播放器启动播放时间太长,需要优化启动时间.
通过日志观察,耗时操作主要是 解码器类型识别部分,接下来我将针对函数进行优化。

libavformat/utils.c / avformat_find_stream_info() 

相关学习研究过程,将还有 ijkplayer 代码走读方式分享出来。

 类似资料: