--- /dev/null
+*.d
+*.o
+*.pb.h
+*.pb.cc
+*.moc.cpp
+futatabi
+frames/*.jpeg
--- /dev/null
+CXX=g++
+PKG_MODULES := Qt5Core Qt5Gui Qt5Widgets Qt5PrintSupport libjpeg
+CXXFLAGS ?= -O2 -g -Wall # Will be overridden by environment.
+CXXFLAGS += -std=gnu++11 -fPIC $(shell pkg-config --cflags $(PKG_MODULES)) -pthread
+
+LDLIBS=$(shell pkg-config --libs $(PKG_MODULES)) -pthread -lavformat -lavcodec -lavutil -lswscale
+
+# Qt objects
+OBJS_WITH_MOC =
+OBJS += $(OBJS_WITH_MOC)
+OBJS += $(OBJS_WITH_MOC:.o=.moc.o)
+
+OBJS += ffmpeg_raii.o main.o
+
+%.o: %.cpp
+ $(CXX) -MMD -MP $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
+%.o: %.cc
+ $(CXX) -MMD -MP $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
+%.pb.cc %.pb.h : %.proto
+ $(PROTOC) --cpp_out=. $<
+
+%.h: %.ui
+ uic $< -o $@
+
+%.moc.cpp: %.h
+ moc $< -o $@
+
+all: futatabi
+
+futatabi: $(OBJS) $(CEF_LIBS)
+ $(CXX) -o $@ $^ $(LDFLAGS) $(LDLIBS)
+
+DEPS=$(OBJS:.o=.d)
+-include $(DEPS)
+
+clean:
+ $(RM) $(OBJS) $(DEPS) futatabi $(OBJS_WITH_MOC:.o=.moc.cpp)
--- /dev/null
+#include "ffmpeg_raii.h"
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libavutil/dict.h>
+#include <libavutil/frame.h>
+#include <libswscale/swscale.h>
+}
+
+using namespace std;
+
+// AVFormatContext
+
+void avformat_close_input_unique::operator() (AVFormatContext *format_ctx) const
+{
+ avformat_close_input(&format_ctx);
+}
+
+AVFormatContextWithCloser avformat_open_input_unique(
+ const char *pathname, AVInputFormat *fmt,
+ AVDictionary **options)
+{
+ return avformat_open_input_unique(pathname, fmt, options, AVIOInterruptCB{ nullptr, nullptr });
+}
+
+AVFormatContextWithCloser avformat_open_input_unique(
+ const char *pathname, AVInputFormat *fmt,
+ AVDictionary **options,
+ const AVIOInterruptCB &interrupt_cb)
+{
+ AVFormatContext *format_ctx = avformat_alloc_context();
+ format_ctx->interrupt_callback = interrupt_cb;
+ if (avformat_open_input(&format_ctx, pathname, fmt, options) != 0) {
+ format_ctx = nullptr;
+ }
+ return AVFormatContextWithCloser(format_ctx);
+}
+
+// AVCodecContext
+
+void avcodec_free_context_unique::operator() (AVCodecContext *codec_ctx) const
+{
+ avcodec_free_context(&codec_ctx);
+}
+
+AVCodecContextWithDeleter avcodec_alloc_context3_unique(const AVCodec *codec)
+{
+ return AVCodecContextWithDeleter(avcodec_alloc_context3(codec));
+}
+
+
+// AVCodecParameters
+
+void avcodec_parameters_free_unique::operator() (AVCodecParameters *codec_par) const
+{
+ avcodec_parameters_free(&codec_par);
+}
+
+// AVFrame
+
+void av_frame_free_unique::operator() (AVFrame *frame) const
+{
+ av_frame_free(&frame);
+}
+
+AVFrameWithDeleter av_frame_alloc_unique()
+{
+ return AVFrameWithDeleter(av_frame_alloc());
+}
+
+// SwsContext
+
+void sws_free_context_unique::operator() (SwsContext *context) const
+{
+ sws_freeContext(context);
+}
--- /dev/null
+#ifndef _FFMPEG_RAII_H
+#define _FFMPEG_RAII_H 1
+
+// Some helpers to make RAII versions of FFmpeg objects.
+// The cleanup functions don't interact all that well with unique_ptr,
+// so things get a bit messy and verbose, but overall it's worth it to ensure
+// we never leak things by accident in error paths.
+//
+// This does not cover any of the types that can actually be declared as
+// a unique_ptr with no helper functions for deleter.
+
+#include <memory>
+
+struct AVCodec;
+struct AVCodecContext;
+struct AVCodecParameters;
+struct AVDictionary;
+struct AVFormatContext;
+struct AVFrame;
+struct AVInputFormat;
+struct SwsContext;
+typedef struct AVIOInterruptCB AVIOInterruptCB;
+
+// AVFormatContext
+struct avformat_close_input_unique {
+ void operator() (AVFormatContext *format_ctx) const;
+};
+
+typedef std::unique_ptr<AVFormatContext, avformat_close_input_unique>
+ AVFormatContextWithCloser;
+
+AVFormatContextWithCloser avformat_open_input_unique(
+ const char *pathname, AVInputFormat *fmt,
+ AVDictionary **options);
+
+AVFormatContextWithCloser avformat_open_input_unique(
+ const char *pathname, AVInputFormat *fmt,
+ AVDictionary **options,
+ const AVIOInterruptCB &interrupt_cb);
+
+
+// AVCodecContext
+struct avcodec_free_context_unique {
+ void operator() (AVCodecContext *ctx) const;
+};
+
+typedef std::unique_ptr<AVCodecContext, avcodec_free_context_unique>
+ AVCodecContextWithDeleter;
+
+AVCodecContextWithDeleter avcodec_alloc_context3_unique(const AVCodec *codec);
+
+
+// AVCodecParameters
+struct avcodec_parameters_free_unique {
+ void operator() (AVCodecParameters *codec_par) const;
+};
+
+typedef std::unique_ptr<AVCodecParameters, avcodec_parameters_free_unique>
+ AVCodecParametersWithDeleter;
+
+
+// AVFrame
+struct av_frame_free_unique {
+ void operator() (AVFrame *frame) const;
+};
+
+typedef std::unique_ptr<AVFrame, av_frame_free_unique>
+ AVFrameWithDeleter;
+
+AVFrameWithDeleter av_frame_alloc_unique();
+
+// SwsContext
+struct sws_free_context_unique {
+ void operator() (SwsContext *context) const;
+};
+
+typedef std::unique_ptr<SwsContext, sws_free_context_unique>
+ SwsContextWithDeleter;
+
+#endif // !defined(_FFMPEG_RAII_H)
--- /dev/null
+#include <stdio.h>
+#include <memory>
+
+extern "C" {
+#include <libavformat/avformat.h>
+}
+
+#include "ffmpeg_raii.h"
+
+using namespace std;
+
+int main(void)
+{
+ av_register_all();
+ avformat_network_init();
+
+ auto format_ctx = avformat_open_input_unique("example.mp4", nullptr, nullptr);
+ if (format_ctx == nullptr) {
+ fprintf(stderr, "%s: Error opening file\n", "example.mp4");
+ return 1;
+ }
+
+ for ( ;; ) {
+ AVPacket pkt;
+ unique_ptr<AVPacket, decltype(av_packet_unref)*> pkt_cleanup(
+ &pkt, av_packet_unref);
+ av_init_packet(&pkt);
+ pkt.data = nullptr;
+ pkt.size = 0;
+ if (av_read_frame(format_ctx.get(), &pkt) != 0) {
+ break;
+ }
+ fprintf(stderr, "Got a frame from camera %d, pts = %ld, size = %d\n",
+ pkt.stream_index, pkt.pts, pkt.size);
+ char filename[256];
+ snprintf(filename, sizeof(filename), "frames/cam%d-pts%09ld.jpeg", pkt.stream_index, pkt.pts);
+ FILE *fp = fopen(filename, "wb");
+ if (fp == nullptr) {
+ perror(filename);
+ exit(1);
+ }
+ fwrite(pkt.data, pkt.size, 1, fp);
+ fclose(fp);
+ }
+
+ return 0;
+}