mirror of
https://github.com/xenia-project/FFmpeg.git
synced 2024-11-23 11:39:49 +00:00
movenc: Support muxing VC1
Signed-off-by: Martin Storsjö <martin@martin.st>
This commit is contained in:
parent
ae5a935574
commit
4ace130ee6
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user