[libdvdcss-devel] [Git][videolan/libdvdcss][master] 20 commits: CPRM: stop after a failed media key pack read
Jean-Baptiste Kempf (@jbk)
gitlab at videolan.org
Sat Jul 4 07:50:40 UTC 2026
Jean-Baptiste Kempf pushed to branch master at VideoLAN / libdvdcss
Commits:
9209e896 by Saifelden Mohamed Ismail at 2026-06-30T15:22:35+03:00
CPRM: stop after a failed media key pack read
- - - - -
f8ca98df by Saifelden Mohamed Ismail at 2026-06-30T15:22:35+03:00
CPPM: fix copy-control byte define
- - - - -
4c4ba8f4 by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPPM: limit copy-flag clearing to the current block
- - - - -
8049671e by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPRM: use the pack count from the first media key read
- - - - -
0a92d75f by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPPM: free the media key when album lookup fails
- - - - -
403b51e9 by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPPM: check stat before using the decryption cache
- - - - -
d8c1329d by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPRM: initialize media type when opening a disc
- - - - -
f0a2919b by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPRM: stop after a failed media id read
- - - - -
450a2930 by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPRM: fix Darwin CPRM ioctl paths
- - - - -
dfac5f39 by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPPM: fix decryption cache teardown on close
- - - - -
622cdc2d by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPPM: return an error when decrypting without a context
- - - - -
8b446274 by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPRM: fix Darwin MKB pack ioctl path
- - - - -
4908732b by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPXM: stop reading past the end of the MKB buffer
- - - - -
dc468e84 by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPXM: refcount the cache so we don't double free it
- - - - -
63aa58e7 by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPRM: don't derive a title key without a media key
- - - - -
527d1dcd by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPRM: fix the swapped title key decrypt args
- - - - -
8f91024b by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPXM: bail out when we can't get the keys
- - - - -
19e66a6a by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPXM: set media_type once the copyright read is done
- - - - -
b5fd3e25 by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPRM: sanity check the pack count from the drive
- - - - -
2f254cf9 by Saifelden Mohamed Ismail at 2026-07-04T02:09:08+03:00
CPXM: fix my email in the header
- - - - -
5 changed files:
- src/css.c
- src/ioctl.c
- src/libdvdcpxm.c
- src/libdvdcpxm.h
- src/libdvdcss.c
Changes:
=====================================
src/css.c
=====================================
@@ -100,13 +100,10 @@ int dvdcss_test( dvdcss_t dvdcss )
const char *psz_type, *psz_rpc;
char psz_region[17];
char *p_region = psz_region;
- int i_ret, i_copyright, i_type, i_mask, i_rpc, i_region;
+ int i_ret, i_copyright = 0, i_type, i_mask, i_rpc, i_region;
i_ret = ioctl_ReadCopyright( dvdcss->i_fd, 0 /* i_layer */, &i_copyright );
- /* value may be used in cpxm decryption */
- dvdcss->media_type = i_copyright;
-
if( i_ret < 0 )
{
#ifdef _WIN32
@@ -132,6 +129,9 @@ int dvdcss_test( dvdcss_t dvdcss )
#endif /* _WIN32 */
}
+ /* value may be used in cpxm decryption */
+ dvdcss->media_type = i_copyright;
+
print_debug( dvdcss, "disc reports copyright information 0x%x",
i_copyright );
=====================================
src/ioctl.c
=====================================
@@ -1013,7 +1013,7 @@ int ioctl_ReadCPRMMediaId(int i_fd,int *p_agid, uint8_t *p_data_buffer)
uint8_t *data_buf = malloc(CPRM_MEDIA_ID_SIZE + 4);
if (!data_buf)
- i_ret = -1;
+ return -1;
memset(&io_hdr, 0, sizeof(io_hdr));
memset(data_buf, 0, CPRM_MEDIA_ID_SIZE + 4);
@@ -1037,12 +1037,12 @@ int ioctl_ReadCPRMMediaId(int i_fd,int *p_agid, uint8_t *p_data_buffer)
i_ret = ioctl(i_fd, SG_IO, &io_hdr);
if (i_ret < 0 || io_hdr.status) {
free(data_buf);
- i_ret = -1;
+ return -1;
}
memcpy(p_data_buffer, data_buf + 4, CPRM_MEDIA_ID_SIZE);
free(data_buf);
- i_ret = 0;
+ return 0;
#elif defined( _WIN32 )
DWORD tmp;
@@ -1067,18 +1067,16 @@ int ioctl_ReadCPRMMediaId(int i_fd,int *p_agid, uint8_t *p_data_buffer)
CPRM_MEDIA_ID_SIZE);
#elif defined( DARWIN_DVD_IOCTL )
- int h_dvd;
- dk_dvd_read_structure_t dvd;
- uint8_t dvdbs[CPRM_MEDIA_ID_SIZE + 4];
+ dk_dvd_read_structure_t dvd = { 0 };
+ uint8_t dvdbs[CPRM_MEDIA_ID_SIZE + 4] = { 0 };
dvd.format = CPRM_STRUCT_MEDIA_ID;
dvd.buffer = &dvdbs;
- dvd.bufferLength = sizeof(dvdbs);
-
+ dvd.bufferLength = sizeof( dvdbs );
dvd.grantID = *p_agid;
- i_ret = ioctl( h_dvd, DKIOCDVDREADSTRUCTURE, &dvd );
- if (i_ret == 0)
- memcpy(p_data_buffer, dvd.buffer, sizeof(dvd.bufferLength));
+ i_ret = ioctl( i_fd, DKIOCDVDREADSTRUCTURE, &dvd );
+ if ( i_ret == 0 )
+ memcpy( p_data_buffer, dvdbs + 4, CPRM_MEDIA_ID_SIZE );
#elif defined( __OS2__ )
INIT_SSC(GPCMD_READ_DVD_STRUCTURE, CPRM_MEDIA_ID_SIZE + 4);
@@ -1113,7 +1111,7 @@ int ioctl_ReadCPRMMKBPack(int i_fd, int *p_agid, int mkb_pack, uint8_t *p_mkb_pa
uint8_t sense[32] = { 0 };
if ( !sptd_buf )
- i_ret = -1;
+ return -1;
memset( sptd_buf, 0, CPRM_MKB_PACK_SIZE + 4 );
@@ -1144,7 +1142,7 @@ int ioctl_ReadCPRMMKBPack(int i_fd, int *p_agid, int mkb_pack, uint8_t *p_mkb_pa
if ( i_ret < 0 || io_hdr.status )
{
free( sptd_buf );
- i_ret = -1;
+ return -1;
}
*p_total_packs = sptd_buf[3];
@@ -1152,18 +1150,20 @@ int ioctl_ReadCPRMMKBPack(int i_fd, int *p_agid, int mkb_pack, uint8_t *p_mkb_pa
free( sptd_buf );
#elif defined( DARWIN_DVD_IOCTL )
- int h_dvd;
- dk_dvd_read_structure_t dvd;
- uint8_t dvdbs[CPRM_MKB_PACK_SIZE];
+ dk_dvd_read_structure_t dvd = { 0 };
+ uint8_t dvdbs[CPRM_MKB_PACK_SIZE + 4] = { 0 };
dvd.format = CPRM_STRUCT_MKB;
dvd.buffer = &dvdbs;
dvd.bufferLength = sizeof( dvdbs );
-
+ dvd.address = mkb_pack;
dvd.grantID = *p_agid;
- i_ret = ioctl( h_dvd, DKIOCDVDREADSTRUCTURE, &dvd );
+ i_ret = ioctl( i_fd, DKIOCDVDREADSTRUCTURE, &dvd );
if ( i_ret == 0 )
- memcpy( p_mkb_pack, dvd.buffer, sizeof( dvd.bufferLength ) );
+ {
+ *p_total_packs = dvdbs[3];
+ memcpy( p_mkb_pack, dvdbs + 4, CPRM_MKB_PACK_SIZE );
+ }
#elif defined( _WIN32 )
DWORD tmp;
=====================================
src/libdvdcpxm.c
=====================================
@@ -50,14 +50,33 @@
#define IS_SYNC_CODE(word) \
( (word)[0] == 0x00 && (word)[1] == 0x00 && (word)[2] == 0x01 && (word)[3] == 0xBA )
+#define PACK_HEADER_SIZE 14
+#define PES_HEADER_SIZE 6
+#define PES_PAYLOAD_OFFSET 9
+#define AUDIO_SUBHEADER_SIZE 13
+
typedef struct cpxm_cache {
p_cpxm cpxm;
dev_t st_dev;
+ int refcount;
struct cpxm_cache * p_next;
} cpxm_cache;
cpxm_cache *g_cpxm_cache = NULL;
+static int cpxm_is_cached( p_cpxm cpxm )
+{
+ cpxm_cache *it = g_cpxm_cache;
+
+ while ( it != NULL )
+ {
+ if ( it->cpxm == cpxm )
+ return 1;
+ it = it->p_next;
+ }
+ return 0;
+}
+
/* these values are used by libdvdcpxm to process the Media Key Block */
/* They are present inside DVD-Audio players and are used in conjunction with keys taken from the disc in order to generate the final media_key used by the C2 Cypher */
uint32_t sbox_f[256];
@@ -361,25 +380,30 @@ void c2_dcbc( void *p_buffer, uint64_t key, int length )
}
/* for CPPM, libdvdread is responsible for retrieving the Media Key Block */
-uint8_t *cprm_get_mkb( dvdcss_t dvdcss )
+uint8_t *cprm_get_mkb( dvdcss_t dvdcss, size_t *p_mkb_len )
{
uint8_t mkb_pack[CPRM_MKB_PACK_SIZE];
uint8_t *p_mkb = NULL;
- int mkb_packs, i;
+ int mkb_packs, total_packs, i;
mkb_packs = 16;
if ( ioctl_ReadCPRMMKBPack( dvdcss->i_fd, &dvdcss->css.i_agid, 0,
(uint8_t *) mkb_pack, &mkb_packs ) )
return NULL;
- p_mkb = malloc( mkb_packs * CPRM_MKB_PACK_SIZE - 16 );
+ total_packs = mkb_packs;
+ if ( total_packs < 1 || total_packs > 16 )
+ return NULL;
+
+ *p_mkb_len = (size_t) total_packs * CPRM_MKB_PACK_SIZE - 16;
+ p_mkb = malloc( *p_mkb_len );
if (!p_mkb)
return NULL;
memcpy( p_mkb, &mkb_pack[16], CPRM_MKB_PACK_SIZE - 16 );
- for ( i = 1; i < mkb_packs; i++ )
+ for ( i = 1; i < total_packs; i++ )
{
if ( ioctl_ReadCPRMMKBPack( dvdcss->i_fd,&dvdcss->css.i_agid, i,
(uint8_t *) p_mkb + i * CPRM_MKB_PACK_SIZE - 16, &mkb_packs ) )
@@ -398,7 +422,7 @@ uint8_t *cprm_get_mkb( dvdcss_t dvdcss )
/* This function retrieves the main key used to decryption; this key is derived
* from applying the C2 cypher to the MKB and the DVD-Audio player device keys,
* as well as a unique album_id and media_id */
-int process_mkb( uint8_t *p_mkb, device_key_t *p_dev_keys, int nr_dev_keys, uint64_t *p_media_key )
+int process_mkb( uint8_t *p_mkb, size_t mkb_len, device_key_t *p_dev_keys, int nr_dev_keys, uint64_t *p_media_key )
{
int mkb_pos, length, i, i_dev_key, no_more_keys, no_more_records;
uint8_t record_type, column;
@@ -408,13 +432,16 @@ int process_mkb( uint8_t *p_mkb, device_key_t *p_dev_keys, int nr_dev_keys, uint
i_dev_key = no_more_keys = 0;
buffer = media_key = verification_data = 0;
- while ( !no_more_keys )
+ while ( !no_more_keys && i_dev_key < nr_dev_keys )
{
/* skip the file identifier and the length */
mkb_pos = 16;
no_more_records = 0;
while (!no_more_records)
{
+ if ( mkb_pos < 0 || (size_t) mkb_pos + 4 > mkb_len )
+ break;
+
record_type = *(uint8_t *) &p_mkb[mkb_pos];
memcpy( &length, &p_mkb[mkb_pos], sizeof( length ) );
length &= 0xffffff00;
@@ -422,6 +449,8 @@ int process_mkb( uint8_t *p_mkb, device_key_t *p_dev_keys, int nr_dev_keys, uint
if (length >= 12)
{
+ if ( (size_t) mkb_pos + 12 > mkb_len )
+ break;
memcpy( &buffer, &p_mkb[mkb_pos + 4], sizeof( buffer ) );
}
else
@@ -462,6 +491,8 @@ int process_mkb( uint8_t *p_mkb, device_key_t *p_dev_keys, int nr_dev_keys, uint
break;
if ( 12 + p_dev_keys[i_dev_key].row * 8 + 8 > length )
break;
+ if ( (size_t) mkb_pos + 12 + p_dev_keys[i_dev_key].row * 8 + 8 > mkb_len )
+ break;
memcpy( &buffer, &p_mkb[mkb_pos + 12 + p_dev_keys[i_dev_key].row * 8], sizeof( buffer ) );
B2N_64( buffer );
@@ -506,14 +537,16 @@ LIBDVDCSS_EXPORT int dvdcpxm_init( dvdcss_t dvdcss, uint8_t *p_input )
{
cpxm_cache *cpxm_iterator = g_cpxm_cache;
struct stat file_stat;
- fstat( dvdcss->i_fd, &file_stat );
+ if ( fstat( dvdcss->i_fd, &file_stat ) != 0 )
+ return -1;
while ( cpxm_iterator != NULL )
{
/* look for match in cache */
if ( file_stat.st_dev == cpxm_iterator->st_dev )
{
dvdcss->cpxm = cpxm_iterator->cpxm;
- dvdcss->cpxm_was_cached = 0;
+ cpxm_iterator->refcount++;
+ dvdcss->cpxm_was_cached = 1;
return dvdcss->media_type;
}
cpxm_iterator = cpxm_iterator->p_next;
@@ -541,46 +574,49 @@ LIBDVDCSS_EXPORT int dvdcpxm_init( dvdcss_t dvdcss, uint8_t *p_input )
/* the input is the media key block */
p_mkb = p_input;
if ( cppm_set_id_album( dvdcss ) == 0 )
- {
- if ( p_mkb )
- {
- ret = process_mkb( p_mkb, cppm_device_keys,
- sizeof(cppm_device_keys) / sizeof(*cppm_device_keys),
- &dvdcss->cpxm->media_key );
- free(p_mkb);
- if (ret) break;
- }
- }
+ ret = process_mkb( p_mkb, CPRM_MKB_SIZE, cppm_device_keys,
+ sizeof(cppm_device_keys) / sizeof(*cppm_device_keys),
+ &dvdcss->cpxm->media_key );
+ free( p_mkb );
+ if ( ret ) break;
break;
case COPYRIGHT_PROTECTION_CPRM:
if ( cprm_set_id_media( dvdcss ) == 0 )
{
- p_mkb = cprm_get_mkb( dvdcss );
+ size_t mkb_len = 0;
+ p_mkb = cprm_get_mkb( dvdcss, &mkb_len );
if ( p_mkb )
{
- ret = process_mkb( p_mkb, cprm_device_keys,
+ ret = process_mkb( p_mkb, mkb_len, cprm_device_keys,
sizeof( cprm_device_keys ) / sizeof( *cprm_device_keys ),
&dvdcss->cpxm->media_key );
free( p_mkb );
- if (ret) break;
}
+ if ( ret == 0 )
+ {
+ /* get the media unique key */
+ uint64_t k_mu = c2_g( cpxm->media_key, cpxm->id_media ) & 0x00ffffffffffffff;
- /* get the media unique key */
- uint64_t k_mu = c2_g( cpxm->media_key, cpxm->id_media ) & 0x00ffffffffffffff;
-
- /* decrypt the encrypted title key */
- uint64_t k_te;
- READ64_BE( k_te , p_input );
- uint64_t k_t = c2_dec( k_mu, k_te ) & 0x00ffffffffffffff;
-
- /* store decrypted title key for vr decryption */
- cpxm->vr_k_t = k_t;
+ /* decrypt the encrypted title key */
+ uint64_t k_te;
+ READ64_BE( k_te , p_input );
+ uint64_t k_t = c2_dec( k_te, k_mu ) & 0x00ffffffffffffff;
+ /* store decrypted title key for vr decryption */
+ cpxm->vr_k_t = k_t;
+ }
}
break;
}
+ if ( ret != 0 )
+ {
+ free( dvdcss->cpxm );
+ dvdcss->cpxm = NULL;
+ return dvdcss->media_type;
+ }
+
/* store in cache */
cpxm_cache* cpxm_cache_addition = malloc(sizeof(cpxm_cache));
@@ -588,11 +624,16 @@ LIBDVDCSS_EXPORT int dvdcpxm_init( dvdcss_t dvdcss, uint8_t *p_input )
return -1;
struct stat stat;
- fstat(dvdcss->i_fd, &stat);
+ if ( fstat(dvdcss->i_fd, &stat) != 0 )
+ {
+ free( cpxm_cache_addition );
+ return dvdcss->media_type;
+ }
/* create cache node */
cpxm_cache_addition->cpxm = dvdcss->cpxm;
cpxm_cache_addition->st_dev = stat.st_dev;
+ cpxm_cache_addition->refcount = 1;
cpxm_cache_addition->p_next = NULL;
/* copy over so needs to be set only once */
@@ -633,15 +674,16 @@ void mpeg2_reset_pes_scrambling_control( uint8_t *p_block )
void mpeg2_reset_cci( uint8_t *p_block )
{
uint8_t *p_mlp_pcm, *p_curr;
+ uint8_t *p_end = p_block + DVDCPXM_BLOCK_SIZE;
int pes_sid;
int pes_len;
p_curr = p_block;
if ( IS_SYNC_CODE(p_block) )
{
- p_curr += 14 + (p_curr[13] & 0x07);
+ p_curr += PACK_HEADER_SIZE + (p_curr[13] & 0x07);
- while ( p_curr < p_block + DVDCPXM_BLOCK_SIZE )
+ while ( p_curr + PES_HEADER_SIZE <= p_end )
{
pes_len = (p_curr[4] << 8) + p_curr[5];
@@ -650,9 +692,10 @@ void mpeg2_reset_cci( uint8_t *p_block )
p_curr[2] == 0x01)
{
pes_sid = p_curr[3];
- if ( pes_sid == 0xbd ) // private stream 1
+ if ( pes_sid == 0xbd && p_curr + PES_PAYLOAD_OFFSET <= p_end ) // private stream 1
{
- p_mlp_pcm = p_curr + 9 + p_curr[8];
+ p_mlp_pcm = p_curr + PES_PAYLOAD_OFFSET + p_curr[8];
+ if ( p_mlp_pcm + AUDIO_SUBHEADER_SIZE <= p_end )
switch ( p_mlp_pcm[0] ) // stream id
{
case 0xa0: // PCM stream id
@@ -663,7 +706,7 @@ void mpeg2_reset_cci( uint8_t *p_block )
break;
}
}
- p_curr += 6 + pes_len;
+ p_curr += PES_HEADER_SIZE + pes_len;
}
else break;
}
@@ -812,40 +855,40 @@ int dvdcpxm_decrypt( p_cpxm cpxm, int media_type,void *p_buffer, int flags )
/* this function is used internally */
int dvdcpxm_close_internal ( dvdcss_t dvdcss )
{
- if( dvdcss->cpxm )
- free( dvdcss->cpxm );
-
- if ( !g_cpxm_cache )
- return 0;
+ p_cpxm cpxm = dvdcss->cpxm;
- /* check ID */
- struct stat stat;
- if ( fstat( dvdcss->i_fd, &stat ) < 0 )
- return -1;
-
- cpxm_cache *prev = NULL;
- cpxm_cache *cpxm_iterator = g_cpxm_cache;
-
- /* remove only if cached */
- while ( cpxm_iterator != NULL && dvdcss->cpxm_was_cached )
+ if ( cpxm != NULL && dvdcss->cpxm_was_cached )
{
- cpxm_cache *next = cpxm_iterator->p_next;
- if ( cpxm_iterator->st_dev == stat.st_dev )
+ cpxm_cache *prev = NULL;
+ cpxm_cache *it = g_cpxm_cache;
+
+ while ( it != NULL )
{
- if ( prev == NULL )
- g_cpxm_cache = next;
- else
- prev->p_next = next;
+ cpxm_cache *next = it->p_next;
+ if ( it->cpxm == cpxm )
+ {
+ if ( --it->refcount > 0 )
+ break;
- free( cpxm_iterator->cpxm );
- free( cpxm_iterator );
- break;
- }
- else
- prev = cpxm_iterator;
+ if ( prev == NULL )
+ g_cpxm_cache = next;
+ else
+ prev->p_next = next;
- cpxm_iterator = next;
+ free( it->cpxm );
+ free( it );
+ break;
+ }
+ prev = it;
+ it = next;
+ }
}
+ else if ( cpxm != NULL && !cpxm_is_cached( cpxm ) )
+ {
+ free( cpxm );
+ }
+
+ dvdcss->cpxm = NULL;
return 0;
}
@@ -872,6 +915,9 @@ int dvdcpxm_read ( dvdcss_t dvdcss, void *p_buffer,
return i_ret;
}
+ if ( !dvdcss->cpxm )
+ return -1;
+
/* Decrypt the blocks we managed to read */
for( i_index = i_ret; i_index; i_index-- )
{
@@ -905,6 +951,9 @@ int dvdcpxm_readv ( dvdcss_t dvdcss, void *p_iovec,
return i_ret;
}
+ if ( !dvdcss->cpxm )
+ return -1;
+
/* Initialize loop for decryption */
iov_base = _p_iovec->iov_base;
iov_len = _p_iovec->iov_len;
=====================================
src/libdvdcpxm.h
=====================================
@@ -1,7 +1,7 @@
/**
* \file libdvdcpxm.h
* \author Maxim V.Anisiutkin <Maxim.Anisiutkin at gmail.com>
- * \author Saifelden Ismail <saifelden at gmail.com>
+ * \author Saifelden Ismail <saifeldenmi at gmail.com>
*
* \brief Integration of libdvdcpxm functionality into libdvdcss.
*
@@ -45,7 +45,7 @@
#define CPRM_MKB_PACK_SIZE 24576
#define CPRM_MKB_SIZE (16 * CPRM_MKB_PACK_SIZE - 16)
-#define CCI_BYTE 0x00;
+#define CCI_BYTE 0x00
#include <stdint.h>
#include "dvdcss/dvdcss.h"
=====================================
src/libdvdcss.c
=====================================
@@ -556,6 +556,7 @@ static dvdcss_t dvdcss_open_common ( const char *psz_target, void *p_stream,
dvdcss->cpxm = NULL;
dvdcss->cpxm_was_cached = 0;
+ dvdcss->media_type = COPYRIGHT_PROTECTION_NONE;
/* Set library verbosity from DVDCSS_VERBOSE environment variable. */
set_verbosity( dvdcss );
View it on GitLab: https://code.videolan.org/videolan/libdvdcss/-/compare/2682a4a7ed782e700a5b920f6f85c4f9736921c3...2f254cf91b120b78276438153e33b3414156b32a
--
View it on GitLab: https://code.videolan.org/videolan/libdvdcss/-/compare/2682a4a7ed782e700a5b920f6f85c4f9736921c3...2f254cf91b120b78276438153e33b3414156b32a
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help
More information about the libdvdcss-devel
mailing list