FFmpeg
movenccenc.c
Go to the documentation of this file.
1 /*
2  * MOV CENC (Common Encryption) writer
3  * Copyright (c) 2015 Eran Kornblau <erankor at gmail dot com>
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg 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  * FFmpeg 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 FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 #include "movenccenc.h"
22 #include "libavutil/intreadwrite.h"
23 #include "libavutil/mem.h"
24 #include "avio_internal.h"
25 #include "movenc.h"
26 #include "avc.h"
27 #include "nal.h"
28 
30 {
31  size_t new_alloc_size;
32 
33  if (ctx->auxiliary_info_size + size > ctx->auxiliary_info_alloc_size) {
34  new_alloc_size = FFMAX(ctx->auxiliary_info_size + size, ctx->auxiliary_info_alloc_size * 2);
35  if (av_reallocp(&ctx->auxiliary_info, new_alloc_size)) {
36  return AVERROR(ENOMEM);
37  }
38 
39  ctx->auxiliary_info_alloc_size = new_alloc_size;
40  }
41 
42  return 0;
43 }
44 
46  const uint8_t *buf_in, int size)
47 {
48  int ret;
49 
51  if (ret) {
52  return ret;
53  }
54  memcpy(ctx->auxiliary_info + ctx->auxiliary_info_size, buf_in, size);
55  ctx->auxiliary_info_size += size;
56 
57  return 0;
58 }
59 
61  uint16_t clear_bytes, uint32_t encrypted_bytes)
62 {
63  uint8_t* p;
64  int ret;
65 
66  if (!ctx->use_subsamples) {
67  return 0;
68  }
69 
71  if (ret) {
72  return ret;
73  }
74 
75  p = ctx->auxiliary_info + ctx->auxiliary_info_size;
76 
77  AV_WB16(p, clear_bytes);
78  p += sizeof(uint16_t);
79 
80  AV_WB32(p, encrypted_bytes);
81 
82  ctx->auxiliary_info_size += 6;
83  ctx->subsample_count++;
84 
85  return 0;
86 }
87 
88 /**
89  * Encrypt the input buffer and write using avio_write
90  */
92  const uint8_t *buf_in, int size)
93 {
94  uint8_t chunk[4096];
95  const uint8_t* cur_pos = buf_in;
96  int size_left = size;
97  int cur_size;
98 
99  while (size_left > 0) {
100  cur_size = FFMIN(size_left, sizeof(chunk));
101  av_aes_ctr_crypt(ctx->aes_ctr, chunk, cur_pos, cur_size);
102  avio_write(pb, chunk, cur_size);
103  cur_pos += cur_size;
104  size_left -= cur_size;
105  }
106 }
107 
108 /**
109  * Start writing a packet
110  */
112 {
113  int ret;
114 
115  /* write the iv */
117  if (ret) {
118  return ret;
119  }
120 
121  if (!ctx->use_subsamples) {
122  return 0;
123  }
124 
125  /* write a zero subsample count */
126  ctx->auxiliary_info_subsample_start = ctx->auxiliary_info_size;
127  ctx->subsample_count = 0;
128  ret = auxiliary_info_write(ctx, (uint8_t*)&ctx->subsample_count, sizeof(ctx->subsample_count));
129  if (ret) {
130  return ret;
131  }
132 
133  return 0;
134 }
135 
136 /**
137  * Finalize a packet
138  */
140 {
141  size_t new_alloc_size;
142 
143  av_aes_ctr_increment_iv(ctx->aes_ctr);
144 
145  if (!ctx->use_subsamples) {
146  ctx->auxiliary_info_entries++;
147  return 0;
148  }
149 
150  /* add the auxiliary info entry size*/
151  if (ctx->auxiliary_info_entries >= ctx->auxiliary_info_sizes_alloc_size) {
152  new_alloc_size = ctx->auxiliary_info_entries * 2 + 1;
153  if (av_reallocp(&ctx->auxiliary_info_sizes, new_alloc_size)) {
154  return AVERROR(ENOMEM);
155  }
156 
157  ctx->auxiliary_info_sizes_alloc_size = new_alloc_size;
158  }
159  ctx->auxiliary_info_sizes[ctx->auxiliary_info_entries] =
160  AES_CTR_IV_SIZE + ctx->auxiliary_info_size - ctx->auxiliary_info_subsample_start;
161  ctx->auxiliary_info_entries++;
162 
163  /* update the subsample count*/
164  AV_WB16(ctx->auxiliary_info + ctx->auxiliary_info_subsample_start, ctx->subsample_count);
165 
166  return 0;
167 }
168 
170  const uint8_t *buf_in, int size)
171 {
172  int ret;
173 
175  if (ret) {
176  return ret;
177  }
178 
180  if (ret) {
181  return ret;
182  }
183 
184  mov_cenc_write_encrypted(ctx, pb, buf_in, size);
185 
187  if (ret) {
188  return ret;
189  }
190 
191  return 0;
192 }
193 
195  const uint8_t *buf_in, int size)
196 {
197  const uint8_t *p = buf_in;
198  const uint8_t *end = p + size;
199  const uint8_t *nal_start, *nal_end;
200  int ret;
201 
203  if (ret) {
204  return ret;
205  }
206 
207  size = 0;
208  nal_start = ff_nal_find_startcode(p, end);
209  for (;;) {
210  while (nal_start < end && !*(nal_start++));
211  if (nal_start == end)
212  break;
213 
214  nal_end = ff_nal_find_startcode(nal_start, end);
215 
216  avio_wb32(pb, nal_end - nal_start);
217  avio_w8(pb, *nal_start);
218  mov_cenc_write_encrypted(ctx, pb, nal_start + 1, nal_end - nal_start - 1);
219 
220  auxiliary_info_add_subsample(ctx, 5, nal_end - nal_start - 1);
221 
222  size += 4 + nal_end - nal_start;
223  nal_start = nal_end;
224  }
225 
227  if (ret) {
228  return ret;
229  }
230 
231  return size;
232 }
233 
235  int nal_length_size, AVIOContext *pb, const uint8_t *buf_in, int size)
236 {
237  int nalsize;
238  int ret;
239  int j;
240 
242  if (ret) {
243  return ret;
244  }
245 
246  while (size > 0) {
247  /* parse the nal size */
248  if (size < nal_length_size + 1) {
249  av_log(s, AV_LOG_ERROR, "CENC-AVC: remaining size %d smaller than nal length+type %d\n",
250  size, nal_length_size + 1);
251  return -1;
252  }
253 
254  avio_write(pb, buf_in, nal_length_size + 1);
255 
256  nalsize = 0;
257  for (j = 0; j < nal_length_size; j++) {
258  nalsize = (nalsize << 8) | *buf_in++;
259  }
260  size -= nal_length_size;
261 
262  /* encrypt the nal body */
263  if (nalsize <= 0 || nalsize > size) {
264  av_log(s, AV_LOG_ERROR, "CENC-AVC: nal size %d remaining %d\n", nalsize, size);
265  return -1;
266  }
267 
268  mov_cenc_write_encrypted(ctx, pb, buf_in + 1, nalsize - 1);
269  buf_in += nalsize;
270  size -= nalsize;
271 
272  auxiliary_info_add_subsample(ctx, nal_length_size + 1, nalsize - 1);
273  }
274 
276  if (ret) {
277  return ret;
278  }
279 
280  return 0;
281 }
282 
283 /* TODO: reuse this function from movenc.c */
284 static int64_t update_size(AVIOContext *pb, int64_t pos)
285 {
286  int64_t curpos = avio_tell(pb);
287  avio_seek(pb, pos, SEEK_SET);
288  avio_wb32(pb, curpos - pos); /* rewrite size */
289  avio_seek(pb, curpos, SEEK_SET);
290 
291  return curpos - pos;
292 }
293 
295  int64_t* auxiliary_info_offset)
296 {
297  int64_t pos = avio_tell(pb);
298 
299  avio_wb32(pb, 0); /* size */
300  ffio_wfourcc(pb, "senc");
301  avio_wb32(pb, ctx->use_subsamples ? 0x02 : 0); /* version & flags */
302  avio_wb32(pb, ctx->auxiliary_info_entries); /* entry count */
303  *auxiliary_info_offset = avio_tell(pb);
304  avio_write(pb, ctx->auxiliary_info, ctx->auxiliary_info_size);
305  return update_size(pb, pos);
306 }
307 
308 static int mov_cenc_write_saio_tag(AVIOContext *pb, int64_t auxiliary_info_offset)
309 {
310  int64_t pos = avio_tell(pb);
311  uint8_t version;
312 
313  avio_wb32(pb, 0); /* size */
314  ffio_wfourcc(pb, "saio");
315  version = auxiliary_info_offset > 0xffffffff ? 1 : 0;
316  avio_w8(pb, version);
317  avio_wb24(pb, 0); /* flags */
318  avio_wb32(pb, 1); /* entry count */
319  if (version) {
320  avio_wb64(pb, auxiliary_info_offset);
321  } else {
322  avio_wb32(pb, auxiliary_info_offset);
323  }
324  return update_size(pb, pos);
325 }
326 
328 {
329  int64_t pos = avio_tell(pb);
330  avio_wb32(pb, 0); /* size */
331  ffio_wfourcc(pb, "saiz");
332  avio_wb32(pb, 0); /* version & flags */
333  avio_w8(pb, ctx->use_subsamples ? 0 : AES_CTR_IV_SIZE); /* default size*/
334  avio_wb32(pb, ctx->auxiliary_info_entries); /* entry count */
335  if (ctx->use_subsamples) {
336  avio_write(pb, ctx->auxiliary_info_sizes, ctx->auxiliary_info_entries);
337  }
338  return update_size(pb, pos);
339 }
340 
342 {
343  int64_t auxiliary_info_offset;
344 
345  mov_cenc_write_senc_tag(ctx, pb, &auxiliary_info_offset);
346  mov_cenc_write_saio_tag(pb, auxiliary_info_offset);
348 }
349 
350 static int mov_cenc_write_schi_tag(AVIOContext *pb, uint8_t* kid)
351 {
352  int64_t pos = avio_tell(pb);
353  avio_wb32(pb, 0); /* size */
354  ffio_wfourcc(pb, "schi");
355 
356  avio_wb32(pb, 32); /* size */
357  ffio_wfourcc(pb, "tenc");
358  avio_wb32(pb, 0); /* version & flags */
359  avio_wb24(pb, 1); /* is encrypted */
360  avio_w8(pb, AES_CTR_IV_SIZE); /* iv size */
361  avio_write(pb, kid, CENC_KID_SIZE);
362 
363  return update_size(pb, pos);
364 }
365 
366 int ff_mov_cenc_write_sinf_tag(MOVTrack* track, AVIOContext *pb, uint8_t* kid)
367 {
368  int64_t pos = avio_tell(pb);
369  avio_wb32(pb, 0); /* size */
370  ffio_wfourcc(pb, "sinf");
371 
372  /* frma */
373  avio_wb32(pb, 12); /* size */
374  ffio_wfourcc(pb, "frma");
375  avio_wl32(pb, track->tag);
376 
377  /* schm */
378  avio_wb32(pb, 20); /* size */
379  ffio_wfourcc(pb, "schm");
380  avio_wb32(pb, 0); /* version & flags */
381  ffio_wfourcc(pb, "cenc"); /* scheme type*/
382  avio_wb32(pb, 0x10000); /* scheme version */
383 
384  /* schi */
385  mov_cenc_write_schi_tag(pb, kid);
386 
387  return update_size(pb, pos);
388 }
389 
390 int ff_mov_cenc_init(MOVMuxCencContext* ctx, uint8_t* encryption_key,
391  int use_subsamples, int bitexact)
392 {
393  int ret;
394 
395  ctx->aes_ctr = av_aes_ctr_alloc();
396  if (!ctx->aes_ctr) {
397  return AVERROR(ENOMEM);
398  }
399 
400  ret = av_aes_ctr_init(ctx->aes_ctr, encryption_key);
401  if (ret != 0) {
402  return ret;
403  }
404 
405  if (!bitexact) {
406  av_aes_ctr_set_random_iv(ctx->aes_ctr);
407  }
408 
409  ctx->use_subsamples = use_subsamples;
410 
411  return 0;
412 }
413 
415 {
416  av_aes_ctr_free(ctx->aes_ctr);
417  av_freep(&ctx->auxiliary_info);
418  av_freep(&ctx->auxiliary_info_sizes);
419 }
mov_cenc_write_senc_tag
static int mov_cenc_write_senc_tag(MOVMuxCencContext *ctx, AVIOContext *pb, int64_t *auxiliary_info_offset)
Definition: movenccenc.c:294
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
ffio_wfourcc
static av_always_inline void ffio_wfourcc(AVIOContext *pb, const uint8_t *s)
Definition: avio_internal.h:124
mov_cenc_write_saio_tag
static int mov_cenc_write_saio_tag(AVIOContext *pb, int64_t auxiliary_info_offset)
Definition: movenccenc.c:308
auxiliary_info_alloc_size
static int auxiliary_info_alloc_size(MOVMuxCencContext *ctx, int size)
Definition: movenccenc.c:29
mov_cenc_write_encrypted
static void mov_cenc_write_encrypted(MOVMuxCencContext *ctx, AVIOContext *pb, const uint8_t *buf_in, int size)
Encrypt the input buffer and write using avio_write.
Definition: movenccenc.c:91
MOVTrack::tag
int tag
stsd fourcc
Definition: movenc.h:108
mov_cenc_end_packet
static int mov_cenc_end_packet(MOVMuxCencContext *ctx)
Finalize a packet.
Definition: movenccenc.c:139
av_aes_ctr_set_random_iv
void av_aes_ctr_set_random_iv(struct AVAESCTR *a)
Generate a random iv.
Definition: aes_ctr.c:63
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
mov_cenc_start_packet
static int mov_cenc_start_packet(MOVMuxCencContext *ctx)
Start writing a packet.
Definition: movenccenc.c:111
MOVTrack
Definition: movenc.h:86
avio_tell
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition: avio.h:494
CENC_KID_SIZE
#define CENC_KID_SIZE
Definition: movenccenc.h:29
ff_mov_cenc_free
void ff_mov_cenc_free(MOVMuxCencContext *ctx)
Free a CENC context.
Definition: movenccenc.c:414
mov_cenc_write_schi_tag
static int mov_cenc_write_schi_tag(AVIOContext *pb, uint8_t *kid)
Definition: movenccenc.c:350
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
movenccenc.h
intreadwrite.h
s
#define s(width, name)
Definition: cbs_vp9.c:198
update_size
static int64_t update_size(AVIOContext *pb, int64_t pos)
Definition: movenccenc.c:284
ctx
AVFormatContext * ctx
Definition: movenc.c:49
av_aes_ctr_get_iv
const uint8_t * av_aes_ctr_get_iv(struct AVAESCTR *a)
Get the current iv.
Definition: aes_ctr.c:58
AVFormatContext
Format I/O context.
Definition: avformat.h:1255
AV_WB16
#define AV_WB16(p, v)
Definition: intreadwrite.h:399
av_aes_ctr_alloc
struct AVAESCTR * av_aes_ctr_alloc(void)
Allocate an AVAESCTR context.
Definition: aes_ctr.c:40
ff_mov_cenc_avc_write_nal_units
int ff_mov_cenc_avc_write_nal_units(AVFormatContext *s, MOVMuxCencContext *ctx, int nal_length_size, AVIOContext *pb, const uint8_t *buf_in, int size)
Write AVC NAL units that are in MP4 format, the nal size and type are written in the clear while the ...
Definition: movenccenc.c:234
avc.h
avio_w8
void avio_w8(AVIOContext *s, int b)
Definition: aviobuf.c:179
ff_mov_cenc_init
int ff_mov_cenc_init(MOVMuxCencContext *ctx, uint8_t *encryption_key, int use_subsamples, int bitexact)
Initialize a CENC context.
Definition: movenccenc.c:390
movenc.h
AV_WB32
#define AV_WB32(p, v)
Definition: intreadwrite.h:413
AVIOContext
Bytestream IO Context.
Definition: avio.h:160
av_aes_ctr_init
int av_aes_ctr_init(struct AVAESCTR *a, const uint8_t *key)
Initialize an AVAESCTR context.
Definition: aes_ctr.c:73
mov_cenc_write_saiz_tag
static int mov_cenc_write_saiz_tag(MOVMuxCencContext *ctx, AVIOContext *pb)
Definition: movenccenc.c:327
ff_mov_cenc_write_packet
int ff_mov_cenc_write_packet(MOVMuxCencContext *ctx, AVIOContext *pb, const uint8_t *buf_in, int size)
Write a fully encrypted packet.
Definition: movenccenc.c:169
size
int size
Definition: twinvq_data.h:10344
av_reallocp
int av_reallocp(void *ptr, size_t size)
Allocate, reallocate, or free a block of memory through a pointer to a pointer.
Definition: mem.c:188
avio_write
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition: aviobuf.c:201
avio_wb32
void avio_wb32(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:365
avio_wl32
void avio_wl32(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:357
ff_mov_cenc_write_stbl_atoms
void ff_mov_cenc_write_stbl_atoms(MOVMuxCencContext *ctx, AVIOContext *pb)
Write the cenc atoms that should reside inside stbl.
Definition: movenccenc.c:341
version
version
Definition: libkvazaar.c:321
av_aes_ctr_free
void av_aes_ctr_free(struct AVAESCTR *a)
Release an AVAESCTR context.
Definition: aes_ctr.c:83
auxiliary_info_write
static int auxiliary_info_write(MOVMuxCencContext *ctx, const uint8_t *buf_in, int size)
Definition: movenccenc.c:45
avio_internal.h
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
nal.h
ret
ret
Definition: filter_design.txt:187
avio_seek
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:231
ff_mov_cenc_write_sinf_tag
int ff_mov_cenc_write_sinf_tag(MOVTrack *track, AVIOContext *pb, uint8_t *kid)
Write the sinf atom, contained inside stsd.
Definition: movenccenc.c:366
auxiliary_info_add_subsample
static int auxiliary_info_add_subsample(MOVMuxCencContext *ctx, uint16_t clear_bytes, uint32_t encrypted_bytes)
Definition: movenccenc.c:60
pos
unsigned int pos
Definition: spdifenc.c:414
av_aes_ctr_increment_iv
void av_aes_ctr_increment_iv(struct AVAESCTR *a)
Increment the top 64 bit of the iv (performed after each frame)
Definition: aes_ctr.c:100
av_aes_ctr_crypt
void av_aes_ctr_crypt(struct AVAESCTR *a, uint8_t *dst, const uint8_t *src, int count)
Process a buffer using a previously initialized context.
Definition: aes_ctr.c:107
avio_wb64
void avio_wb64(AVIOContext *s, uint64_t val)
Definition: aviobuf.c:431
mem.h
MOVMuxCencContext
Definition: movenccenc.h:33
avio_wb24
void avio_wb24(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:455
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
AES_CTR_IV_SIZE
#define AES_CTR_IV_SIZE
Definition: aes_ctr.h:36
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
ff_mov_cenc_avc_parse_nal_units
int ff_mov_cenc_avc_parse_nal_units(MOVMuxCencContext *ctx, AVIOContext *pb, const uint8_t *buf_in, int size)
Parse AVC NAL units from annex B format, the nal size and type are written in the clear while the bod...
Definition: movenccenc.c:194
ff_nal_find_startcode
const uint8_t * ff_nal_find_startcode(const uint8_t *p, const uint8_t *end)
Definition: nal.c:68