nginx_rtmp_module 之 ngx_rtmp_mp4_module 的mp4源码分析

一:整体代码函数预览

复制代码
static ngx_int_t
ngx_rtmp_mp4_postconfiguration(ngx_conf_t *cf)
{
    ngx_rtmp_play_main_conf_t      *pmcf;
    ngx_rtmp_play_fmt_t           **pfmt, *fmt;
    pmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_play_module);
    pfmt = ngx_array_push(&pmcf->fmts);
    if (pfmt == NULL) {
        return NGX_ERROR;
    }
    fmt = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_play_fmt_t));
    if (fmt == NULL) {
        return NGX_ERROR;
    }
    *pfmt = fmt;
    ngx_str_set(&fmt->name, "mp4-format");
    ngx_str_set(&fmt->pfx, "mp4:");
    ngx_str_set(&fmt->sfx, ".mp4");
    fmt->init  = ngx_rtmp_mp4_init;  // 初始化
    fmt->done  = ngx_rtmp_mp4_done;  // 完成
    fmt->seek  = ngx_rtmp_mp4_seek;  // seek
    fmt->start = ngx_rtmp_mp4_start; // 开始
    fmt->stop  = ngx_rtmp_mp4_stop;  // 结束
    fmt->send  = ngx_rtmp_mp4_send;  // 发送数据
    return NGX_OK;
}

二:数据结构定义

|---------------------------------------|---------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 定义 | 标准定义 | nginx 数据结构定义 |
| stsc 【Sample To Chunk Box】 | | type 4 字节已经偏移过, box_size 4 字节已经偏移过 typedef struct { uint32_t first_chunk; uint32_t samples_per_chunk; uint32_t sample_descrption_index; } ngx_rtmp_mp4_chunk_entry_t; typedef struct { uint32_t version_flags; uint32_t entry_count; ngx_rtmp_mp4_chunk_entry_t entries[0]; } ngx_rtmp_mp4_chunks_t; |
| stts 【Decoding Time to Sample Box】 | | type 4 字节已经偏移过, box_size 4 字节已经偏移过 typedef struct { uint32_t sample_count; uint32_t sample_delta; } ngx_rtmp_mp4_time_entry_t; typedef struct { uint32_t version_flags; uint32_t entry_count; ngx_rtmp_mp4_time_entry_t entries[0]; } ngx_rtmp_mp4_times_t; |
| ctts 【Composition Time to Sample Box】 | | type 4 字节已经偏移过, box_size 4 字节已经偏移过 typedef struct { uint32_t sample_count; uint32_t sample_offset; } ngx_rtmp_mp4_delay_entry_t; typedef struct { uint32_t version_flags; uint32_t entry_count; ngx_rtmp_mp4_delay_entry_t entries[0]; } ngx_rtmp_mp4_delays_t; |
| stss 【Sync Sample Box】 | | type 4 字节已经偏移过, box_size 4 字节已经偏移过 typedef struct { uint32_t version_flags; uint32_t entry_count; uint32_t entries[0]; } ngx_rtmp_mp4_keys_t; |
| stsz 【Sample Size Boxes】 | | type 4 字节已经偏移过, box_size 4 字节已经偏移过 typedef struct { uint32_t version_flags; 4 字节 uint32_t sample_size; uint32_t sample_count; uint32_t entries[0]; } ngx_rtmp_mp4_sizes_t; |
| stco 【Chunk Offset Box】 co64 | | type 4 字节已经偏移过, box_size 4 字节已经偏移过 typedef struct { uint32_t version_flags; 4 字节固定 uint32_t entry_count; uint32_t entries[0]; } ngx_rtmp_mp4_offsets_t; typedef struct { uint32_t version_flags; uint32_t entry_count; uint64_t entries[0]; } ngx_rtmp_mp4_offsets64_t; |

三:数据结构解析

复制代码
static ngx_rtmp_mp4_box_t                       ngx_rtmp_mp4_boxes[] = {
    { ngx_rtmp_mp4_make_tag('t','r','a','k'),   ngx_rtmp_mp4_parse_trak   },
    { ngx_rtmp_mp4_make_tag('m','d','i','a'),   ngx_rtmp_mp4_parse        },
    { ngx_rtmp_mp4_make_tag('m','d','h','d'),   ngx_rtmp_mp4_parse_mdhd   },
    { ngx_rtmp_mp4_make_tag('h','d','l','r'),   ngx_rtmp_mp4_parse_hdlr   },
    { ngx_rtmp_mp4_make_tag('m','i','n','f'),   ngx_rtmp_mp4_parse        },
    { ngx_rtmp_mp4_make_tag('s','t','b','l'),   ngx_rtmp_mp4_parse        },
    { ngx_rtmp_mp4_make_tag('s','t','s','d'),   ngx_rtmp_mp4_parse_stsd   },
    { ngx_rtmp_mp4_make_tag('s','t','s','c'),   ngx_rtmp_mp4_parse_stsc   },  // 记录了每个chunk中包含多少sample
    { ngx_rtmp_mp4_make_tag('s','t','t','s'),   ngx_rtmp_mp4_parse_stts   },  // sample 解码时间的压缩表
    { ngx_rtmp_mp4_make_tag('c','t','t','s'),   ngx_rtmp_mp4_parse_ctts   },  // 帧解码到渲染的时间差值,通常用在B帧的场景,sample的CTS与DTS的时间差的压缩表
    { ngx_rtmp_mp4_make_tag('s','t','s','s'),   ngx_rtmp_mp4_parse_stss   },  // 关键帧映射表
    { ngx_rtmp_mp4_make_tag('s','t','s','z'),   ngx_rtmp_mp4_parse_stsz   },  // 每帧数据的大小
    { ngx_rtmp_mp4_make_tag('s','t','z','2'),   ngx_rtmp_mp4_parse_stz2   },
    { ngx_rtmp_mp4_make_tag('s','t','c','o'),   ngx_rtmp_mp4_parse_stco   },  // 记录了chunk对应的offset
    { ngx_rtmp_mp4_make_tag('c','o','6','4'),   ngx_rtmp_mp4_parse_co64   },
    { ngx_rtmp_mp4_make_tag('a','v','c','1'),   ngx_rtmp_mp4_parse_avc1   },
    { ngx_rtmp_mp4_make_tag('a','v','c','C'),   ngx_rtmp_mp4_parse_avcC   },   // sps pps 解析
    { ngx_rtmp_mp4_make_tag('m','p','4','a'),   ngx_rtmp_mp4_parse_mp4a   },
    { ngx_rtmp_mp4_make_tag('m','p','4','v'),   ngx_rtmp_mp4_parse_mp4v   },
    { ngx_rtmp_mp4_make_tag('e','s','d','s'),   ngx_rtmp_mp4_parse_esds   },
    { ngx_rtmp_mp4_make_tag('.','m','p','3'),   ngx_rtmp_mp4_parse_mp3    },
    { ngx_rtmp_mp4_make_tag('n','m','o','s'),   ngx_rtmp_mp4_parse_nmos   },
    { ngx_rtmp_mp4_make_tag('s','p','e','x'),   ngx_rtmp_mp4_parse_spex   },
    { ngx_rtmp_mp4_make_tag('w','a','v','e'),   ngx_rtmp_mp4_parse        }
};

ngx_rtmp_mp4_parse_stsc 记录了每个chunk中包含多少sample

上图数据解析

00 00 00 28(size) --> 40 字节总大小包含的自己在内的所有数据:

00 00 00 28(size) 73 74 73 63(stsc) 00 00 00 00(version_flags) 00 00 00 02(entry_count) 00 00 00 01(first_chunk) 00 00 00 02(samples_per_chunk) 00 00 00 01(sample_descrption_index) 00 00 00 02(first_chunk) 00 00 00 01(samples_per_chunk) 00 00 00 01(sample_descrption_index)

复制代码
static ngx_int_t
ngx_rtmp_mp4_parse_stsc(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
    ngx_rtmp_mp4_ctx_t         *ctx;
    ngx_rtmp_mp4_track_t       *t;
    ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_mp4_module);
    t = ctx->track;
    if (t == NULL) {
        return NGX_OK;
    }
    t->chunks = (ngx_rtmp_mp4_chunks_t *) pos;  // 内存数据上面的解析
    if (pos + sizeof(*t->chunks) + ngx_rtmp_r32(t->chunks->entry_count) *
                                   sizeof(t->chunks->entries[0])
        <= last)// 内存越界判断
    {
        return NGX_OK;
    }
    t->chunks = NULL;
    return NGX_ERROR;
}

ngx_rtmp_mp4_parse_stco 记录了chunk对应的offset

00 00 03 D4(980 长度) 73 74 63 6F (stco)00 00 00 00(version_flags) 00 00 00 F1( entry_count 241 ) 00 00 00 30( entrie) 00 00 10 4D ( entrie)00 00 20 1C( entrie) 00 00 29 38( entrie) 00 00 49 2D( entrie) 00 00 70 C9( entrie) 00 00 B1 C1 ( entrie) ...............

复制代码
static ngx_int_t
ngx_rtmp_mp4_parse_stco(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
    ngx_rtmp_mp4_ctx_t         *ctx;
    ngx_rtmp_mp4_track_t       *t;
    ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_mp4_module);
    t = ctx->track;
    if (t == NULL) {
        return NGX_OK;
    }
    t->offsets = (ngx_rtmp_mp4_offsets_t *) pos; // 数据结构的赋值
    if (pos + sizeof(*t->offsets) + ngx_rtmp_r32(t->offsets->entry_count) *
                                    sizeof(t->offsets->entries[0])
        <= last) // 内存越界判断
    {
        return NGX_OK;
    }
    t->offsets = NULL;
    return NGX_ERROR;
}

其他数据结构的解析与上面的流程基本一样

四:数据处理流程

五:play 流程代码分析

ffplay -analyzeduration 1000000 "rtmp://10.90.103.5/vod/0001.mp4

1> ngx_rtmp_mp4_init 初始化变量解析 mp4 moov box 的数据结构

2> ngx_rtmp_mp4_seek (ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t timestamp), timestamp = 0 ,seek 时间戳为零标识从头开始播放。 ngx_rtmp_mp4_seek_track 有两个轨道,音频轨和视频轨,分别进行seek

复制代码
static ngx_int_t
ngx_rtmp_mp4_seek_track(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t,
                        ngx_int_t timestamp)
{
    ngx_rtmp_mp4_cursor_t          *cr;
    cr = &t->cursor;
    ngx_memzero(cr, sizeof(*cr));
    if (ngx_rtmp_mp4_seek_time(s, t, ngx_rtmp_mp4_from_rtmp_timestamp(  // ngx_rtmp_mp4_from_rtmp_timestamp 时间戳的
                          t, timestamp)) != NGX_OK ||
        ngx_rtmp_mp4_seek_key(s, t)   != NGX_OK ||
        ngx_rtmp_mp4_seek_chunk(s, t) != NGX_OK ||
        ngx_rtmp_mp4_seek_size(s, t)  != NGX_OK ||
        ngx_rtmp_mp4_seek_delay(s, t) != NGX_OK)
    {
        return NGX_ERROR;
    }
    cr->valid = 1;
    return NGX_OK;
} 

// 时间戳的转换函数,mp4 文件到rtmp
// rtmp_mp4 存放的是timescale 时间戳 通常是 12288
// rtmp 时间刻度通常是 1000 
static ngx_inline uint32_t
ngx_rtmp_mp4_to_rtmp_timestamp(ngx_rtmp_mp4_track_t *t, uint64_t ts) // 给定一个时间值 从rtmp_mp4 12288 刻度转换到 1000
{
    return (uint32_t) (ts * 1000 / t->time_scale); 
}
static ngx_inline uint32_t
ngx_rtmp_mp4_from_rtmp_timestamp(ngx_rtmp_mp4_track_t *t, uint32_t ts) // 给定一个时间值 从rtmp 1000 刻度转换到12288
{
    return (uint64_t) ts * t->time_scale / 1000;
}

ngx_rtmp_mp4_seek_time :

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
| stts: typedef struct { uint32_t sample_count; uint32_t sample_delta; } ngx_rtmp_mp4_time_entry_t; typedef struct { uint32_t version_flags; uint32_t entry_count; ngx_rtmp_mp4_time_entry_t entries[0]; } ngx_rtmp_mp4_times_t; | | |

复制代码
static ngx_int_t
ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t,
                       uint32_t timestamp)
{
    ngx_rtmp_mp4_cursor_t      *cr;
    ngx_rtmp_mp4_time_entry_t  *te;
    uint32_t                    dt;
    if (t->times == NULL) {
        return NGX_ERROR;
    }
    cr = &t->cursor; // 对应音频或者视频轨道的游标器
    te = t->times->entries; // times --> ngx_rtmp_mp4_times_t
    // sample_counts	6,1,5,1,5,1,1,1,1,1
    // sample_deltas	1024,3040,1024,3088,1024,1008,1024,1032,1024,1048     
    // 整个循环就是处理,遍历sample_counts个sample_deltas值与 参数 timestamp比大小. 1. 小于记录 cr->timestamp += dt,
    while (cr->time_pos < ngx_rtmp_r32(t->times->entry_count)) {
        dt = ngx_rtmp_r32(te->sample_delta) * ngx_rtmp_r32(te->sample_count); // 242 * 1024 = 22757376 
        // cr->timestamp + dt >= timestamp ,在这个范围说明找到
        if (cr->timestamp + dt >= timestamp) {
            if (te->sample_delta == 0) {
                return NGX_ERROR;
            }
            cr->time_count = (timestamp - cr->timestamp) /
                             ngx_rtmp_r32(te->sample_delta);                   // sample_delta 相同,cr->time_count 等于当前时间戳距离这个entry开始时间错的插值,的第几个delta
            cr->timestamp += ngx_rtmp_r32(te->sample_delta) * cr->time_count;  // seek 的时间戳,就是把 cr->timestamp 修改当前的时间戳
            cr->pos += cr->time_count;                                         // 记录当前seek 对应时间戳具体的位置
            break;
        }
        // 没找到,
        cr->timestamp += dt;                       // 记录值进行累加,访问下一个
        cr->pos += ngx_rtmp_r32(te->sample_count); // 记录pos 的位置
        cr->time_pos++;                            // 记录已经访问第几个entry
        te++;                                      // te++ 下一个entry
    }
    if (cr->time_pos >= ngx_rtmp_r32(t->times->entry_count)) {
        return  NGX_ERROR;
    }
    return NGX_OK;
}

ngx_rtmp_mp4_seek_key

|--------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|---|
| stss typedef struct { uint32_t version_flags; uint32_t entry_count; // 个数 uint32_t entries[0]; // pos } ngx_rtmp_mp4_keys_t; | | |

复制代码
static ngx_int_t
ngx_rtmp_mp4_seek_key(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
{
    ngx_rtmp_mp4_cursor_t      *cr;
    uint32_t                   *ke;
    ngx_int_t                   dpos;
    cr = &t->cursor;       // 对应音频或者视频轨道的游标器
    if (t->keys == NULL) {
        return NGX_OK;
    }
    // cr->key_pos 初始值为0 ,t->keys->entry_count 关键帧的个数
    while (cr->key_pos < ngx_rtmp_r32(t->keys->entry_count)) {
        // 遍历每一个entry得到帧号,与当前pos位置进行对比,找到当前位置的下一个关键帧
        if (ngx_rtmp_r32(t->keys->entries[cr->key_pos]) > cr->pos) {    // cr->pos 上面seek 后具体位置
            break;
        }
        cr->key_pos++;
    }
    if (cr->key_pos >= ngx_rtmp_r32(t->keys->entry_count)) {
        return NGX_OK;
    }
    ke = &t->keys->entries[cr->key_pos];                                // 
    dpos = ngx_rtmp_r32(*ke) - cr->pos - 1;                             // 计算距离下一个关键帧的差值
    cr->key = 1;
    /* TODO: range version needed */
    for (; dpos > 0; --dpos) {
        ngx_rtmp_mp4_next_time(s, t);
    }
    return NGX_OK;
}

ngx_rtmp_mp4_seek_chunk

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
| stsc typedef struct { uint32_t first_chunk; uint32_t samples_per_chunk; uint32_t sample_descrption_index; } ngx_rtmp_mp4_chunk_entry_t; typedef struct { uint32_t version_flags; uint32_t entry_count; ngx_rtmp_mp4_chunk_entry_t entries[0]; } ngx_rtmp_mp4_chunks_t; | | |

复制代码
static ngx_int_t
ngx_rtmp_mp4_seek_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
{
    ngx_rtmp_mp4_cursor_t          *cr;
    ngx_rtmp_mp4_chunk_entry_t     *ce, *nce;
    ngx_uint_t                      pos, dpos, dchunk;
    cr = &t->cursor;                                              // 对应音频或者视频轨道的游标器
    if (t->chunks == NULL || t->chunks->entry_count == 0) {
        cr->chunk = 1;
        return NGX_OK;
    }
    ce = t->chunks->entries;                                      // entry 实体结构  
    pos = 0;
    // cr->chunk_pos 初始值是0 
    while (cr->chunk_pos + 1 < ngx_rtmp_r32(t->chunks->entry_count)) {
        nce = ce + 1;                                             // 下一个entry
        dpos = (ngx_rtmp_r32(nce->first_chunk) -
                ngx_rtmp_r32(ce->first_chunk)) *
                ngx_rtmp_r32(ce->samples_per_chunk);              // 距离下一个chunk总共有多少点,(2-1) * 2 = 2 帧
        if (pos + dpos > cr->pos) {                               // 判断当前位置是不是在这个chunk 中
            break;
        }
        pos += dpos;                                              // pos 从头遍历累加 
        ce++;                                                     // 访问下一个entry
        cr->chunk_pos++;                                          // chunk_pos ++,记录游标当前在第几个chunk位置 
    }
    if (ce->samples_per_chunk == 0) {
        return NGX_ERROR;
    }
    dchunk = (cr->pos - pos) / ngx_rtmp_r32(ce->samples_per_chunk); // seek 之后的当前pos 位置,与找到chunk的pos差值,相同连续的chunk 可能不记录写进去。
    // 比如:  first_chunk   1,2,3,4,5,6,12,13
    //  samples_per_chunk   1,4,2,4,2,4,3, 4      ---> 1 + 4 + 2 + 4 + 2 + 4 = 17 , 18 --> 38 之间都是; 这个时候会有一个 dchunk差值
    
    cr->chunk = ngx_rtmp_r32(ce->first_chunk) + dchunk;             // 得到当前游标chunk的值,真实对应chunk 位置。
    cr->chunk_pos = (ngx_uint_t) (ce - t->chunks->entries);         // cr->chunk_pos 记录的是存储的真实解析位置
    cr->chunk_count = (ngx_uint_t) (cr->pos - pos - dchunk *
                                    ngx_rtmp_r32(ce->samples_per_chunk)); // 剩余是 chunk 偏移的变量帧
    return ngx_rtmp_mp4_update_offset(s, t);
}

static ngx_int_t
ngx_rtmp_mp4_update_offset(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
{
    ngx_rtmp_mp4_cursor_t          *cr;
    ngx_uint_t                      chunk;
    cr = &t->cursor;
    if (cr->chunk < 1) {
        return NGX_ERROR;
    }
    chunk = cr->chunk - 1; // 数组访问 -1  
    if (t->offsets) {
        if (chunk >= ngx_rtmp_r32(t->offsets->entry_count)) {
            return NGX_ERROR;
        }
        cr->offset = (off_t) ngx_rtmp_r32(t->offsets->entries[chunk]); // 取出第几个chunk偏移大小进行赋值
        cr->size = 0;
        return NGX_OK;
    }
    if (t->offsets64) {
        if (chunk >= ngx_rtmp_r32(t->offsets64->entry_count)) {
            return NGX_ERROR;
        }
        cr->offset = (off_t) ngx_rtmp_r64(t->offsets64->entries[chunk]);
        cr->size = 0;
        return NGX_OK;
    }
    return NGX_ERROR;
}

ngx_rtmp_mp4_seek_size

|------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
| stsz typedef struct { uint32_t version_flags; 4 字节 uint32_t sample_size; uint32_t sample_count; uint32_t entries[0]; } ngx_rtmp_mp4_sizes_t; | |

复制代码
static ngx_int_t
ngx_rtmp_mp4_seek_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
{
    ngx_rtmp_mp4_cursor_t      *cr;
    ngx_uint_t                  pos;
    cr = &t->cursor;                                             // 当前游标
    if (cr->chunk_count > cr->pos) {                             // 》 ??
        return NGX_ERROR;
    }
    if (t->sizes) {
        if (t->sizes->sample_size) {
            cr->size = ngx_rtmp_r32(t->sizes->sample_size);
            cr->offset += cr->size * cr->chunk_count;
            return NGX_OK;
        }
        if (cr->pos >= ngx_rtmp_r32(t->sizes->sample_count)) {
            return NGX_ERROR;
        }
        for (pos = 1; pos <= cr->chunk_count; ++pos) {           // chunk_count ??
            cr->offset += ngx_rtmp_r32(t->sizes->entries[cr->pos - pos]); 
        }
        cr->size_pos = cr->pos;                                   // seek 时间戳当前pos
        cr->size = ngx_rtmp_r32(t->sizes->entries[cr->size_pos]); // 获取当前pos对应帧数据大小
        return NGX_OK;
    }
    if (t->sizes2) {
        if (cr->size_pos >= ngx_rtmp_r32(t->sizes2->sample_count)) {
            return NGX_ERROR;
        }
        cr->size_pos = cr->pos;
        return NGX_OK;
    }
    return NGX_ERROR;
}

ngx_rtmp_mp4_seek_delay

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
| ctts typedef struct { uint32_t sample_count; uint32_t sample_offset; } ngx_rtmp_mp4_delay_entry_t; typedef struct { uint32_t version_flags; uint32_t entry_count; ngx_rtmp_mp4_delay_entry_t entries[0]; } ngx_rtmp_mp4_delays_t; | |

复制代码
static ngx_int_t
ngx_rtmp_mp4_seek_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
{
    ngx_rtmp_mp4_cursor_t      *cr;
    ngx_rtmp_mp4_delay_entry_t *de;
    uint32_t                    pos, dpos;
    cr = &t->cursor;
    if (t->delays == NULL) {
        return NGX_OK;
    }
    pos = 0;
    de = t->delays->entries;
    while (cr->delay_pos < ngx_rtmp_r32(t->delays->entry_count)) { // 遍历所有entry
        dpos = ngx_rtmp_r32(de->sample_count);                     // 2 表示:sample的个数
        if (pos + dpos > cr->pos) {                                // 0 + 2 > 0 , 当前pos 帧在其中
            cr->delay_count = cr->pos - pos;                       // 差值 count
            cr->delay = ngx_rtmp_r32(de->sample_offset);           // 偏移大小
            break;
        }
        cr->delay_pos++; // 记录entry访问的位置
        pos += dpos;     // pos 累加计数
        de++;            // 下一个entry
    }
    if (cr->delay_pos >= ngx_rtmp_r32(t->delays->entry_count)) {
        return NGX_OK;
    }
    return NGX_OK;
}

3> ngx_rtmp_mp4_next play 播放一直取下一帧数据

复制代码
static ngx_int_t
ngx_rtmp_mp4_next(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
{
    if (ngx_rtmp_mp4_next_time(s, t)  != NGX_OK ||
        ngx_rtmp_mp4_next_key(s, t)   != NGX_OK ||
        ngx_rtmp_mp4_next_chunk(s, t) != NGX_OK ||
        ngx_rtmp_mp4_next_size(s, t)  != NGX_OK ||
        ngx_rtmp_mp4_next_delay(s, t) != NGX_OK)
    {
        t->cursor.valid = 0;
        return NGX_ERROR;
    }
    t->cursor.valid = 1;
    return NGX_OK;
}

六:代码调试

使用进行查看变量的值: ngx_rtmp_r32(t->delays->entry_count)

相关推荐
Panesle10 分钟前
HunyuanCustom:文生视频框架论文速读
人工智能·算法·音视频·文生视频
又逢乱世4 小时前
Ubuntu 安装 Nginx
运维·nginx
程序员JerrySUN6 小时前
驱动开发硬核特训 · Day 30(下篇): 深入解析 lm48100q I2C 音频编解码器驱动模型(基于 i.MX8MP)
linux·驱动开发·架构·音视频
matrixlzp10 小时前
Nginx yum 安装
nginx
matrixlzp11 小时前
Nginx 使用 Keepalived 搭建 nginx 高可用
运维·nginx
读心悦11 小时前
5000字总结 HTML5 中的音频和视频,关羽标签、属性、API 和最佳实践
前端·音视频·html5
东风西巷13 小时前
BLURRR剪辑软件免费版:创意剪辑,轻松上手,打造个性视频
android·智能手机·音视频·生活·软件需求
weixin_4462608513 小时前
视觉革命来袭!ComfyUI-LTXVideo 让视频创作更高效
人工智能·音视频
拧螺丝专业户13 小时前
外网访问内网海康威视监控视频的方案:WebRTC + Coturn 搭建
音视频·webrtc·监控视频
Yang三少喜欢撸铁13 小时前
【阿里云免费领取域名以及ssl证书,通过Nginx反向代理web服务】
nginx·阿里云·代理模式·ssl