FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
hapenc.c
Go to the documentation of this file.
1 /*
2  * Vidvox Hap encoder
3  * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com>
4  * Copyright (C) 2015 Tom Butterworth <bangnoise@gmail.com>
5  *
6  * This file is part of FFmpeg.
7  *
8  * FFmpeg is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * FFmpeg is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with FFmpeg; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 /**
24  * @file
25  * Hap encoder
26  *
27  * Fourcc: Hap1, Hap5, HapY
28  *
29  * https://github.com/Vidvox/hap/blob/master/documentation/HapVideoDRAFT.md
30  */
31 
32 #include <stdint.h>
33 #include "snappy-c.h"
34 
35 #include "libavutil/frame.h"
36 #include "libavutil/imgutils.h"
37 #include "libavutil/intreadwrite.h"
38 #include "libavutil/opt.h"
39 
40 #include "avcodec.h"
41 #include "bytestream.h"
42 #include "hap.h"
43 #include "internal.h"
44 #include "texturedsp.h"
45 
46 #define HAP_MAX_CHUNKS 64
47 
49  /* Short header: four bytes with a 24 bit size value */
51  /* Long header: eight bytes with a 32 bit size value */
53 };
54 
55 static void compress_texture(AVCodecContext *avctx, const AVFrame *f)
56 {
57  HapContext *ctx = avctx->priv_data;
58  uint8_t *out = ctx->tex_buf;
59  int i, j;
60 
61  for (j = 0; j < avctx->height; j += 4) {
62  for (i = 0; i < avctx->width; i += 4) {
63  uint8_t *p = f->data[0] + i * 4 + j * f->linesize[0];
64  const int step = ctx->tex_fun(out, f->linesize[0], p);
65  out += step;
66  }
67  }
68 }
69 
70 /* section_length does not include the header */
72  enum HapHeaderLength header_length,
73  int section_length,
74  enum HapSectionType section_type)
75 {
76  /* The first three bytes are the length of the section (not including the
77  * header) or zero if using an eight-byte header.
78  * For an eight-byte header, the length is in the last four bytes.
79  * The fourth byte stores the section type. */
80  bytestream2_put_le24(pbc, header_length == HAP_HDR_LONG ? 0 : section_length);
81  bytestream2_put_byte(pbc, section_type);
82 
83  if (header_length == HAP_HDR_LONG) {
84  bytestream2_put_le32(pbc, section_length);
85  }
86 }
87 
88 static int hap_compress_frame(AVCodecContext *avctx, uint8_t *dst)
89 {
90  HapContext *ctx = avctx->priv_data;
91  int i, final_size = 0;
92 
93  for (i = 0; i < ctx->chunk_count; i++) {
94  HapChunk *chunk = &ctx->chunks[i];
95  uint8_t *chunk_src, *chunk_dst;
96  int ret;
97 
98  if (i == 0) {
99  chunk->compressed_offset = 0;
100  } else {
101  chunk->compressed_offset = ctx->chunks[i-1].compressed_offset
102  + ctx->chunks[i-1].compressed_size;
103  }
104  chunk->uncompressed_size = ctx->tex_size / ctx->chunk_count;
105  chunk->uncompressed_offset = i * chunk->uncompressed_size;
106  chunk->compressed_size = ctx->max_snappy;
107  chunk_src = ctx->tex_buf + chunk->uncompressed_offset;
108  chunk_dst = dst + chunk->compressed_offset;
109 
110  /* Compress with snappy too, write directly on packet buffer. */
111  ret = snappy_compress(chunk_src, chunk->uncompressed_size,
112  chunk_dst, &chunk->compressed_size);
113  if (ret != SNAPPY_OK) {
114  av_log(avctx, AV_LOG_ERROR, "Snappy compress error.\n");
115  return AVERROR_BUG;
116  }
117 
118  /* If there is no gain from snappy, just use the raw texture. */
119  if (chunk->compressed_size >= chunk->uncompressed_size) {
120  av_log(avctx, AV_LOG_VERBOSE,
121  "Snappy buffer bigger than uncompressed (%lu >= %lu bytes).\n",
122  chunk->compressed_size, chunk->uncompressed_size);
123  memcpy(chunk_dst, chunk_src, chunk->uncompressed_size);
124  chunk->compressor = HAP_COMP_NONE;
125  chunk->compressed_size = chunk->uncompressed_size;
126  } else {
127  chunk->compressor = HAP_COMP_SNAPPY;
128  }
129 
130  final_size += chunk->compressed_size;
131  }
132 
133  return final_size;
134 }
135 
137 {
138  /* Second-Stage Compressor Table (one byte per entry)
139  * + Chunk Size Table (four bytes per entry)
140  * + headers for both sections (short versions)
141  * = chunk_count + (4 * chunk_count) + 4 + 4 */
142  return (5 * ctx->chunk_count) + 8;
143 }
144 
146 {
147  /* Top section header (long version) */
148  int length = HAP_HDR_LONG;
149 
150  if (ctx->chunk_count > 1) {
151  /* Decode Instructions header (short) + Decode Instructions Container */
153  }
154 
155  return length;
156 }
157 
158 static void hap_write_frame_header(HapContext *ctx, uint8_t *dst, int frame_length)
159 {
160  PutByteContext pbc;
161  int i;
162 
163  bytestream2_init_writer(&pbc, dst, frame_length);
164  if (ctx->chunk_count == 1) {
165  /* Write a simple header */
166  hap_write_section_header(&pbc, HAP_HDR_LONG, frame_length - 8,
167  ctx->chunks[0].compressor | ctx->opt_tex_fmt);
168  } else {
169  /* Write a complex header with Decode Instructions Container */
170  hap_write_section_header(&pbc, HAP_HDR_LONG, frame_length - 8,
176 
177  for (i = 0; i < ctx->chunk_count; i++) {
178  bytestream2_put_byte(&pbc, ctx->chunks[i].compressor >> 4);
179  }
180 
183 
184  for (i = 0; i < ctx->chunk_count; i++) {
185  bytestream2_put_le32(&pbc, ctx->chunks[i].compressed_size);
186  }
187  }
188 }
189 
190 static int hap_encode(AVCodecContext *avctx, AVPacket *pkt,
191  const AVFrame *frame, int *got_packet)
192 {
193  HapContext *ctx = avctx->priv_data;
194  int header_length = hap_header_length(ctx);
195  int final_data_size, ret;
196  int pktsize = FFMAX(ctx->tex_size, ctx->max_snappy * ctx->chunk_count) + header_length;
197 
198  /* Allocate maximum size packet, shrink later. */
199  ret = ff_alloc_packet2(avctx, pkt, pktsize, header_length);
200  if (ret < 0)
201  return ret;
202 
203  /* DXTC compression. */
204  compress_texture(avctx, frame);
205 
206  /* Compress (using Snappy) the frame */
207  final_data_size = hap_compress_frame(avctx, pkt->data + header_length);
208  if (final_data_size < 0)
209  return final_data_size;
210 
211  /* Write header at the start. */
212  hap_write_frame_header(ctx, pkt->data, final_data_size + header_length);
213 
214  av_shrink_packet(pkt, final_data_size + header_length);
215  pkt->flags |= AV_PKT_FLAG_KEY;
216  *got_packet = 1;
217  return 0;
218 }
219 
220 static av_cold int hap_init(AVCodecContext *avctx)
221 {
222  HapContext *ctx = avctx->priv_data;
223  int ratio;
224  int corrected_chunk_count;
225  int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
226 
227  if (ret < 0) {
228  av_log(avctx, AV_LOG_ERROR, "Invalid video size %dx%d.\n",
229  avctx->width, avctx->height);
230  return ret;
231  }
232 
233  if (avctx->width % 4 || avctx->height % 4) {
234  av_log(avctx, AV_LOG_ERROR, "Video size %dx%d is not multiple of 4.\n",
235  avctx->width, avctx->height);
236  return AVERROR_INVALIDDATA;
237  }
238 
240 
241  switch (ctx->opt_tex_fmt) {
242  case HAP_FMT_RGBDXT1:
243  ratio = 8;
244  avctx->codec_tag = MKTAG('H', 'a', 'p', '1');
245  avctx->bits_per_coded_sample = 24;
246  ctx->tex_fun = ctx->dxtc.dxt1_block;
247  break;
248  case HAP_FMT_RGBADXT5:
249  ratio = 4;
250  avctx->codec_tag = MKTAG('H', 'a', 'p', '5');
251  avctx->bits_per_coded_sample = 32;
252  ctx->tex_fun = ctx->dxtc.dxt5_block;
253  break;
254  case HAP_FMT_YCOCGDXT5:
255  ratio = 4;
256  avctx->codec_tag = MKTAG('H', 'a', 'p', 'Y');
257  avctx->bits_per_coded_sample = 24;
258  ctx->tex_fun = ctx->dxtc.dxt5ys_block;
259  break;
260  default:
261  av_log(avctx, AV_LOG_ERROR, "Invalid format %02X\n", ctx->opt_tex_fmt);
262  return AVERROR_INVALIDDATA;
263  }
264 
265  /* Texture compression ratio is constant, so can we computer
266  * beforehand the final size of the uncompressed buffer. */
267  ctx->tex_size = FFALIGN(avctx->width, TEXTURE_BLOCK_W) *
268  FFALIGN(avctx->height, TEXTURE_BLOCK_H) * 4 / ratio;
269 
270  /* Round the chunk count to divide evenly on DXT block edges */
271  corrected_chunk_count = av_clip(ctx->opt_chunk_count, 1, HAP_MAX_CHUNKS);
272  while ((ctx->tex_size / (64 / ratio)) % corrected_chunk_count != 0) {
273  corrected_chunk_count--;
274  }
275  if (corrected_chunk_count != ctx->opt_chunk_count) {
276  av_log(avctx, AV_LOG_INFO, "%d chunks requested but %d used.\n",
277  ctx->opt_chunk_count, corrected_chunk_count);
278  }
279  ret = ff_hap_set_chunk_count(ctx, corrected_chunk_count, 1);
280  if (ret != 0)
281  return ret;
282 
283  ctx->max_snappy = snappy_max_compressed_length(ctx->tex_size / corrected_chunk_count);
284 
285  ctx->tex_buf = av_malloc(ctx->tex_size);
286  if (!ctx->tex_buf)
287  return AVERROR(ENOMEM);
288 
289  return 0;
290 }
291 
292 static av_cold int hap_close(AVCodecContext *avctx)
293 {
294  HapContext *ctx = avctx->priv_data;
295 
296  ff_hap_free_context(ctx);
297 
298  return 0;
299 }
300 
301 #define OFFSET(x) offsetof(HapContext, x)
302 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
303 static const AVOption options[] = {
304  { "format", NULL, OFFSET(opt_tex_fmt), AV_OPT_TYPE_INT, { .i64 = HAP_FMT_RGBDXT1 }, HAP_FMT_RGBDXT1, HAP_FMT_YCOCGDXT5, FLAGS, "format" },
305  { "hap", "Hap 1 (DXT1 textures)", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_FMT_RGBDXT1 }, 0, 0, FLAGS, "format" },
306  { "hap_alpha", "Hap Alpha (DXT5 textures)", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_FMT_RGBADXT5 }, 0, 0, FLAGS, "format" },
307  { "hap_q", "Hap Q (DXT5-YCoCg textures)", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_FMT_YCOCGDXT5 }, 0, 0, FLAGS, "format" },
308  { "chunks", "chunk count", OFFSET(opt_chunk_count), AV_OPT_TYPE_INT, {.i64 = 1 }, 1, HAP_MAX_CHUNKS, FLAGS, },
309  { NULL },
310 };
311 
312 static const AVClass hapenc_class = {
313  .class_name = "Hap encoder",
314  .item_name = av_default_item_name,
315  .option = options,
316  .version = LIBAVUTIL_VERSION_INT,
317 };
318 
320  .name = "hap",
321  .long_name = NULL_IF_CONFIG_SMALL("Vidvox Hap encoder"),
322  .type = AVMEDIA_TYPE_VIDEO,
323  .id = AV_CODEC_ID_HAP,
324  .priv_data_size = sizeof(HapContext),
325  .priv_class = &hapenc_class,
326  .init = hap_init,
327  .encode2 = hap_encode,
328  .close = hap_close,
329  .pix_fmts = (const enum AVPixelFormat[]) {
331  },
332  .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE |
334 };
#define FF_CODEC_CAP_INIT_CLEANUP
The codec allows calling the close function for deallocation even if the init function returned a fai...
Definition: internal.h:48
Definition: hap.h:60
#define NULL
Definition: coverity.c:32
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
#define HAP_MAX_CHUNKS
Definition: hapenc.c:46
This structure describes decoded (raw) audio or video data.
Definition: frame.h:184
HapChunk * chunks
Definition: hap.h:70
AVOption.
Definition: opt.h:245
HapSectionType
Definition: hap.h:45
int(* dxt1_block)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block)
Definition: texturedsp.h:46
misc image utilities
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
void av_shrink_packet(AVPacket *pkt, int size)
Reduce packet size, correctly zeroing padding.
Definition: avpacket.c:101
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:35
int(* tex_fun)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block)
Definition: hap.h:83
Texture block (4x4) module.
static av_always_inline void bytestream2_init_writer(PutByteContext *p, uint8_t *buf, int buf_size)
Definition: bytestream.h:143
static av_cold int hap_init(AVCodecContext *avctx)
Definition: hapenc.c:220
static AVPacket pkt
size_t compressed_size
Definition: hap.h:55
int compressed_offset
Definition: hap.h:54
AVCodec.
Definition: avcodec.h:3600
size_t uncompressed_size
Definition: hap.h:57
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:72
#define FF_CODEC_CAP_INIT_THREADSAFE
The codec does not modify any global variables in the init function, allowing to call the init functi...
Definition: internal.h:40
uint8_t
#define av_cold
Definition: attributes.h:82
#define av_malloc(s)
HapHeaderLength
Definition: hapenc.c:48
static void hap_write_section_header(PutByteContext *pbc, enum HapHeaderLength header_length, int section_length, enum HapSectionType section_type)
Definition: hapenc.c:71
AVOptions.
static const AVClass hapenc_class
Definition: hapenc.c:312
int ff_hap_set_chunk_count(HapContext *ctx, int count, int first_in_frame)
Definition: hap.c:28
static AVFrame * frame
#define TEXTURE_BLOCK_H
Definition: texturedsp.h:43
TextureDSPContext dxtc
Definition: hap.h:63
uint8_t * data
Definition: avcodec.h:1601
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:192
int bits_per_coded_sample
bits per sample/pixel from the demuxer (needed for huffyuv).
Definition: avcodec.h:3070
#define FFALIGN(x, a)
Definition: macros.h:48
#define av_log(a,...)
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
Definition: avcodec.h:1633
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
av_default_item_name
#define AVERROR(e)
Definition: error.h:43
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:176
GLsizei GLsizei * length
Definition: opengl_enc.c:115
const char * name
Name of the codec implementation.
Definition: avcodec.h:3607
static av_cold int hap_close(AVCodecContext *avctx)
Definition: hapenc.c:292
int(* dxt5_block)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block)
Definition: texturedsp.h:51
int opt_chunk_count
Definition: hap.h:67
#define FFMAX(a, b)
Definition: common.h:94
packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
Definition: pixfmt.h:94
int flags
A combination of AV_PKT_FLAG values.
Definition: avcodec.h:1607
reference-counted frame API
int av_image_check_size(unsigned int w, unsigned int h, int log_offset, void *log_ctx)
Check if the given dimension of an image is valid, meaning that all bytes of the image can be address...
Definition: imgutils.c:251
#define FLAGS
Definition: hapenc.c:302
int uncompressed_offset
Definition: hap.h:56
static void hap_write_frame_header(HapContext *ctx, uint8_t *dst, int frame_length)
Definition: hapenc.c:158
int width
picture width / height.
Definition: avcodec.h:1863
static const AVOption options[]
Definition: hapenc.c:303
AVFormatContext * ctx
Definition: movenc.c:48
size_t max_snappy
Definition: hap.h:78
#define AV_LOG_INFO
Standard information.
Definition: log.h:187
Libavcodec external API header.
int linesize[AV_NUM_DATA_POINTERS]
For video, size in bytes of each picture line.
Definition: frame.h:215
av_cold void ff_hap_free_context(HapContext *ctx)
Definition: hap.c:50
main external API structure.
Definition: avcodec.h:1676
unsigned int codec_tag
fourcc (LSB first, so "ABCD" -> ('D'<<24) + ('C'<<16) + ('B'<<8) + 'A').
Definition: avcodec.h:1708
int chunk_count
Definition: hap.h:69
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition: error.h:50
Describe the class of an AVClass context structure.
Definition: log.h:67
AVCodec ff_hap_encoder
Definition: hapenc.c:319
int ff_alloc_packet2(AVCodecContext *avctx, AVPacket *avpkt, int64_t size, int64_t min_size)
Check AVPacket size and/or allocate data.
Definition: utils.c:1722
enum HapTextureFormat opt_tex_fmt
Definition: hap.h:66
void ff_texturedspenc_init(TextureDSPContext *c)
static enum AVPixelFormat pix_fmts[]
Definition: libkvazaar.c:262
uint8_t * tex_buf
Definition: hap.h:75
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:198
static int hap_encode(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet)
Definition: hapenc.c:190
common internal api header.
#define OFFSET(x)
Definition: hapenc.c:301
size_t tex_size
Definition: hap.h:76
Definition: hap.h:52
static int hap_decode_instructions_length(HapContext *ctx)
Definition: hapenc.c:136
static void compress_texture(AVCodecContext *avctx, const AVFrame *f)
Definition: hapenc.c:55
void * priv_data
Definition: avcodec.h:1718
FILE * out
Definition: movenc.c:54
static int hap_compress_frame(AVCodecContext *avctx, uint8_t *dst)
Definition: hapenc.c:88
enum HapCompressor compressor
Definition: hap.h:53
#define MKTAG(a, b, c, d)
Definition: common.h:342
#define TEXTURE_BLOCK_W
Definition: texturedsp.h:42
AVPixelFormat
Pixel format.
Definition: pixfmt.h:60
This structure stores compressed data.
Definition: avcodec.h:1578
int(* dxt5ys_block)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block)
Definition: texturedsp.h:53
static int hap_header_length(HapContext *ctx)
Definition: hapenc.c:145