[FFmpeg-devel] Mov / Mp4 Muxer in streaming
Marco Fucci
mfucci
Tue Apr 14 20:09:16 CEST 2009
Hi,
Here is the patch, I hope it is correct, I made it with Tortoise. Let me
know if this works and when this is merged with the main trunk.
Best,
Marco
---------------------------------------------------------------------------------------------------------------------
Index: movenc.c
===================================================================
--- movenc.c (revision 18510)
+++ movenc.c (working copy)
@@ -26,8 +26,7 @@
#include "avio.h"
#include "isom.h"
#include "avc.h"
-#include "libavcodec/get_bits.h"
-#include "libavcodec/put_bits.h"
+#include "libavcodec/bitstream.h"
#undef NDEBUG
#include <assert.h>
@@ -547,154 +546,98 @@
return 0;
}
-static int mp4_get_codec_tag(AVFormatContext *s, MOVTrack *track)
-{
- int tag = track->enc->codec_tag;
-
- if (!codec_get_tag(ff_mp4_obj_type, track->enc->codec_id))
- return 0;
-
- if (track->enc->codec_id == CODEC_ID_H264) tag =
MKTAG('a','v','c','1');
- else if (track->enc->codec_id == CODEC_ID_AC3) tag =
MKTAG('a','c','-','3');
- else if (track->enc->codec_id == CODEC_ID_DIRAC) tag =
MKTAG('d','r','a','c');
- else if (track->enc->codec_id == CODEC_ID_MOV_TEXT) tag =
MKTAG('t','x','3','g');
- else if (track->enc->codec_type == CODEC_TYPE_VIDEO) tag =
MKTAG('m','p','4','v');
- else if (track->enc->codec_type == CODEC_TYPE_AUDIO) tag =
MKTAG('m','p','4','a');
-
- return tag;
-}
-
-static const AVCodecTag codec_ipod_tags[] = {
+static const AVCodecTag codec_3gp_tags[] = {
+ { CODEC_ID_H263, MKTAG('s','2','6','3') },
{ CODEC_ID_H264, MKTAG('a','v','c','1') },
{ CODEC_ID_MPEG4, MKTAG('m','p','4','v') },
{ CODEC_ID_AAC, MKTAG('m','p','4','a') },
- { CODEC_ID_ALAC, MKTAG('a','l','a','c') },
- { CODEC_ID_AC3, MKTAG('a','c','-','3') },
+ { CODEC_ID_AMR_NB, MKTAG('s','a','m','r') },
+ { CODEC_ID_AMR_WB, MKTAG('s','a','w','b') },
{ CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') },
- { CODEC_ID_MOV_TEXT, MKTAG('t','e','x','t') },
{ CODEC_ID_NONE, 0 },
};
-static int ipod_get_codec_tag(AVFormatContext *s, MOVTrack *track)
-{
- int tag = track->enc->codec_tag;
-
- // keep original tag for subs, ipod supports both formats
- if (!(track->enc->codec_type == CODEC_TYPE_SUBTITLE &&
- (tag == MKTAG('t','x','3','g') ||
- tag == MKTAG('t','e','x','t'))))
- tag = codec_get_tag(codec_ipod_tags, track->enc->codec_id);
-
- if (!match_ext(s->filename, "m4a") && !match_ext(s->filename, "m4v"))
- av_log(s, AV_LOG_WARNING, "Warning, extension is not .m4a nor .m4v "
- "Quicktime/Ipod might not play the file\n");
-
- return tag;
-}
-
-static int mov_get_dv_codec_tag(AVFormatContext *s, MOVTrack *track)
-{
- int tag;
-
- if (track->enc->height == 480) /* NTSC */
- if (track->enc->pix_fmt == PIX_FMT_YUV422P) tag =
MKTAG('d','v','5','n');
- else tag =
MKTAG('d','v','c',' ');
- else if (track->enc->pix_fmt == PIX_FMT_YUV422P) tag =
MKTAG('d','v','5','p');
- else if (track->enc->pix_fmt == PIX_FMT_YUV420P) tag =
MKTAG('d','v','c','p');
- else tag =
MKTAG('d','v','p','p');
-
- return tag;
-}
-
-static const struct {
- enum PixelFormat pix_fmt;
- uint32_t tag;
- unsigned bps;
-} mov_pix_fmt_tags[] = {
- { PIX_FMT_YUYV422, MKTAG('y','u','v','s'), 0 },
- { PIX_FMT_UYVY422, MKTAG('2','v','u','y'), 0 },
- { PIX_FMT_BGR555, MKTAG('r','a','w',' '), 16 },
- { PIX_FMT_RGB24, MKTAG('r','a','w',' '), 24 },
- { PIX_FMT_BGR32_1, MKTAG('r','a','w',' '), 32 },
+static const AVCodecTag mov_pix_fmt_tags[] = {
+ { PIX_FMT_YUYV422, MKTAG('y','u','v','s') },
+ { PIX_FMT_UYVY422, MKTAG('2','v','u','y') },
+ { PIX_FMT_BGR555, MKTAG('r','a','w',' ') },
+ { PIX_FMT_RGB24, MKTAG('r','a','w',' ') },
+ { PIX_FMT_BGR32_1, MKTAG('r','a','w',' ') },
};
-static int mov_get_rawvideo_codec_tag(AVFormatContext *s, MOVTrack *track)
-{
- int tag = track->enc->codec_tag;
- int i;
-
- for (i = 0; i < FF_ARRAY_ELEMS(mov_pix_fmt_tags); i++) {
- if (track->enc->pix_fmt == mov_pix_fmt_tags[i].pix_fmt) {
- tag = mov_pix_fmt_tags[i].tag;
- track->enc->bits_per_coded_sample = mov_pix_fmt_tags[i].bps;
- break;
- }
- }
-
- return tag;
-}
-
-static int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track)
-{
- int tag = track->enc->codec_tag;
-
- if (!tag || (track->enc->strict_std_compliance >= FF_COMPLIANCE_NORMAL &&
- (tag == MKTAG('d','v','c','p') ||
- track->enc->codec_id == CODEC_ID_RAWVIDEO ||
- av_get_bits_per_sample(track->enc->codec_id)))) {
// pcm audio
- if (track->enc->codec_id == CODEC_ID_DVVIDEO)
- tag = mov_get_dv_codec_tag(s, track);
- else if (track->enc->codec_id == CODEC_ID_RAWVIDEO)
- tag = mov_get_rawvideo_codec_tag(s, track);
- else if (track->enc->codec_type == CODEC_TYPE_VIDEO) {
- tag = codec_get_tag(codec_movvideo_tags, track->enc->codec_id);
- if (!tag) { // if no mac fcc found, try with Microsoft tags
- tag = codec_get_tag(codec_bmp_tags, track->enc->codec_id);
- if (tag)
- av_log(s, AV_LOG_INFO, "Warning, using MS style
video codec tag, "
- "the file may be unplayable!\n");
- }
- } else if (track->enc->codec_type == CODEC_TYPE_AUDIO) {
- tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id);
- if (!tag) { // if no mac fcc found, try with Microsoft tags
- int ms_tag = codec_get_tag(codec_wav_tags,
track->enc->codec_id);
- if (ms_tag) {
- tag = MKTAG('m', 's', ((ms_tag >> 8) & 0xff),
(ms_tag & 0xff));
- av_log(s, AV_LOG_INFO, "Warning, using MS style
audio codec tag, "
- "the file may be unplayable!\n");
- }
- }
- } else if (track->enc->codec_type == CODEC_TYPE_SUBTITLE)
- tag = codec_get_tag(ff_codec_movsubtitle_tags,
track->enc->codec_id);
- }
-
- return tag;
-}
-
-static const AVCodecTag codec_3gp_tags[] = {
- { CODEC_ID_H263, MKTAG('s','2','6','3') },
+static const AVCodecTag codec_ipod_tags[] = {
{ CODEC_ID_H264, MKTAG('a','v','c','1') },
{ CODEC_ID_MPEG4, MKTAG('m','p','4','v') },
{ CODEC_ID_AAC, MKTAG('m','p','4','a') },
- { CODEC_ID_AMR_NB, MKTAG('s','a','m','r') },
- { CODEC_ID_AMR_WB, MKTAG('s','a','w','b') },
+ { CODEC_ID_ALAC, MKTAG('a','l','a','c') },
+ { CODEC_ID_AC3, MKTAG('a','c','-','3') },
{ CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') },
+ { CODEC_ID_MOV_TEXT, MKTAG('t','e','x','t') },
{ CODEC_ID_NONE, 0 },
};
static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track)
{
int tag = track->enc->codec_tag;
-
- if (track->mode == MODE_MP4 || track->mode == MODE_PSP)
- tag = mp4_get_codec_tag(s, track);
- else if (track->mode == MODE_IPOD)
- tag = ipod_get_codec_tag(s, track);
- else if (track->mode & MODE_3GP)
+ if (track->mode == MODE_MP4 || track->mode == MODE_PSP) {
+ if (!codec_get_tag(ff_mp4_obj_type, track->enc->codec_id))
+ return 0;
+ if (track->enc->codec_id == CODEC_ID_H264) tag =
MKTAG('a','v','c','1');
+ else if (track->enc->codec_id == CODEC_ID_AC3) tag =
MKTAG('a','c','-','3');
+ else if (track->enc->codec_id == CODEC_ID_DIRAC) tag =
MKTAG('d','r','a','c');
+ else if (track->enc->codec_id == CODEC_ID_MOV_TEXT) tag =
MKTAG('t','x','3','g');
+ else if (track->enc->codec_type == CODEC_TYPE_VIDEO) tag =
MKTAG('m','p','4','v');
+ else if (track->enc->codec_type == CODEC_TYPE_AUDIO) tag =
MKTAG('m','p','4','a');
+ } else if (track->mode == MODE_IPOD) {
+ if (track->enc->codec_type == CODEC_TYPE_SUBTITLE &&
+ (tag == MKTAG('t','x','3','g') ||
+ tag == MKTAG('t','e','x','t')))
+ track->tag = tag; // keep original tag
+ else
+ tag = codec_get_tag(codec_ipod_tags, track->enc->codec_id);
+ if (!match_ext(s->filename, "m4a") && !match_ext(s->filename, "m4v"))
+ av_log(s, AV_LOG_WARNING, "Warning, extension is not .m4a
nor .m4v "
+ "Quicktime/Ipod might not play the file\n");
+ } else if (track->mode & MODE_3GP) {
tag = codec_get_tag(codec_3gp_tags, track->enc->codec_id);
- else
- tag = mov_get_codec_tag(s, track);
-
+ } else if (!tag || (track->enc->strict_std_compliance >=
FF_COMPLIANCE_NORMAL &&
+ (tag == MKTAG('d','v','c','p') ||
+ track->enc->codec_id == CODEC_ID_RAWVIDEO))) {
+ if (track->enc->codec_id == CODEC_ID_DVVIDEO) {
+ if (track->enc->height == 480) /* NTSC */
+ if (track->enc->pix_fmt == PIX_FMT_YUV422P) tag =
MKTAG('d','v','5','n');
+ else tag =
MKTAG('d','v','c',' ');
+ else if (track->enc->pix_fmt == PIX_FMT_YUV422P) tag =
MKTAG('d','v','5','p');
+ else if (track->enc->pix_fmt == PIX_FMT_YUV420P) tag =
MKTAG('d','v','c','p');
+ else tag =
MKTAG('d','v','p','p');
+ } else if (track->enc->codec_id == CODEC_ID_RAWVIDEO) {
+ tag = codec_get_tag(mov_pix_fmt_tags, track->enc->pix_fmt);
+ if (!tag) // restore tag
+ tag = track->enc->codec_tag;
+ } else {
+ if (track->enc->codec_type == CODEC_TYPE_VIDEO) {
+ tag = codec_get_tag(codec_movvideo_tags, track->enc->codec_id);
+ if (!tag) { // if no mac fcc found, try with Microsoft tags
+ tag = codec_get_tag(codec_bmp_tags, track->enc->codec_id);
+ if (tag)
+ av_log(s, AV_LOG_INFO, "Warning, using MS
style video codec tag, "
+ "the file may be unplayable!\n");
+ }
+ } else if (track->enc->codec_type == CODEC_TYPE_AUDIO) {
+ tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id);
+ if (!tag) { // if no mac fcc found, try with Microsoft tags
+ int ms_tag = codec_get_tag(codec_wav_tags,
track->enc->codec_id);
+ if (ms_tag) {
+ tag = MKTAG('m', 's', ((ms_tag >> 8) & 0xff),
(ms_tag & 0xff));
+ av_log(s, AV_LOG_INFO, "Warning, using MS
style audio codec tag, "
+ "the file may be unplayable!\n");
+ }
+ }
+ } else if (track->enc->codec_type == CODEC_TYPE_SUBTITLE) {
+ tag = codec_get_tag(ff_codec_movsubtitle_tags,
track->enc->codec_id);
+ }
+ }
+ }
return tag;
}
@@ -1561,6 +1504,15 @@
return updateSize(pb, pos);
}
+static int mov_write_mdat_tag_size(ByteIOContext *pb, MOVMuxContext
*mov, int size) {
+ put_be32(pb, 8); // placeholder for extended size field (64 bit)
+ put_tag(pb, mov->mode == MODE_MOV ? "wide" : "free");
+
+ put_be32(pb, size+8); // size
+ put_tag(pb, "mdat");
+ return 16;
+}
+
static int mov_write_mdat_tag(ByteIOContext *pb, MOVMuxContext *mov)
{
put_be32(pb, 8); // placeholder for extended size field (64 bit)
@@ -1695,12 +1647,9 @@
ByteIOContext *pb = s->pb;
MOVMuxContext *mov = s->priv_data;
int i;
+
+ int64_t pos = url_ftell(pb);
- if (url_is_streamed(s->pb)) {
- av_log(s, AV_LOG_ERROR, "muxer does not support non seekable
output\n");
- return -1;
- }
-
/* Default mode == MP4 */
mov->mode = MODE_MP4;
@@ -1784,7 +1733,13 @@
track->height = st->codec->height;
}
- mov_write_mdat_tag(pb, mov);
+ if (!url_is_streamed(s->pb)) {
+ mov_write_mdat_tag(pb, mov);
+ } else {
+ int movHeaderSize = updateSize(pb, pos);
+ mov->mdat_pos = movHeaderSize;
+ }
+
mov->time = s->timestamp + 0x7C25B080; //1970 based -> 1904 based
mov->nb_streams = s->nb_streams;
@@ -1796,6 +1751,23 @@
return -1;
}
+int ff_avc_parse_nal_units_size(ByteIOContext *pb, const uint8_t
*buf_in, int size)
+{
+ const uint8_t *p = buf_in;
+ const uint8_t *end = p + size;
+ const uint8_t *nal_start, *nal_end;
+
+ size = 0;
+ nal_start = ff_avc_find_startcode(p, end);
+ while (nal_start < end) {
+ while(!*(nal_start++));
+ nal_end = ff_avc_find_startcode(nal_start, end);
+ size += 4 + nal_end - nal_start;
+ nal_start = nal_end;
+ }
+ return size;
+}
+
static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
{
MOVMuxContext *mov = s->priv_data;
@@ -1804,8 +1776,8 @@
AVCodecContext *enc = trk->enc;
unsigned int samplesInChunk = 0;
int size= pkt->size;
+ int mdatHeaderSize = 0;
- if (url_is_streamed(s->pb)) return 0; /* Can't handle that */
if (!size) return 0; /* Discard 0 sized packets */
if (enc->codec_id == CODEC_ID_AMR_NB) {
@@ -1837,8 +1809,15 @@
if (enc->codec_id == CODEC_ID_H264 && trk->vosLen > 0 &&
*(uint8_t *)trk->vosData != 1) {
/* from x264 or from bytestream h264 */
/* nal reformating needed */
+ if (url_is_streamed(s->pb)) {
+ size = ff_avc_parse_nal_units_size(pb, pkt->data, pkt->size);
+ mdatHeaderSize = mov_write_mdat_tag_size(pb, mov, size);
+ }
size = ff_avc_parse_nal_units(pb, pkt->data, pkt->size);
} else {
+ if (url_is_streamed(s->pb)) {
+ mdatHeaderSize = mov_write_mdat_tag_size(pb, mov, size);
+ }
put_buffer(pb, pkt->data, size);
}
@@ -1857,8 +1836,12 @@
if (!trk->cluster)
return -1;
}
-
- trk->cluster[trk->entry].pos = url_ftell(pb) - size;
+
+ if (url_is_streamed(s->pb)) {
+ trk->cluster[trk->entry].pos = mov->mdat_pos + mdatHeaderSize;
+ } else {
+ trk->cluster[trk->entry].pos = url_ftell(pb) - size;
+ }
trk->cluster[trk->entry].samplesInChunk = samplesInChunk;
trk->cluster[trk->entry].size = size;
trk->cluster[trk->entry].entries = samplesInChunk;
@@ -1877,7 +1860,11 @@
trk->hasKeyframes++;
trk->entry++;
trk->sampleCount += samplesInChunk;
- mov->mdat_size += size;
+ if (url_is_streamed(s->pb)) {
+ mov->mdat_pos += mdatHeaderSize+size;
+ } else {
+ mov->mdat_size += size;
+ }
put_flush_packet(pb);
return 0;
@@ -1890,20 +1877,22 @@
int res = 0;
int i;
- int64_t moov_pos = url_ftell(pb);
+ if (!url_is_streamed(s->pb)) {
+ int64_t moov_pos = url_ftell(pb);
- /* Write size of mdat tag */
- if (mov->mdat_size+8 <= UINT32_MAX) {
- url_fseek(pb, mov->mdat_pos, SEEK_SET);
- put_be32(pb, mov->mdat_size+8);
- } else {
- /* overwrite 'wide' placeholder atom */
- url_fseek(pb, mov->mdat_pos - 8, SEEK_SET);
- put_be32(pb, 1); /* special value: real atom size will be 64
bit value after tag field */
- put_tag(pb, "mdat");
- put_be64(pb, mov->mdat_size+16);
- }
- url_fseek(pb, moov_pos, SEEK_SET);
+ /* Write size of mdat tag */
+ if (mov->mdat_size+8 <= UINT32_MAX) {
+ url_fseek(pb, mov->mdat_pos, SEEK_SET);
+ put_be32(pb, mov->mdat_size+8);
+ } else {
+ /* overwrite 'wide' placeholder atom */
+ url_fseek(pb, mov->mdat_pos - 8, SEEK_SET);
+ put_be32(pb, 1); /* special value: real atom size will be 64 bit
value after tag field */
+ put_tag(pb, "mdat");
+ put_be64(pb, mov->mdat_size+16);
+ }
+ url_fseek(pb, moov_pos, SEEK_SET);
+ }
mov_write_moov_tag(pb, mov, s);
> We want a patch, not the new file.
>
> Diego
>
>
More information about the ffmpeg-devel
mailing list