1 
2 /*
3  * Copyright (C) Roman Arutyunyan
4  */
5 
6 
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include "ngx_rtmp_play_module.h"
10 #include "ngx_rtmp_codec_module.h"
11 #include "ngx_rtmp_streams.h"
12 
13 
14 static ngx_int_t ngx_rtmp_flv_postconfiguration(ngx_conf_t *cf);
15 static void ngx_rtmp_flv_read_meta(ngx_rtmp_session_t *s, ngx_file_t *f);
16 static ngx_int_t ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t *s,
17        ngx_file_t *f, ngx_int_t timestamp);
18 static ngx_int_t ngx_rtmp_flv_init(ngx_rtmp_session_t *s, ngx_file_t *f,
19        ngx_int_t aindex, ngx_int_t vindex);
20 static ngx_int_t ngx_rtmp_flv_start(ngx_rtmp_session_t *s, ngx_file_t *f);
21 static ngx_int_t ngx_rtmp_flv_seek(ngx_rtmp_session_t *s, ngx_file_t *f,
22        ngx_uint_t offset);
23 static ngx_int_t ngx_rtmp_flv_stop(ngx_rtmp_session_t *s, ngx_file_t *f);
24 static ngx_int_t ngx_rtmp_flv_send(ngx_rtmp_session_t *s, ngx_file_t *f,
25                                    ngx_uint_t *ts);
26 
27 
28 typedef struct {
29     ngx_uint_t                          nelts;
30     ngx_uint_t                          offset;
31 } ngx_rtmp_flv_index_t;
32 
33 
34 typedef struct {
35     ngx_int_t                           offset;
36     ngx_int_t                           start_timestamp;
37     ngx_event_t                         write_evt;
38     uint32_t                            last_audio;
39     uint32_t                            last_video;
40     ngx_uint_t                          msg_mask;
41     uint32_t                            epoch;
42 
43     unsigned                            meta_read:1;
44     ngx_rtmp_flv_index_t                filepositions;
45     ngx_rtmp_flv_index_t                times;
46 } ngx_rtmp_flv_ctx_t;
47 
48 
49 #define NGX_RTMP_FLV_BUFFER             (1024*1024)
50 #define NGX_RTMP_FLV_BUFLEN_ADDON       1000
51 #define NGX_RTMP_FLV_TAG_HEADER         11
52 #define NGX_RTMP_FLV_DATA_OFFSET        13
53 
54 
55 static u_char                           ngx_rtmp_flv_buffer[
56                                         NGX_RTMP_FLV_BUFFER];
57 static u_char                           ngx_rtmp_flv_header[
58                                         NGX_RTMP_FLV_TAG_HEADER];
59 
60 
61 static ngx_rtmp_module_t  ngx_rtmp_flv_module_ctx = {
62     NULL,                                   /* preconfiguration */
63     ngx_rtmp_flv_postconfiguration,         /* postconfiguration */
64     NULL,                                   /* create main configuration */
65     NULL,                                   /* init main configuration */
66     NULL,                                   /* create server configuration */
67     NULL,                                   /* merge server configuration */
68     NULL,                                   /* create app configuration */
69     NULL                                    /* merge app configuration */
70 };
71 
72 
73 ngx_module_t  ngx_rtmp_flv_module = {
74     NGX_MODULE_V1,
75     &ngx_rtmp_flv_module_ctx,               /* module context */
76     NULL,                                   /* module directives */
77     NGX_RTMP_MODULE,                        /* module type */
78     NULL,                                   /* init master */
79     NULL,                                   /* init module */
80     NULL,                                   /* init process */
81     NULL,                                   /* init thread */
82     NULL,                                   /* exit thread */
83     NULL,                                   /* exit process */
84     NULL,                                   /* exit master */
85     NGX_MODULE_V1_PADDING
86 };
87 
88 
89 static ngx_int_t
ngx_rtmp_flv_fill_index(ngx_rtmp_amf_ctx_t * ctx,ngx_rtmp_flv_index_t * idx)90 ngx_rtmp_flv_fill_index(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_flv_index_t *idx)
91 {
92     uint32_t                        nelts;
93     ngx_buf_t                      *b;
94 
95     /* we have AMF array pointed by context;
96      * need to extract its size (4 bytes) &
97      * save offset of actual array data */
98 
99     b = ctx->link->buf;
100 
101     if (b->last - b->pos < (ngx_int_t) ctx->offset + 4) {
102         return NGX_ERROR;
103     }
104 
105     ngx_rtmp_rmemcpy(&nelts, b->pos + ctx->offset, 4);
106 
107     idx->nelts = nelts;
108     idx->offset = ctx->offset + 4;
109 
110     return NGX_OK;
111 }
112 
113 
114 static ngx_int_t
ngx_rtmp_flv_init_index(ngx_rtmp_session_t * s,ngx_chain_t * in)115 ngx_rtmp_flv_init_index(ngx_rtmp_session_t *s, ngx_chain_t *in)
116 {
117     ngx_rtmp_flv_ctx_t             *ctx;
118 
119     static ngx_rtmp_amf_ctx_t       filepositions_ctx;
120     static ngx_rtmp_amf_ctx_t       times_ctx;
121 
122     static ngx_rtmp_amf_elt_t       in_keyframes[] = {
123 
124         { NGX_RTMP_AMF_ARRAY | NGX_RTMP_AMF_CONTEXT,
125           ngx_string("filepositions"),
126           &filepositions_ctx, 0 },
127 
128         { NGX_RTMP_AMF_ARRAY | NGX_RTMP_AMF_CONTEXT,
129           ngx_string("times"),
130           &times_ctx, 0 }
131     };
132 
133     static ngx_rtmp_amf_elt_t       in_inf[] = {
134 
135         { NGX_RTMP_AMF_OBJECT,
136           ngx_string("keyframes"),
137           in_keyframes, sizeof(in_keyframes) }
138     };
139 
140     static ngx_rtmp_amf_elt_t       in_elts[] = {
141 
142         { NGX_RTMP_AMF_STRING,
143           ngx_null_string,
144           NULL, 0 },
145 
146         { NGX_RTMP_AMF_OBJECT,
147           ngx_null_string,
148           in_inf, sizeof(in_inf) },
149     };
150 
151     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_flv_module);
152 
153     if (ctx == NULL || in == NULL) {
154         return NGX_OK;
155     }
156 
157     ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
158                   "flv: init index");
159 
160     ngx_memzero(&filepositions_ctx, sizeof(filepositions_ctx));
161     ngx_memzero(&times_ctx, sizeof(times_ctx));
162 
163     if (ngx_rtmp_receive_amf(s, in, in_elts,
164                              sizeof(in_elts) / sizeof(in_elts[0])))
165     {
166         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
167                      "flv: init index error");
168         return NGX_OK;
169     }
170 
171     if (filepositions_ctx.link && ngx_rtmp_flv_fill_index(&filepositions_ctx,
172                                                           &ctx->filepositions)
173         != NGX_OK)
174     {
175         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
176                      "flv: failed to init filepositions");
177         return NGX_ERROR;
178     }
179 
180     ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
181                   "flv: filepositions nelts=%ui offset=%ui",
182                    ctx->filepositions.nelts, ctx->filepositions.offset);
183 
184     if (times_ctx.link && ngx_rtmp_flv_fill_index(&times_ctx,
185                                                   &ctx->times)
186         != NGX_OK)
187     {
188         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
189                      "flv: failed to init times");
190         return NGX_ERROR;
191     }
192 
193     ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
194                   "flv: times nelts=%ui offset=%ui",
195                    ctx->times.nelts, ctx->times.offset);
196 
197     return  NGX_OK;
198 }
199 
200 
201 static double
ngx_rtmp_flv_index_value(void * src)202 ngx_rtmp_flv_index_value(void *src)
203 {
204     double      v;
205 
206     ngx_rtmp_rmemcpy(&v, src, 8);
207 
208     return v;
209 }
210 
211 
212 static ngx_int_t
ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t * s,ngx_file_t * f,ngx_int_t timestamp)213 ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t *s, ngx_file_t *f,
214     ngx_int_t timestamp)
215 {
216     ngx_rtmp_flv_ctx_t             *ctx;
217     ssize_t                         n, size;
218     ngx_uint_t                      offset, index, ret, nelts;
219     double                          v;
220 
221     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_flv_module);
222 
223     if (ctx == NULL) {
224         goto rewind;
225     }
226 
227     ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
228                   "flv: lookup index start timestamp=%i",
229                    timestamp);
230 
231     if (ctx->meta_read == 0) {
232         ngx_rtmp_flv_read_meta(s, f);
233         ctx->meta_read = 1;
234     }
235 
236     if (timestamp <= 0 || ctx->filepositions.nelts == 0
237                        || ctx->times.nelts == 0)
238     {
239         goto rewind;
240     }
241 
242     /* read index table from file given offset */
243     offset = NGX_RTMP_FLV_DATA_OFFSET + NGX_RTMP_FLV_TAG_HEADER +
244              ctx->times.offset;
245 
246     /* index should fit in the buffer */
247     nelts = ngx_min(ctx->times.nelts, sizeof(ngx_rtmp_flv_buffer) / 9);
248     size = nelts * 9;
249 
250     n = ngx_read_file(f, ngx_rtmp_flv_buffer, size, offset);
251 
252     if (n != size) {
253         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
254                      "flv: could not read times index");
255         goto rewind;
256     }
257 
258     /*TODO: implement binary search */
259     ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
260                   "flv: lookup times nelts=%ui", nelts);
261 
262     for (index = 0; index < nelts - 1; ++index) {
263         v = ngx_rtmp_flv_index_value(ngx_rtmp_flv_buffer +
264                                      index * 9 + 1) * 1000;
265 
266         ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
267                       "flv: lookup times index=%ui value=%ui",
268                       index, (ngx_uint_t) v);
269 
270         if (timestamp < v) {
271             break;
272         }
273     }
274 
275     if (index >= ctx->filepositions.nelts) {
276         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
277                      "flv: index out of bounds: %ui>=%ui",
278                      index, ctx->filepositions.nelts);
279         goto rewind;
280     }
281 
282     /* take value from filepositions */
283     offset = NGX_RTMP_FLV_DATA_OFFSET + NGX_RTMP_FLV_TAG_HEADER +
284              ctx->filepositions.offset + index * 9;
285 
286     n = ngx_read_file(f, ngx_rtmp_flv_buffer, 8, offset + 1);
287 
288     if (n != 8) {
289         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
290                      "flv: could not read filepositions index");
291         goto rewind;
292     }
293 
294     ret = (ngx_uint_t) ngx_rtmp_flv_index_value(ngx_rtmp_flv_buffer);
295 
296     ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
297                   "flv: lookup index timestamp=%i offset=%ui",
298                    timestamp, ret);
299 
300     return ret;
301 
302 rewind:
303     ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
304                   "flv: lookup index timestamp=%i offset=begin",
305                    timestamp);
306 
307     return NGX_RTMP_FLV_DATA_OFFSET;
308 }
309 
310 
311 static void
ngx_rtmp_flv_read_meta(ngx_rtmp_session_t * s,ngx_file_t * f)312 ngx_rtmp_flv_read_meta(ngx_rtmp_session_t *s, ngx_file_t *f)
313 {
314     ngx_rtmp_flv_ctx_t             *ctx;
315     ssize_t                         n;
316     ngx_rtmp_header_t               h;
317     ngx_chain_t                    *out, in;
318     ngx_buf_t                       in_buf;
319     ngx_rtmp_core_srv_conf_t       *cscf;
320     uint32_t                        size;
321 
322     cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
323 
324     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_flv_module);
325 
326     if (ctx == NULL) {
327         return;
328     }
329 
330     ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
331                   "flv: read meta");
332 
333     /* read tag header */
334     n = ngx_read_file(f, ngx_rtmp_flv_header, sizeof(ngx_rtmp_flv_header),
335                       NGX_RTMP_FLV_DATA_OFFSET);
336 
337     if (n != sizeof(ngx_rtmp_flv_header)) {
338         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
339                      "flv: could not read metadata tag header");
340         return;
341     }
342 
343     if (ngx_rtmp_flv_header[0] != NGX_RTMP_MSG_AMF_META) {
344         ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
345                       "flv: first tag is not metadata, giving up");
346         return;
347     }
348 
349     ngx_memzero(&h, sizeof(h));
350 
351     h.type = NGX_RTMP_MSG_AMF_META;
352     h.msid = NGX_RTMP_MSID;
353     h.csid = NGX_RTMP_CSID_AMF;
354 
355     size = 0;
356     ngx_rtmp_rmemcpy(&size, ngx_rtmp_flv_header + 1, 3);
357 
358     ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
359                   "flv: metadata size=%D", size);
360 
361     if (size > sizeof(ngx_rtmp_flv_buffer)) {
362         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
363                      "flv: too big metadata");
364         return;
365     }
366 
367     /* read metadata */
368     n = ngx_read_file(f, ngx_rtmp_flv_buffer, size,
369                       sizeof(ngx_rtmp_flv_header) +
370                       NGX_RTMP_FLV_DATA_OFFSET);
371 
372     if (n != (ssize_t) size) {
373         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
374                       "flv: could not read metadata");
375         return;
376     }
377 
378     /* prepare input chain */
379     ngx_memzero(&in, sizeof(in));
380     ngx_memzero(&in_buf, sizeof(in_buf));
381 
382     in.buf = &in_buf;
383     in_buf.pos  = ngx_rtmp_flv_buffer;
384     in_buf.last = ngx_rtmp_flv_buffer + size;
385 
386     ngx_rtmp_flv_init_index(s, &in);
387 
388     /* output chain */
389     out = ngx_rtmp_append_shared_bufs(cscf, NULL, &in);
390 
391     ngx_rtmp_prepare_message(s, &h, NULL, out);
392     ngx_rtmp_send_message(s, out, 0);
393     ngx_rtmp_free_shared_chain(cscf, out);
394 }
395 
396 
397 static ngx_int_t
ngx_rtmp_flv_send(ngx_rtmp_session_t * s,ngx_file_t * f,ngx_uint_t * ts)398 ngx_rtmp_flv_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
399 {
400     ngx_rtmp_flv_ctx_t             *ctx;
401     uint32_t                        last_timestamp;
402     ngx_rtmp_header_t               h, lh;
403     ngx_rtmp_core_srv_conf_t       *cscf;
404     ngx_chain_t                    *out, in;
405     ngx_buf_t                       in_buf;
406     ngx_int_t                       rc;
407     ssize_t                         n;
408     uint32_t                        buflen, end_timestamp, size;
409 
410     cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
411 
412     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_flv_module);
413 
414     if (ctx == NULL) {
415         return NGX_ERROR;
416     }
417 
418     if (ctx->offset == -1) {
419         ctx->offset = ngx_rtmp_flv_timestamp_to_offset(s, f,
420                                                        ctx->start_timestamp);
421         ctx->start_timestamp = -1; /* set later from actual timestamp */
422     }
423 
424     ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
425                   "flv: read tag at offset=%i", ctx->offset);
426 
427     /* read tag header */
428     n = ngx_read_file(f, ngx_rtmp_flv_header,
429                       sizeof(ngx_rtmp_flv_header), ctx->offset);
430 
431     if (n != sizeof(ngx_rtmp_flv_header)) {
432         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
433                      "flv: could not read flv tag header");
434         return NGX_DONE;
435     }
436 
437     /* parse header fields */
438     ngx_memzero(&h, sizeof(h));
439 
440     h.msid = NGX_RTMP_MSID;
441     h.type = ngx_rtmp_flv_header[0];
442 
443     size = 0;
444 
445     ngx_rtmp_rmemcpy(&size, ngx_rtmp_flv_header + 1, 3);
446     ngx_rtmp_rmemcpy(&h.timestamp, ngx_rtmp_flv_header + 4, 3);
447 
448     ((u_char *) &h.timestamp)[3] = ngx_rtmp_flv_header[7];
449 
450     ctx->offset += (sizeof(ngx_rtmp_flv_header) + size + 4);
451 
452     last_timestamp = 0;
453 
454     switch (h.type) {
455 
456         case NGX_RTMP_MSG_AUDIO:
457             h.csid = NGX_RTMP_CSID_AUDIO;
458             last_timestamp = ctx->last_audio;
459             ctx->last_audio = h.timestamp;
460             break;
461 
462         case NGX_RTMP_MSG_VIDEO:
463             h.csid = NGX_RTMP_CSID_VIDEO;
464             last_timestamp = ctx->last_video;
465             ctx->last_video = h.timestamp;
466             break;
467 
468         default:
469             return NGX_OK;
470     }
471 
472     ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
473                   "flv: read tag type=%i size=%uD timestamp=%uD "
474                   "last_timestamp=%uD",
475                   (ngx_int_t) h.type,size, h.timestamp, last_timestamp);
476 
477     lh = h;
478     lh.timestamp = last_timestamp;
479 
480     if (size > sizeof(ngx_rtmp_flv_buffer)) {
481         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
482                      "flv: too big message: %D>%uz", size,
483                       sizeof(ngx_rtmp_flv_buffer));
484         goto next;
485     }
486 
487     /* read tag body */
488     n = ngx_read_file(f, ngx_rtmp_flv_buffer, size,
489                       ctx->offset - size - 4);
490 
491     if (n != (ssize_t) size) {
492         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
493                      "flv: could not read flv tag");
494         return NGX_ERROR;
495     }
496 
497     /* prepare input chain */
498     ngx_memzero(&in, sizeof(in));
499     ngx_memzero(&in_buf, sizeof(in_buf));
500 
501     in.buf = &in_buf;
502     in_buf.pos  = ngx_rtmp_flv_buffer;
503     in_buf.last = ngx_rtmp_flv_buffer + size;
504 
505     /* output chain */
506     out = ngx_rtmp_append_shared_bufs(cscf, NULL, &in);
507 
508     ngx_rtmp_prepare_message(s, &h, ctx->msg_mask & (1 << h.type) ?
509                              &lh : NULL, out);
510     rc = ngx_rtmp_send_message(s, out, 0);
511     ngx_rtmp_free_shared_chain(cscf, out);
512 
513     if (rc == NGX_AGAIN) {
514         return NGX_AGAIN;
515     }
516 
517     if (rc != NGX_OK) {
518         return NGX_ERROR;
519     }
520 
521     ctx->msg_mask |= (1 << h.type);
522 
523 next:
524     if (ctx->start_timestamp == -1) {
525         ctx->start_timestamp = h.timestamp;
526         ctx->epoch = ngx_current_msec;
527 
528         ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
529                       "flv: start_timestamp=%i", ctx->start_timestamp);
530         return NGX_OK;
531     }
532 
533     buflen = s->buflen + NGX_RTMP_FLV_BUFLEN_ADDON;
534 
535     end_timestamp = (ngx_current_msec - ctx->epoch) +
536                      ctx->start_timestamp + buflen;
537 
538     ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
539            "flv: %s wait=%D timestamp=%D end_timestamp=%D bufen=%i",
540             h.timestamp > end_timestamp ? "schedule" : "advance",
541             h.timestamp > end_timestamp ? h.timestamp - end_timestamp : 0,
542             h.timestamp, end_timestamp, (ngx_int_t) buflen);
543 
544     s->current_time = h.timestamp;
545 
546     /* too much data sent; schedule timeout */
547     if (h.timestamp > end_timestamp) {
548         return h.timestamp - end_timestamp;
549     }
550 
551     return NGX_OK;
552 }
553 
554 
555 static ngx_int_t
ngx_rtmp_flv_init(ngx_rtmp_session_t * s,ngx_file_t * f,ngx_int_t aindex,ngx_int_t vindex)556 ngx_rtmp_flv_init(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_int_t aindex,
557                   ngx_int_t vindex)
558 {
559     ngx_rtmp_flv_ctx_t             *ctx;
560 
561     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_flv_module);
562 
563     if (ctx == NULL) {
564         ctx = ngx_palloc(s->connection->pool, sizeof(ngx_rtmp_flv_ctx_t));
565 
566         if (ctx == NULL) {
567             return NGX_ERROR;
568         }
569 
570         ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_flv_module);
571     }
572 
573     ngx_memzero(ctx, sizeof(*ctx));
574 
575     return NGX_OK;
576 }
577 
578 
579 static ngx_int_t
ngx_rtmp_flv_start(ngx_rtmp_session_t * s,ngx_file_t * f)580 ngx_rtmp_flv_start(ngx_rtmp_session_t *s, ngx_file_t *f)
581 {
582     ngx_rtmp_flv_ctx_t             *ctx;
583 
584     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_flv_module);
585 
586     if (ctx == NULL) {
587         return NGX_OK;
588     }
589 
590     ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
591                   "flv: start");
592 
593     ctx->offset = -1;
594     ctx->msg_mask = 0;
595 
596     return NGX_OK;
597 }
598 
599 
600 static ngx_int_t
ngx_rtmp_flv_seek(ngx_rtmp_session_t * s,ngx_file_t * f,ngx_uint_t timestamp)601 ngx_rtmp_flv_seek(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t timestamp)
602 {
603     ngx_rtmp_flv_ctx_t             *ctx;
604 
605     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_flv_module);
606 
607     if (ctx == NULL) {
608         return NGX_OK;
609     }
610 
611     ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
612                   "flv: seek timestamp=%ui", timestamp);
613 
614     ctx->start_timestamp = timestamp;
615     ctx->epoch = ngx_current_msec;
616     ctx->offset = -1;
617     ctx->msg_mask = 0;
618 
619     return NGX_OK;
620 }
621 
622 
623 static ngx_int_t
ngx_rtmp_flv_stop(ngx_rtmp_session_t * s,ngx_file_t * f)624 ngx_rtmp_flv_stop(ngx_rtmp_session_t *s, ngx_file_t *f)
625 {
626     ngx_rtmp_flv_ctx_t             *ctx;
627 
628     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_flv_module);
629 
630     if (ctx == NULL) {
631         return NGX_OK;
632     }
633 
634     ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
635                   "flv: stop");
636 
637     return NGX_OK;
638 }
639 
640 
641 static ngx_int_t
ngx_rtmp_flv_postconfiguration(ngx_conf_t * cf)642 ngx_rtmp_flv_postconfiguration(ngx_conf_t *cf)
643 {
644     ngx_rtmp_play_main_conf_t      *pmcf;
645     ngx_rtmp_play_fmt_t           **pfmt, *fmt;
646 
647     pmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_play_module);
648 
649     pfmt = ngx_array_push(&pmcf->fmts);
650 
651     if (pfmt == NULL) {
652         return NGX_ERROR;
653     }
654 
655     fmt = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_play_fmt_t));
656 
657     if (fmt == NULL) {
658         return NGX_ERROR;
659     }
660 
661     *pfmt = fmt;
662 
663     ngx_str_set(&fmt->name, "flv-format");
664 
665     ngx_str_null(&fmt->pfx); /* default fmt */
666     ngx_str_set(&fmt->sfx, ".flv");
667 
668     fmt->init  = ngx_rtmp_flv_init;
669     fmt->start = ngx_rtmp_flv_start;
670     fmt->seek  = ngx_rtmp_flv_seek;
671     fmt->stop  = ngx_rtmp_flv_stop;
672     fmt->send  = ngx_rtmp_flv_send;
673 
674     return NGX_OK;
675 }
676