Commit 4ace130e authored by Martin Storsjö's avatar Martin Storsjö
Browse files

movenc: Support muxing VC1


Signed-off-by: default avatarMartin Storsjö <martin@martin.st>
parent ae5a9355
......@@ -30,6 +30,7 @@
#include "avc.h"
#include "libavcodec/get_bits.h"
#include "libavcodec/put_bits.h"
#include "libavcodec/vc1.h"
#include "internal.h"
#include "libavutil/avstring.h"
#include "libavutil/intfloat.h"
......@@ -421,6 +422,98 @@ static int mov_write_wave_tag(AVIOContext *pb, MOVTrack *track)
return update_size(pb, pos);
}
static int mov_write_dvc1_structs(MOVTrack *track, uint8_t *buf)
{
uint8_t *unescaped;
const uint8_t *start, *next, *end = track->vos_data + track->vos_len;
int unescaped_size, seq_found = 0;
int level = 0, interlace = 0;
int packet_seq = track->vc1_info.packet_seq;
int packet_entry = track->vc1_info.packet_entry;
int slices = track->vc1_info.slices;
PutBitContext pbc;
if (track->start_dts == AV_NOPTS_VALUE) {
/* No packets written yet, vc1_info isn't authoritative yet. */
/* Assume inline sequence and entry headers. This will be
* overwritten at the end if the file is seekable. */
packet_seq = packet_entry = 1;
}
unescaped = av_mallocz(track->vos_len + FF_INPUT_BUFFER_PADDING_SIZE);
if (!unescaped)
return AVERROR(ENOMEM);
start = find_next_marker(track->vos_data, end);
for (next = start; next < end; start = next) {
GetBitContext gb;
int size;
next = find_next_marker(start + 4, end);
size = next - start - 4;
if (size <= 0)
continue;
unescaped_size = vc1_unescape_buffer(start + 4, size, unescaped);
init_get_bits(&gb, unescaped, 8 * unescaped_size);
if (AV_RB32(start) == VC1_CODE_SEQHDR) {
int profile = get_bits(&gb, 2);
if (profile != PROFILE_ADVANCED) {
av_free(unescaped);
return AVERROR(ENOSYS);
}
seq_found = 1;
level = get_bits(&gb, 3);
/* chromaformat, frmrtq_postproc, bitrtq_postproc, postprocflag,
* width, height */
skip_bits_long(&gb, 2 + 3 + 5 + 1 + 2*12);
skip_bits(&gb, 1); /* broadcast */
interlace = get_bits1(&gb);
skip_bits(&gb, 4); /* tfcntrflag, finterpflag, reserved, psf */
}
}
if (!seq_found) {
av_free(unescaped);
return AVERROR(ENOSYS);
}
init_put_bits(&pbc, buf, 7);
/* VC1DecSpecStruc */
put_bits(&pbc, 4, 12); /* profile - advanced */
put_bits(&pbc, 3, level);
put_bits(&pbc, 1, 0); /* reserved */
/* VC1AdvDecSpecStruc */
put_bits(&pbc, 3, level);
put_bits(&pbc, 1, 0); /* cbr */
put_bits(&pbc, 6, 0); /* reserved */
put_bits(&pbc, 1, !interlace); /* no interlace */
put_bits(&pbc, 1, !packet_seq); /* no multiple seq */
put_bits(&pbc, 1, !packet_entry); /* no multiple entry */
put_bits(&pbc, 1, !slices); /* no slice code */
put_bits(&pbc, 1, 0); /* no bframe */
put_bits(&pbc, 1, 0); /* reserved */
put_bits32(&pbc, track->enc->time_base.den); /* framerate */
flush_put_bits(&pbc);
av_free(unescaped);
return 0;
}
static int mov_write_dvc1_tag(AVIOContext *pb, MOVTrack *track)
{
uint8_t buf[7] = { 0 };
int ret;
if ((ret = mov_write_dvc1_structs(track, buf)) < 0)
return ret;
avio_wb32(pb, track->vos_len + 8 + sizeof(buf));
ffio_wfourcc(pb, "dvc1");
track->vc1_info.struct_offset = avio_tell(pb);
avio_write(pb, buf, sizeof(buf));
avio_write(pb, track->vos_data, track->vos_len);
return 0;
}
static int mov_write_glbl_tag(AVIOContext *pb, MOVTrack *track)
{
avio_wb32(pb, track->vos_len + 8);
......@@ -625,6 +718,7 @@ static int mp4_get_codec_tag(AVFormatContext *s, MOVTrack *track)
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_id == CODEC_ID_VC1) tag = MKTAG('v','c','-','1');
else if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) tag = MKTAG('m','p','4','v');
else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO) tag = MKTAG('m','p','4','a');
......@@ -911,6 +1005,8 @@ static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track)
mov_write_uuid_tag_ipod(pb);
} else if (track->enc->field_order != AV_FIELD_UNKNOWN)
mov_write_fiel_tag(pb, track);
else if (track->enc->codec_id == CODEC_ID_VC1 && track->vos_len > 0)
mov_write_dvc1_tag(pb, track);
else if (track->vos_len > 0)
mov_write_glbl_tag(pb, track);
......@@ -2502,6 +2598,63 @@ static int mov_parse_mpeg2_frame(AVPacket *pkt, uint32_t *flags)
return 0;
}
static void mov_parse_vc1_frame(AVPacket *pkt, MOVTrack *trk, int fragment)
{
const uint8_t *start, *next, *end = pkt->data + pkt->size;
int seq = 0, entry = 0;
int key = pkt->flags & AV_PKT_FLAG_KEY;
start = find_next_marker(pkt->data, end);
for (next = start; next < end; start = next) {
next = find_next_marker(start + 4, end);
switch (AV_RB32(start)) {
case VC1_CODE_SEQHDR:
seq = 1;
break;
case VC1_CODE_ENTRYPOINT:
entry = 1;
break;
case VC1_CODE_SLICE:
trk->vc1_info.slices = 1;
break;
}
}
if (!trk->entry && !fragment) {
/* First packet in first fragment */
trk->vc1_info.first_packet_seq = seq;
trk->vc1_info.first_packet_entry = entry;
} else if ((seq && !trk->vc1_info.packet_seq) ||
(entry && !trk->vc1_info.packet_entry)) {
int i;
for (i = 0; i < trk->entry; i++)
trk->cluster[i].flags &= ~MOV_SYNC_SAMPLE;
trk->has_keyframes = 0;
if (seq)
trk->vc1_info.packet_seq = 1;
if (entry)
trk->vc1_info.packet_entry = 1;
if (!fragment) {
/* First fragment */
if ((!seq || trk->vc1_info.first_packet_seq) &&
(!entry || trk->vc1_info.first_packet_entry)) {
/* First packet had the same headers as this one, readd the
* sync sample flag. */
trk->cluster[0].flags |= MOV_SYNC_SAMPLE;
trk->has_keyframes = 1;
}
}
}
if (trk->vc1_info.packet_seq && trk->vc1_info.packet_entry)
key = seq && entry;
else if (trk->vc1_info.packet_seq)
key = seq;
else if (trk->vc1_info.packet_entry)
key = entry;
if (key) {
trk->cluster[trk->entry].flags |= MOV_SYNC_SAMPLE;
trk->has_keyframes++;
}
}
static int mov_flush_fragment(AVFormatContext *s)
{
MOVMuxContext *mov = s->priv_data;
......@@ -2725,7 +2878,9 @@ static int mov_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
trk->flags |= MOV_TRACK_CTTS;
trk->cluster[trk->entry].cts = pkt->pts - pkt->dts;
trk->cluster[trk->entry].flags = 0;
if (pkt->flags & AV_PKT_FLAG_KEY) {
if (enc->codec_id == CODEC_ID_VC1) {
mov_parse_vc1_frame(pkt, trk, mov->fragments);
} else if (pkt->flags & AV_PKT_FLAG_KEY) {
if (mov->mode == MODE_MOV && enc->codec_id == CODEC_ID_MPEG2VIDEO &&
trk->entry > 0) { // force sync sample for the first key frame
mov_parse_mpeg2_frame(pkt, &trk->cluster[trk->entry].flags);
......@@ -3033,6 +3188,16 @@ static int mov_write_trailer(AVFormatContext *s)
for (i=0; i<mov->nb_streams; i++) {
if (mov->tracks[i].tag == MKTAG('r','t','p',' '))
ff_mov_close_hinting(&mov->tracks[i]);
if (mov->flags & FF_MOV_FLAG_FRAGMENT &&
mov->tracks[i].vc1_info.struct_offset && s->pb->seekable) {
int64_t off = avio_tell(pb);
uint8_t buf[7];
if (mov_write_dvc1_structs(&mov->tracks[i], buf) >= 0) {
avio_seek(pb, mov->tracks[i].vc1_info.struct_offset, SEEK_SET);
avio_write(pb, buf, 7);
avio_seek(pb, off, SEEK_SET);
}
}
av_freep(&mov->tracks[i].cluster);
av_freep(&mov->tracks[i].frag_info);
......
......@@ -120,6 +120,15 @@ typedef struct MOVIndex {
int nb_frag_info;
MOVFragmentInfo *frag_info;
struct {
int64_t struct_offset;
int first_packet_seq;
int first_packet_entry;
int packet_seq;
int packet_entry;
int slices;
} vc1_info;
} MOVTrack;
typedef struct MOVMuxContext {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment