243 lines
5.5 KiB
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;
|
|
}
|