Skip to content
Snippets Groups Projects
opengl_enc.c 48.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • Lukasz Marek's avatar
    Lukasz Marek committed
        OpenGLContext *opengl = h->priv_data;
        AVStream *st;
        int ret;
    
        if (h->nb_streams != 1 ||
            h->streams[0]->codec->codec_type != AVMEDIA_TYPE_VIDEO ||
            h->streams[0]->codec->codec_id != AV_CODEC_ID_RAWVIDEO) {
            av_log(opengl, AV_LOG_ERROR, "Only a single video stream is supported.\n");
            return AVERROR(EINVAL);
        }
        st = h->streams[0];
        opengl->width = st->codec->width;
        opengl->height = st->codec->height;
        opengl->pix_fmt = st->codec->pix_fmt;
    
        if (!opengl->window_title && !opengl->no_window)
            opengl->window_title = av_strdup(h->filename);
    
        if (!opengl->no_window) {
    #if HAVE_SDL
            if ((ret = opengl_sdl_create_window(h)) < 0)
                goto fail;
    #else
            av_log(opengl, AV_LOG_ERROR, "FFmpeg is compiled without SDL. Cannot create default window.\n");
            ret = AVERROR(ENOSYS);
            goto fail;
    #endif
        } else {
            if ((ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_CREATE_WINDOW_BUFFER, NULL , 0)) < 0) {
                av_log(opengl, AV_LOG_ERROR, "Application failed to create window buffer.\n");
                goto fail;
            }
            if ((ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_PREPARE_WINDOW_BUFFER, NULL , 0)) < 0) {
                av_log(opengl, AV_LOG_ERROR, "Application failed to prepare window buffer.\n");
                goto fail;
            }
        }
    
        if ((ret = opengl_read_limits(opengl)) < 0)
            goto fail;
    
        if (opengl->width > opengl->max_texture_size || opengl->height > opengl->max_texture_size) {
            av_log(opengl, AV_LOG_ERROR, "Too big picture %dx%d, max supported size is %dx%d\n",
                   opengl->width, opengl->height, opengl->max_texture_size, opengl->max_texture_size);
            ret = AVERROR(EINVAL);
            goto fail;
        }
    
        if (!opengl->no_window) {
    #if HAVE_SDL
            if ((ret = opengl_sdl_load_procedures(opengl)) < 0)
                goto fail;
    #endif
        } else if ((ret = opengl_load_procedures(opengl)) < 0)
            goto fail;
    
        opengl_fill_color_map(opengl);
        opengl_get_texture_params(opengl);
    
        if ((ret = opengl_init_context(opengl)) < 0)
            goto fail;
    
        if ((ret = opengl_prepare_vertex(h)) < 0)
            goto fail;
    
        glClear(GL_COLOR_BUFFER_BIT);
    
    #if HAVE_SDL
        if (!opengl->no_window)
            SDL_GL_SwapBuffers();
    #endif
        if (opengl->no_window &&
            (ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_DISPLAY_WINDOW_BUFFER, NULL , 0)) < 0) {
            av_log(opengl, AV_LOG_ERROR, "Application failed to display window buffer.\n");
            goto fail;
        }
    
        ret = AVERROR_EXTERNAL;
        OPENGL_ERROR_CHECK(opengl);
        return 0;
    
      fail:
        opengl_write_trailer(h);
        return ret;
    }
    
    static uint8_t* opengl_get_plane_pointer(OpenGLContext *opengl, AVPacket *pkt, int comp_index,
                                             const AVPixFmtDescriptor *desc)
    {
        uint8_t *data = pkt->data;
        int wordsize = opengl_type_size(opengl->type);
        int width_chroma = FF_CEIL_RSHIFT(opengl->width, desc->log2_chroma_w);
        int height_chroma = FF_CEIL_RSHIFT(opengl->height, desc->log2_chroma_h);
        int plane = desc->comp[comp_index].plane;
    
        switch(plane) {
        case 0:
            break;
        case 1:
            data += opengl->width * opengl->height * wordsize;
            break;
        case 2:
            data += opengl->width * opengl->height * wordsize;
            data += width_chroma * height_chroma * wordsize;
            break;
        case 3:
            data += opengl->width * opengl->height * wordsize;
            data += 2 * width_chroma * height_chroma * wordsize;
            break;
        default:
            return NULL;
        }
        return data;
    }
    
    static int opengl_draw(AVFormatContext *h, AVPacket *pkt, int repaint)
    {
        OpenGLContext *opengl = h->priv_data;
        enum AVPixelFormat pix_fmt = h->streams[0]->codec->pix_fmt;
        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
        int ret;
    
    #if HAVE_SDL
        if (!opengl->no_window && (ret = opengl_sdl_process_events(h)) < 0)
            goto fail;
    #endif
        if (opengl->no_window &&
            (ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_PREPARE_WINDOW_BUFFER, NULL , 0)) < 0) {
            av_log(opengl, AV_LOG_ERROR, "Application failed to prepare window buffer.\n");
            goto fail;
        }
    
        glClear(GL_COLOR_BUFFER_BIT);
    
        if (!repaint) {
            glBindTexture(GL_TEXTURE_2D, opengl->texture_name[0]);
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, opengl->width, opengl->height, opengl->format, opengl->type,
                            opengl_get_plane_pointer(opengl, pkt, 0, desc));
            if (desc->flags & AV_PIX_FMT_FLAG_PLANAR) {
                int width_chroma = FF_CEIL_RSHIFT(opengl->width, desc->log2_chroma_w);
                int height_chroma = FF_CEIL_RSHIFT(opengl->height, desc->log2_chroma_h);
                glBindTexture(GL_TEXTURE_2D, opengl->texture_name[1]);
                glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_chroma, height_chroma, opengl->format, opengl->type,
                                opengl_get_plane_pointer(opengl, pkt, 1, desc));
                glBindTexture(GL_TEXTURE_2D, opengl->texture_name[2]);
                glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_chroma, height_chroma, opengl->format, opengl->type,
                                opengl_get_plane_pointer(opengl, pkt, 2, desc));
                if (desc->flags & AV_PIX_FMT_FLAG_ALPHA) {
                    glBindTexture(GL_TEXTURE_2D, opengl->texture_name[3]);
                    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, opengl->width, opengl->height, opengl->format, opengl->type,
                                    opengl_get_plane_pointer(opengl, pkt, 3, desc));
                }
            }
        }
        ret = AVERROR_EXTERNAL;
        OPENGL_ERROR_CHECK(opengl);
    
        if ((ret = opengl_prepare(opengl)) < 0)
            goto fail;
    
        opengl->glprocs.glBindBuffer(FF_GL_ARRAY_BUFFER, opengl->vertex_buffer);
        opengl->glprocs.glBindBuffer(FF_GL_ELEMENT_ARRAY_BUFFER, opengl->index_buffer);
        opengl->glprocs.glVertexAttribPointer(opengl->position_attrib, 3, GL_FLOAT, GL_FALSE, sizeof(OpenGLVertexInfo), 0);
        opengl->glprocs.glEnableVertexAttribArray(opengl->position_attrib);
        opengl->glprocs.glVertexAttribPointer(opengl->texture_coords_attrib, 2, GL_FLOAT, GL_FALSE, sizeof(OpenGLVertexInfo), 12);
        opengl->glprocs.glEnableVertexAttribArray(opengl->texture_coords_attrib);
    
        glDrawElements(GL_TRIANGLES, FF_ARRAY_ELEMS(g_index), GL_UNSIGNED_SHORT, 0);
    
        ret = AVERROR_EXTERNAL;
        OPENGL_ERROR_CHECK(opengl);
    
    #if HAVE_SDL
        if (!opengl->no_window)
            SDL_GL_SwapBuffers();
    #endif
        if (opengl->no_window &&
            (ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_DISPLAY_WINDOW_BUFFER, NULL , 0)) < 0) {
            av_log(opengl, AV_LOG_ERROR, "Application failed to display window buffer.\n");
            goto fail;
        }
    
        return 0;
      fail:
        return ret;
    }
    
    static int opengl_write_packet(AVFormatContext *h, AVPacket *pkt)
    {
        return opengl_draw(h, pkt, 0);
    }
    
    #define OFFSET(x) offsetof(OpenGLContext, x)
    #define ENC AV_OPT_FLAG_ENCODING_PARAM
    static const AVOption options[] = {
        { "background",   "set background color",   OFFSET(background),   AV_OPT_TYPE_COLOR,  {.str = "black"}, CHAR_MIN, CHAR_MAX, ENC },
        { "no_window",    "disable default window", OFFSET(no_window),    AV_OPT_TYPE_INT,    {.i64 = 0}, INT_MIN, INT_MAX, ENC },
        { "window_title", "set window title",       OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, ENC },
        { NULL }
    };
    
    static const AVClass opengl_class = {
        .class_name = "opengl outdev",
        .item_name  = av_default_item_name,
        .option     = options,
        .version    = LIBAVUTIL_VERSION_INT,
    };
    
    AVOutputFormat ff_opengl_muxer = {
        .name           = "opengl",
        .long_name      = NULL_IF_CONFIG_SMALL("OpenGL output"),
        .priv_data_size = sizeof(OpenGLContext),
        .audio_codec    = AV_CODEC_ID_NONE,
        .video_codec    = AV_CODEC_ID_RAWVIDEO,
        .write_header   = opengl_write_header,
        .write_packet   = opengl_write_packet,
        .write_trailer  = opengl_write_trailer,
        .control_message = opengl_control_message,
        .flags          = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS,
        .priv_class     = &opengl_class,
    };