]> git.sesse.net Git - nageru/commitdiff
Make load_image() return nullptr instead of exit()-ing.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 9 Apr 2016 10:44:41 +0000 (12:44 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 9 Apr 2016 10:48:09 +0000 (12:48 +0200)
Will be used when we implement image reload support soon.
In the process, RAII-ify everything in load_image(), so that
we can return nullptr at any given place without being worried
about resource leaks.

In the process, get rid of some deprecated FFmpeg API use.

image_input.cpp

index ab1af285189f6099635ca2b83f77874af073f822..ad193ff008213f7c4b8112d1e735f4567ebeef6e 100644 (file)
@@ -16,24 +16,68 @@ ImageInput::ImageInput(const string &filename)
        : movit::FlatInput({movit::COLORSPACE_sRGB, movit::GAMMA_sRGB}, movit::FORMAT_RGBA_POSTMULTIPLIED_ALPHA,
                           GL_UNSIGNED_BYTE, 1280, 720)  // FIXME
 {
-       set_pixel_data(load_image(filename));
+       const uint8_t *pixel_data = load_image(filename);
+       if (pixel_data == nullptr) {
+               fprintf(stderr, "Couldn't load image, exiting.\n");
+               exit(1);
+       }
+       set_pixel_data(pixel_data);
+}
+
+// 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.
+
+namespace {
+
+void avformat_close_input_unique(AVFormatContext *format_ctx)
+{
+       avformat_close_input(&format_ctx);
+}
+
+unique_ptr<AVFormatContext, decltype(avformat_close_input_unique)*>
+avformat_open_input_unique(const char *filename,
+                           AVInputFormat *fmt, AVDictionary **options)
+{
+       AVFormatContext *format_ctx = nullptr;
+       if (avformat_open_input(&format_ctx, filename, fmt, options) != 0) {
+               format_ctx = nullptr;
+       }
+       return unique_ptr<AVFormatContext, decltype(avformat_close_input_unique)*>(
+               format_ctx, avformat_close_input_unique);
+}
+
+void av_frame_free_unique(AVFrame *frame)
+{
+       av_frame_free(&frame);
 }
 
+unique_ptr<AVFrame, decltype(av_frame_free_unique)*>
+av_frame_alloc_unique()
+{
+       AVFrame *frame = av_frame_alloc();
+       return unique_ptr<AVFrame, decltype(av_frame_free_unique)*>(
+               frame, av_frame_free_unique);
+}
+
+}  // namespace
+
 const uint8_t *ImageInput::load_image(const string &filename)
 {
        if (all_images.count(filename)) {
                return all_images[filename].get();
        }
 
-       AVFormatContext *format_ctx = nullptr;
-       if (avformat_open_input(&format_ctx, filename.c_str(), nullptr, nullptr) != 0) {
+       auto format_ctx = avformat_open_input_unique(filename.c_str(), nullptr, nullptr);
+       if (format_ctx == nullptr) {
                fprintf(stderr, "%s: Error opening file\n", filename.c_str());
-               exit(1);
+               return nullptr;
        }
 
-       if (avformat_find_stream_info(format_ctx, nullptr) < 0) {
+       if (avformat_find_stream_info(format_ctx.get(), nullptr) < 0) {
                fprintf(stderr, "%s: Error finding stream info\n", filename.c_str());
-               exit(1);
+               return nullptr;
        }
 
        int stream_index = -1;
@@ -45,65 +89,69 @@ const uint8_t *ImageInput::load_image(const string &filename)
        }
        if (stream_index == -1) {
                fprintf(stderr, "%s: No video stream found\n", filename.c_str());
-               exit(1);
+               return nullptr;
        }
 
        AVCodecContext *codec_ctx = format_ctx->streams[stream_index]->codec;
        AVCodec *codec = avcodec_find_decoder(codec_ctx->codec_id);
        if (codec == nullptr) {
                fprintf(stderr, "%s: Cannot find decoder\n", filename.c_str());
-               exit(1);
+               return nullptr;
        }
        if (avcodec_open2(codec_ctx, codec, nullptr) < 0) {
                fprintf(stderr, "%s: Cannot open decoder\n", filename.c_str());
-               exit(1);
+               return nullptr;
        }
+       unique_ptr<AVCodecContext, decltype(avcodec_close)*> codec_ctx_cleanup(
+               codec_ctx, avcodec_close);
 
        // Read packets until we have a frame.
        int frame_finished = 0;
-       AVFrame *frame = av_frame_alloc();
+       auto frame = av_frame_alloc_unique();
        do {
                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, &pkt) < 0) {
+               if (av_read_frame(format_ctx.get(), &pkt) < 0) {
                        fprintf(stderr, "%s: Cannot read frame\n", filename.c_str());
-                       exit(1);
+                       return nullptr;
                }
                if (pkt.stream_index != stream_index) {
-                       av_free_packet(&pkt);
                        continue;
                }
 
-               if (avcodec_decode_video2(codec_ctx, frame, &frame_finished, &pkt) < 0) {
+               if (avcodec_decode_video2(codec_ctx, frame.get(), &frame_finished, &pkt) < 0) {
                        fprintf(stderr, "%s: Cannot decode frame\n", filename.c_str());
-                       exit(1);
+                       return nullptr;
                }
-               av_free_packet(&pkt);
        } while (!frame_finished);
 
        // TODO: Scale down if needed!
-       AVPicture pic;
-       avpicture_alloc(&pic, AV_PIX_FMT_RGBA, frame->width, frame->height);
-       SwsContext *sws_ctx = sws_getContext(frame->width, frame->height,
-               (AVPixelFormat)frame->format, frame->width, frame->height,
-               AV_PIX_FMT_RGBA, SWS_BICUBIC, nullptr, nullptr, nullptr);
+       uint8_t *pic_data[4] = {nullptr};
+       unique_ptr<uint8_t *, decltype(av_freep)*> pic_data_cleanup(
+               &pic_data[0], av_freep);
+       int linesizes[4];
+       if (av_image_alloc(pic_data, linesizes, frame->width, frame->height, AV_PIX_FMT_RGBA, 1) < 0) {
+               fprintf(stderr, "%s: Could not allocate picture data\n", filename.c_str());
+               return nullptr;
+       }
+       unique_ptr<SwsContext, decltype(sws_freeContext)*> sws_ctx(
+               sws_getContext(frame->width, frame->height,
+                       (AVPixelFormat)frame->format, frame->width, frame->height,
+                       AV_PIX_FMT_RGBA, SWS_BICUBIC, nullptr, nullptr, nullptr),
+               sws_freeContext);
        if (sws_ctx == nullptr) {
                fprintf(stderr, "%s: Could not create scaler context\n", filename.c_str());
-               exit(1);
+               return nullptr;
        }
-       sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, pic.data, pic.linesize);
-       sws_freeContext(sws_ctx);
+       sws_scale(sws_ctx.get(), frame->data, frame->linesize, 0, frame->height, pic_data, linesizes);
 
        size_t len = frame->width * frame->height * 4;
        unique_ptr<uint8_t[]> image_data(new uint8_t[len]);
-       av_image_copy_to_buffer(image_data.get(), len, pic.data, pic.linesize, AV_PIX_FMT_RGBA, frame->width, frame->height, 1);
-
-       avpicture_free(&pic);
-       av_frame_free(&frame);
-       avcodec_close(codec_ctx);
-       avformat_close_input(&format_ctx);
+       av_image_copy_to_buffer(image_data.get(), len, pic_data, linesizes, AV_PIX_FMT_RGBA, frame->width, frame->height, 1);
 
        all_images[filename] = move(image_data);
        return all_images[filename].get();