Add ffmpeg_remuxer
This commit is contained in:
parent
253628b61e
commit
3240fa9336
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
|
36
ffmpeg/remuxer.h
Normal file
36
ffmpeg/remuxer.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifdef USE_FFMPEG
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavformat/avio.h>
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
|
#define FFMPEG_DATA_PACKET_EOF AVERROR_EOF
|
||||||
|
#else
|
||||||
|
#define FFMPEG_DATA_PACKET_EOF -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef int (*ffmpeg_data_packet)(void *opaque, uint8_t *buf, int buf_size);
|
||||||
|
|
||||||
|
typedef struct ffmpeg_remuxer_s {
|
||||||
|
const char *name;
|
||||||
|
const char *input_format;
|
||||||
|
const char *video_format;
|
||||||
|
void *opaque;
|
||||||
|
ffmpeg_data_packet read_packet;
|
||||||
|
ffmpeg_data_packet write_packet;
|
||||||
|
|
||||||
|
#ifdef USE_FFMPEG
|
||||||
|
AVIOContext *input_avio;
|
||||||
|
AVFormatContext *input_context;
|
||||||
|
AVDictionary *input_opts;
|
||||||
|
AVIOContext *output_avio;
|
||||||
|
AVFormatContext *output_context;
|
||||||
|
AVPacket *packet;
|
||||||
|
AVDictionary *output_opts;
|
||||||
|
int video_stream;
|
||||||
|
uint64_t start_time;
|
||||||
|
#endif
|
||||||
|
} ffmpeg_remuxer_t;
|
||||||
|
|
||||||
|
int ffmpeg_remuxer_open(ffmpeg_remuxer_t *remuxer);
|
||||||
|
int ffmpeg_remuxer_feed(ffmpeg_remuxer_t *remuxer);
|
||||||
|
int ffmpeg_remuxer_close(ffmpeg_remuxer_t *remuxer);
|
4
html/html.h
Normal file
4
html/html.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
;
|
||||||
|
;
|
||||||
|
;
|
||||||
|
;
|
@ -1,5 +1,3 @@
|
|||||||
#ifdef USE_FFMPEG
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@ -8,10 +6,7 @@
|
|||||||
#include "hw/buffer_lock.h"
|
#include "hw/buffer_lock.h"
|
||||||
#include "hw/buffer_list.h"
|
#include "hw/buffer_list.h"
|
||||||
#include "hw/device.h"
|
#include "hw/device.h"
|
||||||
|
#include "ffmpeg/remuxer.h"
|
||||||
#include <libavcodec/avcodec.h>
|
|
||||||
#include <libavformat/avio.h>
|
|
||||||
#include <libavformat/avformat.h>
|
|
||||||
|
|
||||||
buffer_lock_t *http_h264_buffer_for_res(http_worker_t *worker);
|
buffer_lock_t *http_h264_buffer_for_res(http_worker_t *worker);
|
||||||
|
|
||||||
@ -22,43 +17,31 @@ static const char *const VIDEO_HEADER =
|
|||||||
"Content-Type: %s\r\n"
|
"Content-Type: %s\r\n"
|
||||||
"\r\n";
|
"\r\n";
|
||||||
|
|
||||||
static AVRational time_base = {1, 1000LL * 1000LL};
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
FILE *stream;
|
FILE *stream;
|
||||||
const char *input_format;
|
|
||||||
const char *content_type;
|
const char *content_type;
|
||||||
const char *video_format;
|
|
||||||
|
|
||||||
AVIOContext *input_avio;
|
|
||||||
AVFormatContext *input_context;
|
|
||||||
AVDictionary *input_opts;
|
|
||||||
AVIOContext *output_avio;
|
|
||||||
AVFormatContext *output_context;
|
|
||||||
AVPacket *packet;
|
|
||||||
AVDictionary *output_opts;
|
|
||||||
|
|
||||||
bool had_key_frame;
|
bool had_key_frame;
|
||||||
bool requested_key_frame;
|
bool requested_key_frame;
|
||||||
bool wrote_header;
|
bool wrote_header;
|
||||||
|
|
||||||
uint64_t start_time;
|
|
||||||
int video_stream;
|
|
||||||
buffer_t *buf;
|
buffer_t *buf;
|
||||||
unsigned buf_offset;
|
unsigned buf_offset;
|
||||||
unsigned stream_offset;
|
unsigned stream_offset;
|
||||||
|
|
||||||
|
ffmpeg_remuxer_t *remuxer;
|
||||||
} http_ffmpeg_status_t;
|
} http_ffmpeg_status_t;
|
||||||
|
|
||||||
static int http_ffmpeg_read_from_buf(void *opaque, uint8_t *buf, int buf_size)
|
static int http_ffmpeg_read_from_buf(void *opaque, uint8_t *buf, int buf_size)
|
||||||
{
|
{
|
||||||
http_ffmpeg_status_t *status = opaque;
|
http_ffmpeg_status_t *status = opaque;
|
||||||
if (!status->buf)
|
if (!status->buf)
|
||||||
return AVERROR_EOF;
|
return FFMPEG_DATA_PACKET_EOF;
|
||||||
|
|
||||||
buf_size = FFMIN(buf_size, status->buf->used - status->buf_offset);
|
buf_size = FFMIN(buf_size, status->buf->used - status->buf_offset);
|
||||||
if (!buf_size)
|
if (!buf_size)
|
||||||
return AVERROR_EOF;
|
return FFMPEG_DATA_PACKET_EOF;
|
||||||
|
|
||||||
E_LOG_DEBUG(status, "http_ffmpeg_read_from_buf: offset=%d, n=%d", status->buf_offset, buf_size);
|
E_LOG_DEBUG(status, "http_ffmpeg_read_from_buf: offset=%d, n=%d", status->buf_offset, buf_size);
|
||||||
memcpy(buf, (char*)status->buf->start + status->buf_offset, buf_size);
|
memcpy(buf, (char*)status->buf->start + status->buf_offset, buf_size);
|
||||||
@ -70,7 +53,7 @@ static int http_ffmpeg_write_to_stream(void *opaque, uint8_t *buf, int buf_size)
|
|||||||
{
|
{
|
||||||
http_ffmpeg_status_t *status = opaque;
|
http_ffmpeg_status_t *status = opaque;
|
||||||
if (!status->stream)
|
if (!status->stream)
|
||||||
return AVERROR_EOF;
|
return FFMPEG_DATA_PACKET_EOF;
|
||||||
|
|
||||||
if (!status->wrote_header) {
|
if (!status->wrote_header) {
|
||||||
fprintf(status->stream, VIDEO_HEADER, status->content_type);
|
fprintf(status->stream, VIDEO_HEADER, status->content_type);
|
||||||
@ -84,175 +67,11 @@ static int http_ffmpeg_write_to_stream(void *opaque, uint8_t *buf, int buf_size)
|
|||||||
status->stream_offset, n, buf_size, ferror(status->stream));
|
status->stream_offset, n, buf_size, ferror(status->stream));
|
||||||
status->stream_offset += n;
|
status->stream_offset += n;
|
||||||
if (ferror(status->stream))
|
if (ferror(status->stream))
|
||||||
return AVERROR_EOF;
|
return FFMPEG_DATA_PACKET_EOF;
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int http_ffmpeg_init_avcontext(AVFormatContext **context, http_ffmpeg_status_t *status, 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, status, output ? NULL : packet, output ? packet : NULL, NULL);
|
|
||||||
if (!avio)
|
|
||||||
goto error;
|
|
||||||
if (output && (ret = avformat_alloc_output_context2(context, NULL, status->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 http_ffmpeg_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 http_ffmpeg_copy_streams(http_ffmpeg_status_t *status)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < status->input_context->nb_streams; i++) {
|
|
||||||
AVStream *out_stream;
|
|
||||||
AVStream *in_stream = status->input_context->streams[i];
|
|
||||||
AVCodecParameters *in_codecpar = in_stream->codecpar;
|
|
||||||
|
|
||||||
if (in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
out_stream = avformat_new_stream(status->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;
|
|
||||||
status->video_stream = i;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int http_ffmpeg_open_status(http_ffmpeg_status_t *status)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (status->packet)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
AVInputFormat *input_format = av_find_input_format(status->input_format);
|
|
||||||
if (!input_format)
|
|
||||||
return AVERROR(EINVAL);
|
|
||||||
|
|
||||||
status->packet = av_packet_alloc();
|
|
||||||
if (!status->packet)
|
|
||||||
return AVERROR(ENOMEM);
|
|
||||||
if ((ret = http_ffmpeg_init_avcontext(&status->input_context, status, 0, http_ffmpeg_read_from_buf)) < 0)
|
|
||||||
return ret;
|
|
||||||
if ((ret = http_ffmpeg_init_avcontext(&status->output_context, status, 1, http_ffmpeg_write_to_stream)) < 0)
|
|
||||||
return ret;
|
|
||||||
if ((ret = avformat_open_input(&status->input_context, NULL, input_format, &status->input_opts)) < 0)
|
|
||||||
return ret;
|
|
||||||
if ((ret = avformat_find_stream_info(status->input_context, NULL)) < 0)
|
|
||||||
return ret;
|
|
||||||
if ((ret = http_ffmpeg_copy_streams(status)) < 0)
|
|
||||||
return ret;
|
|
||||||
if ((ret = avformat_write_header(status->output_context, &status->output_opts)) < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
status->start_time = get_monotonic_time_us(NULL, NULL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int http_ffmpeg_close_status(http_ffmpeg_status_t *status)
|
|
||||||
{
|
|
||||||
http_ffmpeg_close_avcontext(&status->input_context);
|
|
||||||
http_ffmpeg_close_avcontext(&status->output_context);
|
|
||||||
av_packet_free(&status->packet);
|
|
||||||
av_dict_free(&status->input_opts);
|
|
||||||
av_dict_free(&status->output_opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int http_ffmpeg_copy_packets(http_ffmpeg_status_t *status)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
while (ret >= 0) {
|
|
||||||
ret = av_read_frame(status->input_context, status->packet);
|
|
||||||
if (ret == AVERROR_EOF) {
|
|
||||||
ret = 0;
|
|
||||||
E_LOG_DEBUG(status, "av_read_frame: EOF", ret);
|
|
||||||
break;
|
|
||||||
} else if (ret < 0) {
|
|
||||||
E_LOG_DEBUG(status, "av_read_frame: %08x, pts: %d", ret, status->packet->pts);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status->packet->stream_index != status->video_stream) {
|
|
||||||
av_packet_unref(status->packet);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVStream *in_stream = status->input_context->streams[status->packet->stream_index];
|
|
||||||
status->packet->stream_index = 0;
|
|
||||||
AVStream *out_stream = status->output_context->streams[status->packet->stream_index];
|
|
||||||
|
|
||||||
int since_start = get_monotonic_time_us(NULL, NULL) - status->start_time;
|
|
||||||
|
|
||||||
// TODO: fix a PTS to be valid
|
|
||||||
status->packet->pos = -1;
|
|
||||||
int pts = status->packet->dts = status->packet->pts = av_rescale_q(
|
|
||||||
get_monotonic_time_us(NULL, NULL) - status->start_time,
|
|
||||||
time_base,
|
|
||||||
out_stream->time_base
|
|
||||||
);
|
|
||||||
|
|
||||||
ret = av_interleaved_write_frame(status->output_context, status->packet);
|
|
||||||
av_packet_unref(status->packet);
|
|
||||||
|
|
||||||
if (ret == AVERROR_EOF) {
|
|
||||||
E_LOG_DEBUG(status, "av_interleaved_write_frame: EOF, pts: %d, since_start: %d", ret, pts, since_start);
|
|
||||||
} else {
|
|
||||||
E_LOG_DEBUG(status, "av_interleaved_write_frame: %08x, pts: %d, since_start: %d", ret, pts, since_start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int http_ffmpeg_video_buf_part(buffer_lock_t *buf_lock, buffer_t *buf, int frame, http_ffmpeg_status_t *status)
|
static int http_ffmpeg_video_buf_part(buffer_lock_t *buf_lock, buffer_t *buf, int frame, http_ffmpeg_status_t *status)
|
||||||
{
|
{
|
||||||
if (!status->had_key_frame) {
|
if (!status->had_key_frame) {
|
||||||
@ -272,9 +91,9 @@ static int http_ffmpeg_video_buf_part(buffer_lock_t *buf_lock, buffer_t *buf, in
|
|||||||
status->buf = buf;
|
status->buf = buf;
|
||||||
status->buf_offset = 0;
|
status->buf_offset = 0;
|
||||||
|
|
||||||
if ((ret = http_ffmpeg_open_status(status)) < 0)
|
if ((ret = ffmpeg_remuxer_open(status->remuxer)) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
if ((ret = http_ffmpeg_copy_packets(status)) < 0)
|
if ((ret = ffmpeg_remuxer_feed(status->remuxer)) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
ret = 1;
|
ret = 1;
|
||||||
@ -289,20 +108,35 @@ static void http_ffmpeg_video(http_worker_t *worker, FILE *stream, const char *c
|
|||||||
http_ffmpeg_status_t status = {
|
http_ffmpeg_status_t status = {
|
||||||
.name = worker->name,
|
.name = worker->name,
|
||||||
.stream = stream,
|
.stream = stream,
|
||||||
.input_format = "h264",
|
|
||||||
.content_type = content_type,
|
.content_type = content_type,
|
||||||
.video_format = video_format
|
|
||||||
};
|
};
|
||||||
|
|
||||||
av_dict_set_int(&status.output_opts, "direct", 1, 0);
|
ffmpeg_remuxer_t remuxer = {
|
||||||
//av_dict_set_int(&status.output_opts, "frag_duration", 1, 0);
|
.name = worker->name,
|
||||||
av_dict_set_int(&status.output_opts, "frag_size", 4096, 0);
|
.input_format = "h264",
|
||||||
av_dict_set_int(&status.output_opts, "low_delay", 1, 0);
|
.video_format = video_format,
|
||||||
av_dict_set_int(&status.output_opts, "nobuffer", 1, 0);
|
.opaque = &status,
|
||||||
av_dict_set_int(&status.output_opts, "flush_packets", 1, 0);
|
.read_packet = http_ffmpeg_read_from_buf,
|
||||||
|
.write_packet = http_ffmpeg_write_to_stream,
|
||||||
|
};
|
||||||
|
|
||||||
int n = buffer_lock_write_loop(http_h264_buffer_for_res(worker), 0, (buffer_write_fn)http_ffmpeg_video_buf_part, &status);
|
status.remuxer = &remuxer;
|
||||||
http_ffmpeg_close_status(&status);
|
|
||||||
|
#ifdef USE_FFMPEG
|
||||||
|
av_dict_set_int(&remuxer.output_opts, "direct", 1, 0);
|
||||||
|
//av_dict_set_int(&remuxer.output_opts, "frag_duration", 1, 0);
|
||||||
|
av_dict_set_int(&remuxer.output_opts, "frag_size", 4096, 0);
|
||||||
|
av_dict_set_int(&remuxer.output_opts, "low_delay", 1, 0);
|
||||||
|
av_dict_set_int(&remuxer.output_opts, "nobuffer", 1, 0);
|
||||||
|
av_dict_set_int(&remuxer.output_opts, "flush_packets", 1, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int n = buffer_lock_write_loop(
|
||||||
|
http_h264_buffer_for_res(worker),
|
||||||
|
0,
|
||||||
|
(buffer_write_fn)http_ffmpeg_video_buf_part,
|
||||||
|
&status);
|
||||||
|
ffmpeg_remuxer_close(&remuxer);
|
||||||
|
|
||||||
if (status.wrote_header) {
|
if (status.wrote_header) {
|
||||||
return;
|
return;
|
||||||
@ -327,4 +161,3 @@ void http_mp4_video(http_worker_t *worker, FILE *stream)
|
|||||||
http_ffmpeg_video(worker, stream, "video/mp4", "mp4");
|
http_ffmpeg_video(worker, stream, "video/mp4", "mp4");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // USE_FFMPEG
|
|
||||||
|
42
hw/v4l2.c
42
hw/v4l2.c
@ -74,45 +74,3 @@ unsigned fourcc_to_stride(unsigned width, unsigned format)
|
|||||||
E_LOG_PERROR(NULL, "Unknown format: %s", fourcc_to_string(format).buf);
|
E_LOG_PERROR(NULL, "Unknown format: %s", fourcc_to_string(format).buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int shrink_to_block(int size, int block)
|
|
||||||
{
|
|
||||||
return size / block * block;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t get_time_us(clockid_t clock, struct timespec *ts, struct timeval *tv, int64_t delays_us)
|
|
||||||
{
|
|
||||||
struct timespec now;
|
|
||||||
|
|
||||||
if (clock != CLOCK_FROM_PARAMS) {
|
|
||||||
clock_gettime(clock, &now);
|
|
||||||
} else if (ts) {
|
|
||||||
now = *ts;
|
|
||||||
} else if (tv) {
|
|
||||||
now.tv_sec = tv->tv_sec;
|
|
||||||
now.tv_nsec = tv->tv_usec * 1000;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delays_us > 0) {
|
|
||||||
#define NS_IN_S (1000LL * 1000LL * 1000LL)
|
|
||||||
int64_t nsec = now.tv_nsec + delays_us * 1000LL;
|
|
||||||
now.tv_nsec = nsec % NS_IN_S;
|
|
||||||
now.tv_sec += nsec / NS_IN_S;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ts) {
|
|
||||||
*ts = now;
|
|
||||||
}
|
|
||||||
if (tv) {
|
|
||||||
tv->tv_sec = now.tv_sec;
|
|
||||||
tv->tv_usec = now.tv_nsec / 1000;
|
|
||||||
}
|
|
||||||
return now.tv_sec * 1000000LL + now.tv_nsec / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t get_monotonic_time_us(struct timespec *ts, struct timeval *tv)
|
|
||||||
{
|
|
||||||
return get_time_us(CLOCK_MONOTONIC, ts, tv, 0);
|
|
||||||
}
|
|
||||||
|
@ -30,14 +30,10 @@ typedef struct {
|
|||||||
char buf[10];
|
char buf[10];
|
||||||
} fourcc_string;
|
} fourcc_string;
|
||||||
|
|
||||||
#define CLOCK_FROM_PARAMS -1
|
|
||||||
|
|
||||||
fourcc_string fourcc_to_string(unsigned format);
|
fourcc_string fourcc_to_string(unsigned format);
|
||||||
unsigned fourcc_to_stride(unsigned width, unsigned format);
|
unsigned fourcc_to_stride(unsigned width, unsigned format);
|
||||||
int xioctl(const char *name, int fd, int request, void *arg);
|
int xioctl(const char *name, int fd, int request, void *arg);
|
||||||
uint64_t get_monotonic_time_us(struct timespec *ts, struct timeval *tv);
|
|
||||||
uint64_t get_time_us(clockid_t clock, struct timespec *ts, struct timeval *tv, int64_t delays_us);
|
|
||||||
int shrink_to_block(int size, int block);
|
|
||||||
|
|
||||||
#define E_XIOCTL(dev, _fd, _request, _value, _msg, ...) do { \
|
#define E_XIOCTL(dev, _fd, _request, _value, _msg, ...) do { \
|
||||||
int ret; \
|
int ret; \
|
||||||
|
42
opts/log.c
42
opts/log.c
@ -40,3 +40,45 @@ bool filter_log(const char *filename)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int shrink_to_block(int size, int block)
|
||||||
|
{
|
||||||
|
return size / block * block;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t get_time_us(clockid_t clock, struct timespec *ts, struct timeval *tv, int64_t delays_us)
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
|
||||||
|
if (clock != CLOCK_FROM_PARAMS) {
|
||||||
|
clock_gettime(clock, &now);
|
||||||
|
} else if (ts) {
|
||||||
|
now = *ts;
|
||||||
|
} else if (tv) {
|
||||||
|
now.tv_sec = tv->tv_sec;
|
||||||
|
now.tv_nsec = tv->tv_usec * 1000;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delays_us > 0) {
|
||||||
|
#define NS_IN_S (1000LL * 1000LL * 1000LL)
|
||||||
|
int64_t nsec = now.tv_nsec + delays_us * 1000LL;
|
||||||
|
now.tv_nsec = nsec % NS_IN_S;
|
||||||
|
now.tv_sec += nsec / NS_IN_S;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts) {
|
||||||
|
*ts = now;
|
||||||
|
}
|
||||||
|
if (tv) {
|
||||||
|
tv->tv_sec = now.tv_sec;
|
||||||
|
tv->tv_usec = now.tv_nsec / 1000;
|
||||||
|
}
|
||||||
|
return now.tv_sec * 1000000LL + now.tv_nsec / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t get_monotonic_time_us(struct timespec *ts, struct timeval *tv)
|
||||||
|
{
|
||||||
|
return get_time_us(CLOCK_MONOTONIC, ts, tv, 0);
|
||||||
|
}
|
||||||
|
10
opts/log.h
10
opts/log.h
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
#define __FILENAME__ __FILE__
|
#define __FILENAME__ __FILE__
|
||||||
|
|
||||||
@ -25,3 +29,9 @@ bool filter_log(const char *filename);
|
|||||||
#define E_LOG_INFO(dev, _msg, ...) do { fprintf(stderr, "%s: %s: " _msg "\n", __FILENAME__, dev_name(dev), ##__VA_ARGS__); } while(0)
|
#define E_LOG_INFO(dev, _msg, ...) do { fprintf(stderr, "%s: %s: " _msg "\n", __FILENAME__, dev_name(dev), ##__VA_ARGS__); } while(0)
|
||||||
#define E_LOG_VERBOSE(dev, _msg, ...) do { if (log_options.debug || log_options.verbose || filter_log(__FILENAME__)) { fprintf(stderr, "%s: %s: " _msg "\n", __FILENAME__, dev_name(dev), ##__VA_ARGS__); } } while(0)
|
#define E_LOG_VERBOSE(dev, _msg, ...) do { if (log_options.debug || log_options.verbose || filter_log(__FILENAME__)) { fprintf(stderr, "%s: %s: " _msg "\n", __FILENAME__, dev_name(dev), ##__VA_ARGS__); } } while(0)
|
||||||
#define E_LOG_DEBUG(dev, _msg, ...) do { if (log_options.debug || filter_log(__FILENAME__)) { fprintf(stderr, "%s: %s: " _msg "\n", __FILENAME__, dev_name(dev), ##__VA_ARGS__); } } while(0)
|
#define E_LOG_DEBUG(dev, _msg, ...) do { if (log_options.debug || filter_log(__FILENAME__)) { fprintf(stderr, "%s: %s: " _msg "\n", __FILENAME__, dev_name(dev), ##__VA_ARGS__); } } while(0)
|
||||||
|
|
||||||
|
#define CLOCK_FROM_PARAMS -1
|
||||||
|
|
||||||
|
uint64_t get_monotonic_time_us(struct timespec *ts, struct timeval *tv);
|
||||||
|
uint64_t get_time_us(clockid_t clock, struct timespec *ts, struct timeval *tv, int64_t delays_us);
|
||||||
|
int shrink_to_block(int size, int block);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user