Add ffmpeg_remuxer
This commit is contained in:
185
ffmpeg/remuxer.c
Normal file
185
ffmpeg/remuxer.c
Normal file
@ -0,0 +1,185 @@
|
||||
#include "ffmpeg/remuxer.h"
|
||||
#include "opts/log.h"
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
static AVRational time_base = {1, 1000LL * 1000LL};
|
||||
|
||||
static int ffmpeg_remuxer_init_avcontext(AVFormatContext **context, ffmpeg_remuxer_t *remuxer, int output, int (*packet)(void *opaque, uint8_t *buf, int buf_size))
|
||||
{
|
||||
static int avio_ctx_buffer_size = 4096;
|
||||
uint8_t *buffer = NULL;
|
||||
AVIOContext *avio = NULL;
|
||||
int ret = -1;
|
||||
|
||||
buffer = av_malloc(avio_ctx_buffer_size);
|
||||
if (!buffer)
|
||||
return AVERROR(ENOMEM);
|
||||
avio = avio_alloc_context(buffer, avio_ctx_buffer_size, output, remuxer->opaque, output ? NULL : packet, output ? packet : NULL, NULL);
|
||||
if (!avio)
|
||||
goto error;
|
||||
if (output && (ret = avformat_alloc_output_context2(context, NULL, remuxer->video_format, NULL)) < 0)
|
||||
goto error;
|
||||
if (!output && (*context = avformat_alloc_context()) == NULL)
|
||||
goto error;
|
||||
|
||||
(*context)->flush_packets = true;
|
||||
(*context)->pb = avio;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
av_freep(&buffer);
|
||||
avio_context_free(&avio);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ffmpeg_remuxer_close_avcontext(AVFormatContext **contextp)
|
||||
{
|
||||
if (!*contextp)
|
||||
return;
|
||||
|
||||
AVFormatContext *context = *contextp;
|
||||
|
||||
if (context->pb)
|
||||
av_freep(&context->pb->buffer);
|
||||
avio_context_free(&context->pb);
|
||||
if (context->iformat)
|
||||
avformat_close_input(contextp);
|
||||
else
|
||||
avformat_free_context(context);
|
||||
*contextp = NULL;
|
||||
}
|
||||
|
||||
static int ffmpeg_remuxer_copy_streams(ffmpeg_remuxer_t *remuxer)
|
||||
{
|
||||
for (int i = 0; i < remuxer->input_context->nb_streams; i++) {
|
||||
AVStream *out_stream;
|
||||
AVStream *in_stream = remuxer->input_context->streams[i];
|
||||
AVCodecParameters *in_codecpar = in_stream->codecpar;
|
||||
|
||||
if (in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out_stream = avformat_new_stream(remuxer->output_context, NULL);
|
||||
if (!out_stream) {
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
|
||||
int ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
out_stream->time_base.num = 1;
|
||||
out_stream->time_base.den = 1;
|
||||
out_stream->codecpar->codec_tag = 0;
|
||||
remuxer->video_stream = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ffmpeg_remuxer_open(ffmpeg_remuxer_t *remuxer)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (remuxer->packet)
|
||||
return 0;
|
||||
|
||||
AVInputFormat *input_format = av_find_input_format(remuxer->input_format);
|
||||
if (!input_format)
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
remuxer->packet = av_packet_alloc();
|
||||
if (!remuxer->packet)
|
||||
return AVERROR(ENOMEM);
|
||||
if ((ret = ffmpeg_remuxer_init_avcontext(&remuxer->input_context, remuxer, 0, remuxer->read_packet)) < 0)
|
||||
return ret;
|
||||
if ((ret = ffmpeg_remuxer_init_avcontext(&remuxer->output_context, remuxer, 1, remuxer->write_packet)) < 0)
|
||||
return ret;
|
||||
if ((ret = avformat_open_input(&remuxer->input_context, NULL, input_format, &remuxer->input_opts)) < 0)
|
||||
return ret;
|
||||
if ((ret = avformat_find_stream_info(remuxer->input_context, NULL)) < 0)
|
||||
return ret;
|
||||
if ((ret = ffmpeg_remuxer_copy_streams(remuxer)) < 0)
|
||||
return ret;
|
||||
if ((ret = avformat_write_header(remuxer->output_context, &remuxer->output_opts)) < 0)
|
||||
return ret;
|
||||
|
||||
remuxer->start_time = get_monotonic_time_us(NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ffmpeg_remuxer_close(ffmpeg_remuxer_t *remuxer)
|
||||
{
|
||||
ffmpeg_remuxer_close_avcontext(&remuxer->input_context);
|
||||
ffmpeg_remuxer_close_avcontext(&remuxer->output_context);
|
||||
av_packet_free(&remuxer->packet);
|
||||
av_dict_free(&remuxer->input_opts);
|
||||
av_dict_free(&remuxer->output_opts);
|
||||
}
|
||||
|
||||
int ffmpeg_remuxer_feed(ffmpeg_remuxer_t *remuxer)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
while (ret >= 0) {
|
||||
ret = av_read_frame(remuxer->input_context, remuxer->packet);
|
||||
if (ret == AVERROR_EOF) {
|
||||
ret = 0;
|
||||
E_LOG_DEBUG(remuxer, "av_read_frame: EOF", ret);
|
||||
break;
|
||||
} else if (ret < 0) {
|
||||
E_LOG_DEBUG(remuxer, "av_read_frame: %08x, pts: %d", ret, remuxer->packet->pts);
|
||||
break;
|
||||
}
|
||||
|
||||
if (remuxer->packet->stream_index != remuxer->video_stream) {
|
||||
av_packet_unref(remuxer->packet);
|
||||
continue;
|
||||
}
|
||||
|
||||
AVStream *in_stream = remuxer->input_context->streams[remuxer->packet->stream_index];
|
||||
remuxer->packet->stream_index = 0;
|
||||
AVStream *out_stream = remuxer->output_context->streams[remuxer->packet->stream_index];
|
||||
|
||||
int since_start = get_monotonic_time_us(NULL, NULL) - remuxer->start_time;
|
||||
|
||||
// TODO: fix a PTS to be valid
|
||||
remuxer->packet->pos = -1;
|
||||
int pts = remuxer->packet->dts = remuxer->packet->pts = av_rescale_q(
|
||||
get_monotonic_time_us(NULL, NULL) - remuxer->start_time,
|
||||
time_base,
|
||||
out_stream->time_base
|
||||
);
|
||||
|
||||
ret = av_interleaved_write_frame(remuxer->output_context, remuxer->packet);
|
||||
av_packet_unref(remuxer->packet);
|
||||
|
||||
if (ret == AVERROR_EOF) {
|
||||
E_LOG_DEBUG(remuxer, "av_interleaved_write_frame: EOF, pts: %d, since_start: %d", ret, pts, since_start);
|
||||
} else {
|
||||
E_LOG_DEBUG(remuxer, "av_interleaved_write_frame: %08x, pts: %d, since_start: %d", ret, pts, since_start);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
int ffmpeg_remuxer_open(ffmpeg_remuxer_t *remuxer)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ffmpeg_remuxer_feed(ffmpeg_remuxer_t *remuxer)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ffmpeg_remuxer_close(ffmpeg_remuxer_t *remuxer)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
Reference in New Issue
Block a user