深入理解nginx mp4流媒体模块[下下]

深入理解nginx mp4流媒体模块[上]
深入理解nginx mp4流媒体模块[中]
深入理解nginx mp4流媒体模块[下]
深入理解nginx mp4流媒体模块[下下]

3.2.2 生成目标MP4文件

  重新回到ngx_http_mp4_process,在ngx_http_mp4_read_atom函数将moov读取到缓冲区后,接下去要准备生成用于发送给用户的mp4文件了。

  首先它会判断是否读取到了trak,如果一个trak都没有加载到,那么认为这个MP4文件是有问题的,接着在判断是否MP4文件里面是否有mdat atom,如果没有那么也是有个问题的,源码如下:

  if (mp4->trak.nelts == 0) {
   
        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
                      "no mp4 trak atoms were found in \"%s\"",
                      mp4->file.name.data);
        return NGX_ERROR;
    }

    if (mp4->mdat_atom.buf == NULL) {
   
        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
                      "no mp4 mdat atom was found in \"%s\"",
                      mp4->file.name.data);
        return NGX_ERROR;
    }

  接着就开始把之前读取到的atom缓冲区用ngx_chain_t链接起来,如下:

    prev = &mp4->out;

    if (mp4->ftyp_atom.buf) {
   
        *prev = &mp4->ftyp_atom;
        prev = &mp4->ftyp_atom.next;
    }

    *prev = &mp4->moov_atom;
    prev = &mp4->moov_atom.next;

    if (mp4->mvhd_atom.buf) {
   
        mp4->moov_size += mp4->mvhd_atom_buf.last - mp4->mvhd_atom_buf.pos;
        *prev = &mp4->mvhd_atom;
        prev = &mp4->mvhd_atom.next;
    }
......


  由以上代码可以看到,mp4->out中最终保存了准备要响应给客户端的ngx_chain_t链。这里县链接的是ftyp atom,接着是moov atom,然后是mvhd atom…
  在以上代码中 mp4->moov_size 用来保存目标MP4文件中的moov atom的大小。

  接下去就是来生成各个trak atom了,在生成trak atom的时候,需要根据请求的时间偏移量对相应的atom进行调整。

  for (i = 0; i < mp4->trak.nelts; i++) {
   

		/* 更新调整各个atom */
        if (ngx_http_mp4_update_stts_atom(mp4, &trak[i]) != NGX_OK) {
   
            return NGX_ERROR;
        }

        if (ngx_http_mp4_update_stss_atom(mp4, &trak[i]) != NGX_OK) {
   
            return NGX_ERROR;
        }

        ngx_http_mp4_update_ctts_atom(mp4, &trak[i]);

        if (ngx_http_mp4_update_stsc_atom(mp4, &trak[i]) != NGX_OK) {
   
            return NGX_ERROR;
        }

        if (ngx_http_mp4_update_stsz_atom(mp4, &trak[i]) != NGX_OK) {
   
            return NGX_ERROR;
        }

        if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
   
            if (ngx_http_mp4_update_co64_atom(mp4, &trak[i]) != NGX_OK) {
   
                return NGX_ERROR;
            }

        } else {
   
            if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) {
   
                return NGX_ERROR;
            }
        }

        ngx_http_mp4_update_stbl_atom(mp4, &trak[i]);
        ngx_http_mp4_update_minf_atom(mp4, &trak[i]);
        ngx_http_mp4_update_mdhd_atom(mp4, &trak[i]);
        trak[i].size += trak[i].hdlr_size;
        ngx_http_mp4_update_mdia_atom(mp4, &trak[i]);
        trak[i].size += trak[i].tkhd_size;
        ngx_http_mp4_update_edts_atom(mp4, &trak[i]);
        ngx_http_mp4_update_trak_atom(mp4, &trak[i]);

		/* 统计moov atom的大小 */
        mp4->moov_size += trak[i].size;

		/* start_ofset和end_offset分别为准备要发送给客户端的mdat atom
		   中帧数据的起始和结束偏移量 
		*/
        if (start_offset > trak[i].start_offset) {
   
            start_offset = trak[i].start_offset;
        }

        if (end_offset < trak[i].end_offset) {
   
            end_offset = trak[i].end_offset;
        }

        /* 将当前trak atom中的各个子孙atom链接到输出链中 */
        *prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM];
        prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next;

        for (j = 0; j < NGX_HTTP_MP4_LAST_ATOM + 1; j++) {
   
            if (trak[i].out[j].buf) {
   
                *prev = &trak[i].out[j];
                prev = &trak[i].out[j].next;
            }
        }
    }

1. 调整stts atom
   stts atom即Decoding Time to Samle Box, 记录了每个sample对应的解码时间。需要根据客户端的请求时间区间[start, end]来进行裁减。源码如下:

static ngx_int_t
ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
    ngx_http_mp4_trak_t *trak)
{
   
    size_t                atom_size;
    ngx_buf_t            *atom, *data;
    ngx_mp4_stts_atom_t  *stts_atom;

    /*
     * mdia.minf.stbl.stts updating requires trak->timescale
     * from mdia.mdhd atom which may reside after mdia.minf
     */

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
                   "mp4 stts atom update");

    data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;

    if (data == NULL) {
   
        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
                      "no mp4 stts atoms were found in \"%s\"",
                      mp4->file.name.data);
        return NGX_ERROR;
    }

	/* 裁减stts atom的头部记录 */
    if (ngx_http_mp4_crop_stts_data(mp4, trak, 1) != NGX_OK) {
   
        return NGX_ERROR;
    }
    
	/* 裁减stts atom的尾部记录 */
    if (ngx_http_mp4_crop_stts_data(mp4, trak, 0) != NGX_OK) {
   
        return NGX_ERROR;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
                   "time-to-sample entries:%uD", trak->time_to_sample_entries);

    atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos);
    trak->size += atom_size;    /* 更新当前trak atom的大小  */

	/* 更新stts atom头部信息 */
    atom = trak->out[NGX_HTTP_MP4_STTS_ATOM].buf;
    stts_atom = (ngx_mp4_stts_atom_t *) atom->pos;
    ngx_mp4_set_32value(stts_atom->size, atom_size);
    ngx_mp4_set_32value(stts_atom->entries, trak->time_to_sample_entries);

    return NGX_OK;
}

  所以,裁减的主要逻辑是在ngx_http_mp4_crop_stts_data函数中来实现的。

static ngx_int_t
ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
    ngx_http_mp4_trak_t *trak, ngx_uint_t start)
{
   
    uint32_t               count, duration, rest, key_prefix;
    uint64_t               start_time;
    ngx_buf_t             *data;
    ngx_uint_t             start_sample, entries, start_sec;
    ngx_mp4_stts_entry_t  *entry, *end;

    if (start) {
   
        start_sec = mp4->start;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
                       "mp4 stts crop start_time:%ui", start_sec);

    } else if (mp4->length) {
   
        start_sec = mp4->length;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
                       "mp4 stts crop end_time:%ui", start_sec);

    } else {
   
        return NGX_OK;
    }

    data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;

	/* 将要裁减的时间戳单位转换为当前trak的时间单位*/
    start_time = (uint64_t) start_sec * trak->timescale / 1000 + trak->prefix;

    entries = trak->time_to_sample_entries;     /* 当前stts总共有多少记录 */
    start_sample = 0;                           /* 当前记录的起始sample id */
    entry = (ngx_mp4_stts_entry_t *) data->pos; /* stts记录的起始位置 */
    end = (ngx_mp4_stts_entry_t *) data->last;  /* stts记录的结束位置 */

	/* 查找start_time对应的记录位置 */
    while <

相关推荐

  1. 深入理解nginx mp4媒体模块[]

    2024-04-05 15:22:06       16 阅读
  2. 深入理解nginx mp4媒体模块[中]

    2024-04-05 15:22:06       20 阅读
  3. 深入理解nginx的请求限速模块[]

    2024-04-05 15:22:06       19 阅读
  4. 4、Flink执行模式/批)详解(

    2024-04-05 15:22:06       14 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-05 15:22:06       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-05 15:22:06       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-05 15:22:06       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-05 15:22:06       20 阅读

热门阅读

  1. Day.21

    2024-04-05 15:22:06       11 阅读
  2. 教你如何在 WebView 中实现优雅的后退键处理

    2024-04-05 15:22:06       15 阅读
  3. C# 委托与事件 深入

    2024-04-05 15:22:06       14 阅读
  4. 金融科技包含领域

    2024-04-05 15:22:06       15 阅读
  5. [环境配置]conda 64位安装32位python

    2024-04-05 15:22:06       16 阅读
  6. LeetCode的使用方法

    2024-04-05 15:22:06       11 阅读
  7. 初学者如何入门深度学习?

    2024-04-05 15:22:06       16 阅读
  8. TypeScript:泛型

    2024-04-05 15:22:06       16 阅读
  9. SSH数据加密传输:安全连接新体验

    2024-04-05 15:22:06       16 阅读
  10. android 扫描二维码

    2024-04-05 15:22:06       12 阅读
  11. think:该写什么样的blog

    2024-04-05 15:22:06       17 阅读
  12. vue如何搭建项目?

    2024-04-05 15:22:06       17 阅读