2007-09-05 03:22:37 +03:00
/*
2007-09-05 03:25:18 +03:00
* Matroska muxer
2007-09-05 03:22:37 +03:00
* Copyright ( c ) 2007 David Conrad
*
2011-03-18 19:35:10 +02:00
* This file is part of Libav .
2007-09-05 03:22:37 +03:00
*
2011-03-18 19:35:10 +02:00
* Libav is free software ; you can redistribute it and / or
2007-09-05 03:22:37 +03:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
2011-03-18 19:35:10 +02:00
* Libav is distributed in the hope that it will be useful ,
2007-09-05 03:22:37 +03:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
2011-03-18 19:35:10 +02:00
* License along with Libav ; if not , write to the Free Software
2007-09-05 03:22:37 +03:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
2013-11-23 23:32:55 +03:00
# include <stdint.h>
2013-04-30 08:52:50 +03:00
# include "avc.h"
2014-03-03 17:53:44 +03:00
# include "hevc.h"
2007-09-05 03:22:37 +03:00
# include "avformat.h"
2013-04-30 08:52:50 +03:00
# include "avlanguage.h"
# include "flacenc.h"
2011-11-29 21:28:15 +03:00
# include "internal.h"
2008-07-27 18:11:04 +03:00
# include "isom.h"
2007-09-05 03:22:37 +03:00
# include "matroska.h"
2013-04-30 08:52:50 +03:00
# include "riff.h"
2013-05-28 11:09:21 +03:00
# include "wv.h"
2013-04-30 08:52:50 +03:00
# include "libavutil/avstring.h"
# include "libavutil/dict.h"
2011-11-27 17:04:16 +03:00
# include "libavutil/intfloat.h"
2013-04-30 08:52:50 +03:00
# include "libavutil/intreadwrite.h"
# include "libavutil/lfg.h"
2011-06-04 14:58:23 +03:00
# include "libavutil/mathematics.h"
2013-04-30 09:36:20 +03:00
# include "libavutil/opt.h"
2010-06-05 01:41:03 +03:00
# include "libavutil/random_seed.h"
2013-04-30 08:52:50 +03:00
# include "libavutil/samplefmt.h"
2008-05-09 14:56:36 +03:00
# include "libavcodec/xiph.h"
2008-04-03 00:41:48 +03:00
# include "libavcodec/mpeg4audio.h"
2007-09-05 03:22:37 +03:00
2007-09-05 03:24:56 +03:00
typedef struct ebml_master {
2008-10-03 13:16:29 +03:00
int64_t pos ; ///< absolute offset in the file where the master's elements start
2007-09-05 03:24:56 +03:00
int sizebytes ; ///< how many bytes were reserved for the size
} ebml_master ;
2007-09-05 03:23:19 +03:00
typedef struct mkv_seekhead_entry {
unsigned int elementid ;
uint64_t segmentpos ;
} mkv_seekhead_entry ;
typedef struct mkv_seekhead {
2008-10-03 13:16:29 +03:00
int64_t filepos ;
int64_t segment_offset ; ///< the file offset to the beginning of the segment
2007-09-05 03:23:26 +03:00
int reserved_size ; ///< -1 if appending to file
2007-09-05 03:23:19 +03:00
int max_entries ;
mkv_seekhead_entry * entries ;
int num_entries ;
} mkv_seekhead ;
2007-09-05 03:23:27 +03:00
typedef struct {
uint64_t pts ;
int tracknum ;
2008-10-03 13:16:29 +03:00
int64_t cluster_pos ; ///< file offset of the cluster containing the block
2007-09-05 03:23:27 +03:00
} mkv_cuepoint ;
typedef struct {
2008-10-03 13:16:29 +03:00
int64_t segment_offset ;
2007-09-05 03:23:27 +03:00
mkv_cuepoint * entries ;
int num_entries ;
} mkv_cues ;
2010-03-09 06:23:28 +02:00
typedef struct {
int write_dts ;
} mkv_track ;
2010-06-05 01:40:54 +03:00
# define MODE_MATROSKAv2 0x01
# define MODE_WEBM 0x02
2007-09-05 03:22:37 +03:00
typedef struct MatroskaMuxContext {
2013-04-30 09:36:20 +03:00
const AVClass * class ;
2010-06-05 01:40:54 +03:00
int mode ;
2011-02-20 12:04:12 +02:00
AVIOContext * dyn_bc ;
2007-09-05 03:24:56 +03:00
ebml_master segment ;
2008-10-03 13:16:29 +03:00
int64_t segment_offset ;
2007-09-05 03:24:56 +03:00
ebml_master cluster ;
2008-10-03 13:16:29 +03:00
int64_t cluster_pos ; ///< file offset of the current cluster
2010-03-09 06:23:36 +02:00
int64_t cluster_pts ;
2008-10-03 13:16:29 +03:00
int64_t duration_offset ;
2010-03-09 06:23:36 +02:00
int64_t duration ;
2007-09-05 03:23:19 +03:00
mkv_seekhead * main_seekhead ;
2007-09-05 03:23:27 +03:00
mkv_cues * cues ;
2010-03-09 06:23:28 +02:00
mkv_track * tracks ;
2007-09-05 03:24:29 +03:00
2010-06-05 01:40:50 +03:00
AVPacket cur_audio_pkt ;
2011-07-30 14:37:36 +03:00
int have_attachments ;
2013-04-30 09:36:20 +03:00
int reserve_cues_space ;
2013-08-18 21:09:40 +03:00
int cluster_size_limit ;
2013-04-30 09:36:20 +03:00
int64_t cues_pos ;
2013-08-18 21:09:40 +03:00
int64_t cluster_time_limit ;
2013-09-03 20:53:34 +03:00
int wrote_chapters ;
2007-09-05 03:22:37 +03:00
} MatroskaMuxContext ;
2007-09-05 03:24:56 +03:00
2007-09-05 03:25:42 +03:00
/** 2 bytes * 3 for EBML IDs, 3 1-byte EBML lengths, 8 bytes for 64 bit
* offset , 4 bytes for target EBML ID */
2007-09-05 03:24:56 +03:00
# define MAX_SEEKENTRY_SIZE 21
2007-09-05 03:25:42 +03:00
/** per-cuepoint-track - 3 1-byte EBML IDs, 3 1-byte EBML sizes, 2
* 8 - byte uint max */
2007-09-05 03:24:56 +03:00
# define MAX_CUETRACKPOS_SIZE 22
2007-09-05 03:25:42 +03:00
/** per-cuepoint - 2 1-byte EBML IDs, 2 1-byte EBML sizes, 8-byte uint max */
2007-09-05 03:24:56 +03:00
# define MAX_CUEPOINT_SIZE(num_tracks) 12 + MAX_CUETRACKPOS_SIZE*num_tracks
2007-09-05 03:24:34 +03:00
static int ebml_id_size ( unsigned int id )
{
return ( av_log2 ( id + 1 ) - 1 ) / 7 + 1 ;
}
2011-02-20 12:04:12 +02:00
static void put_ebml_id ( AVIOContext * pb , unsigned int id )
2007-09-05 03:22:37 +03:00
{
2007-09-05 03:24:36 +03:00
int i = ebml_id_size ( id ) ;
while ( i - - )
2011-02-21 20:28:17 +02:00
avio_w8 ( pb , id > > ( i * 8 ) ) ;
2007-09-05 03:22:37 +03:00
}
2007-09-05 03:24:31 +03:00
/**
2007-09-05 23:31:40 +03:00
* Write an EBML size meaning " unknown size " .
2007-09-05 03:24:31 +03:00
*
2007-09-05 23:31:40 +03:00
* @ param bytes The number of bytes the size should occupy ( maximum : 8 ) .
2007-09-05 03:24:31 +03:00
*/
2011-02-20 12:04:12 +02:00
static void put_ebml_size_unknown ( AVIOContext * pb , int bytes )
2007-09-05 03:24:31 +03:00
{
2007-09-05 03:25:22 +03:00
assert ( bytes < = 8 ) ;
2011-02-21 20:28:17 +02:00
avio_w8 ( pb , 0x1ff > > bytes ) ;
2007-09-05 03:25:20 +03:00
while ( - - bytes )
2011-02-21 20:28:17 +02:00
avio_w8 ( pb , 0xff ) ;
2007-09-05 03:24:31 +03:00
}
2007-09-05 03:24:42 +03:00
/**
2007-09-05 03:26:07 +03:00
* Calculate how many bytes are needed to represent a given number in EBML .
2007-09-05 03:24:42 +03:00
*/
2007-09-05 03:26:07 +03:00
static int ebml_num_size ( uint64_t num )
2007-09-05 03:24:42 +03:00
{
int bytes = 1 ;
2007-09-05 03:26:07 +03:00
while ( ( num + 1 ) > > bytes * 7 ) bytes + + ;
2007-09-05 03:24:42 +03:00
return bytes ;
}
2007-09-05 03:24:50 +03:00
/**
2007-09-05 03:26:07 +03:00
* Write a number in EBML variable length format .
2007-09-05 03:24:50 +03:00
*
2007-09-05 03:26:07 +03:00
* @ param bytes The number of bytes that need to be used to write the number .
2007-09-05 03:24:50 +03:00
* If zero , any number of bytes can be used .
*/
2011-02-20 12:04:12 +02:00
static void put_ebml_num ( AVIOContext * pb , uint64_t num , int bytes )
2007-09-05 03:22:37 +03:00
{
2007-09-05 03:26:07 +03:00
int i , needed_bytes = ebml_num_size ( num ) ;
2007-09-05 03:22:37 +03:00
// sizes larger than this are currently undefined in EBML
2007-09-05 03:26:07 +03:00
assert ( num < ( 1ULL < < 56 ) - 1 ) ;
2007-09-05 03:23:06 +03:00
2007-09-05 03:24:50 +03:00
if ( bytes = = 0 )
// don't care how many bytes are used, so use the min
bytes = needed_bytes ;
2007-09-05 03:25:24 +03:00
// the bytes needed to write the given size would exceed the bytes
// that we need to use, so write unknown size. This shouldn't happen.
2007-09-05 03:25:22 +03:00
assert ( bytes > = needed_bytes ) ;
2007-09-05 03:24:50 +03:00
2007-09-05 03:26:07 +03:00
num | = 1ULL < < bytes * 7 ;
2007-09-05 03:24:44 +03:00
for ( i = bytes - 1 ; i > = 0 ; i - - )
2011-02-21 20:28:17 +02:00
avio_w8 ( pb , num > > i * 8 ) ;
2007-09-05 03:22:37 +03:00
}
2011-02-20 12:04:12 +02:00
static void put_ebml_uint ( AVIOContext * pb , unsigned int elementid , uint64_t val )
2007-09-05 03:22:37 +03:00
{
2007-09-05 03:24:39 +03:00
int i , bytes = 1 ;
2008-03-01 02:26:19 +02:00
uint64_t tmp = val ;
while ( tmp > > = 8 ) bytes + + ;
2007-09-05 03:22:37 +03:00
put_ebml_id ( pb , elementid ) ;
2007-09-05 03:26:07 +03:00
put_ebml_num ( pb , bytes , 0 ) ;
2007-09-05 03:24:39 +03:00
for ( i = bytes - 1 ; i > = 0 ; i - - )
2011-02-21 20:28:17 +02:00
avio_w8 ( pb , val > > i * 8 ) ;
2007-09-05 03:22:37 +03:00
}
2011-02-20 12:04:12 +02:00
static void put_ebml_float ( AVIOContext * pb , unsigned int elementid , double val )
2007-09-05 03:22:47 +03:00
{
put_ebml_id ( pb , elementid ) ;
2007-09-05 03:26:07 +03:00
put_ebml_num ( pb , 8 , 0 ) ;
2011-11-27 17:04:16 +03:00
avio_wb64 ( pb , av_double2int ( val ) ) ;
2007-09-05 03:22:47 +03:00
}
2011-02-20 12:04:12 +02:00
static void put_ebml_binary ( AVIOContext * pb , unsigned int elementid ,
2010-06-05 01:40:58 +03:00
const void * buf , int size )
2007-09-05 03:22:37 +03:00
{
put_ebml_id ( pb , elementid ) ;
2007-09-05 03:26:07 +03:00
put_ebml_num ( pb , size , 0 ) ;
2011-02-21 20:28:17 +02:00
avio_write ( pb , buf , size ) ;
2007-09-05 03:22:37 +03:00
}
2011-02-20 12:04:12 +02:00
static void put_ebml_string ( AVIOContext * pb , unsigned int elementid , const char * str )
2007-09-05 03:22:37 +03:00
{
put_ebml_binary ( pb , elementid , str , strlen ( str ) ) ;
}
2007-09-05 03:24:25 +03:00
/**
2010-06-30 18:38:06 +03:00
* Write a void element of a given size . Useful for reserving space in
2007-09-05 03:25:16 +03:00
* the file to be written to later .
2007-09-05 03:24:25 +03:00
*
2007-09-05 03:25:04 +03:00
* @ param size The number of bytes to reserve , which must be at least 2.
2007-09-05 03:24:25 +03:00
*/
2011-02-20 12:04:12 +02:00
static void put_ebml_void ( AVIOContext * pb , uint64_t size )
2007-09-05 03:23:09 +03:00
{
2011-03-03 21:11:45 +02:00
int64_t currentpos = avio_tell ( pb ) ;
2007-09-05 03:23:09 +03:00
2007-09-05 03:25:22 +03:00
assert ( size > = 2 ) ;
2007-09-05 03:23:09 +03:00
put_ebml_id ( pb , EBML_ID_VOID ) ;
2007-09-05 03:25:16 +03:00
// we need to subtract the length needed to store the size from the
// size we need to reserve so 2 cases, we use 8 bytes to store the
// size if possible, 1 byte otherwise
2007-09-05 03:23:09 +03:00
if ( size < 10 )
2007-09-05 03:26:07 +03:00
put_ebml_num ( pb , size - 1 , 0 ) ;
2007-09-05 03:23:09 +03:00
else
2007-09-05 03:26:07 +03:00
put_ebml_num ( pb , size - 9 , 8 ) ;
2011-03-03 21:11:45 +02:00
while ( avio_tell ( pb ) < currentpos + size )
2011-02-21 20:28:17 +02:00
avio_w8 ( pb , 0 ) ;
2007-09-05 03:23:09 +03:00
}
2011-02-20 12:04:12 +02:00
static ebml_master start_ebml_master ( AVIOContext * pb , unsigned int elementid , uint64_t expectedsize )
2007-09-05 03:22:37 +03:00
{
2007-09-05 03:26:07 +03:00
int bytes = expectedsize ? ebml_num_size ( expectedsize ) : 8 ;
2007-09-05 03:22:37 +03:00
put_ebml_id ( pb , elementid ) ;
2007-09-05 03:24:56 +03:00
put_ebml_size_unknown ( pb , bytes ) ;
2011-03-03 21:11:45 +02:00
return ( ebml_master ) { avio_tell ( pb ) , bytes } ;
2007-09-05 03:22:37 +03:00
}
2011-02-20 12:04:12 +02:00
static void end_ebml_master ( AVIOContext * pb , ebml_master master )
2007-09-05 03:22:37 +03:00
{
2011-03-03 21:11:45 +02:00
int64_t pos = avio_tell ( pb ) ;
2007-09-05 03:22:37 +03:00
2011-02-28 15:57:54 +02:00
if ( avio_seek ( pb , master . pos - master . sizebytes , SEEK_SET ) < 0 )
2007-09-05 03:26:00 +03:00
return ;
2007-09-05 03:26:07 +03:00
put_ebml_num ( pb , pos - master . pos , master . sizebytes ) ;
2011-02-28 15:57:54 +02:00
avio_seek ( pb , pos , SEEK_SET ) ;
2007-09-05 03:22:37 +03:00
}
2011-02-20 12:04:12 +02:00
static void put_xiph_size ( AVIOContext * pb , int size )
2007-09-05 03:24:06 +03:00
{
int i ;
for ( i = 0 ; i < size / 255 ; i + + )
2011-02-21 20:28:17 +02:00
avio_w8 ( pb , 255 ) ;
avio_w8 ( pb , size % 255 ) ;
2007-09-05 03:24:06 +03:00
}
2007-09-05 03:24:25 +03:00
/**
2007-09-05 03:25:16 +03:00
* Initialize a mkv_seekhead element to be ready to index level 1 Matroska
* elements . If a maximum number of elements is specified , enough space
* will be reserved at the current file location to write a seek head of
* that size .
2007-09-05 03:24:25 +03:00
*
2007-09-05 03:25:16 +03:00
* @ param segment_offset The absolute offset to the position in the file
2007-09-05 03:26:11 +03:00
* where the segment begins .
* @ param numelements The maximum number of elements that will be indexed
2007-09-05 03:25:16 +03:00
* by this seek head , 0 if unlimited .
2007-09-05 03:24:25 +03:00
*/
2011-02-20 12:04:12 +02:00
static mkv_seekhead * mkv_start_seekhead ( AVIOContext * pb , int64_t segment_offset , int numelements )
2007-09-05 03:23:19 +03:00
{
mkv_seekhead * new_seekhead = av_mallocz ( sizeof ( mkv_seekhead ) ) ;
if ( new_seekhead = = NULL )
return NULL ;
new_seekhead - > segment_offset = segment_offset ;
if ( numelements > 0 ) {
2011-03-03 21:11:45 +02:00
new_seekhead - > filepos = avio_tell ( pb ) ;
2007-09-05 03:25:16 +03:00
// 21 bytes max for a seek entry, 10 bytes max for the SeekHead ID
// and size, and 3 bytes to guarantee that an EBML void element
// will fit afterwards
2007-09-05 03:24:56 +03:00
new_seekhead - > reserved_size = numelements * MAX_SEEKENTRY_SIZE + 13 ;
2007-09-05 03:23:19 +03:00
new_seekhead - > max_entries = numelements ;
put_ebml_void ( pb , new_seekhead - > reserved_size ) ;
}
return new_seekhead ;
}
static int mkv_add_seekhead_entry ( mkv_seekhead * seekhead , unsigned int elementid , uint64_t filepos )
{
2013-09-10 12:57:35 +03:00
int err ;
2007-09-05 03:23:19 +03:00
// don't store more elements than we reserved space for
if ( seekhead - > max_entries > 0 & & seekhead - > max_entries < = seekhead - > num_entries )
return - 1 ;
2013-09-12 10:49:38 +03:00
if ( ( err = av_reallocp_array ( & seekhead - > entries , seekhead - > num_entries + 1 ,
sizeof ( * seekhead - > entries ) ) ) < 0 ) {
2013-09-10 12:57:35 +03:00
seekhead - > num_entries = 0 ;
return err ;
}
2007-09-05 03:23:19 +03:00
2013-09-12 10:49:38 +03:00
seekhead - > entries [ seekhead - > num_entries ] . elementid = elementid ;
seekhead - > entries [ seekhead - > num_entries + + ] . segmentpos = filepos - seekhead - > segment_offset ;
2007-09-05 03:23:19 +03:00
return 0 ;
}
2007-09-05 03:24:25 +03:00
/**
2007-09-05 03:25:16 +03:00
* Write the seek head to the file and free it . If a maximum number of
* elements was specified to mkv_start_seekhead ( ) , the seek head will
* be written at the location reserved for it . Otherwise , it is written
* at the current location in the file .
2007-09-05 03:24:25 +03:00
*
2010-03-04 10:53:04 +02:00
* @ return The file offset where the seekhead was written ,
* - 1 if an error occurred .
2007-09-05 03:24:25 +03:00
*/
2011-02-20 12:04:12 +02:00
static int64_t mkv_write_seekhead ( AVIOContext * pb , mkv_seekhead * seekhead )
2007-09-05 03:23:19 +03:00
{
2007-09-05 03:24:56 +03:00
ebml_master metaseek , seekentry ;
2008-10-03 13:16:29 +03:00
int64_t currentpos ;
2007-09-05 03:23:19 +03:00
int i ;
2011-03-03 21:11:45 +02:00
currentpos = avio_tell ( pb ) ;
2007-09-05 03:23:19 +03:00
2011-11-07 22:06:01 +03:00
if ( seekhead - > reserved_size > 0 ) {
if ( avio_seek ( pb , seekhead - > filepos , SEEK_SET ) < 0 ) {
currentpos = - 1 ;
goto fail ;
}
}
2007-09-05 03:23:19 +03:00
2007-09-05 03:24:56 +03:00
metaseek = start_ebml_master ( pb , MATROSKA_ID_SEEKHEAD , seekhead - > reserved_size ) ;
2007-09-05 03:23:19 +03:00
for ( i = 0 ; i < seekhead - > num_entries ; i + + ) {
mkv_seekhead_entry * entry = & seekhead - > entries [ i ] ;
2007-09-05 03:24:56 +03:00
seekentry = start_ebml_master ( pb , MATROSKA_ID_SEEKENTRY , MAX_SEEKENTRY_SIZE ) ;
2007-09-05 03:23:19 +03:00
put_ebml_id ( pb , MATROSKA_ID_SEEKID ) ;
2007-09-05 03:26:07 +03:00
put_ebml_num ( pb , ebml_id_size ( entry - > elementid ) , 0 ) ;
2007-09-05 03:23:19 +03:00
put_ebml_id ( pb , entry - > elementid ) ;
put_ebml_uint ( pb , MATROSKA_ID_SEEKPOSITION , entry - > segmentpos ) ;
end_ebml_master ( pb , seekentry ) ;
}
end_ebml_master ( pb , metaseek ) ;
if ( seekhead - > reserved_size > 0 ) {
2011-03-03 21:11:45 +02:00
uint64_t remaining = seekhead - > filepos + seekhead - > reserved_size - avio_tell ( pb ) ;
2007-09-05 03:23:19 +03:00
put_ebml_void ( pb , remaining ) ;
2011-02-28 15:57:54 +02:00
avio_seek ( pb , currentpos , SEEK_SET ) ;
2007-09-05 03:23:19 +03:00
currentpos = seekhead - > filepos ;
}
2011-11-07 22:06:01 +03:00
fail :
2007-09-05 03:23:19 +03:00
av_free ( seekhead - > entries ) ;
av_free ( seekhead ) ;
return currentpos ;
}
2008-10-03 13:16:29 +03:00
static mkv_cues * mkv_start_cues ( int64_t segment_offset )
2007-09-05 03:23:27 +03:00
{
mkv_cues * cues = av_mallocz ( sizeof ( mkv_cues ) ) ;
if ( cues = = NULL )
return NULL ;
cues - > segment_offset = segment_offset ;
return cues ;
}
2010-03-09 06:23:28 +02:00
static int mkv_add_cuepoint ( mkv_cues * cues , int stream , int64_t ts , int64_t cluster_pos )
2007-09-05 03:23:27 +03:00
{
2013-09-10 12:57:35 +03:00
int err ;
2007-09-05 03:23:27 +03:00
2012-10-09 15:54:01 +03:00
if ( ts < 0 )
return 0 ;
2013-09-12 10:49:38 +03:00
if ( ( err = av_reallocp_array ( & cues - > entries , cues - > num_entries + 1 ,
sizeof ( * cues - > entries ) ) ) < 0 ) {
2013-09-10 12:57:35 +03:00
cues - > num_entries = 0 ;
return err ;
}
2007-09-05 03:23:27 +03:00
2013-09-12 10:49:38 +03:00
cues - > entries [ cues - > num_entries ] . pts = ts ;
cues - > entries [ cues - > num_entries ] . tracknum = stream + 1 ;
cues - > entries [ cues - > num_entries + + ] . cluster_pos = cluster_pos - cues - > segment_offset ;
2007-09-05 03:23:27 +03:00
return 0 ;
}
2011-02-20 12:04:12 +02:00
static int64_t mkv_write_cues ( AVIOContext * pb , mkv_cues * cues , int num_tracks )
2007-09-05 03:23:27 +03:00
{
2007-09-05 03:24:56 +03:00
ebml_master cues_element ;
2008-10-03 13:16:29 +03:00
int64_t currentpos ;
2007-09-05 03:23:27 +03:00
int i , j ;
2011-03-03 21:11:45 +02:00
currentpos = avio_tell ( pb ) ;
2007-09-05 03:24:56 +03:00
cues_element = start_ebml_master ( pb , MATROSKA_ID_CUES , 0 ) ;
2007-09-05 03:23:27 +03:00
for ( i = 0 ; i < cues - > num_entries ; i + + ) {
2007-09-05 03:24:56 +03:00
ebml_master cuepoint , track_positions ;
2007-09-05 03:23:27 +03:00
mkv_cuepoint * entry = & cues - > entries [ i ] ;
uint64_t pts = entry - > pts ;
2007-09-05 03:24:56 +03:00
cuepoint = start_ebml_master ( pb , MATROSKA_ID_POINTENTRY , MAX_CUEPOINT_SIZE ( num_tracks ) ) ;
2007-09-05 03:23:27 +03:00
put_ebml_uint ( pb , MATROSKA_ID_CUETIME , pts ) ;
// put all the entries from different tracks that have the exact same
// timestamp into the same CuePoint
for ( j = 0 ; j < cues - > num_entries - i & & entry [ j ] . pts = = pts ; j + + ) {
2007-09-05 03:24:56 +03:00
track_positions = start_ebml_master ( pb , MATROSKA_ID_CUETRACKPOSITION , MAX_CUETRACKPOS_SIZE ) ;
2007-09-05 03:23:27 +03:00
put_ebml_uint ( pb , MATROSKA_ID_CUETRACK , entry [ j ] . tracknum ) ;
put_ebml_uint ( pb , MATROSKA_ID_CUECLUSTERPOSITION , entry [ j ] . cluster_pos ) ;
end_ebml_master ( pb , track_positions ) ;
}
i + = j - 1 ;
end_ebml_master ( pb , cuepoint ) ;
}
end_ebml_master ( pb , cues_element ) ;
return currentpos ;
}
2011-02-20 12:04:12 +02:00
static int put_xiph_codecpriv ( AVFormatContext * s , AVIOContext * pb , AVCodecContext * codec )
2007-09-05 03:23:13 +03:00
{
uint8_t * header_start [ 3 ] ;
int header_len [ 3 ] ;
int first_header_size ;
2007-09-05 03:24:06 +03:00
int j ;
2007-09-05 03:23:13 +03:00
2012-08-05 12:11:04 +03:00
if ( codec - > codec_id = = AV_CODEC_ID_VORBIS )
2007-09-05 03:23:13 +03:00
first_header_size = 30 ;
else
first_header_size = 42 ;
2011-10-17 10:45:27 +03:00
if ( avpriv_split_xiph_headers ( codec - > extradata , codec - > extradata_size ,
2007-09-05 03:23:13 +03:00
first_header_size , header_start , header_len ) < 0 ) {
2007-09-05 03:26:09 +03:00
av_log ( s , AV_LOG_ERROR , " Extradata corrupt. \n " ) ;
2007-09-05 03:23:13 +03:00
return - 1 ;
}
2011-02-21 20:28:17 +02:00
avio_w8 ( pb , 2 ) ; // number packets - 1
2007-09-05 03:23:13 +03:00
for ( j = 0 ; j < 2 ; j + + ) {
2007-09-05 03:24:06 +03:00
put_xiph_size ( pb , header_len [ j ] ) ;
2007-09-05 03:23:13 +03:00
}
for ( j = 0 ; j < 3 ; j + + )
2011-02-21 20:28:17 +02:00
avio_write ( pb , header_start [ j ] , header_len [ j ] ) ;
2007-09-05 03:23:17 +03:00
return 0 ;
2007-09-05 03:23:13 +03:00
}
2007-09-05 03:22:37 +03:00
2013-05-28 11:09:21 +03:00
static int put_wv_codecpriv ( AVIOContext * pb , AVCodecContext * codec )
{
if ( codec - > extradata & & codec - > extradata_size = = 2 )
avio_write ( pb , codec - > extradata , 2 ) ;
else
avio_wl16 ( pb , 0x403 ) ; // fallback to the version mentioned in matroska specs
return 0 ;
}
2007-09-05 03:26:09 +03:00
static void get_aac_sample_rates ( AVFormatContext * s , AVCodecContext * codec , int * sample_rate , int * output_sample_rate )
2007-09-05 03:23:43 +03:00
{
2011-03-21 10:05:36 +02:00
MPEG4AudioConfig mp4ac ;
2007-09-05 03:23:43 +03:00
2011-10-21 18:25:30 +03:00
if ( avpriv_mpeg4audio_get_config ( & mp4ac , codec - > extradata ,
codec - > extradata_size * 8 , 1 ) < 0 ) {
2011-03-21 10:05:36 +02:00
av_log ( s , AV_LOG_WARNING , " Error parsing AAC extradata, unable to determine samplerate. \n " ) ;
2007-09-05 03:23:43 +03:00
return ;
}
2011-03-21 10:05:36 +02:00
* sample_rate = mp4ac . sample_rate ;
* output_sample_rate = mp4ac . ext_sample_rate ;
2007-09-05 03:23:43 +03:00
}
2011-02-20 12:04:12 +02:00
static int mkv_write_codecprivate ( AVFormatContext * s , AVIOContext * pb , AVCodecContext * codec , int native_id , int qt_id )
2007-09-05 03:25:49 +03:00
{
2011-02-20 12:04:12 +02:00
AVIOContext * dyn_cp ;
2007-09-05 03:25:52 +03:00
uint8_t * codecpriv ;
2007-11-21 09:41:00 +02:00
int ret , codecpriv_size ;
2007-09-05 03:25:52 +03:00
2011-03-17 09:13:34 +02:00
ret = avio_open_dyn_buf ( & dyn_cp ) ;
2007-11-21 09:41:00 +02:00
if ( ret < 0 )
return ret ;
2007-09-05 03:25:49 +03:00
if ( native_id ) {
2012-08-05 12:11:04 +03:00
if ( codec - > codec_id = = AV_CODEC_ID_VORBIS | | codec - > codec_id = = AV_CODEC_ID_THEORA )
2007-11-21 09:41:00 +02:00
ret = put_xiph_codecpriv ( s , dyn_cp , codec ) ;
2012-08-05 12:11:04 +03:00
else if ( codec - > codec_id = = AV_CODEC_ID_FLAC )
2010-03-20 15:36:43 +02:00
ret = ff_flac_write_header ( dyn_cp , codec , 1 ) ;
2013-05-28 11:09:21 +03:00
else if ( codec - > codec_id = = AV_CODEC_ID_WAVPACK )
ret = put_wv_codecpriv ( dyn_cp , codec ) ;
2012-08-05 12:11:04 +03:00
else if ( codec - > codec_id = = AV_CODEC_ID_H264 )
2008-01-12 01:33:32 +02:00
ret = ff_isom_write_avcc ( dyn_cp , codec - > extradata , codec - > extradata_size ) ;
2014-03-03 17:53:44 +03:00
else if ( codec - > codec_id = = AV_CODEC_ID_HEVC )
ret = ff_isom_write_hvcc ( dyn_cp , codec - > extradata , codec - > extradata_size , 0 ) ;
2012-09-14 23:26:14 +03:00
else if ( codec - > codec_id = = AV_CODEC_ID_ALAC ) {
if ( codec - > extradata_size < 36 ) {
av_log ( s , AV_LOG_ERROR ,
" Invalid extradata found, ALAC expects a 36-byte "
" QuickTime atom. " ) ;
ret = AVERROR_INVALIDDATA ;
} else
avio_write ( dyn_cp , codec - > extradata + 12 ,
codec - > extradata_size - 12 ) ;
}
2007-09-05 03:25:58 +03:00
else if ( codec - > extradata_size )
2011-02-21 20:28:17 +02:00
avio_write ( dyn_cp , codec - > extradata , codec - > extradata_size ) ;
2010-03-31 02:30:55 +03:00
} else if ( codec - > codec_type = = AVMEDIA_TYPE_VIDEO ) {
2008-07-27 18:11:04 +03:00
if ( qt_id ) {
if ( ! codec - > codec_tag )
2012-02-15 12:28:39 +03:00
codec - > codec_tag = ff_codec_get_tag ( ff_codec_movvideo_tags , codec - > codec_id ) ;
2008-07-27 18:11:04 +03:00
if ( codec - > extradata_size )
2011-02-21 20:28:17 +02:00
avio_write ( dyn_cp , codec - > extradata , codec - > extradata_size ) ;
2008-07-27 18:11:04 +03:00
} else {
2010-03-04 10:53:11 +02:00
if ( ! codec - > codec_tag )
codec - > codec_tag = ff_codec_get_tag ( ff_codec_bmp_tags , codec - > codec_id ) ;
if ( ! codec - > codec_tag ) {
2010-03-15 21:03:48 +02:00
av_log ( s , AV_LOG_ERROR , " No bmp codec ID found. \n " ) ;
2010-03-04 10:53:11 +02:00
ret = - 1 ;
}
2007-09-05 03:25:49 +03:00
2010-03-04 10:53:11 +02:00
ff_put_bmp_header ( dyn_cp , codec , ff_codec_bmp_tags , 0 ) ;
2008-07-27 18:11:04 +03:00
}
2007-09-05 03:25:49 +03:00
2010-03-31 02:30:55 +03:00
} else if ( codec - > codec_type = = AVMEDIA_TYPE_AUDIO ) {
2010-03-25 16:34:19 +02:00
unsigned int tag ;
tag = ff_codec_get_tag ( ff_codec_wav_tags , codec - > codec_id ) ;
if ( ! tag ) {
2010-03-15 21:03:48 +02:00
av_log ( s , AV_LOG_ERROR , " No wav codec ID found. \n " ) ;
2007-09-05 03:25:52 +03:00
ret = - 1 ;
2007-09-05 03:25:49 +03:00
}
2010-03-25 16:34:19 +02:00
if ( ! codec - > codec_tag )
codec - > codec_tag = tag ;
2007-09-05 03:25:49 +03:00
2009-06-23 02:09:34 +03:00
ff_put_wav_header ( dyn_cp , codec ) ;
2007-09-05 03:25:49 +03:00
}
2007-09-05 03:25:52 +03:00
2011-03-17 09:16:07 +02:00
codecpriv_size = avio_close_dyn_buf ( dyn_cp , & codecpriv ) ;
2007-09-05 03:25:52 +03:00
if ( codecpriv_size )
put_ebml_binary ( pb , MATROSKA_ID_CODECPRIVATE , codecpriv , codecpriv_size ) ;
av_free ( codecpriv ) ;
return ret ;
2007-09-05 03:25:49 +03:00
}
2007-09-05 03:23:15 +03:00
static int mkv_write_tracks ( AVFormatContext * s )
2007-09-05 03:22:37 +03:00
{
MatroskaMuxContext * mkv = s - > priv_data ;
2011-02-20 12:04:12 +02:00
AVIOContext * pb = s - > pb ;
2007-09-05 03:24:56 +03:00
ebml_master tracks ;
2007-09-05 03:25:32 +03:00
int i , j , ret ;
2007-09-05 03:22:37 +03:00
2011-03-03 21:11:45 +02:00
ret = mkv_add_seekhead_entry ( mkv - > main_seekhead , MATROSKA_ID_TRACKS , avio_tell ( pb ) ) ;
2007-09-05 03:25:32 +03:00
if ( ret < 0 ) return ret ;
2007-09-05 03:23:19 +03:00
2007-09-05 03:24:56 +03:00
tracks = start_ebml_master ( pb , MATROSKA_ID_TRACKS , 0 ) ;
2007-09-05 03:22:37 +03:00
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
AVStream * st = s - > streams [ i ] ;
2007-09-05 03:22:50 +03:00
AVCodecContext * codec = st - > codec ;
2007-09-05 03:24:56 +03:00
ebml_master subinfo , track ;
2007-09-05 03:22:50 +03:00
int native_id = 0 ;
2008-07-27 18:11:04 +03:00
int qt_id = 0 ;
2007-09-05 03:23:34 +03:00
int bit_depth = av_get_bits_per_sample ( codec - > codec_id ) ;
2007-09-05 03:23:43 +03:00
int sample_rate = codec - > sample_rate ;
int output_sample_rate = 0 ;
2011-05-22 13:46:29 +03:00
AVDictionaryEntry * tag ;
2007-09-05 03:23:43 +03:00
2011-07-30 14:37:36 +03:00
if ( codec - > codec_type = = AVMEDIA_TYPE_ATTACHMENT ) {
mkv - > have_attachments = 1 ;
continue ;
}
2007-09-05 03:25:14 +03:00
if ( ! bit_depth )
2011-06-07 20:40:22 +03:00
bit_depth = av_get_bytes_per_sample ( codec - > sample_fmt ) < < 3 ;
2007-09-05 03:25:14 +03:00
2012-08-05 12:11:04 +03:00
if ( codec - > codec_id = = AV_CODEC_ID_AAC )
2007-09-05 03:26:09 +03:00
get_aac_sample_rates ( s , codec , & sample_rate , & output_sample_rate ) ;
2007-09-05 03:22:50 +03:00
2007-09-05 03:24:56 +03:00
track = start_ebml_master ( pb , MATROSKA_ID_TRACKENTRY , 0 ) ;
2007-09-05 03:23:03 +03:00
put_ebml_uint ( pb , MATROSKA_ID_TRACKNUMBER , i + 1 ) ;
put_ebml_uint ( pb , MATROSKA_ID_TRACKUID , i + 1 ) ;
2007-09-05 03:22:50 +03:00
put_ebml_uint ( pb , MATROSKA_ID_TRACKFLAGLACING , 0 ) ; // no lacing (yet)
2011-05-22 13:46:29 +03:00
if ( ( tag = av_dict_get ( st - > metadata , " title " , NULL , 0 ) ) )
2009-02-15 18:53:05 +02:00
put_ebml_string ( pb , MATROSKA_ID_TRACKNAME , tag - > value ) ;
2011-05-22 13:46:29 +03:00
tag = av_dict_get ( st - > metadata , " language " , NULL , 0 ) ;
2009-02-15 18:53:05 +02:00
put_ebml_string ( pb , MATROSKA_ID_TRACKLANGUAGE , tag ? tag - > value : " und " ) ;
2007-09-05 03:22:50 +03:00
2013-08-22 18:54:59 +03:00
// The default value for TRACKFLAGDEFAULT is 1, so add element
// if we need to clear it.
if ( ! ( st - > disposition & AV_DISPOSITION_DEFAULT ) )
2009-01-17 06:31:42 +02:00
put_ebml_uint ( pb , MATROSKA_ID_TRACKFLAGDEFAULT , ! ! ( st - > disposition & AV_DISPOSITION_DEFAULT ) ) ;
2008-03-07 21:25:09 +02:00
2007-09-05 03:26:11 +03:00
// look for a codec ID string specific to mkv to use,
2007-09-05 03:25:16 +03:00
// if none are found, use AVI codes
2012-08-05 12:11:04 +03:00
for ( j = 0 ; ff_mkv_codec_tags [ j ] . id ! = AV_CODEC_ID_NONE ; j + + ) {
2007-09-05 03:22:50 +03:00
if ( ff_mkv_codec_tags [ j ] . id = = codec - > codec_id ) {
put_ebml_string ( pb , MATROSKA_ID_CODECID , ff_mkv_codec_tags [ j ] . str ) ;
native_id = 1 ;
break ;
}
}
2012-08-05 12:11:04 +03:00
if ( mkv - > mode = = MODE_WEBM & & ! ( codec - > codec_id = = AV_CODEC_ID_VP8 | |
codec - > codec_id = = AV_CODEC_ID_VORBIS ) ) {
2010-06-05 01:40:54 +03:00
av_log ( s , AV_LOG_ERROR ,
" Only VP8 video and Vorbis audio are supported for WebM. \n " ) ;
return AVERROR ( EINVAL ) ;
}
2007-09-05 03:22:50 +03:00
switch ( codec - > codec_type ) {
2010-03-31 02:30:55 +03:00
case AVMEDIA_TYPE_VIDEO :
2007-09-05 03:22:50 +03:00
put_ebml_uint ( pb , MATROSKA_ID_TRACKTYPE , MATROSKA_TRACK_TYPE_VIDEO ) ;
2010-05-24 11:58:19 +03:00
put_ebml_uint ( pb , MATROSKA_ID_TRACKDEFAULTDURATION , av_q2d ( codec - > time_base ) * 1E9 ) ;
2007-09-05 03:22:50 +03:00
2008-07-27 18:11:04 +03:00
if ( ! native_id & &
2012-02-15 12:28:39 +03:00
ff_codec_get_tag ( ff_codec_movvideo_tags , codec - > codec_id ) & &
2009-06-23 02:09:34 +03:00
( ! ff_codec_get_tag ( ff_codec_bmp_tags , codec - > codec_id )
2012-08-05 12:11:04 +03:00
| | codec - > codec_id = = AV_CODEC_ID_SVQ1
| | codec - > codec_id = = AV_CODEC_ID_SVQ3
| | codec - > codec_id = = AV_CODEC_ID_CINEPAK ) )
2008-07-27 18:11:04 +03:00
qt_id = 1 ;
if ( qt_id )
put_ebml_string ( pb , MATROSKA_ID_CODECID , " V_QUICKTIME " ) ;
2010-03-09 06:23:28 +02:00
else if ( ! native_id ) {
2007-09-05 03:26:11 +03:00
// if there is no mkv-specific codec ID, use VFW mode
2008-08-05 03:42:49 +03:00
put_ebml_string ( pb , MATROSKA_ID_CODECID , " V_MS/VFW/FOURCC " ) ;
2010-03-09 06:23:28 +02:00
mkv - > tracks [ i ] . write_dts = 1 ;
}
2007-09-05 03:25:58 +03:00
2007-09-05 03:24:56 +03:00
subinfo = start_ebml_master ( pb , MATROSKA_ID_TRACKVIDEO , 0 ) ;
2007-09-05 03:22:50 +03:00
// XXX: interlace flag?
put_ebml_uint ( pb , MATROSKA_ID_VIDEOPIXELWIDTH , codec - > width ) ;
put_ebml_uint ( pb , MATROSKA_ID_VIDEOPIXELHEIGHT , codec - > height ) ;
2011-05-22 13:46:29 +03:00
if ( ( tag = av_dict_get ( s - > metadata , " stereo_mode " , NULL , 0 ) ) ) {
2011-05-28 09:12:22 +03:00
uint8_t stereo_fmt = atoi ( tag - > value ) ;
int valid_fmt = 0 ;
switch ( mkv - > mode ) {
case MODE_WEBM :
if ( stereo_fmt < = MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM
| | stereo_fmt = = MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT )
valid_fmt = 1 ;
break ;
case MODE_MATROSKAv2 :
if ( stereo_fmt < = MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_RL )
valid_fmt = 1 ;
break ;
}
if ( valid_fmt )
put_ebml_uint ( pb , MATROSKA_ID_VIDEOSTEREOMODE , stereo_fmt ) ;
}
2008-08-24 02:43:20 +03:00
if ( st - > sample_aspect_ratio . num ) {
int d_width = codec - > width * av_q2d ( st - > sample_aspect_ratio ) ;
put_ebml_uint ( pb , MATROSKA_ID_VIDEODISPLAYWIDTH , d_width ) ;
put_ebml_uint ( pb , MATROSKA_ID_VIDEODISPLAYHEIGHT , codec - > height ) ;
2010-07-14 22:36:14 +03:00
put_ebml_uint ( pb , MATROSKA_ID_VIDEODISPLAYUNIT , 3 ) ;
2007-09-05 03:24:15 +03:00
}
2007-09-05 03:22:50 +03:00
end_ebml_master ( pb , subinfo ) ;
break ;
2010-03-31 02:30:55 +03:00
case AVMEDIA_TYPE_AUDIO :
2007-09-05 03:22:50 +03:00
put_ebml_uint ( pb , MATROSKA_ID_TRACKTYPE , MATROSKA_TRACK_TYPE_AUDIO ) ;
2007-09-05 03:25:58 +03:00
if ( ! native_id )
2007-09-05 03:23:29 +03:00
// no mkv-specific ID, use ACM mode
2008-08-05 03:42:49 +03:00
put_ebml_string ( pb , MATROSKA_ID_CODECID , " A_MS/ACM " ) ;
2007-09-05 03:25:58 +03:00
2007-09-05 03:24:56 +03:00
subinfo = start_ebml_master ( pb , MATROSKA_ID_TRACKAUDIO , 0 ) ;
2007-09-05 03:22:50 +03:00
put_ebml_uint ( pb , MATROSKA_ID_AUDIOCHANNELS , codec - > channels ) ;
2007-09-05 03:23:43 +03:00
put_ebml_float ( pb , MATROSKA_ID_AUDIOSAMPLINGFREQ , sample_rate ) ;
if ( output_sample_rate )
put_ebml_float ( pb , MATROSKA_ID_AUDIOOUTSAMPLINGFREQ , output_sample_rate ) ;
2007-09-05 03:23:31 +03:00
if ( bit_depth )
put_ebml_uint ( pb , MATROSKA_ID_AUDIOBITDEPTH , bit_depth ) ;
2007-09-05 03:22:50 +03:00
end_ebml_master ( pb , subinfo ) ;
break ;
2010-03-31 02:30:55 +03:00
case AVMEDIA_TYPE_SUBTITLE :
2007-09-05 03:25:24 +03:00
put_ebml_uint ( pb , MATROSKA_ID_TRACKTYPE , MATROSKA_TRACK_TYPE_SUBTITLE ) ;
2011-03-24 02:56:26 +02:00
if ( ! native_id ) {
av_log ( s , AV_LOG_ERROR , " Subtitle codec %d is not supported. \n " , codec - > codec_id ) ;
return AVERROR ( ENOSYS ) ;
}
2007-09-05 03:25:24 +03:00
break ;
2007-09-05 03:22:50 +03:00
default :
2011-03-24 02:54:15 +02:00
av_log ( s , AV_LOG_ERROR , " Only audio, video, and subtitles are supported for Matroska. \n " ) ;
2007-09-05 03:22:50 +03:00
break ;
}
2008-07-27 18:11:04 +03:00
ret = mkv_write_codecprivate ( s , pb , codec , native_id , qt_id ) ;
2007-09-05 03:25:49 +03:00
if ( ret < 0 ) return ret ;
2007-09-05 03:22:37 +03:00
end_ebml_master ( pb , track ) ;
2007-09-05 03:22:50 +03:00
// ms precision is the de-facto standard timescale for mkv files
2011-11-29 21:28:15 +03:00
avpriv_set_pts_info ( st , 64 , 1 , 1000 ) ;
2007-09-05 03:22:37 +03:00
}
end_ebml_master ( pb , tracks ) ;
2007-09-05 03:23:17 +03:00
return 0 ;
2007-09-05 03:23:15 +03:00
}
2009-10-18 15:17:01 +03:00
static int mkv_write_chapters ( AVFormatContext * s )
{
MatroskaMuxContext * mkv = s - > priv_data ;
2011-02-20 12:04:12 +02:00
AVIOContext * pb = s - > pb ;
2009-10-18 15:17:01 +03:00
ebml_master chapters , editionentry ;
AVRational scale = { 1 , 1E9 } ;
int i , ret ;
2013-09-03 20:53:34 +03:00
if ( ! s - > nb_chapters | | mkv - > wrote_chapters )
2009-10-18 15:17:01 +03:00
return 0 ;
2011-03-03 21:11:45 +02:00
ret = mkv_add_seekhead_entry ( mkv - > main_seekhead , MATROSKA_ID_CHAPTERS , avio_tell ( pb ) ) ;
2009-10-18 15:17:01 +03:00
if ( ret < 0 ) return ret ;
chapters = start_ebml_master ( pb , MATROSKA_ID_CHAPTERS , 0 ) ;
editionentry = start_ebml_master ( pb , MATROSKA_ID_EDITIONENTRY , 0 ) ;
put_ebml_uint ( pb , MATROSKA_ID_EDITIONFLAGDEFAULT , 1 ) ;
put_ebml_uint ( pb , MATROSKA_ID_EDITIONFLAGHIDDEN , 0 ) ;
for ( i = 0 ; i < s - > nb_chapters ; i + + ) {
ebml_master chapteratom , chapterdisplay ;
AVChapter * c = s - > chapters [ i ] ;
2011-05-22 13:46:29 +03:00
AVDictionaryEntry * t = NULL ;
2009-10-18 15:17:01 +03:00
chapteratom = start_ebml_master ( pb , MATROSKA_ID_CHAPTERATOM , 0 ) ;
put_ebml_uint ( pb , MATROSKA_ID_CHAPTERUID , c - > id ) ;
put_ebml_uint ( pb , MATROSKA_ID_CHAPTERTIMESTART ,
av_rescale_q ( c - > start , c - > time_base , scale ) ) ;
put_ebml_uint ( pb , MATROSKA_ID_CHAPTERTIMEEND ,
av_rescale_q ( c - > end , c - > time_base , scale ) ) ;
put_ebml_uint ( pb , MATROSKA_ID_CHAPTERFLAGHIDDEN , 0 ) ;
put_ebml_uint ( pb , MATROSKA_ID_CHAPTERFLAGENABLED , 1 ) ;
2011-05-22 13:46:29 +03:00
if ( ( t = av_dict_get ( c - > metadata , " title " , NULL , 0 ) ) ) {
2009-10-18 15:17:01 +03:00
chapterdisplay = start_ebml_master ( pb , MATROSKA_ID_CHAPTERDISPLAY , 0 ) ;
put_ebml_string ( pb , MATROSKA_ID_CHAPSTRING , t - > value ) ;
put_ebml_string ( pb , MATROSKA_ID_CHAPLANG , " und " ) ;
end_ebml_master ( pb , chapterdisplay ) ;
}
end_ebml_master ( pb , chapteratom ) ;
}
end_ebml_master ( pb , editionentry ) ;
end_ebml_master ( pb , chapters ) ;
2013-09-03 20:53:34 +03:00
mkv - > wrote_chapters = 1 ;
2009-10-18 15:17:01 +03:00
return 0 ;
}
2011-05-22 13:46:29 +03:00
static void mkv_write_simpletag ( AVIOContext * pb , AVDictionaryEntry * t )
2010-10-05 12:40:43 +03:00
{
uint8_t * key = av_strdup ( t - > key ) ;
uint8_t * p = key ;
const uint8_t * lang = NULL ;
ebml_master tag ;
if ( ( p = strrchr ( p , ' - ' ) ) & &
( lang = av_convert_lang_to ( p + 1 , AV_LANG_ISO639_2_BIBL ) ) )
* p = 0 ;
p = key ;
while ( * p ) {
if ( * p = = ' ' )
* p = ' _ ' ;
else if ( * p > = ' a ' & & * p < = ' z ' )
* p - = ' a ' - ' A ' ;
p + + ;
}
tag = start_ebml_master ( pb , MATROSKA_ID_SIMPLETAG , 0 ) ;
put_ebml_string ( pb , MATROSKA_ID_TAGNAME , key ) ;
if ( lang )
put_ebml_string ( pb , MATROSKA_ID_TAGLANG , lang ) ;
put_ebml_string ( pb , MATROSKA_ID_TAGSTRING , t - > value ) ;
end_ebml_master ( pb , tag ) ;
av_freep ( & key ) ;
}
2011-05-22 13:46:29 +03:00
static int mkv_write_tag ( AVFormatContext * s , AVDictionary * m , unsigned int elementid ,
2010-10-05 12:40:43 +03:00
unsigned int uid , ebml_master * tags )
{
MatroskaMuxContext * mkv = s - > priv_data ;
ebml_master tag , targets ;
2011-05-22 13:46:29 +03:00
AVDictionaryEntry * t = NULL ;
2010-10-05 12:40:43 +03:00
int ret ;
if ( ! tags - > pos ) {
2011-03-03 21:11:45 +02:00
ret = mkv_add_seekhead_entry ( mkv - > main_seekhead , MATROSKA_ID_TAGS , avio_tell ( s - > pb ) ) ;
2010-10-05 12:40:43 +03:00
if ( ret < 0 ) return ret ;
* tags = start_ebml_master ( s - > pb , MATROSKA_ID_TAGS , 0 ) ;
}
tag = start_ebml_master ( s - > pb , MATROSKA_ID_TAG , 0 ) ;
targets = start_ebml_master ( s - > pb , MATROSKA_ID_TAGTARGETS , 0 ) ;
if ( elementid )
put_ebml_uint ( s - > pb , elementid , uid ) ;
end_ebml_master ( s - > pb , targets ) ;
2011-05-22 13:46:29 +03:00
while ( ( t = av_dict_get ( m , " " , t , AV_DICT_IGNORE_SUFFIX ) ) )
2014-03-03 23:20:14 +03:00
if ( av_strcasecmp ( t - > key , " title " ) & &
av_strcasecmp ( t - > key , " encoding_tool " ) )
2010-11-23 10:24:41 +02:00
mkv_write_simpletag ( s - > pb , t ) ;
2010-10-05 12:40:43 +03:00
end_ebml_master ( s - > pb , tag ) ;
return 0 ;
}
static int mkv_write_tags ( AVFormatContext * s )
{
ebml_master tags = { 0 } ;
int i , ret ;
2010-10-15 22:04:25 +03:00
ff_metadata_conv_ctx ( s , ff_mkv_metadata_conv , NULL ) ;
2011-05-22 13:46:29 +03:00
if ( av_dict_get ( s - > metadata , " " , NULL , AV_DICT_IGNORE_SUFFIX ) ) {
2010-10-05 12:40:43 +03:00
ret = mkv_write_tag ( s , s - > metadata , 0 , 0 , & tags ) ;
if ( ret < 0 ) return ret ;
}
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
AVStream * st = s - > streams [ i ] ;
2011-05-22 13:46:29 +03:00
if ( ! av_dict_get ( st - > metadata , " " , 0 , AV_DICT_IGNORE_SUFFIX ) )
2010-10-05 12:40:43 +03:00
continue ;
ret = mkv_write_tag ( s , st - > metadata , MATROSKA_ID_TAGTARGETS_TRACKUID , i + 1 , & tags ) ;
if ( ret < 0 ) return ret ;
}
for ( i = 0 ; i < s - > nb_chapters ; i + + ) {
AVChapter * ch = s - > chapters [ i ] ;
2011-05-22 13:46:29 +03:00
if ( ! av_dict_get ( ch - > metadata , " " , NULL , AV_DICT_IGNORE_SUFFIX ) )
2010-10-05 12:40:43 +03:00
continue ;
ret = mkv_write_tag ( s , ch - > metadata , MATROSKA_ID_TAGTARGETS_CHAPTERUID , ch - > id , & tags ) ;
if ( ret < 0 ) return ret ;
}
if ( tags . pos )
end_ebml_master ( s - > pb , tags ) ;
return 0 ;
}
2011-07-30 14:37:36 +03:00
static int mkv_write_attachments ( AVFormatContext * s )
{
MatroskaMuxContext * mkv = s - > priv_data ;
AVIOContext * pb = s - > pb ;
ebml_master attachments ;
AVLFG c ;
int i , ret ;
if ( ! mkv - > have_attachments )
return 0 ;
av_lfg_init ( & c , av_get_random_seed ( ) ) ;
ret = mkv_add_seekhead_entry ( mkv - > main_seekhead , MATROSKA_ID_ATTACHMENTS , avio_tell ( pb ) ) ;
if ( ret < 0 ) return ret ;
attachments = start_ebml_master ( pb , MATROSKA_ID_ATTACHMENTS , 0 ) ;
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
AVStream * st = s - > streams [ i ] ;
ebml_master attached_file ;
AVDictionaryEntry * t ;
const char * mimetype = NULL ;
if ( st - > codec - > codec_type ! = AVMEDIA_TYPE_ATTACHMENT )
continue ;
attached_file = start_ebml_master ( pb , MATROSKA_ID_ATTACHEDFILE , 0 ) ;
if ( t = av_dict_get ( st - > metadata , " title " , NULL , 0 ) )
put_ebml_string ( pb , MATROSKA_ID_FILEDESC , t - > value ) ;
if ( ! ( t = av_dict_get ( st - > metadata , " filename " , NULL , 0 ) ) ) {
av_log ( s , AV_LOG_ERROR , " Attachment stream %d has no filename tag. \n " , i ) ;
return AVERROR ( EINVAL ) ;
}
put_ebml_string ( pb , MATROSKA_ID_FILENAME , t - > value ) ;
if ( t = av_dict_get ( st - > metadata , " mimetype " , NULL , 0 ) )
mimetype = t - > value ;
2012-08-05 12:11:04 +03:00
else if ( st - > codec - > codec_id ! = AV_CODEC_ID_NONE ) {
2011-07-30 14:37:36 +03:00
int i ;
2012-08-05 12:11:04 +03:00
for ( i = 0 ; ff_mkv_mime_tags [ i ] . id ! = AV_CODEC_ID_NONE ; i + + )
2011-07-30 14:37:36 +03:00
if ( ff_mkv_mime_tags [ i ] . id = = st - > codec - > codec_id ) {
mimetype = ff_mkv_mime_tags [ i ] . str ;
break ;
}
}
if ( ! mimetype ) {
av_log ( s , AV_LOG_ERROR , " Attachment stream %d has no mimetype tag and "
" it cannot be deduced from the codec id. \n " , i ) ;
return AVERROR ( EINVAL ) ;
}
put_ebml_string ( pb , MATROSKA_ID_FILEMIMETYPE , mimetype ) ;
put_ebml_binary ( pb , MATROSKA_ID_FILEDATA , st - > codec - > extradata , st - > codec - > extradata_size ) ;
put_ebml_uint ( pb , MATROSKA_ID_FILEUID , av_lfg_get ( & c ) ) ;
end_ebml_master ( pb , attached_file ) ;
}
end_ebml_master ( pb , attachments ) ;
return 0 ;
}
2007-09-05 03:23:15 +03:00
static int mkv_write_header ( AVFormatContext * s )
{
MatroskaMuxContext * mkv = s - > priv_data ;
2011-02-20 12:04:12 +02:00
AVIOContext * pb = s - > pb ;
2007-09-05 03:24:56 +03:00
ebml_master ebml_header , segment_info ;
2011-05-22 13:46:29 +03:00
AVDictionaryEntry * tag ;
2010-06-05 01:41:03 +03:00
int ret , i ;
2007-09-05 03:23:15 +03:00
2010-06-05 01:40:54 +03:00
if ( ! strcmp ( s - > oformat - > name , " webm " ) ) mkv - > mode = MODE_WEBM ;
else mkv - > mode = MODE_MATROSKAv2 ;
2010-03-09 06:23:28 +02:00
mkv - > tracks = av_mallocz ( s - > nb_streams * sizeof ( * mkv - > tracks ) ) ;
2010-06-05 01:41:06 +03:00
if ( ! mkv - > tracks )
return AVERROR ( ENOMEM ) ;
2007-09-05 03:24:29 +03:00
2007-09-05 03:24:56 +03:00
ebml_header = start_ebml_master ( pb , EBML_ID_HEADER , 0 ) ;
2007-09-05 03:23:15 +03:00
put_ebml_uint ( pb , EBML_ID_EBMLVERSION , 1 ) ;
put_ebml_uint ( pb , EBML_ID_EBMLREADVERSION , 1 ) ;
put_ebml_uint ( pb , EBML_ID_EBMLMAXIDLENGTH , 4 ) ;
put_ebml_uint ( pb , EBML_ID_EBMLMAXSIZELENGTH , 8 ) ;
2010-06-05 01:40:54 +03:00
put_ebml_string ( pb , EBML_ID_DOCTYPE , s - > oformat - > name ) ;
2007-09-05 03:23:15 +03:00
put_ebml_uint ( pb , EBML_ID_DOCTYPEVERSION , 2 ) ;
put_ebml_uint ( pb , EBML_ID_DOCTYPEREADVERSION , 2 ) ;
end_ebml_master ( pb , ebml_header ) ;
2007-09-05 03:24:56 +03:00
mkv - > segment = start_ebml_master ( pb , MATROSKA_ID_SEGMENT , 0 ) ;
2011-03-03 21:11:45 +02:00
mkv - > segment_offset = avio_tell ( pb ) ;
2007-09-05 03:23:19 +03:00
2007-09-05 03:25:16 +03:00
// we write 2 seek heads - one at the end of the file to point to each
// cluster, and one at the beginning to point to all other level one
// elements (including the seek head at the end of the file), which
// isn't more than 10 elements if we only write one of each other
// currently defined level 1 element
2007-09-05 03:23:19 +03:00
mkv - > main_seekhead = mkv_start_seekhead ( pb , mkv - > segment_offset , 10 ) ;
2010-06-05 01:41:11 +03:00
if ( ! mkv - > main_seekhead )
2007-09-05 03:25:29 +03:00
return AVERROR ( ENOMEM ) ;
2007-09-05 03:23:19 +03:00
2011-03-03 21:11:45 +02:00
ret = mkv_add_seekhead_entry ( mkv - > main_seekhead , MATROSKA_ID_INFO , avio_tell ( pb ) ) ;
2007-09-05 03:25:32 +03:00
if ( ret < 0 ) return ret ;
2007-09-05 03:23:15 +03:00
2007-09-05 03:24:56 +03:00
segment_info = start_ebml_master ( pb , MATROSKA_ID_INFO , 0 ) ;
2007-09-05 03:23:15 +03:00
put_ebml_uint ( pb , MATROSKA_ID_TIMECODESCALE , 1000000 ) ;
2011-05-22 13:46:29 +03:00
if ( ( tag = av_dict_get ( s - > metadata , " title " , NULL , 0 ) ) )
2009-02-15 18:53:05 +02:00
put_ebml_string ( pb , MATROSKA_ID_TITLE , tag - > value ) ;
2007-09-05 03:23:15 +03:00
if ( ! ( s - > streams [ 0 ] - > codec - > flags & CODEC_FLAG_BITEXACT ) ) {
2010-06-05 01:41:03 +03:00
uint32_t segment_uid [ 4 ] ;
2010-06-08 13:44:27 +03:00
AVLFG lfg ;
av_lfg_init ( & lfg , av_get_random_seed ( ) ) ;
2010-06-05 01:41:03 +03:00
for ( i = 0 ; i < 4 ; i + + )
2010-06-08 13:44:27 +03:00
segment_uid [ i ] = av_lfg_get ( & lfg ) ;
2010-06-05 01:41:03 +03:00
2007-09-05 03:23:59 +03:00
put_ebml_string ( pb , MATROSKA_ID_MUXINGAPP , LIBAVFORMAT_IDENT ) ;
2014-03-03 23:20:14 +03:00
if ( ( tag = av_dict_get ( s - > metadata , " encoding_tool " , NULL , 0 ) ) )
put_ebml_string ( pb , MATROSKA_ID_WRITINGAPP , tag - > value ) ;
else
put_ebml_string ( pb , MATROSKA_ID_WRITINGAPP , LIBAVFORMAT_IDENT ) ;
2010-06-05 01:41:03 +03:00
put_ebml_binary ( pb , MATROSKA_ID_SEGMENTUID , segment_uid , 16 ) ;
2007-09-05 03:23:15 +03:00
}
2007-09-05 03:24:03 +03:00
2007-09-05 03:23:15 +03:00
// reserve space for the duration
mkv - > duration = 0 ;
2011-03-03 21:11:45 +02:00
mkv - > duration_offset = avio_tell ( pb ) ;
2007-09-05 03:23:15 +03:00
put_ebml_void ( pb , 11 ) ; // assumes double-precision float to be written
end_ebml_master ( pb , segment_info ) ;
2007-09-05 03:25:32 +03:00
ret = mkv_write_tracks ( s ) ;
if ( ret < 0 ) return ret ;
2007-09-05 03:22:37 +03:00
2010-06-05 01:40:54 +03:00
if ( mkv - > mode ! = MODE_WEBM ) {
ret = mkv_write_chapters ( s ) ;
if ( ret < 0 ) return ret ;
2010-10-05 12:40:43 +03:00
ret = mkv_write_tags ( s ) ;
if ( ret < 0 ) return ret ;
2011-07-30 14:37:36 +03:00
ret = mkv_write_attachments ( s ) ;
if ( ret < 0 ) return ret ;
2010-06-05 01:40:54 +03:00
}
2009-10-18 15:17:01 +03:00
2011-03-05 22:06:46 +02:00
if ( ! s - > pb - > seekable )
2010-03-04 10:53:04 +02:00
mkv_write_seekhead ( pb , mkv - > main_seekhead ) ;
2007-09-05 03:23:27 +03:00
mkv - > cues = mkv_start_cues ( mkv - > segment_offset ) ;
if ( mkv - > cues = = NULL )
2007-09-05 03:25:27 +03:00
return AVERROR ( ENOMEM ) ;
2007-09-05 03:23:27 +03:00
2013-04-30 09:36:20 +03:00
if ( pb - > seekable & & mkv - > reserve_cues_space ) {
mkv - > cues_pos = avio_tell ( pb ) ;
put_ebml_void ( pb , mkv - > reserve_cues_space ) ;
}
2010-06-05 01:40:50 +03:00
av_init_packet ( & mkv - > cur_audio_pkt ) ;
mkv - > cur_audio_pkt . size = 0 ;
2011-03-14 21:39:06 +02:00
avio_flush ( pb ) ;
2013-08-18 21:09:40 +03:00
// start a new cluster every 5 MB or 5 sec, or 32k / 1 sec for streaming or
// after 4k and on a keyframe
if ( pb - > seekable ) {
if ( mkv - > cluster_time_limit < 0 )
mkv - > cluster_time_limit = 5000 ;
if ( mkv - > cluster_size_limit < 0 )
mkv - > cluster_size_limit = 5 * 1024 * 1024 ;
} else {
if ( mkv - > cluster_time_limit < 0 )
mkv - > cluster_time_limit = 1000 ;
if ( mkv - > cluster_size_limit < 0 )
mkv - > cluster_size_limit = 32 * 1024 ;
}
2007-09-05 03:22:37 +03:00
return 0 ;
}
2008-10-05 01:44:50 +03:00
static int mkv_blockgroup_size ( int pkt_size )
2007-09-05 03:24:56 +03:00
{
2008-10-05 01:44:50 +03:00
int size = pkt_size + 4 ;
2007-09-05 03:26:07 +03:00
size + = ebml_num_size ( size ) ;
2007-09-05 03:24:56 +03:00
size + = 2 ; // EBML ID for block and block duration
size + = 8 ; // max size of block duration
2007-09-05 03:26:07 +03:00
size + = ebml_num_size ( size ) ;
2007-09-05 03:24:56 +03:00
size + = 1 ; // blockgroup EBML ID
return size ;
}
2008-10-05 01:57:56 +03:00
static int ass_get_duration ( const uint8_t * p )
{
int sh , sm , ss , sc , eh , em , es , ec ;
uint64_t start , end ;
if ( sscanf ( p , " %*[^,],%d:%d:%d%*c%d,%d:%d:%d%*c%d " ,
& sh , & sm , & ss , & sc , & eh , & em , & es , & ec ) ! = 8 )
return 0 ;
start = 3600000 * sh + 60000 * sm + 1000 * ss + 10 * sc ;
end = 3600000 * eh + 60000 * em + 1000 * es + 10 * ec ;
return end - start ;
}
2011-02-20 12:04:12 +02:00
static int mkv_write_ass_blocks ( AVFormatContext * s , AVIOContext * pb , AVPacket * pkt )
2008-10-05 01:57:56 +03:00
{
MatroskaMuxContext * mkv = s - > priv_data ;
int i , layer = 0 , max_duration = 0 , size , line_size , data_size = pkt - > size ;
uint8_t * start , * end , * data = pkt - > data ;
ebml_master blockgroup ;
char buffer [ 2048 ] ;
while ( data_size ) {
int duration = ass_get_duration ( data ) ;
max_duration = FFMAX ( duration , max_duration ) ;
end = memchr ( data , ' \n ' , data_size ) ;
size = line_size = end ? end - data + 1 : data_size ;
size - = end ? ( end [ - 1 ] = = ' \r ' ) + 1 : 0 ;
start = data ;
for ( i = 0 ; i < 3 ; i + + , start + + )
if ( ! ( start = memchr ( start , ' , ' , size - ( start - data ) ) ) )
return max_duration ;
size - = start - data ;
sscanf ( data , " Dialogue: %d, " , & layer ) ;
i = snprintf ( buffer , sizeof ( buffer ) , " % " PRId64 " ,%d, " ,
2011-03-16 09:45:14 +02:00
s - > streams [ pkt - > stream_index ] - > nb_frames , layer ) ;
2008-10-05 01:57:56 +03:00
size = FFMIN ( i + size , sizeof ( buffer ) ) ;
memcpy ( buffer + i , start , size - i ) ;
av_log ( s , AV_LOG_DEBUG , " Writing block at offset % " PRIu64 " , size %d, "
" pts % " PRId64 " , duration %d \n " ,
2011-03-03 21:11:45 +02:00
avio_tell ( pb ) , size , pkt - > pts , duration ) ;
2008-10-05 01:57:56 +03:00
blockgroup = start_ebml_master ( pb , MATROSKA_ID_BLOCKGROUP , mkv_blockgroup_size ( size ) ) ;
put_ebml_id ( pb , MATROSKA_ID_BLOCK ) ;
put_ebml_num ( pb , size + 4 , 0 ) ;
2011-02-21 20:28:17 +02:00
avio_w8 ( pb , 0x80 | ( pkt - > stream_index + 1 ) ) ; // this assumes stream_index is less than 126
avio_wb16 ( pb , pkt - > pts - mkv - > cluster_pts ) ;
avio_w8 ( pb , 0 ) ;
avio_write ( pb , buffer , size ) ;
2008-10-05 01:57:56 +03:00
put_ebml_uint ( pb , MATROSKA_ID_BLOCKDURATION , duration ) ;
end_ebml_master ( pb , blockgroup ) ;
data + = line_size ;
data_size - = line_size ;
}
return max_duration ;
}
2013-05-28 11:09:21 +03:00
static int mkv_strip_wavpack ( const uint8_t * src , uint8_t * * pdst , int * size )
{
uint8_t * dst ;
int srclen = * size ;
int offset = 0 ;
int ret ;
dst = av_malloc ( srclen ) ;
if ( ! dst )
return AVERROR ( ENOMEM ) ;
while ( srclen > = WV_HEADER_SIZE ) {
WvHeader header ;
ret = ff_wv_parse_header ( & header , src ) ;
if ( ret < 0 )
goto fail ;
src + = WV_HEADER_SIZE ;
srclen - = WV_HEADER_SIZE ;
if ( srclen < header . blocksize ) {
ret = AVERROR_INVALIDDATA ;
goto fail ;
}
if ( header . initial ) {
AV_WL32 ( dst + offset , header . samples ) ;
offset + = 4 ;
}
AV_WL32 ( dst + offset , header . flags ) ;
AV_WL32 ( dst + offset + 4 , header . crc ) ;
offset + = 8 ;
if ( ! ( header . initial & & header . final ) ) {
AV_WL32 ( dst + offset , header . blocksize ) ;
offset + = 4 ;
}
memcpy ( dst + offset , src , header . blocksize ) ;
src + = header . blocksize ;
srclen - = header . blocksize ;
offset + = header . blocksize ;
}
* pdst = dst ;
* size = offset ;
return 0 ;
fail :
av_freep ( & dst ) ;
return ret ;
}
2011-02-20 12:04:12 +02:00
static void mkv_write_block ( AVFormatContext * s , AVIOContext * pb ,
2010-03-04 10:53:14 +02:00
unsigned int blockid , AVPacket * pkt , int flags )
2007-09-05 03:23:47 +03:00
{
MatroskaMuxContext * mkv = s - > priv_data ;
2009-01-15 16:03:07 +02:00
AVCodecContext * codec = s - > streams [ pkt - > stream_index ] - > codec ;
2009-01-20 02:52:12 +02:00
uint8_t * data = NULL ;
2012-09-15 01:59:05 +03:00
int offset = 0 , size = pkt - > size ;
2010-03-09 06:23:28 +02:00
int64_t ts = mkv - > tracks [ pkt - > stream_index ] . write_dts ? pkt - > dts : pkt - > pts ;
2007-09-05 03:23:47 +03:00
2007-09-05 03:25:38 +03:00
av_log ( s , AV_LOG_DEBUG , " Writing block at offset % " PRIu64 " , size %d, "
" pts % " PRId64 " , dts % " PRId64 " , duration %d, flags %d \n " ,
2011-03-03 21:11:45 +02:00
avio_tell ( pb ) , pkt - > size , pkt - > pts , pkt - > dts , pkt - > duration , flags ) ;
2012-08-05 12:11:04 +03:00
if ( codec - > codec_id = = AV_CODEC_ID_H264 & & codec - > extradata_size > 0 & &
2009-01-20 02:52:12 +02:00
( AV_RB24 ( codec - > extradata ) = = 1 | | AV_RB32 ( codec - > extradata ) = = 1 ) )
ff_avc_parse_nal_units_buf ( pkt - > data , & data , & size ) ;
2014-03-03 17:53:45 +03:00
else if ( codec - > codec_id = = AV_CODEC_ID_HEVC & & codec - > extradata_size > 6 & &
( AV_RB24 ( codec - > extradata ) = = 1 | | AV_RB32 ( codec - > extradata ) = = 1 ) )
/* extradata is Annex B, assume the bitstream is too and convert it */
ff_hevc_annexb2mp4_buf ( pkt - > data , & data , & size , 0 , NULL ) ;
2013-05-28 11:09:21 +03:00
else if ( codec - > codec_id = = AV_CODEC_ID_WAVPACK ) {
int ret = mkv_strip_wavpack ( pkt - > data , & data , & size ) ;
if ( ret < 0 ) {
av_log ( s , AV_LOG_ERROR , " Error stripping a WavPack packet. \n " ) ;
return ;
}
} else
2009-01-20 02:52:12 +02:00
data = pkt - > data ;
2012-09-15 01:59:05 +03:00
if ( codec - > codec_id = = AV_CODEC_ID_PRORES ) {
/* Matroska specification requires to remove the first QuickTime atom
*/
size - = 8 ;
offset = 8 ;
}
2007-09-05 03:23:47 +03:00
put_ebml_id ( pb , blockid ) ;
2009-01-20 02:52:12 +02:00
put_ebml_num ( pb , size + 4 , 0 ) ;
2011-02-21 20:28:17 +02:00
avio_w8 ( pb , 0x80 | ( pkt - > stream_index + 1 ) ) ; // this assumes stream_index is less than 126
avio_wb16 ( pb , ts - mkv - > cluster_pts ) ;
avio_w8 ( pb , flags ) ;
2012-09-15 01:59:05 +03:00
avio_write ( pb , data + offset , size ) ;
2009-01-20 02:52:12 +02:00
if ( data ! = pkt - > data )
av_free ( data ) ;
2007-09-05 03:23:47 +03:00
}
2010-07-26 00:16:58 +03:00
static int srt_get_duration ( uint8_t * * buf )
{
int i , duration = 0 ;
for ( i = 0 ; i < 2 & & ! duration ; i + + ) {
int s_hour , s_min , s_sec , s_hsec , e_hour , e_min , e_sec , e_hsec ;
if ( sscanf ( * buf , " %d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d " ,
& s_hour , & s_min , & s_sec , & s_hsec ,
& e_hour , & e_min , & e_sec , & e_hsec ) = = 8 ) {
s_min + = 60 * s_hour ; e_min + = 60 * e_hour ;
s_sec + = 60 * s_min ; e_sec + = 60 * e_min ;
s_hsec + = 1000 * s_sec ; e_hsec + = 1000 * e_sec ;
duration = e_hsec - s_hsec ;
}
* buf + = strcspn ( * buf , " \n " ) + 1 ;
}
return duration ;
}
2011-02-20 12:04:12 +02:00
static int mkv_write_srt_blocks ( AVFormatContext * s , AVIOContext * pb , AVPacket * pkt )
2010-07-26 00:16:58 +03:00
{
ebml_master blockgroup ;
AVPacket pkt2 = * pkt ;
int64_t duration = srt_get_duration ( & pkt2 . data ) ;
pkt2 . size - = pkt2 . data - pkt - > data ;
blockgroup = start_ebml_master ( pb , MATROSKA_ID_BLOCKGROUP ,
mkv_blockgroup_size ( pkt2 . size ) ) ;
mkv_write_block ( s , pb , MATROSKA_ID_BLOCK , & pkt2 , 0 ) ;
put_ebml_uint ( pb , MATROSKA_ID_BLOCKDURATION , duration ) ;
end_ebml_master ( pb , blockgroup ) ;
return duration ;
}
2010-03-04 10:53:14 +02:00
static void mkv_flush_dynbuf ( AVFormatContext * s )
{
MatroskaMuxContext * mkv = s - > priv_data ;
int bufsize ;
uint8_t * dyn_buf ;
if ( ! mkv - > dyn_bc )
return ;
2011-03-17 09:16:07 +02:00
bufsize = avio_close_dyn_buf ( mkv - > dyn_bc , & dyn_buf ) ;
2011-02-21 20:28:17 +02:00
avio_write ( s - > pb , dyn_buf , bufsize ) ;
2010-03-04 10:53:14 +02:00
av_free ( dyn_buf ) ;
mkv - > dyn_bc = NULL ;
}
2010-06-05 01:40:50 +03:00
static int mkv_write_packet_internal ( AVFormatContext * s , AVPacket * pkt )
2007-09-05 03:22:37 +03:00
{
2007-09-05 03:23:00 +03:00
MatroskaMuxContext * mkv = s - > priv_data ;
2011-02-20 12:04:12 +02:00
AVIOContext * pb = s - > pb ;
2007-09-05 03:23:53 +03:00
AVCodecContext * codec = s - > streams [ pkt - > stream_index ] - > codec ;
2010-03-31 15:29:58 +03:00
int keyframe = ! ! ( pkt - > flags & AV_PKT_FLAG_KEY ) ;
2008-09-05 02:08:19 +03:00
int duration = pkt - > duration ;
2007-09-05 03:25:32 +03:00
int ret ;
2010-03-09 06:23:28 +02:00
int64_t ts = mkv - > tracks [ pkt - > stream_index ] . write_dts ? pkt - > dts : pkt - > pts ;
2007-09-05 03:22:52 +03:00
2010-03-09 06:23:33 +02:00
if ( ts = = AV_NOPTS_VALUE ) {
av_log ( s , AV_LOG_ERROR , " Can't write packet with unknown timestamp \n " ) ;
return AVERROR ( EINVAL ) ;
}
2011-03-05 22:06:46 +02:00
if ( ! s - > pb - > seekable ) {
2010-03-04 10:53:14 +02:00
if ( ! mkv - > dyn_bc )
2011-03-17 09:13:34 +02:00
avio_open_dyn_buf ( & mkv - > dyn_bc ) ;
2010-03-04 10:53:14 +02:00
pb = mkv - > dyn_bc ;
}
2010-03-04 10:53:08 +02:00
if ( ! mkv - > cluster_pos ) {
2011-03-03 21:11:45 +02:00
mkv - > cluster_pos = avio_tell ( s - > pb ) ;
2007-09-05 03:24:56 +03:00
mkv - > cluster = start_ebml_master ( pb , MATROSKA_ID_CLUSTER , 0 ) ;
2010-03-09 06:23:36 +02:00
put_ebml_uint ( pb , MATROSKA_ID_CLUSTERTIMECODE , FFMAX ( 0 , ts ) ) ;
mkv - > cluster_pts = FFMAX ( 0 , ts ) ;
2007-09-05 03:23:00 +03:00
}
2010-03-31 02:30:55 +03:00
if ( codec - > codec_type ! = AVMEDIA_TYPE_SUBTITLE ) {
2010-03-04 10:53:14 +02:00
mkv_write_block ( s , pb , MATROSKA_ID_SIMPLEBLOCK , pkt , keyframe < < 7 ) ;
2012-08-05 12:11:04 +03:00
} else if ( codec - > codec_id = = AV_CODEC_ID_SSA ) {
2010-03-04 10:53:14 +02:00
duration = mkv_write_ass_blocks ( s , pb , pkt ) ;
2012-08-05 12:11:04 +03:00
} else if ( codec - > codec_id = = AV_CODEC_ID_SRT ) {
2010-07-26 00:16:58 +03:00
duration = mkv_write_srt_blocks ( s , pb , pkt ) ;
2007-09-05 03:23:49 +03:00
} else {
2008-10-05 01:44:50 +03:00
ebml_master blockgroup = start_ebml_master ( pb , MATROSKA_ID_BLOCKGROUP , mkv_blockgroup_size ( pkt - > size ) ) ;
2008-09-05 02:08:19 +03:00
duration = pkt - > convergence_duration ;
2010-03-04 10:53:14 +02:00
mkv_write_block ( s , pb , MATROSKA_ID_BLOCK , pkt , 0 ) ;
2008-10-05 01:47:10 +03:00
put_ebml_uint ( pb , MATROSKA_ID_BLOCKDURATION , duration ) ;
2007-09-05 03:23:49 +03:00
end_ebml_master ( pb , blockgroup ) ;
}
2007-09-05 03:23:11 +03:00
2010-03-31 02:30:55 +03:00
if ( codec - > codec_type = = AVMEDIA_TYPE_VIDEO & & keyframe ) {
2010-03-09 06:23:28 +02:00
ret = mkv_add_cuepoint ( mkv - > cues , pkt - > stream_index , ts , mkv - > cluster_pos ) ;
2007-09-05 03:25:32 +03:00
if ( ret < 0 ) return ret ;
2007-09-05 03:23:27 +03:00
}
2010-06-05 01:40:50 +03:00
mkv - > duration = FFMAX ( mkv - > duration , ts + duration ) ;
return 0 ;
}
static int mkv_write_packet ( AVFormatContext * s , AVPacket * pkt )
{
MatroskaMuxContext * mkv = s - > priv_data ;
2013-08-18 21:09:39 +03:00
int codec_type = s - > streams [ pkt - > stream_index ] - > codec - > codec_type ;
int keyframe = ! ! ( pkt - > flags & AV_PKT_FLAG_KEY ) ;
int cluster_size ;
int64_t cluster_time ;
AVIOContext * pb ;
int ret ;
if ( mkv - > tracks [ pkt - > stream_index ] . write_dts )
cluster_time = pkt - > dts - mkv - > cluster_pts ;
else
cluster_time = pkt - > pts - mkv - > cluster_pts ;
2010-06-05 01:40:50 +03:00
// start a new cluster every 5 MB or 5 sec, or 32k / 1 sec for streaming or
// after 4k and on a keyframe
2013-08-18 21:09:39 +03:00
if ( s - > pb - > seekable ) {
pb = s - > pb ;
cluster_size = avio_tell ( pb ) - mkv - > cluster_pos ;
} else {
pb = mkv - > dyn_bc ;
cluster_size = avio_tell ( pb ) ;
}
2010-06-05 01:40:50 +03:00
if ( mkv - > cluster_pos & &
2013-08-18 21:09:40 +03:00
( cluster_size > mkv - > cluster_size_limit | |
cluster_time > mkv - > cluster_time_limit | |
2013-08-18 21:09:39 +03:00
( codec_type = = AVMEDIA_TYPE_VIDEO & & keyframe & &
cluster_size > 4 * 1024 ) ) ) {
2010-03-04 10:53:08 +02:00
av_log ( s , AV_LOG_DEBUG , " Starting new cluster at offset % " PRIu64
2013-08-18 21:09:39 +03:00
" bytes, pts % " PRIu64 " dts % " PRIu64 " \n " ,
avio_tell ( pb ) , pkt - > pts , pkt - > dts ) ;
2010-03-04 10:53:08 +02:00
end_ebml_master ( pb , mkv - > cluster ) ;
mkv - > cluster_pos = 0 ;
2010-03-04 10:53:14 +02:00
if ( mkv - > dyn_bc )
mkv_flush_dynbuf ( s ) ;
2013-08-18 21:09:41 +03:00
avio_flush ( s - > pb ) ;
2010-03-04 10:53:08 +02:00
}
2010-06-05 01:40:50 +03:00
// check if we have an audio packet cached
if ( mkv - > cur_audio_pkt . size > 0 ) {
ret = mkv_write_packet_internal ( s , & mkv - > cur_audio_pkt ) ;
2012-10-31 10:53:18 +03:00
av_free_packet ( & mkv - > cur_audio_pkt ) ;
2010-06-05 01:40:50 +03:00
if ( ret < 0 ) {
av_log ( s , AV_LOG_ERROR , " Could not write cached audio packet ret:%d \n " , ret ) ;
return ret ;
}
}
// buffer an audio packet to ensure the packet containing the video
// keyframe's timecode is contained in the same cluster for WebM
2013-08-18 21:09:39 +03:00
if ( codec_type = = AVMEDIA_TYPE_AUDIO ) {
2012-10-31 10:53:18 +03:00
mkv - > cur_audio_pkt = * pkt ;
2013-06-24 00:00:33 +03:00
if ( pkt - > buf ) {
mkv - > cur_audio_pkt . buf = av_buffer_ref ( pkt - > buf ) ;
ret = mkv - > cur_audio_pkt . buf ? 0 : AVERROR ( ENOMEM ) ;
} else
ret = av_dup_packet ( & mkv - > cur_audio_pkt ) ;
2012-10-31 10:53:18 +03:00
} else
2010-06-05 01:40:50 +03:00
ret = mkv_write_packet_internal ( s , pkt ) ;
return ret ;
2007-09-05 03:22:37 +03:00
}
2013-08-19 10:33:14 +03:00
static int mkv_write_flush_packet ( AVFormatContext * s , AVPacket * pkt )
{
MatroskaMuxContext * mkv = s - > priv_data ;
AVIOContext * pb ;
if ( s - > pb - > seekable )
pb = s - > pb ;
else
pb = mkv - > dyn_bc ;
if ( ! pkt ) {
if ( mkv - > cluster_pos ) {
av_log ( s , AV_LOG_DEBUG , " Flushing cluster at offset % " PRIu64
" bytes \n " , avio_tell ( pb ) ) ;
end_ebml_master ( pb , mkv - > cluster ) ;
mkv - > cluster_pos = 0 ;
if ( mkv - > dyn_bc )
mkv_flush_dynbuf ( s ) ;
avio_flush ( s - > pb ) ;
}
return 0 ;
}
return mkv_write_packet ( s , pkt ) ;
}
2007-09-05 03:22:37 +03:00
static int mkv_write_trailer ( AVFormatContext * s )
{
MatroskaMuxContext * mkv = s - > priv_data ;
2011-02-20 12:04:12 +02:00
AVIOContext * pb = s - > pb ;
2010-06-05 01:41:11 +03:00
int64_t currentpos , cuespos ;
2007-09-05 03:25:34 +03:00
int ret ;
2007-09-05 03:23:11 +03:00
2010-06-05 01:40:50 +03:00
// check if we have an audio packet cached
if ( mkv - > cur_audio_pkt . size > 0 ) {
ret = mkv_write_packet_internal ( s , & mkv - > cur_audio_pkt ) ;
2012-10-31 10:53:18 +03:00
av_free_packet ( & mkv - > cur_audio_pkt ) ;
2010-06-05 01:40:50 +03:00
if ( ret < 0 ) {
av_log ( s , AV_LOG_ERROR , " Could not write cached audio packet ret:%d \n " , ret ) ;
return ret ;
}
}
2010-03-04 10:53:14 +02:00
if ( mkv - > dyn_bc ) {
end_ebml_master ( mkv - > dyn_bc , mkv - > cluster ) ;
mkv_flush_dynbuf ( s ) ;
} else if ( mkv - > cluster_pos ) {
end_ebml_master ( pb , mkv - > cluster ) ;
}
2007-09-05 03:23:11 +03:00
2013-09-03 20:53:34 +03:00
if ( mkv - > mode ! = MODE_WEBM ) {
ret = mkv_write_chapters ( s ) ;
if ( ret < 0 ) return ret ;
}
2011-03-05 22:06:46 +02:00
if ( pb - > seekable ) {
2011-04-04 23:29:21 +03:00
if ( mkv - > cues - > num_entries ) {
2013-04-30 09:36:20 +03:00
if ( mkv - > reserve_cues_space ) {
int64_t cues_end ;
currentpos = avio_tell ( pb ) ;
avio_seek ( pb , mkv - > cues_pos , SEEK_SET ) ;
cuespos = mkv_write_cues ( pb , mkv - > cues , s - > nb_streams ) ;
cues_end = avio_tell ( pb ) ;
if ( cues_end > cuespos + mkv - > reserve_cues_space ) {
av_log ( s , AV_LOG_ERROR , " Insufficient space reserved for cues: %d "
" (needed: % " PRId64 " ). \n " , mkv - > reserve_cues_space ,
cues_end - cuespos ) ;
return AVERROR ( EINVAL ) ;
}
if ( cues_end < cuespos + mkv - > reserve_cues_space )
put_ebml_void ( pb , mkv - > reserve_cues_space - ( cues_end - cuespos ) ) ;
avio_seek ( pb , currentpos , SEEK_SET ) ;
} else {
cuespos = mkv_write_cues ( pb , mkv - > cues , s - > nb_streams ) ;
}
2011-04-04 23:29:21 +03:00
ret = mkv_add_seekhead_entry ( mkv - > main_seekhead , MATROSKA_ID_CUES , cuespos ) ;
if ( ret < 0 ) return ret ;
}
2007-09-05 03:23:27 +03:00
2007-09-05 03:26:03 +03:00
mkv_write_seekhead ( pb , mkv - > main_seekhead ) ;
// update the duration
av_log ( s , AV_LOG_DEBUG , " end duration = % " PRIu64 " \n " , mkv - > duration ) ;
2011-03-03 21:11:45 +02:00
currentpos = avio_tell ( pb ) ;
2011-02-28 15:57:54 +02:00
avio_seek ( pb , mkv - > duration_offset , SEEK_SET ) ;
2007-09-05 03:26:03 +03:00
put_ebml_float ( pb , MATROSKA_ID_DURATION , mkv - > duration ) ;
2011-02-28 15:57:54 +02:00
avio_seek ( pb , currentpos , SEEK_SET ) ;
2007-09-05 03:26:00 +03:00
}
2007-09-05 03:23:11 +03:00
2007-09-05 03:22:37 +03:00
end_ebml_master ( pb , mkv - > segment ) ;
2010-03-09 06:23:28 +02:00
av_free ( mkv - > tracks ) ;
2011-05-04 19:57:37 +03:00
av_freep ( & mkv - > cues - > entries ) ;
av_freep ( & mkv - > cues ) ;
2012-09-09 22:35:23 +03:00
2007-09-05 03:22:37 +03:00
return 0 ;
}
2012-08-05 12:11:04 +03:00
static int mkv_query_codec ( enum AVCodecID codec_id , int std_compliance )
2011-08-11 21:35:46 +03:00
{
int i ;
2012-08-05 12:11:04 +03:00
for ( i = 0 ; ff_mkv_codec_tags [ i ] . id ! = AV_CODEC_ID_NONE ; i + + )
2011-08-11 21:35:46 +03:00
if ( ff_mkv_codec_tags [ i ] . id = = codec_id )
return 1 ;
if ( std_compliance < FF_COMPLIANCE_NORMAL ) { // mkv theoretically supports any
enum AVMediaType type = avcodec_get_type ( codec_id ) ; // video/audio through VFW/ACM
if ( type = = AVMEDIA_TYPE_VIDEO | | type = = AVMEDIA_TYPE_AUDIO )
return 1 ;
}
return 0 ;
}
2013-04-30 09:36:20 +03:00
# define OFFSET(x) offsetof(MatroskaMuxContext, x)
# define FLAGS AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options [ ] = {
2013-08-18 21:09:40 +03:00
{ " reserve_index_space " , " Reserve a given amount of space (in bytes) at the beginning of the file for the index (cues). " , OFFSET ( reserve_cues_space ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , INT_MAX , FLAGS } ,
{ " cluster_size_limit " , " Store at most the provided amount of bytes in a cluster. " , OFFSET ( cluster_size_limit ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , INT_MAX , FLAGS } ,
{ " cluster_time_limit " , " Store at most the provided number of milliseconds in a cluster. " , OFFSET ( cluster_time_limit ) , AV_OPT_TYPE_INT64 , { . i64 = - 1 } , - 1 , INT64_MAX , FLAGS } ,
2013-04-30 09:36:20 +03:00
{ NULL } ,
} ;
2010-06-05 01:40:54 +03:00
# if CONFIG_MATROSKA_MUXER
2013-04-30 09:36:20 +03:00
static const AVClass matroska_class = {
. class_name = " matroska muxer " ,
. item_name = av_default_item_name ,
. option = options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
2011-01-26 00:03:28 +02:00
AVOutputFormat ff_matroska_muxer = {
2011-07-16 23:18:12 +03:00
. name = " matroska " ,
2012-07-25 00:51:41 +03:00
. long_name = NULL_IF_CONFIG_SMALL ( " Matroska " ) ,
2011-07-16 23:18:12 +03:00
. mime_type = " video/x-matroska " ,
. extensions = " mkv " ,
. priv_data_size = sizeof ( MatroskaMuxContext ) ,
2012-07-22 02:44:26 +03:00
. audio_codec = CONFIG_LIBVORBIS_ENCODER ?
2012-08-05 12:11:04 +03:00
AV_CODEC_ID_VORBIS : AV_CODEC_ID_AC3 ,
2012-07-22 02:44:26 +03:00
. video_codec = CONFIG_LIBX264_ENCODER ?
2012-08-05 12:11:04 +03:00
AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4 ,
2011-07-16 23:18:12 +03:00
. write_header = mkv_write_header ,
2013-08-19 10:33:14 +03:00
. write_packet = mkv_write_flush_packet ,
2011-07-16 23:18:12 +03:00
. write_trailer = mkv_write_trailer ,
2011-05-26 21:19:04 +03:00
. flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
2013-08-19 10:33:14 +03:00
AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH ,
2012-04-06 17:50:48 +03:00
. codec_tag = ( const AVCodecTag * const [ ] ) {
ff_codec_bmp_tags , ff_codec_wav_tags , 0
} ,
2012-08-05 12:11:04 +03:00
. subtitle_codec = AV_CODEC_ID_SSA ,
2011-08-11 21:35:46 +03:00
. query_codec = mkv_query_codec ,
2013-04-30 09:36:20 +03:00
. priv_class = & matroska_class ,
2007-09-05 03:22:37 +03:00
} ;
2010-06-05 01:40:54 +03:00
# endif
# if CONFIG_WEBM_MUXER
2013-04-30 09:36:20 +03:00
static const AVClass webm_class = {
. class_name = " webm muxer " ,
. item_name = av_default_item_name ,
. option = options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
2011-01-26 00:03:28 +02:00
AVOutputFormat ff_webm_muxer = {
2011-07-16 23:18:12 +03:00
. name = " webm " ,
2012-07-25 00:51:41 +03:00
. long_name = NULL_IF_CONFIG_SMALL ( " WebM " ) ,
2011-07-16 23:18:12 +03:00
. mime_type = " video/webm " ,
. extensions = " webm " ,
. priv_data_size = sizeof ( MatroskaMuxContext ) ,
2012-08-05 12:11:04 +03:00
. audio_codec = AV_CODEC_ID_VORBIS ,
. video_codec = AV_CODEC_ID_VP8 ,
2011-07-16 23:18:12 +03:00
. write_header = mkv_write_header ,
2013-08-19 10:33:14 +03:00
. write_packet = mkv_write_flush_packet ,
2011-07-16 23:18:12 +03:00
. write_trailer = mkv_write_trailer ,
2011-05-26 21:19:04 +03:00
. flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
2013-08-19 10:33:14 +03:00
AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH ,
2013-04-30 09:36:20 +03:00
. priv_class = & webm_class ,
2010-06-05 01:40:54 +03:00
} ;
# endif
2007-09-05 03:24:19 +03:00
2010-06-05 01:40:54 +03:00
# if CONFIG_MATROSKA_AUDIO_MUXER
2013-04-30 09:36:20 +03:00
static const AVClass mka_class = {
. class_name = " matroska audio muxer " ,
. item_name = av_default_item_name ,
. option = options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
2011-01-26 00:03:28 +02:00
AVOutputFormat ff_matroska_audio_muxer = {
2011-07-16 23:18:12 +03:00
. name = " matroska " ,
2012-07-25 00:51:41 +03:00
. long_name = NULL_IF_CONFIG_SMALL ( " Matroska " ) ,
2011-07-16 23:18:12 +03:00
. mime_type = " audio/x-matroska " ,
. extensions = " mka " ,
. priv_data_size = sizeof ( MatroskaMuxContext ) ,
2012-07-22 02:44:26 +03:00
. audio_codec = CONFIG_LIBVORBIS_ENCODER ?
2012-08-05 12:11:04 +03:00
AV_CODEC_ID_VORBIS : AV_CODEC_ID_AC3 ,
. video_codec = AV_CODEC_ID_NONE ,
2011-07-16 23:18:12 +03:00
. write_header = mkv_write_header ,
2013-08-19 10:33:14 +03:00
. write_packet = mkv_write_flush_packet ,
2011-07-16 23:18:12 +03:00
. write_trailer = mkv_write_trailer ,
2013-08-19 10:33:14 +03:00
. flags = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT |
AVFMT_ALLOW_FLUSH ,
2012-04-06 17:50:48 +03:00
. codec_tag = ( const AVCodecTag * const [ ] ) { ff_codec_wav_tags , 0 } ,
2013-04-30 09:36:20 +03:00
. priv_class = & mka_class ,
2007-09-05 03:24:19 +03:00
} ;
2010-06-05 01:40:54 +03:00
# endif