Skip to content

Instantly share code, notes, and snippets.

@babelouest
Last active January 31, 2018 16:37
Show Gist options
  • Save babelouest/30e29d45dfba6173f0e74d2c848dc60f to your computer and use it in GitHub Desktop.
Save babelouest/30e29d45dfba6173f0e74d2c848dc60f to your computer and use it in GitHub Desktop.
/**
*
* resize-libav
*
* Use libav to resize an image using its swscale functions, sample code
*
* Copyright 2018 - Nicolas Mora <[email protected]>
* Copyright (c) 2013-2017 Andreas Unterweger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation;
* version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Use the following commang to compile with gcc:
* gcc -o resize-libav resize-libav.c -O3 -lavcodec -lavformat -lswscale -lavutil
*/
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>
#include <libavutil/frame.h>
#include <stdio.h>
#define THUMB_WIDTH 150
#define THUMB_HEIGHT 150
static char * get_error_text(const int error) {
static char error_buffer[255];
av_strerror(error, error_buffer, sizeof(error_buffer));
return error_buffer;
}
static int my_decode(AVCodecContext *input_codec_context, AVFrame *frame, int *data_present, AVPacket *input_packet) {
int ret = avcodec_send_packet(input_codec_context, input_packet);
if (ret < 0) {
return ret;
}
*data_present = 0;
do {
ret = avcodec_receive_frame(input_codec_context, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
fprintf(stderr, "Error during decode (%s)\n", get_error_text(ret));
break;
} else {
*data_present += frame->nb_samples > 0;
}
} while (ret > 0);
return 0;
}
static int my_encode(AVCodecContext *output_codec_context, AVPacket *output_packet, AVFrame *frame, int * data_present) {
int ret;
ret = avcodec_send_frame(output_codec_context, frame);
if (ret < 0) {
return ret;
}
*data_present = 0;
do {
ret = avcodec_receive_packet(output_codec_context, output_packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
fprintf(stderr, "Error during encode: '%s'\n", get_error_text(ret));
return ret;
} else {
*data_present = 1;
}
} while (ret > 0);
return 0;
}
static int resize_image(AVCodecContext * original_image_codec_context, AVCodecContext * resized_image_codec_context, AVPacket * original_image_cover_packet, AVPacket * resized_image_cover_packet, int resized_width, int resized_height) {
int ret = -1, data_present, err;
AVFrame * input_frame = av_frame_alloc(), * output_frame = av_frame_alloc();
struct SwsContext * sws_c;
if (input_frame != NULL && output_frame != NULL) {
if (!my_decode(original_image_codec_context, input_frame, &data_present, original_image_cover_packet)) {
if (!resized_width) {
resized_width = input_frame->width;
}
if (!resized_height) {
resized_height = input_frame->height;
}
sws_c = sws_getContext(input_frame->width, input_frame->height, original_image_codec_context->pix_fmt, resized_width, resized_height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
if (sws_c != NULL) {
av_image_alloc(output_frame->data, output_frame->linesize, resized_width, resized_height, original_image_codec_context->pix_fmt, 32);
output_frame->width = resized_width;
output_frame->height = resized_height;
output_frame->format = AV_PIX_FMT_YUV420P;
if ((err = sws_scale(sws_c, (const uint8_t * const*)input_frame->data, input_frame->linesize, 0, input_frame->height, output_frame->data, output_frame->linesize)) < 0) {
fprintf(stderr, "Error sws_scale: %d\n", err);
ret = -1;
} else if (err > 0) {
ret = my_encode(resized_image_codec_context, resized_image_cover_packet, output_frame, &data_present);
}
sws_freeContext(sws_c);
} else {
fprintf(stderr, "Error sws_getContext\n");
}
}
av_freep(&output_frame->data[0]);
av_frame_free(&output_frame);
av_frame_free(&input_frame);
} else {
fprintf(stderr, "Error allocating resources for input_frame or output_frame\n");
ret = -1;
}
return ret;
}
static int get_media_cover(AVFormatContext * full_size_cover_format_context, AVCodecContext ** full_size_cover_codec_context, AVPacket * full_size_cover_packet) {
int i, ret = 1;
AVCodec * cover_codec = NULL;
int video_stream;
video_stream = -1;
*full_size_cover_codec_context = NULL;
for (i=0; i<full_size_cover_format_context->nb_streams; i++) {
cover_codec = avcodec_find_decoder(full_size_cover_format_context->streams[i]->codecpar->codec_id);
if (cover_codec->type == AVMEDIA_TYPE_VIDEO) {
if (cover_codec->id == AV_CODEC_ID_MJPEG) { // TODO See if we can't convert gif or png to jpeg
video_stream = i;
*full_size_cover_codec_context = avcodec_alloc_context3(cover_codec);
avcodec_parameters_to_context(*full_size_cover_codec_context, full_size_cover_format_context->streams[i]->codecpar);
avcodec_open2(*full_size_cover_codec_context, cover_codec, NULL);
break;
}
}
}
if (video_stream != -1) {
while (av_read_frame(full_size_cover_format_context, full_size_cover_packet) >= 0) {
if (full_size_cover_packet->stream_index == video_stream) {
ret = 0;
break;
}
av_packet_unref(full_size_cover_packet);
}
} else {
ret = 1;
}
return ret;
}
static int init_output_jpeg_image(AVCodecContext ** image_codec_context, int dst_width, int dst_height) {
AVCodec * output_codec = NULL;
int ret = 1;
if ((output_codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG)) == NULL) {
fprintf(stderr, "init_output_jpeg_image - error avcodec_find_encoder\n");
} else if (((*image_codec_context) = avcodec_alloc_context3(output_codec)) == NULL) {
fprintf(stderr, "init_output_jpeg_image - error avcodec_alloc_context3\n");
} else {
(*image_codec_context)->bit_rate = 0;
(*image_codec_context)->width = dst_width;
(*image_codec_context)->height = dst_height;
(*image_codec_context)->time_base.num = 1;
(*image_codec_context)->time_base.den = 30;
(*image_codec_context)->pix_fmt = AV_PIX_FMT_YUVJ420P;
if ((ret = avcodec_open2((*image_codec_context), output_codec, NULL)) < 0) {
fprintf(stderr, "init_output_jpeg_image - error avcodec_open2 (%s)\n", get_error_text(ret));
ret = 1;
} else {
ret = 0;
}
}
return ret;
}
static int resize_image_from_path(const char * path) {
AVFormatContext * full_size_cover_format_context = NULL;
AVCodecContext * full_size_cover_codec_context = NULL;
AVPacket full_size_cover_packet, thumbnail_cover_packet;
AVCodecContext * thumbnail_cover_codec_context = NULL;
int ret, err;
FILE * file;
if (!(err = avformat_open_input(&full_size_cover_format_context, path, NULL, NULL))) {
/* Get information on the input file (number of streams etc.). */
if (!avformat_find_stream_info(full_size_cover_format_context, NULL)) {
av_init_packet(&full_size_cover_packet);
av_init_packet(&thumbnail_cover_packet);
if (!init_output_jpeg_image(&thumbnail_cover_codec_context, THUMB_WIDTH, THUMB_HEIGHT)) {
if (!(ret = get_media_cover(full_size_cover_format_context, &full_size_cover_codec_context, &full_size_cover_packet))) {
if (resize_image(full_size_cover_codec_context, thumbnail_cover_codec_context, &full_size_cover_packet, &thumbnail_cover_packet, THUMB_WIDTH, THUMB_HEIGHT) >= 0) {
fprintf(stdout, "result size: %zu\n", thumbnail_cover_packet.size);
file = fopen("out-libav.jpg", "wb");
fwrite(thumbnail_cover_packet.data, 1, thumbnail_cover_packet.size, file);
fclose(file);
} else {
fprintf(stderr, "media_get_metadata - Error resize_image for %s\n", path);
}
}
} else {
fprintf(stderr, "Error init_output_jpeg_image\n");
}
av_packet_unref(&full_size_cover_packet);
av_packet_unref(&thumbnail_cover_packet);
avcodec_free_context(&full_size_cover_codec_context);
avformat_close_input(&full_size_cover_format_context);
full_size_cover_format_context = NULL;
} else {
fprintf(stderr, "media_get_metadata - Error avformat_open_input for %s\n", path);
}
} else {
fprintf(stderr, "media_get_metadata - Error avformat_find_stream_info: '%s'\n", get_error_text(err));
}
}
int main(int argc, char ** argv) {
av_register_all();
if (argc > 1) {
resize_image_from_path(argv[1]);
} else {
fprintf(stderr, "Usage: %s <path_to_image>\n", argv[0]);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment