summaryrefslogtreecommitdiffstats
path: root/server/mjpeg_encoder.c
blob: a25c3482d799a37575e61134f23f5388f9142176 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
   Copyright (C) 2009 Red Hat, Inc.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.

   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/>.
*/

#include "red_common.h"
#include "mjpeg_encoder.h"
#include <jpeglib.h>

struct MJpegEncoder {
    int width;
    int height;
    int stride;
    uint8_t *frame;
    int first_frame;
    int quality;

    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;
};

MJpegEncoder *mjpeg_encoder_new(int width, int height)
{
    MJpegEncoder *enc;

    enc = spice_new0(MJpegEncoder, 1);

    enc->first_frame = TRUE;
    enc->width = width;
    enc->height = height;
    enc->stride = width * 3;
    enc->quality = 70;
    if (enc->stride < width) {
        abort();
    }
    enc->frame = spice_malloc_n(enc->stride, height);

    enc->cinfo.err = jpeg_std_error(&enc->jerr);

    jpeg_create_compress(&enc->cinfo);

    return enc;
}

void mjpeg_encoder_destroy(MJpegEncoder *encoder)
{
    jpeg_destroy_compress(&encoder->cinfo);
    free(encoder->frame);
    free(encoder);
}

uint8_t *mjpeg_encoder_get_frame(MJpegEncoder *encoder)
{
    return encoder->frame;
}
size_t mjpeg_encoder_get_frame_stride(MJpegEncoder *encoder)
{
    return encoder->stride;
}

void init_destination(j_compress_ptr cinfo)
{
}

boolean empty_output_buffer(j_compress_ptr cinfo)
{
    return FALSE;
}

void term_destination(j_compress_ptr cinfo)
{
}

int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
                               uint8_t *buffer, size_t buffer_len)
{
    struct jpeg_destination_mgr destmgr;
    uint8_t *frame;
    int n;

    destmgr.next_output_byte = buffer;
    destmgr.free_in_buffer = buffer_len;
    destmgr.init_destination = init_destination;
    destmgr.empty_output_buffer = empty_output_buffer;
    destmgr.term_destination = term_destination;

    encoder->cinfo.dest = &destmgr;

    encoder->cinfo.image_width      = encoder->width;
    encoder->cinfo.image_height     = encoder->height;
    encoder->cinfo.input_components = 3;
    encoder->cinfo.in_color_space   = JCS_RGB;

    jpeg_set_defaults(&encoder->cinfo);
    encoder->cinfo.dct_method       = JDCT_IFAST;
    jpeg_set_quality(&encoder->cinfo, encoder->quality, TRUE);
    jpeg_start_compress(&encoder->cinfo, encoder->first_frame);

    frame = encoder->frame;
    while (encoder->cinfo.next_scanline < encoder->cinfo.image_height) {
        n = jpeg_write_scanlines(&encoder->cinfo, &frame, 1);
        if (n == 0) { /* Not enough space */
            jpeg_abort_compress(&encoder->cinfo);
            return 0;
        }
        frame += encoder->stride;
    }

    jpeg_finish_compress(&encoder->cinfo);

    encoder->first_frame = FALSE;
    return destmgr.next_output_byte - buffer;
}