add mediathumb utility
This commit is contained in:
parent
b36a7c31a8
commit
b4de9fb0fb
16
Makefile
16
Makefile
|
@ -1,33 +1,41 @@
|
|||
RM := rm -f
|
||||
CFLAGS := -O3 -Wall -Isrc -I/usr/include/ffmpeg
|
||||
LIBS := -lavformat -lavutil -lavcodec
|
||||
LIBS := -lavformat -lavutil -lavcodec -lswscale
|
||||
LDFLAGS :=
|
||||
INSTALL ?= install
|
||||
PREFIX ?= /usr/local
|
||||
|
||||
COMMON_OBJECTS := build/validation.o
|
||||
COMMON_OBJECTS := build/validation.o build/png.o
|
||||
MEDIASTAT_OBJECTS := build/stat.o
|
||||
MEDIATHUMB_OBJECTS := build/thumb.o
|
||||
|
||||
# Phony rules
|
||||
.PHONY: all mediastat clean
|
||||
|
||||
all: mediastat
|
||||
all: mediastat mediathumb
|
||||
|
||||
install: all
|
||||
$(INSTALL) build/mediastat $(PREFIX)/bin/mediastat
|
||||
$(INSTALL) build/mediathumb $(PREFIX)/bin/mediathumb
|
||||
|
||||
uninstall:
|
||||
$(RM) -rf $(PREFIX)/bin/mediastat
|
||||
$(RM) -rf $(PREFIX)/bin/mediathumb
|
||||
|
||||
mediastat: build/mediastat
|
||||
|
||||
mediathumb: build/mediathumb
|
||||
|
||||
clean:
|
||||
$(RM) $(COMMON_OBJECTS) $(MEDIASTAT_OBJECTS)
|
||||
$(RM) $(COMMON_OBJECTS) $(MEDIASTAT_OBJECTS)$(MEDIATHUMB_OBJECTS)
|
||||
|
||||
# Build rules
|
||||
build/mediastat: $(COMMON_OBJECTS) $(MEDIASTAT_OBJECTS)
|
||||
$(CC) $^ $(LDFLAGS) $(LIBS) -o $@
|
||||
|
||||
build/mediathumb: $(COMMON_OBJECTS) $(MEDIATHUMB_OBJECTS)
|
||||
$(CC) $^ $(LDFLAGS) $(LIBS) -o $@
|
||||
|
||||
build/%.o: src/%.c
|
||||
$(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"
|
||||
|
||||
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[])
|
||||
{
|
||||
AVFormatContext *format = NULL;
|
||||
|
@ -67,8 +76,8 @@ int main(int argc, char *argv[])
|
|||
av_packet_unref(&pkt);
|
||||
}
|
||||
|
||||
AVRational tb = format->streams[last_stream]->time_base;
|
||||
AVRational dur = av_mul_q(av_make_q(last_pts, 1), tb);
|
||||
AVStream *stream = format->streams[last_stream];
|
||||
AVRational dur = av_mul_q(av_make_q(last_pts - start_time(stream), 1), stream->time_base);
|
||||
|
||||
if (!mediatools_validate_duration(dur))
|
||||
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