stream-deploy/ZLM/3rdpart/media-server/libhls/source/hls-fmp4.c

243 lines
5.5 KiB
C++

#include "hls-fmp4.h"
#include "hls-param.h"
#include "mov-format.h"
#include "fmp4-writer.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#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;
}