#include "hls-media.h" #include "hls-param.h" #include "mpeg-ts.h" #include "mpeg-ps.h" #include "mpeg-util.h" #include #include #include #define N_TS_PACKET 188 #define N_TS_FILESIZE (100 * 1024 * 1024) // 100M #define VMAX(a, b) ((a) > (b) ? (a) : (b)) struct hls_media_t { void* ts; uint8_t* ptr; size_t bytes; size_t capacity; size_t maxsize; // max bytes per ts file int64_t duration; // user setting segment duration int64_t dts_last; // last packet dts int64_t dts; // segment first dts int64_t pts; // segment first pts int audio; // audio stream id int video; // video stream id int audio_only_flag;// don't have video stream in segment hls_media_handler handler; void* param; }; static void* hls_ts_alloc(void* param, size_t bytes) { struct hls_media_t* hls; hls = (struct hls_media_t*)param; assert(188 == bytes); assert(hls->capacity >= hls->bytes); if (hls->capacity - hls->bytes < bytes) { void* p = realloc(hls->ptr, hls->capacity + bytes + N_TS_PACKET * 10 * 1024); if (NULL == p) return NULL; hls->ptr = p; hls->capacity += bytes + N_TS_PACKET * 10 * 1024; } return hls->ptr + hls->bytes; } static void hls_ts_free(void* param, void* packet) { struct hls_media_t* hls; hls = (struct hls_media_t*)param; assert(packet == hls->ptr + hls->bytes - 188); assert(hls->ptr <= (uint8_t*)packet && hls->ptr + hls->capacity > (uint8_t*)packet); } static int hls_ts_write(void* param, const void* packet, size_t bytes) { struct hls_media_t* hls; hls = (struct hls_media_t*)param; assert(188 == bytes); assert(hls->ptr <= (uint8_t*)packet && hls->ptr + hls->capacity > (uint8_t*)packet); hls->bytes += bytes; // update packet length return 0; } static void* hls_ts_create(struct hls_media_t* hls) { struct mpeg_ts_func_t handler; handler.alloc = hls_ts_alloc; handler.write = hls_ts_write; handler.free = hls_ts_free; return mpeg_ts_create(&handler, hls); } struct hls_media_t* hls_media_create(int64_t duration, hls_media_handler handler, void* param) { struct hls_media_t* hls; hls = (struct hls_media_t*)malloc(sizeof(*hls)); if (NULL == hls) return NULL; memset(hls, 0, sizeof(struct hls_media_t)); hls->ts = hls_ts_create(hls); if (NULL == hls->ts) { free(hls); return NULL; } //hls->audio = mpeg_ts_add_stream(hls->ts, PSI_STREAM_AAC, NULL, 0); //hls->video = mpeg_ts_add_stream(hls->ts, PSI_STREAM_H264, NULL, 0); hls->audio = -1; hls->video = -1; hls->maxsize = N_TS_FILESIZE; hls->dts = hls->pts = PTS_NO_VALUE; hls->dts_last = PTS_NO_VALUE; hls->duration = duration; hls->handler = handler; hls->param = param; return hls; } void hls_media_destroy(struct hls_media_t* hls) { if (hls->ts) mpeg_ts_destroy(hls->ts); if (hls->ptr) { assert(hls->capacity > 0); free(hls->ptr); } free(hls); } int hls_media_add_stream(hls_media_t* hls, int avtype, const void* extra, size_t bytes) { if (mpeg_stream_type_audio(avtype)) { if(-1 == hls->audio) hls->audio = mpeg_ts_add_stream(hls->ts, avtype, extra, bytes); return hls->audio; } else { if(-1 == hls->video) hls->video = mpeg_ts_add_stream(hls->ts, avtype, extra, bytes); return hls->video; } } int hls_media_input(struct hls_media_t* hls, int avtype, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags) { int r; int stream; int segment; int force_new_segment; int64_t duration; assert(dts < hls->dts_last + hls->duration || PTS_NO_VALUE == hls->dts_last); stream = hls_media_add_stream(hls, avtype, NULL, 0); // PTS/DTS rewind force_new_segment = 0; if (dts + hls->duration < hls->dts_last || NULL == data || 0 == bytes) force_new_segment = 1; // IDR frame // 1. check segment duration // 2. new segment per keyframe // 3. check segment file size if ((dts - hls->dts >= hls->duration || 0 == hls->duration) && (HLS_FLAGS_KEYFRAME & flags || hls->bytes >= hls->maxsize) ) { segment = 1; } else if (hls->audio_only_flag && dts - hls->dts >= hls->duration) { // audio only file segment = 1; } else { segment = 0; } if (0 == hls->bytes || segment || force_new_segment) { if (hls->bytes > 0) { duration = ((force_new_segment || dts > hls->dts_last + 100) ? hls->dts_last : dts) - hls->dts; r = hls->handler(hls->param, hls->ptr, hls->bytes, hls->pts, hls->dts, duration); if (0 != r) return r; // reset mpeg ts generator mpeg_ts_reset(hls->ts); } // new segment hls->pts = pts; hls->dts = dts; hls->bytes = 0; hls->audio_only_flag = 1; } if (hls->audio_only_flag && mpeg_stream_type_video(avtype)) hls->audio_only_flag = 0; // clear audio only flag hls->dts_last = dts; return mpeg_ts_write(hls->ts, stream, HLS_FLAGS_KEYFRAME & flags ? 1 : 0, pts * 90, dts * 90, data, bytes); }