Last active
January 31, 2018 16:37
-
-
Save babelouest/30e29d45dfba6173f0e74d2c848dc60f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* 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