demux: hls: add adaptative based hls demuxer
[vlc.git] / modules / demux / hls / playlist / HLSSegment.cpp
1 /*
2  * HLSSegment.cpp
3  *****************************************************************************
4  * Copyright (C) 2015 VideoLAN authors
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include "HLSSegment.hpp"
25
26 #include <vlc_common.h>
27 #include <vlc_block.h>
28 #ifdef HAVE_GCRYPT
29  #include <vlc_gcrypt.h>
30 #endif
31
32 using namespace hls::playlist;
33
34 SegmentEncryption::SegmentEncryption()
35 {
36     method = SegmentEncryption::NONE;
37 }
38
39 HLSSegment::HLSSegment( ICanonicalUrl *parent, uint64_t seq ) :
40     Segment( parent )
41 {
42     sequence = seq;
43 #ifdef HAVE_GCRYPT
44     ctx = NULL;
45 #endif
46 }
47
48 HLSSegment::~HLSSegment()
49 {
50 #ifdef HAVE_GCRYPT
51     if(ctx)
52         gcry_cipher_close(ctx);
53 #endif
54 }
55
56 void HLSSegment::onChunkDownload(block_t **pp_block, Chunk *chunk, BaseRepresentation *)
57 {
58     block_t *p_block = *pp_block;
59 #ifdef HAVE_GCRYPT
60     if(encryption.method == SegmentEncryption::AES_128)
61     {
62         block_t *p_block = *pp_block;
63         /* first bytes */
64         if(!ctx && chunk->getBytesRead() == p_block->i_buffer)
65         {
66             vlc_gcrypt_init();
67             if (encryption.iv.size() != 16)
68             {
69                 encryption.iv.clear();
70                 encryption.iv.resize(16);
71                 encryption.iv[15] = sequence & 0xff;
72                 encryption.iv[14] = (sequence >> 8)& 0xff;
73                 encryption.iv[13] = (sequence >> 16)& 0xff;
74                 encryption.iv[12] = (sequence >> 24)& 0xff;
75             }
76
77             if( gcry_cipher_open(&ctx, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0) ||
78                 gcry_cipher_setkey(ctx, &encryption.key[0], 16) ||
79                 gcry_cipher_setiv(ctx, &encryption.iv[0], 16) )
80             {
81                 gcry_cipher_close(ctx);
82                 ctx = NULL;
83             }
84         }
85
86         if(ctx)
87         {
88             if ((p_block->i_buffer % 16) != 0 || p_block->i_buffer < 16 ||
89                 gcry_cipher_decrypt(ctx, p_block->p_buffer, p_block->i_buffer, NULL, 0))
90             {
91                 p_block->i_buffer = 0;
92                 gcry_cipher_close(ctx);
93                 ctx = NULL;
94             }
95             else
96             {
97                 /* last bytes */
98                 if(chunk->getBytesToRead() == 0)
99                 {
100                     /* remove the PKCS#7 padding from the buffer */
101                     const uint8_t pad = p_block->p_buffer[p_block->i_buffer - 1];
102                     for(uint8_t i=0; i<pad && i<=16; i++)
103                     {
104                         if(p_block->p_buffer[p_block->i_buffer - i - 1] != pad)
105                             break;
106
107                         if(i==pad)
108                             p_block->i_buffer -= pad;
109                     }
110
111                     gcry_cipher_close(ctx);
112                     ctx = NULL;
113                 }
114             }
115         }
116     }
117     else
118 #endif
119     if(encryption.method != SegmentEncryption::NONE)
120     {
121         p_block->i_buffer = 0;
122     }
123 }
124
125 void HLSSegment::setEncryption(SegmentEncryption &enc)
126 {
127     encryption = enc;
128 }
129
130 void HLSSegment::debug(vlc_object_t *obj, int indent) const
131 {
132     std::stringstream ss;
133     ss << std::string(indent, ' ') << debugName <<
134     " #" << sequence <<
135     " url=" << getUrlSegment().toString();
136     if(startByte!=endByte)
137         ss << " @" << startByte << ".." << endByte;
138     msg_Dbg(obj, "%s", ss.str().c_str());
139 }
140
141 int HLSSegment::compare(ISegment *segment) const
142 {
143     HLSSegment *hlssegment = dynamic_cast<HLSSegment *>(segment);
144     if(hlssegment)
145     {
146         if (sequence > hlssegment->sequence)
147             return 1;
148         else if(sequence < hlssegment->sequence)
149             return -1;
150         else
151             return 0;
152     }
153     else return ISegment::compare(segment);
154 }