How to resize a picture using ffmpeg's sws_scale()?

后端 未结 2 645
栀梦
栀梦 2020-11-29 02:26

I wanna resize a picture by using the ffmpeg\'s func--->sws_scale().

Is there any one knows how to do it?

Do you have the source code for this function?

相关标签:
2条回答
  • 2020-11-29 02:35

    First you need to create a SwsContext (you need to do this only once) :

    struct SwsContext *resize;
    resize = sws_getContext(width1, height1, AV_PIX_FMT_YUV420P, width2, height2, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
    

    You need two frames for conversion, frame1 is the original frame, you need to explicitly allocate frame2 :

    AVFrame* frame1 = avcodec_alloc_frame(); // this is your original frame
    
    AVFrame* frame2 = avcodec_alloc_frame();
    int num_bytes = avpicture_get_size(AV_PIX_FMT_RGB24, width2, height2);
    uint8_t* frame2_buffer = (uint8_t *)av_malloc(num_bytes*sizeof(uint8_t));
    avpicture_fill((AVPicture*)frame2, frame2_buffer, AV_PIX_FMT_RGB24, width2, height2);
    

    You may use this part inside a loop if you need to resize each frame you receive :

    // frame1 should be filled by now (eg using avcodec_decode_video)
    sws_scale(resize, frame1->data, frame1->linesize, 0, height1, frame2->data, frame2->linesize);
    

    Note that I also changed pixel format, but you can use the same pixel format for both frames

    0 讨论(0)
  • 2020-11-29 02:52

    Runnable example in FFmpeg 2.8

    Basically using arash's method, but runnable so you can try it out.

    Generate one short video procedurally, and then convert it to 3 different sizes.

    ffmpeg_encoder_init_frame and ffmpeg_encoder_scale are the key methods.

    Source:

    #include <libavcodec/avcodec.h>
    #include <libavutil/imgutils.h>
    #include <libavutil/opt.h>
    #include <libswscale/swscale.h>
    
    static AVCodecContext *c = NULL;
    static AVFrame *frame;
    static AVFrame *frame2;
    static AVPacket pkt;
    static FILE *file;
    static struct SwsContext *sws_context = NULL;
    
    static void ffmpeg_encoder_init_frame(AVFrame **framep, int width, int height) {
        int ret;
        AVFrame *frame;
        frame = av_frame_alloc();
        if (!frame) {
            fprintf(stderr, "Could not allocate video frame\n");
            exit(1);
        }
        frame->format = c->pix_fmt;
        frame->width  = width;
        frame->height = height;
        ret = av_image_alloc(frame->data, frame->linesize, frame->width, frame->height, frame->format, 32);
        if (ret < 0) {
            fprintf(stderr, "Could not allocate raw picture buffer\n");
            exit(1);
        }
        *framep = frame;
    }
    
    static void ffmpeg_encoder_scale(uint8_t *rgb) {
        sws_context = sws_getCachedContext(sws_context,
                frame->width, frame->height, AV_PIX_FMT_YUV420P,
                frame2->width, frame2->height, AV_PIX_FMT_YUV420P,
                SWS_BICUBIC, NULL, NULL, NULL);
        sws_scale(sws_context, (const uint8_t * const *)frame->data, frame->linesize, 0,
                frame->height, frame2->data, frame2->linesize);
    }
    
    static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) {
        const int in_linesize[1] = { 3 * frame->width };
        sws_context = sws_getCachedContext(sws_context,
                frame->width, frame->height, AV_PIX_FMT_RGB24,
                frame->width, frame->height, AV_PIX_FMT_YUV420P,
                0, NULL, NULL, NULL);
        sws_scale(sws_context, (const uint8_t * const *)&rgb, in_linesize, 0,
                frame->height, frame->data, frame->linesize);
    }
    
    void generate_rgb(int width, int height, int pts, uint8_t **rgbp) {
        int x, y, cur;
        uint8_t *rgb = *rgbp;
        rgb = realloc(rgb, 3 * sizeof(uint8_t) * height * width);
        for (y = 0; y < height; y++) {
            for (x = 0; x < width; x++) {
                cur = 3 * (y * width + x);
                rgb[cur + 0] = 0;
                rgb[cur + 1] = 0;
                rgb[cur + 2] = 0;
                if ((frame->pts / 25) % 2 == 0) {
                    if (y < height / 2) {
                        if (x < width / 2) {
                            /* Black. */
                        } else {
                            rgb[cur + 0] = 255;
                        }
                    } else {
                        if (x < width / 2) {
                            rgb[cur + 1] = 255;
                        } else {
                            rgb[cur + 2] = 255;
                        }
                    }
                } else {
                    if (y < height / 2) {
                        rgb[cur + 0] = 255;
                        if (x < width / 2) {
                            rgb[cur + 1] = 255;
                        } else {
                            rgb[cur + 2] = 255;
                        }
                    } else {
                        if (x < width / 2) {
                            rgb[cur + 1] = 255;
                            rgb[cur + 2] = 255;
                        } else {
                            rgb[cur + 0] = 255;
                            rgb[cur + 1] = 255;
                            rgb[cur + 2] = 255;
                        }
                    }
                }
            }
        }
        *rgbp = rgb;
    }
    
    void ffmpeg_encoder_start(const char *filename, int codec_id, int fps, int width, int height, float factor) {
        AVCodec *codec;
        int ret;
        int width2 = width * factor;
        int height2 = height * factor;
        avcodec_register_all();
        codec = avcodec_find_encoder(codec_id);
        if (!codec) {
            fprintf(stderr, "Codec not found\n");
            exit(1);
        }
        c = avcodec_alloc_context3(codec);
        if (!c) {
            fprintf(stderr, "Could not allocate video codec context\n");
            exit(1);
        }
        c->bit_rate = 400000;
        c->width = width2;
        c->height = height2;
        c->time_base.num = 1;
        c->time_base.den = fps;
        c->gop_size = 10;
        c->max_b_frames = 1;
        c->pix_fmt = AV_PIX_FMT_YUV420P;
        if (codec_id == AV_CODEC_ID_H264)
            av_opt_set(c->priv_data, "preset", "slow", 0);
        if (avcodec_open2(c, codec, NULL) < 0) {
            fprintf(stderr, "Could not open codec\n");
            exit(1);
        }
        file = fopen(filename, "wb");
        if (!file) {
            fprintf(stderr, "Could not open %s\n", filename);
            exit(1);
        }
        ffmpeg_encoder_init_frame(&frame, width, height);
        ffmpeg_encoder_init_frame(&frame2, width2, height2);
    }
    
    void ffmpeg_encoder_finish(void) {
        uint8_t endcode[] = { 0, 0, 1, 0xb7 };
        int got_output, ret;
        do {
            fflush(stdout);
            ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
            if (ret < 0) {
                fprintf(stderr, "Error encoding frame\n");
                exit(1);
            }
            if (got_output) {
                fwrite(pkt.data, 1, pkt.size, file);
                av_packet_unref(&pkt);
            }
        } while (got_output);
        fwrite(endcode, 1, sizeof(endcode), file);
        fclose(file);
        avcodec_close(c);
        av_free(c);
        av_freep(&frame->data[0]);
        av_frame_free(&frame);
        av_freep(&frame2->data[0]);
        av_frame_free(&frame2);
    }
    
    void ffmpeg_encoder_encode_frame(uint8_t *rgb) {
        int ret, got_output;
        ffmpeg_encoder_set_frame_yuv_from_rgb(rgb);
        ffmpeg_encoder_scale(rgb);
        frame2->pts = frame->pts;
        av_init_packet(&pkt);
        pkt.data = NULL;
        pkt.size = 0;
        ret = avcodec_encode_video2(c, &pkt, frame2, &got_output);
        if (ret < 0) {
            fprintf(stderr, "Error encoding frame\n");
            exit(1);
        }
        if (got_output) {
            fwrite(pkt.data, 1, pkt.size, file);
            av_packet_unref(&pkt);
        }
    }
    
    static void encode_example(float factor) {
        char filename[255];
        int pts;
        int width = 320;
        int height = 240;
        uint8_t *rgb = NULL;
        snprintf(filename, 255, "tmp." __FILE__ ".%.2f.h264", factor);
        ffmpeg_encoder_start(filename, AV_CODEC_ID_H264, 25, width, height, factor);
        for (pts = 0; pts < 100; pts++) {
            frame->pts = pts;
            generate_rgb(width, height, pts, &rgb);
            ffmpeg_encoder_encode_frame(rgb);
        }
        ffmpeg_encoder_finish();
        free(rgb);
    }
    
    int main(void) {
        encode_example(0.5);
        encode_example(1.0);
        encode_example(2.0);
        return EXIT_SUCCESS;
    }
    

    Run with:

    gcc main.c -lavformat -lavcodec -lswresample -lswscale -lavutil -lx264
    ./a.out
    ffplay tmp.main.c.0.50.h264
    ffplay tmp.main.c.1.00.h264
    ffplay tmp.main.c.2.00.h264
    

    Tested on Ubuntu 16.04. Source on GitHub.

    0 讨论(0)
提交回复
热议问题