2022-04-08 13:33:54 +02:00

186 lines
5.1 KiB
C

#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