2022-09-02 22:00:01 +02:00

221 lines
5.7 KiB
C

#include "ffmpeg/remuxer.h"
#include "util/opts/log.h"
#include <inttypes.h>
#ifdef USE_FFMPEG
static AVRational time_base = {1, 1000LL * 1000LL};
static unsigned avio_ctx_buffer_size = 4096;
static int ffmpeg_remuxer_init_avcontext(AVFormatContext **context, ffmpeg_remuxer_t *remuxer, int output, int (*packet)(void *opaque, uint8_t *buf, int buf_size))
{
uint8_t *buffer = NULL;
AVIOContext *avio = NULL;
int ret = -1;
unsigned buffer_size = MAX(
output ? remuxer->write_buffer_size : remuxer->read_buffer_size,
avio_ctx_buffer_size);
buffer = av_malloc(buffer_size);
if (!buffer)
return AVERROR(ENOMEM);
avio = avio_alloc_context(buffer, 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;
}
void ffmpeg_remuxer_close(ffmpeg_remuxer_t *remuxer)
{
if (remuxer->output_context)
av_write_trailer(remuxer->output_context);
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 nframes)
{
int ret = 0;
int frames = 0;
while (ret >= 0) {
if (nframes > 0 && frames >= nframes) {
break;
}
ret = av_read_frame(remuxer->input_context, remuxer->packet);
if (ret == AVERROR_EOF) {
ret = 0;
LOG_DEBUG(remuxer, "av_read_frame: EOF");
break;
} else if (ret < 0) {
LOG_DEBUG(remuxer, "av_read_frame: %08x, pts: %" PRId64, ret, remuxer->packet->pts);
break;
}
if (remuxer->packet->stream_index != remuxer->video_stream) {
av_packet_unref(remuxer->packet);
continue;
}
remuxer->frames++;
frames++;
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;
int64_t 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) {
LOG_DEBUG(remuxer, "av_interleaved_write_frame: EOF, pts: %" PRId64 ", since_start: %d", pts, since_start);
} else {
LOG_DEBUG(remuxer, "av_interleaved_write_frame: %08x, pts: %" PRId64 ", since_start: %d", ret, pts, since_start);
}
}
if (ret >= 0) {
return frames;
}
return ret;
}
int ffmpeg_remuxer_flush(ffmpeg_remuxer_t *remuxer)
{
int ret = av_write_frame(remuxer->output_context, NULL);
if (ret == AVERROR_EOF) {
LOG_DEBUG(remuxer, "av_write_frame (flush): EOF");
} else {
LOG_DEBUG(remuxer, "av_write_frame (flush): %08x", ret);
}
return ret;
}
#else
int ffmpeg_remuxer_open(ffmpeg_remuxer_t *remuxer)
{
return -1;
}
int ffmpeg_remuxer_feed(ffmpeg_remuxer_t *remuxer, int nframes)
{
return -1;
}
int ffmpeg_remuxer_flush(ffmpeg_remuxer_t *remuxer)
{
return -1;
}
void ffmpeg_remuxer_close(ffmpeg_remuxer_t *remuxer)
{
}
#endif