]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/matroskaenc.c
set bytes per sample in the context
[ffmpeg] / libavformat / matroskaenc.c
index 5d6bd03820f4905f6f7aa2892773beefa4f1e898..6f45f446c0ef20ca422890198723b4d513baadfd 100644 (file)
@@ -98,9 +98,9 @@ static void put_ebml_id(ByteIOContext *pb, unsigned int id)
 }
 
 /**
- * Write an EBML size meaning "unknown size"
+ * Write an EBML size meaning "unknown size".
  *
- * @param bytes The number of bytes the size should occupy. Maximum of 8.
+ * @param bytes The number of bytes the size should occupy (maximum: 8).
  */
 static void put_ebml_size_unknown(ByteIOContext *pb, int bytes)
 {
@@ -111,31 +111,27 @@ static void put_ebml_size_unknown(ByteIOContext *pb, int bytes)
 }
 
 /**
- * Calculate how many bytes are needed to represent a given size in EBML.
+ * Calculate how many bytes are needed to represent a given number in EBML.
  */
-static int ebml_size_bytes(uint64_t size)
+static int ebml_num_size(uint64_t num)
 {
     int bytes = 1;
-    while ((size+1) >> bytes*7) bytes++;
+    while ((num+1) >> bytes*7) bytes++;
     return bytes;
 }
 
 /**
- * Write a size in EBML variable length format.
+ * Write a number in EBML variable length format.
  *
- * @param bytes The number of bytes that need to be used to write the size.
+ * @param bytes The number of bytes that need to be used to write the number.
  *              If zero, any number of bytes can be used.
  */
-static void put_ebml_size(ByteIOContext *pb, uint64_t size, int bytes)
+static void put_ebml_num(ByteIOContext *pb, uint64_t num, int bytes)
 {
-    int i, needed_bytes = ebml_size_bytes(size);
+    int i, needed_bytes = ebml_num_size(num);
 
     // sizes larger than this are currently undefined in EBML
-    // so write "unknown" size
-    if (size >= (1ULL<<56)-1) {
-        put_ebml_size_unknown(pb, 1);
-        return;
-    }
+    assert(num < (1ULL<<56)-1);
 
     if (bytes == 0)
         // don't care how many bytes are used, so use the min
@@ -144,9 +140,9 @@ static void put_ebml_size(ByteIOContext *pb, uint64_t size, int bytes)
     // that we need to use, so write unknown size. This shouldn't happen.
     assert(bytes >= needed_bytes);
 
-    size |= 1ULL << bytes*7;
+    num |= 1ULL << bytes*7;
     for (i = bytes - 1; i >= 0; i--)
-        put_byte(pb, size >> i*8);
+        put_byte(pb, num >> i*8);
 }
 
 static void put_ebml_uint(ByteIOContext *pb, unsigned int elementid, uint64_t val)
@@ -155,7 +151,7 @@ static void put_ebml_uint(ByteIOContext *pb, unsigned int elementid, uint64_t va
     while (val >> bytes*8) bytes++;
 
     put_ebml_id(pb, elementid);
-    put_ebml_size(pb, bytes, 0);
+    put_ebml_num(pb, bytes, 0);
     for (i = bytes - 1; i >= 0; i--)
         put_byte(pb, val >> i*8);
 }
@@ -163,7 +159,7 @@ static void put_ebml_uint(ByteIOContext *pb, unsigned int elementid, uint64_t va
 static void put_ebml_float(ByteIOContext *pb, unsigned int elementid, double val)
 {
     put_ebml_id(pb, elementid);
-    put_ebml_size(pb, 8, 0);
+    put_ebml_num(pb, 8, 0);
     put_be64(pb, av_dbl2int(val));
 }
 
@@ -171,7 +167,7 @@ static void put_ebml_binary(ByteIOContext *pb, unsigned int elementid,
                             const uint8_t *buf, int size)
 {
     put_ebml_id(pb, elementid);
-    put_ebml_size(pb, size, 0);
+    put_ebml_num(pb, size, 0);
     put_buffer(pb, buf, size);
 }
 
@@ -197,15 +193,15 @@ static void put_ebml_void(ByteIOContext *pb, uint64_t size)
     // size we need to reserve so 2 cases, we use 8 bytes to store the
     // size if possible, 1 byte otherwise
     if (size < 10)
-        put_ebml_size(pb, size-1, 0);
+        put_ebml_num(pb, size-1, 0);
     else
-        put_ebml_size(pb, size-9, 8);
+        put_ebml_num(pb, size-9, 8);
     url_fseek(pb, currentpos + size, SEEK_SET);
 }
 
 static ebml_master start_ebml_master(ByteIOContext *pb, unsigned int elementid, uint64_t expectedsize)
 {
-    int bytes = expectedsize ? ebml_size_bytes(expectedsize) : 8;
+    int bytes = expectedsize ? ebml_num_size(expectedsize) : 8;
     put_ebml_id(pb, elementid);
     put_ebml_size_unknown(pb, bytes);
     return (ebml_master){ url_ftell(pb), bytes };
@@ -215,8 +211,12 @@ static void end_ebml_master(ByteIOContext *pb, ebml_master master)
 {
     offset_t pos = url_ftell(pb);
 
+    // leave the unknown size for masters when streaming
+    if (url_is_streamed(pb))
+        return;
+
     url_fseek(pb, master.pos - master.sizebytes, SEEK_SET);
-    put_ebml_size(pb, pos - master.pos, master.sizebytes);
+    put_ebml_num(pb, pos - master.pos, master.sizebytes);
     url_fseek(pb, pos, SEEK_SET);
 }
 
@@ -235,8 +235,8 @@ static void put_xiph_size(ByteIOContext *pb, int size)
  * that size.
  *
  * @param segment_offset The absolute offset to the position in the file
- *                       where the segment begins
- * @param numelements the maximum number of elements that will be indexed
+ *                       where the segment begins.
+ * @param numelements The maximum number of elements that will be indexed
  *                    by this seek head, 0 if unlimited.
  */
 static mkv_seekhead * mkv_start_seekhead(ByteIOContext *pb, offset_t segment_offset, int numelements)
@@ -275,7 +275,6 @@ static int mkv_add_seekhead_entry(mkv_seekhead *seekhead, unsigned int elementid
     entries[seekhead->num_entries++].segmentpos = filepos - seekhead->segment_offset;
 
     seekhead->entries = entries;
-
     return 0;
 }
 
@@ -285,7 +284,7 @@ static int mkv_add_seekhead_entry(mkv_seekhead *seekhead, unsigned int elementid
  * be written at the location reserved for it. Otherwise, it is written
  * at the current location in the file.
  *
- * @return the file offset where the seekhead was written
+ * @return The file offset where the seekhead was written.
  */
 static offset_t mkv_write_seekhead(ByteIOContext *pb, mkv_seekhead *seekhead)
 {
@@ -305,7 +304,7 @@ static offset_t mkv_write_seekhead(ByteIOContext *pb, mkv_seekhead *seekhead)
         seekentry = start_ebml_master(pb, MATROSKA_ID_SEEKENTRY, MAX_SEEKENTRY_SIZE);
 
         put_ebml_id(pb, MATROSKA_ID_SEEKID);
-        put_ebml_size(pb, ebml_id_size(entry->elementid), 0);
+        put_ebml_num(pb, ebml_id_size(entry->elementid), 0);
         put_ebml_id(pb, entry->elementid);
 
         put_ebml_uint(pb, MATROSKA_ID_SEEKPOSITION, entry->segmentpos);
@@ -387,9 +386,8 @@ static offset_t mkv_write_cues(ByteIOContext *pb, mkv_cues *cues, int num_tracks
     return currentpos;
 }
 
-static int put_xiph_codecpriv(ByteIOContext *pb, AVCodecContext *codec)
+static int put_xiph_codecpriv(AVFormatContext *s, ByteIOContext *pb, AVCodecContext *codec)
 {
-    ebml_master codecprivate;
     uint8_t *header_start[3];
     int header_len[3];
     int first_header_size;
@@ -402,45 +400,40 @@ static int put_xiph_codecpriv(ByteIOContext *pb, AVCodecContext *codec)
 
     if (ff_split_xiph_headers(codec->extradata, codec->extradata_size,
                               first_header_size, header_start, header_len) < 0) {
-        av_log(codec, AV_LOG_ERROR, "Extradata corrupt.\n");
+        av_log(s, AV_LOG_ERROR, "Extradata corrupt.\n");
         return -1;
     }
 
-    codecprivate = start_ebml_master(pb, MATROSKA_ID_CODECPRIVATE, 0);
     put_byte(pb, 2);                    // number packets - 1
     for (j = 0; j < 2; j++) {
         put_xiph_size(pb, header_len[j]);
     }
     for (j = 0; j < 3; j++)
         put_buffer(pb, header_start[j], header_len[j]);
-    end_ebml_master(pb, codecprivate);
 
     return 0;
 }
 
 #define FLAC_STREAMINFO_SIZE 34
 
-static int put_flac_codecpriv(ByteIOContext *pb, AVCodecContext *codec)
+static int put_flac_codecpriv(AVFormatContext *s, ByteIOContext *pb, AVCodecContext *codec)
 {
-    ebml_master codecpriv = start_ebml_master(pb, MATROSKA_ID_CODECPRIVATE, 0);
-
     // if the extradata_size is greater than FLAC_STREAMINFO_SIZE,
     // assume that it's in Matroska's format already
     if (codec->extradata_size < FLAC_STREAMINFO_SIZE) {
-        av_log(codec, AV_LOG_ERROR, "Invalid FLAC extradata\n");
+        av_log(s, AV_LOG_ERROR, "Invalid FLAC extradata\n");
         return -1;
     } else if (codec->extradata_size == FLAC_STREAMINFO_SIZE) {
         // only the streaminfo packet
         put_byte(pb, 0);
         put_xiph_size(pb, codec->extradata_size);
-        av_log(codec, AV_LOG_ERROR, "Only one packet\n");
+        av_log(s, AV_LOG_ERROR, "Only one packet\n");
     }
     put_buffer(pb, codec->extradata, codec->extradata_size);
-    end_ebml_master(pb, codecpriv);
     return 0;
 }
 
-static void get_aac_sample_rates(AVCodecContext *codec, int *sample_rate, int *output_sample_rate)
+static void get_aac_sample_rates(AVFormatContext *s, AVCodecContext *codec, int *sample_rate, int *output_sample_rate)
 {
     static const int aac_sample_rates[] = {
         96000, 88200, 64000, 48000, 44100, 32000,
@@ -449,13 +442,13 @@ static void get_aac_sample_rates(AVCodecContext *codec, int *sample_rate, int *o
     int sri;
 
     if (codec->extradata_size < 2) {
-        av_log(codec, AV_LOG_WARNING, "no AAC extradata, unable to determine samplerate\n");
+        av_log(s, AV_LOG_WARNING, "No AAC extradata, unable to determine samplerate.\n");
         return;
     }
 
     sri = ((codec->extradata[0] << 1) & 0xE) | (codec->extradata[1] >> 7);
     if (sri > 12) {
-        av_log(codec, AV_LOG_WARNING, "AAC samplerate index out of bounds\n");
+        av_log(s, AV_LOG_WARNING, "AAC samplerate index out of bounds\n");
         return;
     }
     *sample_rate = aac_sample_rates[sri];
@@ -464,13 +457,56 @@ static void get_aac_sample_rates(AVCodecContext *codec, int *sample_rate, int *o
     if (codec->extradata_size == 5) {
         sri = (codec->extradata[4] >> 3) & 0xF;
         if (sri > 12) {
-            av_log(codec, AV_LOG_WARNING, "AAC output samplerate index out of bounds\n");
+            av_log(s, AV_LOG_WARNING, "AAC output samplerate index out of bounds\n");
             return;
         }
         *output_sample_rate = aac_sample_rates[sri];
     }
 }
 
+static int mkv_write_codecprivate(AVFormatContext *s, ByteIOContext *pb, AVCodecContext *codec, int native_id)
+{
+    ByteIOContext dyn_cp;
+    uint8_t *codecpriv;
+    int ret = 0, codecpriv_size;
+
+    url_open_dyn_buf(&dyn_cp);
+
+    if (native_id) {
+        if (codec->codec_id == CODEC_ID_VORBIS || codec->codec_id == CODEC_ID_THEORA)
+            ret = put_xiph_codecpriv(s, &dyn_cp, codec);
+        else if (codec->codec_id == CODEC_ID_FLAC)
+            ret = put_flac_codecpriv(s, &dyn_cp, codec);
+        else if (codec->extradata_size)
+            put_buffer(&dyn_cp, codec->extradata, codec->extradata_size);
+    } else if (codec->codec_type == CODEC_TYPE_VIDEO) {
+        if (!codec->codec_tag)
+            codec->codec_tag = codec_get_tag(codec_bmp_tags, codec->codec_id);
+        if (!codec->codec_tag) {
+            av_log(s, AV_LOG_ERROR, "No bmp codec ID found.");
+            ret = -1;
+        }
+
+        put_bmp_header(&dyn_cp, codec, codec_bmp_tags, 0);
+
+    } else if (codec->codec_type == CODEC_TYPE_AUDIO) {
+        if (!codec->codec_tag)
+            codec->codec_tag = codec_get_tag(codec_wav_tags, codec->codec_id);
+        if (!codec->codec_tag) {
+            av_log(s, AV_LOG_ERROR, "No wav codec ID found.");
+            ret = -1;
+        }
+
+        put_wav_header(&dyn_cp, codec);
+    }
+
+    codecpriv_size = url_close_dyn_buf(&dyn_cp, &codecpriv);
+    if (codecpriv_size)
+        put_ebml_binary(pb, MATROSKA_ID_CODECPRIVATE, codecpriv, codecpriv_size);
+    av_free(codecpriv);
+    return ret;
+}
+
 static int mkv_write_tracks(AVFormatContext *s)
 {
     MatroskaMuxContext *mkv = s->priv_data;
@@ -495,7 +531,7 @@ static int mkv_write_tracks(AVFormatContext *s)
             bit_depth = av_get_bits_per_sample_format(codec->sample_fmt);
 
         if (codec->codec_id == CODEC_ID_AAC)
-            get_aac_sample_rates(codec, &sample_rate, &output_sample_rate);
+            get_aac_sample_rates(s, codec, &sample_rate, &output_sample_rate);
 
         track = start_ebml_master(pb, MATROSKA_ID_TRACKENTRY, 0);
         put_ebml_uint (pb, MATROSKA_ID_TRACKNUMBER     , i + 1);
@@ -507,7 +543,7 @@ static int mkv_write_tracks(AVFormatContext *s)
         else
             put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, "und");
 
-        // look for a codec id string specific to mkv to use,
+        // look for a codec ID string specific to mkv to use,
         // if none are found, use AVI codes
         for (j = 0; ff_mkv_codec_tags[j].id != CODEC_ID_NONE; j++) {
             if (ff_mkv_codec_tags[j].id == codec->codec_id) {
@@ -517,33 +553,14 @@ static int mkv_write_tracks(AVFormatContext *s)
             }
         }
 
-        if (native_id) {
-            if (codec->codec_id == CODEC_ID_VORBIS || codec->codec_id == CODEC_ID_THEORA) {
-                ret = put_xiph_codecpriv(pb, codec);
-                if (ret < 0) return ret;
-            } else if (codec->codec_id == CODEC_ID_FLAC) {
-                ret = put_flac_codecpriv(pb, codec);
-                if (ret < 0) return ret;
-            } else if (codec->extradata_size) {
-                put_ebml_binary(pb, MATROSKA_ID_CODECPRIVATE, codec->extradata, codec->extradata_size);
-            }
-        }
-
         switch (codec->codec_type) {
             case CODEC_TYPE_VIDEO:
                 put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_VIDEO);
 
-                if (!native_id) {
-                    ebml_master bmp_header;
-                    // if there is no mkv-specific codec id, use VFW mode
-                    if (!codec->codec_tag)
-                        codec->codec_tag = codec_get_tag(codec_bmp_tags, codec->codec_id);
-
+                if (!native_id)
+                    // if there is no mkv-specific codec ID, use VFW mode
                     put_ebml_string(pb, MATROSKA_ID_CODECID, MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
-                    bmp_header = start_ebml_master(pb, MATROSKA_ID_CODECPRIVATE, 0);
-                    put_bmp_header(pb, codec, codec_bmp_tags, 0);
-                    end_ebml_master(pb, bmp_header);
-                }
+
                 subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKVIDEO, 0);
                 // XXX: interlace flag?
                 put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELWIDTH , codec->width);
@@ -560,20 +577,10 @@ static int mkv_write_tracks(AVFormatContext *s)
             case CODEC_TYPE_AUDIO:
                 put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_AUDIO);
 
-                if (!native_id) {
-                    ebml_master wav_header;
+                if (!native_id)
                     // no mkv-specific ID, use ACM mode
-                    codec->codec_tag = codec_get_tag(codec_wav_tags, codec->codec_id);
-                    if (!codec->codec_tag) {
-                        av_log(s, AV_LOG_ERROR, "no codec id found for stream %d", i);
-                        return -1;
-                    }
-
                     put_ebml_string(pb, MATROSKA_ID_CODECID, MATROSKA_CODEC_ID_AUDIO_ACM);
-                    wav_header = start_ebml_master(pb, MATROSKA_ID_CODECPRIVATE, 0);
-                    put_wav_header(pb, codec);
-                    end_ebml_master(pb, wav_header);
-                }
+
                 subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKAUDIO, 0);
                 put_ebml_uint  (pb, MATROSKA_ID_AUDIOCHANNELS    , codec->channels);
                 put_ebml_float (pb, MATROSKA_ID_AUDIOSAMPLINGFREQ, sample_rate);
@@ -591,6 +598,9 @@ static int mkv_write_tracks(AVFormatContext *s)
                 av_log(s, AV_LOG_ERROR, "Only audio, video, and subtitles are supported for Matroska.");
                 break;
         }
+        ret = mkv_write_codecprivate(s, pb, codec, native_id);
+        if (ret < 0) return ret;
+
         end_ebml_master(pb, track);
 
         // ms precision is the de-facto standard timescale for mkv files
@@ -682,10 +692,10 @@ static int mkv_block_size(AVPacket *pkt)
 static int mkv_blockgroup_size(AVPacket *pkt)
 {
     int size = mkv_block_size(pkt);
-    size += ebml_size_bytes(size);
+    size += ebml_num_size(size);
     size += 2;              // EBML ID for block and block duration
     size += 8;              // max size of block duration
-    size += ebml_size_bytes(size);
+    size += ebml_num_size(size);
     size += 1;              // blockgroup EBML ID
     return size;
 }
@@ -699,7 +709,7 @@ static void mkv_write_block(AVFormatContext *s, unsigned int blockid, AVPacket *
            "pts %" PRId64 ", dts %" PRId64 ", duration %d, flags %d\n",
            url_ftell(pb), pkt->size, pkt->pts, pkt->dts, pkt->duration, flags);
     put_ebml_id(pb, blockid);
-    put_ebml_size(pb, mkv_block_size(pkt), 0);
+    put_ebml_num(pb, mkv_block_size(pkt), 0);
     put_byte(pb, 0x80 | (pkt->stream_index + 1));     // this assumes stream_index is less than 126
     put_be16(pb, pkt->pts - mkv->cluster_pts);
     put_byte(pb, flags);
@@ -757,29 +767,31 @@ static int mkv_write_trailer(AVFormatContext *s)
 
     end_ebml_master(pb, mkv->cluster);
 
-    cuespos = mkv_write_cues(pb, mkv->cues, s->nb_streams);
-    second_seekhead = mkv_write_seekhead(pb, mkv->cluster_seekhead);
-
-    ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_CUES    , cuespos);
-    if (ret < 0) return ret;
-    ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_SEEKHEAD, second_seekhead);
-    if (ret < 0) return ret;
-    mkv_write_seekhead(pb, mkv->main_seekhead);
+    if (!url_is_streamed(pb)) {
+        cuespos = mkv_write_cues(pb, mkv->cues, s->nb_streams);
+        second_seekhead = mkv_write_seekhead(pb, mkv->cluster_seekhead);
 
-    // update the duration
-    av_log(s, AV_LOG_DEBUG, "end duration = %" PRIu64 "\n", mkv->duration);
-    currentpos = url_ftell(pb);
-    url_fseek(pb, mkv->duration_offset, SEEK_SET);
-    put_ebml_float(pb, MATROSKA_ID_DURATION, mkv->duration);
-
-    // write the md5sum of some frames as the segment UID
-    if (!(s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT)) {
-        uint8_t segment_uid[16];
-        av_md5_final(mkv->md5_ctx, segment_uid);
-        url_fseek(pb, mkv->segment_uid, SEEK_SET);
-        put_ebml_binary(pb, MATROSKA_ID_SEGMENTUID, segment_uid, 16);
+        ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_CUES    , cuespos);
+        if (ret < 0) return ret;
+        ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_SEEKHEAD, second_seekhead);
+        if (ret < 0) return ret;
+        mkv_write_seekhead(pb, mkv->main_seekhead);
+
+        // update the duration
+        av_log(s, AV_LOG_DEBUG, "end duration = %" PRIu64 "\n", mkv->duration);
+        currentpos = url_ftell(pb);
+        url_fseek(pb, mkv->duration_offset, SEEK_SET);
+        put_ebml_float(pb, MATROSKA_ID_DURATION, mkv->duration);
+
+        // write the md5sum of some frames as the segment UID
+        if (!(s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT)) {
+            uint8_t segment_uid[16];
+            av_md5_final(mkv->md5_ctx, segment_uid);
+            url_fseek(pb, mkv->segment_uid, SEEK_SET);
+            put_ebml_binary(pb, MATROSKA_ID_SEGMENTUID, segment_uid, 16);
+        }
+        url_fseek(pb, currentpos, SEEK_SET);
     }
-    url_fseek(pb, currentpos, SEEK_SET);
 
     end_ebml_master(pb, mkv->segment);
     av_free(mkv->md5_ctx);