221 lines
5.7 KiB
C
221 lines
5.7 KiB
C
#include "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
|