#include "hls-fmp4.h" #include "hls-param.h" #include "mov-format.h" #include "fmp4-writer.h" #include #include #include #include #define N_SEGMENT (1 * 1024 * 1024) #define N_FILESIZE (100 * 1024 * 1024) // 100M #define VMAX(a, b) ((a) > (b) ? (a) : (b)) struct hls_fmp4_t { fmp4_writer_t* mp4; uint8_t* ptr; size_t bytes; size_t capacity; size_t offset; size_t maxsize; // max bytes per mp4 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 video_track; int audio_only_flag;// don't have video stream in segment hls_fmp4_handler handler; void* param; }; static int mov_buffer_read(void* param, void* data, uint64_t bytes) { struct hls_fmp4_t* fmp4; fmp4 = (struct hls_fmp4_t*)param; if (fmp4->offset + bytes > fmp4->bytes) return -E2BIG; memcpy(data, fmp4->ptr + fmp4->offset, (size_t)bytes); return 0; } static int mov_buffer_write(void* param, const void* data, uint64_t bytes) { void* ptr; size_t capacity; struct hls_fmp4_t* fmp4; fmp4 = (struct hls_fmp4_t*)param; if (fmp4->offset + bytes > fmp4->maxsize) return -E2BIG; if (fmp4->offset + (size_t)bytes > fmp4->capacity) { capacity = fmp4->offset + (size_t)bytes + N_SEGMENT; capacity = capacity > fmp4->maxsize ? fmp4->maxsize : capacity; ptr = realloc(fmp4->ptr, capacity); if (NULL == ptr) return -ENOMEM; fmp4->ptr = ptr; fmp4->capacity = capacity; } memcpy(fmp4->ptr + fmp4->offset, data, (size_t)bytes); fmp4->offset += (size_t)bytes; if(fmp4->offset > fmp4->bytes) fmp4->bytes = fmp4->offset; return 0; } static int mov_buffer_seek(void* param, int64_t offset) { struct hls_fmp4_t* fmp4; fmp4 = (struct hls_fmp4_t*)param; if ((offset >= 0 ? offset : -offset) >= fmp4->maxsize) return -E2BIG; fmp4->offset = (size_t)(offset >= 0 ? offset : fmp4->maxsize+offset); return 0; } static int64_t mov_buffer_tell(void* param) { return (int64_t)((struct hls_fmp4_t*)param)->offset; } static struct mov_buffer_t s_io = { mov_buffer_read, mov_buffer_write, mov_buffer_seek, mov_buffer_tell, }; struct hls_fmp4_t* hls_fmp4_create(int64_t duration, hls_fmp4_handler handler, void* param) { int flags; struct hls_fmp4_t* hls; hls = (struct hls_fmp4_t*)calloc(1, sizeof(*hls)); if (NULL == hls) return NULL; hls->video_track = -1; hls->maxsize = N_FILESIZE; hls->dts = hls->pts = PTS_NO_VALUE; hls->dts_last = PTS_NO_VALUE; hls->duration = duration; hls->handler = handler; hls->param = param; flags = 0; //flags |= MOV_FLAG_FASTSTART; flags |= MOV_FLAG_SEGMENT; hls->mp4 = fmp4_writer_create(&s_io, hls, flags); if (NULL == hls->mp4) { free(hls); return NULL; } return hls; } void hls_fmp4_destroy(struct hls_fmp4_t* hls) { if (hls->mp4) { fmp4_writer_destroy(hls->mp4); hls->mp4 = NULL; } if (hls->ptr) { free(hls->ptr); hls->ptr = NULL; } free(hls); } int hls_fmp4_add_audio(hls_fmp4_t* hls, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size) { return fmp4_writer_add_audio(hls->mp4, object, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size); } int hls_fmp4_add_video(hls_fmp4_t* hls, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size) { hls->video_track = fmp4_writer_add_video(hls->mp4, object, width, height, extra_data, extra_data_size); return hls->video_track; } int hls_fmp4_input(struct hls_fmp4_t* hls, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags) { int r, segment; int force_new_segment; int64_t duration; assert(dts < hls->dts_last + hls->duration || PTS_NO_VALUE == hls->dts_last); // PTS/DTS rewind force_new_segment = 0; if (dts + hls->duration < hls->dts_last || NULL == data || 0 == bytes) force_new_segment = 1; if ((MOV_AV_FLAG_KEYFREAME & flags) && (dts - hls->dts >= hls->duration || 0 == hls->duration)) { segment = 1; } else if (hls->audio_only_flag && dts - hls->dts >= hls->duration) { // audio only file segment = 1; } else { segment = 0; } if (PTS_NO_VALUE == hls->dts_last || segment || force_new_segment) { if (PTS_NO_VALUE != hls->dts_last) { // save and create new segment r = fmp4_writer_save_segment(hls->mp4); if (0 == r) { 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; } hls->pts = pts; hls->dts = dts; hls->audio_only_flag = 1; hls->offset = 0; hls->bytes = 0; } if (NULL == data || 0 == bytes) return 0; if (hls->audio_only_flag && track == hls->video_track) hls->audio_only_flag = 0; // clear audio only flag hls->dts_last = dts; return fmp4_writer_write(hls->mp4, track, data, bytes, pts, dts, flags); } int hls_fmp4_init_segment(hls_fmp4_t* hls, void* data, size_t bytes) { int r; uint8_t* ptr; size_t len; size_t capacity; size_t offset; size_t maxsize; // save ptr = hls->ptr; len = hls->bytes; offset = hls->offset; capacity = hls->capacity; maxsize = hls->maxsize; hls->ptr = (uint8_t*)data; hls->bytes = 0; hls->offset = 0; hls->capacity = bytes; hls->maxsize = bytes; r = fmp4_writer_init_segment(hls->mp4); r = 0 == r ? (int)hls->bytes : -1; // restore hls->ptr = ptr; hls->bytes = len; hls->offset = offset; hls->capacity = capacity; hls->maxsize = maxsize; return r; }