]> git.sesse.net Git - ffmpeg/blob - libavcodec/hapenc.c
asfdec: make nb_sub to be unsigned int
[ffmpeg] / libavcodec / hapenc.c
1 /*
2  * Vidvox Hap encoder
3  * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com>
4  *
5  * This file is part of Libav.
6  *
7  * Libav is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * Libav is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 /**
23  * @file
24  * Hap encoder
25  *
26  * Fourcc: Hap1, Hap5, HapY
27  *
28  * https://github.com/Vidvox/hap/blob/master/documentation/HapVideoDRAFT.md
29  */
30
31 #include <stdint.h>
32 #include "snappy-c.h"
33
34 #include "libavutil/frame.h"
35 #include "libavutil/imgutils.h"
36 #include "libavutil/intreadwrite.h"
37 #include "libavutil/opt.h"
38
39 #include "avcodec.h"
40 #include "bytestream.h"
41 #include "hap.h"
42 #include "internal.h"
43 #include "texturedsp.h"
44
45 /* A fixed header size allows to skip a memcpy */
46 #define HEADER_SIZE 8
47
48 static void compress_texture(AVCodecContext *avctx, const AVFrame *f)
49 {
50     HapContext *ctx = avctx->priv_data;
51     uint8_t *out = ctx->tex_buf;
52     int i, j;
53
54     for (j = 0; j < avctx->height; j += 4) {
55         for (i = 0; i < avctx->width; i += 4) {
56             uint8_t *p = f->data[0] + i * 4 + j * f->linesize[0];
57             const int step = ctx->tex_fun(out, f->linesize[0], p);
58             out += step;
59         }
60     }
61 }
62
63 static int hap_encode(AVCodecContext *avctx, AVPacket *pkt,
64                       const AVFrame *frame, int *got_packet)
65 {
66     HapContext *ctx = avctx->priv_data;
67     size_t final_size = ctx->max_snappy;
68     int ret, comp = HAP_COMP_SNAPPY;
69     int pktsize = FFMAX(ctx->tex_size, ctx->max_snappy) + HEADER_SIZE;
70
71     /* Allocate maximum size packet, shrink later. */
72     ret = ff_alloc_packet(pkt, pktsize);
73     if (ret < 0)
74         return ret;
75
76     /* DXTC compression. */
77     compress_texture(avctx, frame);
78
79     /* Compress with snappy too, write directly on packet buffer. */
80     ret = snappy_compress(ctx->tex_buf, ctx->tex_size,
81                           pkt->data + HEADER_SIZE, &final_size);
82     if (ret != SNAPPY_OK) {
83         av_log(avctx, AV_LOG_ERROR, "Snappy compress error.\n");
84         return AVERROR_BUG;
85     }
86
87     /* If there is no gain from snappy, just use the raw texture. */
88     if (final_size > ctx->tex_size) {
89         comp = HAP_COMP_NONE;
90         av_log(avctx, AV_LOG_VERBOSE,
91                "Snappy buffer bigger than uncompressed (%lu > %lu bytes).\n",
92                final_size, ctx->tex_size);
93         memcpy(pkt->data + HEADER_SIZE, ctx->tex_buf, ctx->tex_size);
94         final_size = ctx->tex_size;
95     }
96
97     /* Write header at the start. */
98     AV_WL24(pkt->data, 0);
99     AV_WL32(pkt->data + 4, final_size);
100     pkt->data[3] = comp | ctx->section_type;
101
102     av_shrink_packet(pkt, final_size + HEADER_SIZE);
103     pkt->flags |= AV_PKT_FLAG_KEY;
104     *got_packet = 1;
105     return 0;
106 }
107
108 static av_cold int hap_init(AVCodecContext *avctx)
109 {
110     HapContext *ctx = avctx->priv_data;
111     int ratio;
112     int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
113
114     if (ret < 0) {
115         av_log(avctx, AV_LOG_ERROR, "Invalid video size %dx%d.\n",
116                avctx->width, avctx->height);
117         return ret;
118     }
119
120     if (avctx->width % 4 || avctx->height % 4) {
121         av_log(avctx, AV_LOG_ERROR, "Video size %dx%d is not multiple of 4.\n",
122                avctx->width, avctx->height);
123         return AVERROR_INVALIDDATA;
124     }
125
126     ff_texturedspenc_init(&ctx->dxtc);
127
128     switch (ctx->section_type & 0x0F) {
129     case HAP_FMT_RGBDXT1:
130         ratio = 8;
131         avctx->codec_tag = MKTAG('H', 'a', 'p', '1');
132         ctx->tex_fun = ctx->dxtc.dxt1_block;
133         break;
134     case HAP_FMT_RGBADXT5:
135         ratio = 4;
136         avctx->codec_tag = MKTAG('H', 'a', 'p', '5');
137         ctx->tex_fun = ctx->dxtc.dxt5_block;
138         break;
139     case HAP_FMT_YCOCGDXT5:
140         ratio = 4;
141         avctx->codec_tag = MKTAG('H', 'a', 'p', 'Y');
142         ctx->tex_fun = ctx->dxtc.dxt5ys_block;
143         break;
144     default:
145         av_log(avctx, AV_LOG_ERROR, "Invalid format %02X\n", ctx->section_type);
146         return AVERROR_INVALIDDATA;
147     }
148
149     /* Texture compression ratio is constant, so can we computer
150      * beforehand the final size of the uncompressed buffer. */
151     ctx->tex_size   = FFALIGN(avctx->width,  TEXTURE_BLOCK_W) *
152                       FFALIGN(avctx->height, TEXTURE_BLOCK_H) * 4 / ratio;
153     ctx->max_snappy = snappy_max_compressed_length(ctx->tex_size);
154
155     ctx->tex_buf  = av_malloc(ctx->tex_size);
156     if (!ctx->tex_buf)
157         return AVERROR(ENOMEM);
158
159     return 0;
160 }
161
162 static av_cold int hap_close(AVCodecContext *avctx)
163 {
164     HapContext *ctx = avctx->priv_data;
165
166     av_freep(&ctx->tex_buf);
167
168     return 0;
169 }
170
171 #define OFFSET(x) offsetof(HapContext, x)
172 #define FLAGS     AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
173 static const AVOption options[] = {
174     { "format", NULL, OFFSET(section_type), AV_OPT_TYPE_INT, { .i64 = HAP_FMT_RGBDXT1 }, HAP_FMT_RGBDXT1, HAP_FMT_YCOCGDXT5, FLAGS, "format" },
175         { "hap",       "Hap 1 (DXT1 textures)", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_FMT_RGBDXT1   }, 0, 0, FLAGS, "format" },
176         { "hap_alpha", "Hap Alpha (DXT5 textures)", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_FMT_RGBADXT5  }, 0, 0, FLAGS, "format" },
177         { "hap_q",     "Hap Q (DXT5-YCoCg textures)", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_FMT_YCOCGDXT5 }, 0, 0, FLAGS, "format" },
178
179     { NULL },
180 };
181
182 static const AVClass hapenc_class = {
183     .class_name = "Hap encoder",
184     .item_name  = av_default_item_name,
185     .option     = options,
186     .version    = LIBAVUTIL_VERSION_INT,
187 };
188
189 AVCodec ff_hap_encoder = {
190     .name           = "hap",
191     .long_name      = NULL_IF_CONFIG_SMALL("Vidvox Hap encoder"),
192     .type           = AVMEDIA_TYPE_VIDEO,
193     .id             = AV_CODEC_ID_HAP,
194     .priv_data_size = sizeof(HapContext),
195     .priv_class     = &hapenc_class,
196     .init           = hap_init,
197     .encode2        = hap_encode,
198     .close          = hap_close,
199     .pix_fmts       = (const enum AVPixelFormat[]) {
200         AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE,
201     },
202     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE |
203                       FF_CODEC_CAP_INIT_CLEANUP,
204 };