text: /*
   * Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
   */
  
  
  #include <ngx_config.h>
  #include <ngx_core.h>
  #include "ngx_rtmp.h"
  #include "ngx_rbuf.h"
  
  
  static void *ngx_rtmp_shared_create_conf(ngx_cycle_t *cycle);
  static char *ngx_rtmp_shared_init_conf(ngx_cycle_t *cycle, void *conf);
  
  
  /* 1316 == 188 * 7 RTP pack 7 MPEG-TS packets as a RTP package */
  #define NGX_MPEGTS_BUF_SIZE   1316
  
  typedef struct {
      ngx_rtmp_frame_t           *free_frame;
      ngx_mpegts_frame_t         *free_mpegts_frame;
      ngx_pool_t                 *pool;
  
      ngx_uint_t                  nalloc_frame;
      ngx_uint_t                  nfree_frame;
  } ngx_rtmp_shared_conf_t;
  
  
  static ngx_command_t  ngx_rtmp_shared_commands[] = {
  
        ngx_null_command
  };
  
  
  static ngx_core_module_t  ngx_rtmp_shared_module_ctx = {
      ngx_string("rtmp_shared"),
      ngx_rtmp_shared_create_conf,            /* create conf */
      ngx_rtmp_shared_init_conf               /* init conf */
  };
  
  
  ngx_module_t  ngx_rtmp_shared_module = {
      NGX_MODULE_V1,
      &ngx_rtmp_shared_module_ctx,            /* module context */
      ngx_rtmp_shared_commands,               /* module directives */
      NGX_CORE_MODULE,                        /* module type */
      NULL,                                   /* init master */
      NULL,                                   /* init module */
      NULL,                                   /* init process */
      NULL,                                   /* init thread */
      NULL,                                   /* exit thread */
      NULL,                                   /* exit process */
      NULL,                                   /* exit master */
      NGX_MODULE_V1_PADDING
  };
  
  
  static void *
  ngx_rtmp_shared_create_conf(ngx_cycle_t *cycle)
  {
      ngx_rtmp_shared_conf_t     *rscf;
  
      rscf = ngx_pcalloc(cycle->pool, sizeof(ngx_rtmp_shared_conf_t));
      if (rscf == NULL) {
          return NULL;
      }
  
      return rscf;
  }
  
  
  static char *
  ngx_rtmp_shared_init_conf(ngx_cycle_t *cycle, void *conf)
  {
      ngx_rtmp_shared_conf_t     *rscf = conf;
  
      rscf->pool = ngx_create_pool(4096, cycle->log);
      if (rscf->pool == NULL) {
          return NGX_CONF_ERROR;
      }
  
      return NGX_CONF_OK;
  }
  
  
  ngx_int_t
  ngx_rtmp_prepare_merge_frame(ngx_rtmp_session_t *s)
  {
      ngx_rtmp_core_app_conf_t   *cacf;
      ngx_chain_t                *ll, **ln;
      ngx_uint_t                  n;
  
      // merge frame not send completely
      if (s->out_chain) {
          return NGX_OK;
      }
  
      cacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_core_module);
  
      ngx_rtmp_free_merge_frame(s);
  
      ln = &s->out_chain;
  
      for (n = 0; n < cacf->merge_frame && s->out_pos != s->out_last; ++n) {
          // save frame chain with rtmp chunk or flv tag
          s->merge[n] = s->prepare_handler(s);
  
          if (s->out_pos == s->out_last) {
              break;
          }
  
          // normal link to chain all merge frames
          for (ll = s->merge[n]; ll; ll = ll->next, ln = &(*ln)->next) {
              *ln = ngx_alloc_chain_link(s->pool);
              if (*ln == NULL) {
                  s->nframe = n;
                  return NGX_ERROR;
              }
              (*ln)->next = NULL;
  
              (*ln)->buf = ll->buf;
          }
  
          // save frame prepare to send
          if (s->live_type == NGX_MPEGTS_LIVE) {
              s->prepare_mpegts_frame[n] = s->mpegts_out[s->out_pos];
          } else {
              s->prepare_frame[n] = s->out[s->out_pos];
          }
  
          ++s->out_pos;
          s->out_pos %= s->out_queue;
      }
  
      s->nframe = n;
  
      return NGX_OK;
  }
  
  void
  ngx_rtmp_free_merge_frame(ngx_rtmp_session_t *s)
  {
      ngx_uint_t                  n;
  
      if (s->live_type == NGX_MPEGTS_LIVE) {
          for (n = 0; n < s->nframe; ++n) {
              ngx_put_chainbufs(s->merge[n]);
              s->merge[n] = NULL;
  
              ngx_rtmp_shared_free_mpegts_frame(s->prepare_mpegts_frame[n]);
              s->prepare_mpegts_frame[n] = NULL;
          }
      } else {
          for (n = 0; n < s->nframe; ++n) {
              ngx_put_chainbufs(s->merge[n]);
              s->merge[n] = NULL;
  
              ngx_rtmp_shared_free_frame(s->prepare_frame[n]);
              s->prepare_frame[n] = NULL;
          }
      }
  
      s->nframe = 0;
  }
  
  void
  ngx_rtmp_shared_append_chain(ngx_rtmp_frame_t *frame, size_t size,
          ngx_chain_t *cl, ngx_flag_t mandatory)
  {
      ngx_chain_t               **ll;
      u_char                     *p;
      size_t                      len;
  
      for (ll = &frame->chain; (*ll) && (*ll)->next; ll = &(*ll)->next);
  
      if (cl == NULL) {
          if (mandatory) {
              *ll = ngx_get_chainbuf(size, 1);
          }
          return;
      }
  
      p = cl->buf->pos;
  
      for (;;) {
          if ((*ll) && (*ll)->buf->last == (*ll)->buf->end) {
              ll = &(*ll)->next;
          }
  
          if (*ll == NULL) {
              *ll = ngx_get_chainbuf(size, 1);
          }
  
          while ((*ll)->buf->end - (*ll)->buf->last >= cl->buf->last - p) {
              len = cl->buf->last - p;
              (*ll)->buf->last = ngx_cpymem((*ll)->buf->last, p, len);
              cl = cl->next;
              if (cl == NULL) {
                  return;
              }
              p = cl->buf->pos;
          }
  
          len = (*ll)->buf->end - (*ll)->buf->last;
          (*ll)->buf->last = ngx_cpymem((*ll)->buf->last, p, len);
          p += len;
      }
  }
  
  ngx_rtmp_frame_t *
  ngx_rtmp_shared_alloc_frame(size_t size, ngx_chain_t *cl, ngx_flag_t mandatory)
  {
      ngx_rtmp_shared_conf_t     *rscf;
      ngx_rtmp_frame_t           *frame;
  
      rscf = (ngx_rtmp_shared_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
                                                     ngx_rtmp_shared_module);
  
      frame = rscf->free_frame;
      if (frame) {
          rscf->free_frame = frame->next;
          frame->chain = NULL;
          --rscf->nfree_frame;
      } else {
          frame = ngx_pcalloc(rscf->pool, sizeof(ngx_rtmp_frame_t));
          if (frame == NULL) {
              return NULL;
          }
          ++rscf->nalloc_frame;
      }
  
      frame->ref = 1;
      frame->next = NULL;
  
      ngx_rtmp_shared_append_chain(frame, size, cl, mandatory);
  
      return frame;
  }
  
  void
  ngx_rtmp_shared_free_frame(ngx_rtmp_frame_t *frame)
  {
      ngx_rtmp_shared_conf_t     *rscf;
      ngx_chain_t                *cl;
  
      rscf = (ngx_rtmp_shared_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
                                                     ngx_rtmp_shared_module);
  
      if (frame == NULL || --frame->ref) {
          return;
      }
  
      /* recycle chainbuf */
      cl = frame->chain;
      while (cl) {
          frame->chain = cl->next;
          ngx_put_chainbuf(cl);
          cl = frame->chain;
      }
  
      /* recycle frame */
      frame->next = rscf->free_frame;
      rscf->free_frame = frame;
      ++rscf->nfree_frame;
  }
  
  void
  ngx_mpegts_shared_append_chain(ngx_mpegts_frame_t *frame, ngx_chain_t *cl,
          ngx_flag_t mandatory)
  {
      ngx_chain_t               **ll;
      u_char                     *p;
      size_t                      len;
  
      for (ll = &frame->chain; (*ll) && (*ll)->next; ll = &(*ll)->next);
  
      if (cl == NULL) {
          if (mandatory) {
              *ll = ngx_get_chainbuf(NGX_MPEGTS_BUF_SIZE, 1);
          }
          return;
      }
  
      p = cl->buf->pos;
  
      for (;;) {
          if ((*ll) && (*ll)->buf->last == (*ll)->buf->end) {
              ll = &(*ll)->next;
          }
  
          if (*ll == NULL) {
              *ll = ngx_get_chainbuf(NGX_MPEGTS_BUF_SIZE, 1);
          }
  
          while ((*ll)->buf->end - (*ll)->buf->last >= cl->buf->last - p) {
              len = cl->buf->last - p;
              (*ll)->buf->last = ngx_cpymem((*ll)->buf->last, p, len);
              cl = cl->next;
              if (cl == NULL) {
                  return;
              }
              p = cl->buf->pos;
          }
  
          len = (*ll)->buf->end - (*ll)->buf->last;
          (*ll)->buf->last = ngx_cpymem((*ll)->buf->last, p, len);
          p += len;
      }
  }
  
  ngx_mpegts_frame_t *
  ngx_rtmp_shared_alloc_mpegts_frame(ngx_chain_t *cl, ngx_flag_t mandatory)
  {
      ngx_rtmp_shared_conf_t     *rscf;
      ngx_mpegts_frame_t         *frame;
  
      rscf = (ngx_rtmp_shared_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
                                                     ngx_rtmp_shared_module);
  
      frame = rscf->free_mpegts_frame;
      if (frame) {
          rscf->free_mpegts_frame = frame->next;
          --rscf->nfree_frame;
      } else {
          frame = ngx_pcalloc(rscf->pool, sizeof(ngx_mpegts_frame_t));
          if (frame == NULL) {
              return NULL;
          }
          ++rscf->nalloc_frame;
      }
  
      ngx_memset(frame, 0, sizeof(ngx_mpegts_frame_t));
      frame->ref = 1;
      frame->next = NULL;
  
      ngx_mpegts_shared_append_chain(frame, cl, mandatory);
  
      return frame;
  }
  
  void
  ngx_rtmp_shared_free_mpegts_frame(ngx_mpegts_frame_t *frame)
  {
      ngx_rtmp_shared_conf_t     *rscf;
      ngx_chain_t                *cl;
  
      rscf = (ngx_rtmp_shared_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
                                                     ngx_rtmp_shared_module);
  
      if (frame == NULL || --frame->ref) {
          return;
      }
  
      /* recycle chainbuf */
      cl = frame->chain;
      while (cl) {
          frame->chain = cl->next;
          ngx_put_chainbuf(cl);
          cl = frame->chain;
      }
  
      /* recycle frame */
      frame->next = rscf->free_mpegts_frame;
      rscf->free_mpegts_frame = frame;
      ++rscf->nfree_frame;
  }
  
  ngx_chain_t *
  ngx_rtmp_shared_state(ngx_http_request_t *r)
  {
  	ngx_rtmp_shared_conf_t     *rscf;
      ngx_chain_t                *cl;
      ngx_buf_t                  *b;
      size_t                      len;
  
      rscf = (ngx_rtmp_shared_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
                                                     ngx_rtmp_shared_module);
  
      len = sizeof("##########rtmp shared state##########\n") - 1
          + sizeof("ngx_rtmp_shared alloc frame: \n") - 1 + NGX_OFF_T_LEN
          + sizeof("ngx_rtmp_shared free frame: \n") - 1 + NGX_OFF_T_LEN;
  
      cl = ngx_alloc_chain_link(r->pool);
      if (cl == NULL) {
          return NULL;
      }
      cl->next = NULL;
  
      b = ngx_create_temp_buf(r->pool, len);
      if (b == NULL) {
          return NULL;
      }
      cl->buf = b;
  
      b->last = ngx_snprintf(b->last, len,
              "##########rtmp shared state##########\n"
              "ngx_rtmp_shared alloc frame: %ui\n"
              "ngx_rtmp_shared free frame: %ui\n",
              rscf->nalloc_frame, rscf->nfree_frame);
  
      return cl;
  }
  