add mediathumb utility

This commit is contained in:
byte[] 2020-05-18 20:37:19 -04:00
parent b36a7c31a8
commit b4de9fb0fb
5 changed files with 248 additions and 6 deletions

View file

@ -1,33 +1,41 @@
RM := rm -f RM := rm -f
CFLAGS := -O3 -Wall -Isrc -I/usr/include/ffmpeg CFLAGS := -O3 -Wall -Isrc -I/usr/include/ffmpeg
LIBS := -lavformat -lavutil -lavcodec LIBS := -lavformat -lavutil -lavcodec -lswscale
LDFLAGS := LDFLAGS :=
INSTALL ?= install INSTALL ?= install
PREFIX ?= /usr/local PREFIX ?= /usr/local
COMMON_OBJECTS := build/validation.o COMMON_OBJECTS := build/validation.o build/png.o
MEDIASTAT_OBJECTS := build/stat.o MEDIASTAT_OBJECTS := build/stat.o
MEDIATHUMB_OBJECTS := build/thumb.o
# Phony rules # Phony rules
.PHONY: all mediastat clean .PHONY: all mediastat clean
all: mediastat all: mediastat mediathumb
install: all install: all
$(INSTALL) build/mediastat $(PREFIX)/bin/mediastat $(INSTALL) build/mediastat $(PREFIX)/bin/mediastat
$(INSTALL) build/mediathumb $(PREFIX)/bin/mediathumb
uninstall: uninstall:
$(RM) -rf $(PREFIX)/bin/mediastat $(RM) -rf $(PREFIX)/bin/mediastat
$(RM) -rf $(PREFIX)/bin/mediathumb
mediastat: build/mediastat mediastat: build/mediastat
mediathumb: build/mediathumb
clean: clean:
$(RM) $(COMMON_OBJECTS) $(MEDIASTAT_OBJECTS) $(RM) $(COMMON_OBJECTS) $(MEDIASTAT_OBJECTS)$(MEDIATHUMB_OBJECTS)
# Build rules # Build rules
build/mediastat: $(COMMON_OBJECTS) $(MEDIASTAT_OBJECTS) build/mediastat: $(COMMON_OBJECTS) $(MEDIASTAT_OBJECTS)
$(CC) $^ $(LDFLAGS) $(LIBS) -o $@ $(CC) $^ $(LDFLAGS) $(LIBS) -o $@
build/mediathumb: $(COMMON_OBJECTS) $(MEDIATHUMB_OBJECTS)
$(CC) $^ $(LDFLAGS) $(LIBS) -o $@
build/%.o: src/%.c build/%.o: src/%.c
$(CC) $(CFLAGS) -MMD -c $< -o $@ $(CC) $(CFLAGS) -MMD -c $< -o $@

106
src/png.c Normal file
View file

@ -0,0 +1,106 @@
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include "png.h"
int mediatools_write_frame_to_png(AVFrame *in_frame, const char *path)
{
struct SwsContext *sws_ctx = NULL;
AVFormatContext *format = NULL;
AVOutputFormat *png = NULL;
AVCodecContext *vctx = NULL;
AVStream *vstream = NULL;
AVFrame *out_frame = NULL;
AVCodec *vcodec = NULL;
AVPacket pkt = { 0 };
int ret = -1;
png = av_guess_format(NULL, NULL, "image/png");
vcodec = avcodec_find_encoder(AV_CODEC_ID_APNG);
vctx = avcodec_alloc_context3(vcodec);
format = avformat_alloc_context();
out_frame = av_frame_alloc();
if (!format || !vctx || !vcodec || !png || !out_frame)
goto error;
format->oformat = png;
format->url = av_strdup(path);
vstream = avformat_new_stream(format, NULL);
if (!vstream)
goto error;
if (png->flags & AVFMT_GLOBALHEADER)
vctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
vctx->time_base = (AVRational) { 1, 1 };
vctx->width = in_frame->width;
vctx->height = in_frame->height;
vctx->pix_fmt = AV_PIX_FMT_RGBA;
if (avcodec_parameters_from_context(vstream->codecpar, vctx) < 0)
goto error;
if (avcodec_open2(vctx, vcodec, NULL) < 0)
goto error;
avio_open(&format->pb, path, AVIO_FLAG_WRITE);
if (avformat_write_header(format, NULL) < 0)
goto error;
av_init_packet(&pkt);
out_frame->format = AV_PIX_FMT_RGBA;
out_frame->width = in_frame->width;
out_frame->height = in_frame->height;
if (av_frame_get_buffer(out_frame, 0) < 0)
goto error;
if (av_frame_make_writable(out_frame) < 0)
goto error;
// Rewrite frame into correct color format
sws_ctx = sws_getContext(in_frame->width, in_frame->height, in_frame->format, out_frame->width, out_frame->height, AV_PIX_FMT_RGBA, SWS_LANCZOS, NULL, NULL, NULL);
if (!sws_ctx)
goto error;
sws_scale(sws_ctx, (const uint8_t **)in_frame->data, in_frame->linesize, 0, in_frame->height, out_frame->data, out_frame->linesize);
if (avcodec_send_frame(vctx, out_frame) < 0)
goto error;
if (avcodec_send_frame(vctx, NULL) < 0)
goto error;
if (avcodec_receive_packet(vctx, &pkt) < 0)
goto error;
pkt.stream_index = vstream->index;
pkt.pts = 1;
pkt.dts = 1;
if (av_write_frame(format, &pkt) < 0)
goto error;
av_packet_unref(&pkt);
if (av_write_trailer(format) < 0)
goto error;
ret = 0;
error:
if (sws_ctx)
sws_freeContext(sws_ctx);
if (format->pb)
avio_close(format->pb);
if (vctx)
avcodec_free_context(&vctx);
if (out_frame)
av_frame_free(&out_frame);
if (format)
avformat_free_context(format);
return ret;
}

8
src/png.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef _PNG_H_DEFINED
#define _PNG_H_DEFINED
#include <libavformat/avformat.h>
int mediatools_write_frame_to_png(AVFrame *in_frame, const char *path);
#endif

View file

@ -7,6 +7,15 @@
#include "validation.h" #include "validation.h"
static int64_t start_time(AVStream *stream)
{
if (stream->start_time == AV_NOPTS_VALUE) {
return 0;
}
return stream->start_time;
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
AVFormatContext *format = NULL; AVFormatContext *format = NULL;
@ -67,8 +76,8 @@ int main(int argc, char *argv[])
av_packet_unref(&pkt); av_packet_unref(&pkt);
} }
AVRational tb = format->streams[last_stream]->time_base; AVStream *stream = format->streams[last_stream];
AVRational dur = av_mul_q(av_make_q(last_pts, 1), tb); AVRational dur = av_mul_q(av_make_q(last_pts - start_time(stream), 1), stream->time_base);
if (!mediatools_validate_duration(dur)) if (!mediatools_validate_duration(dur))
return -1; return -1;

111
src/thumb.c Normal file
View file

@ -0,0 +1,111 @@
#include <libavformat/avformat.h>
#include <stdio.h>
#include "png.h"
int main(int argc, char *argv[])
{
AVFormatContext *format = NULL;
AVCodecContext *vctx = NULL;
AVStream *vstream = NULL;
AVCodec *vcodec = NULL;
AVFrame *frame = NULL;
AVPacket pkt;
int found = 0;
if (argc != 4) {
printf("Expected an input, a time, and an output\n");
return -1;
}
av_log_set_level(AV_LOG_QUIET);
const char *input = argv[1];
const char *output = argv[3];
AVRational time = av_d2q(atof(argv[2]), INT_MAX);
if (avformat_open_input(&format, input, NULL, NULL) != 0) {
printf("Couldn't read file\n");
return -1;
}
if (avformat_find_stream_info(format, NULL) < 0) {
printf("Couldn't read file \n");
return -1;
}
int vstream_idx = av_find_best_stream(format, AVMEDIA_TYPE_VIDEO, -1, -1, &vcodec, 0);
if (vstream_idx < 0) {
printf("Couldn't read file\n");
return -1;
}
vstream = format->streams[vstream_idx];
// Set up decoding context
vctx = avcodec_alloc_context3(vcodec);
if (!vctx) {
printf("Couldn't read file\n");
return -1;
}
if (avcodec_parameters_to_context(vctx, vstream->codecpar) < 0) {
printf("Couldn't read file\n");
return -1;
}
if (avcodec_open2(vctx, vcodec, NULL) < 0) {
printf("Couldn't read file\n");
return -1;
}
frame = av_frame_alloc();
if (!frame) {
printf("Couldn't read file\n");
return -1;
}
av_seek_frame(format, -1, 0, AVSEEK_FLAG_BACKWARD);
// Loop until we get to the first video frame past the intended pts,
// decoding all video frames along the way.
while (!found && av_read_frame(format, &pkt) >= 0) {
if (pkt.stream_index == vstream_idx) {
AVRational cur_time = { pkt.pts, vstream->time_base.den };
AVRational next_time = { pkt.pts + pkt.duration, vstream->time_base.den };
if (avcodec_send_packet(vctx, &pkt) != 0 || avcodec_receive_frame(vctx, frame) != 0) {
// Decoder returned an error
printf("Couldn't read file\n");
return -1;
}
// If this is the first frame past the requested time or the
// current frame contains the requested time, pick this frame.
if (av_cmp_q(cur_time, time) >= 0 || (av_cmp_q(cur_time, time) <= 0 && av_cmp_q(next_time, time) >= 0)) {
found = 1;
// Found the frame; write to the provided file
if (mediatools_write_frame_to_png(frame, output) < 0) {
printf("Couldn't read file\n");
return -1;
}
}
}
av_packet_unref(&pkt);
}
if (!found) {
printf("Couldn't read file\n");
return -1;
}
av_frame_free(&frame);
avcodec_free_context(&vctx);
avformat_close_input(&format);
return 0;
}