add mediathumb utility
This commit is contained in:
parent
b36a7c31a8
commit
b4de9fb0fb
16
Makefile
16
Makefile
|
@ -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
106
src/png.c
Normal 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
8
src/png.h
Normal 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
|
13
src/stat.c
13
src/stat.c
|
@ -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
111
src/thumb.c
Normal 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;
|
||||||
|
}
|
Loading…
Reference in a new issue