Mercurial > libavformat.hg
changeset 4312:385b2fdccbbb libavformat
correctly pack and interleave pcm samples in mxf
author | bcoudurier |
---|---|
date | Sat, 31 Jan 2009 06:18:25 +0000 |
parents | 03065a598506 |
children | bdb984bfa3ed |
files | mxfenc.c |
diffstat | 1 files changed, 189 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/mxfenc.c Sat Jan 31 06:09:31 2009 +0000 +++ b/mxfenc.c Sat Jan 31 06:18:25 2009 +0000 @@ -31,18 +31,33 @@ //#define DEBUG +#include "libavutil/fifo.h" #include "mxf.h" +static const int NTSC_samples_per_frame[] = { 1602, 1601, 1602, 1601, 1602, 0 }; +static const int PAL_samples_per_frame[] = { 1920, 0 }; + +typedef struct { + AVFifoBuffer fifo; + unsigned fifo_size; ///< current fifo size allocated + uint64_t dts; ///< current dts + int sample_size; ///< size of one sample all channels included + const int *samples_per_frame; ///< must be 0 terminated + const int *samples; ///< current samples per frame, pointer to samples_per_frame +} AudioInterleaveContext; + typedef struct { int local_tag; UID uid; } MXFLocalTagPair; typedef struct { + AudioInterleaveContext aic; UID track_essence_element_key; int index; //<<< index in mxf_essence_container_uls table const UID *codec_ul; int64_t duration; + int order; ///< interleaving order if dts are equal } MXFStreamContext; typedef struct { @@ -75,6 +90,7 @@ int64_t footer_partition_offset; int essence_container_count; uint8_t essence_containers_indices[FF_ARRAY_ELEMS(mxf_essence_container_uls)]; + AVRational time_base; } MXFContext; static const uint8_t uuid_base[] = { 0xAD,0xAB,0x44,0x24,0x2f,0x25,0x4d,0xc7,0x92,0xff,0x29,0xbd }; @@ -781,11 +797,40 @@ return NULL; } +static int ff_audio_interleave_init(AVFormatContext *s, const int *samples_per_frame) +{ + int i; + + if (!samples_per_frame) + samples_per_frame = PAL_samples_per_frame; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + AudioInterleaveContext *aic = st->priv_data; + + if (st->codec->codec_type == CODEC_TYPE_AUDIO) { + aic->sample_size = (st->codec->channels * + av_get_bits_per_sample(st->codec->codec_id)) / 8; + if (!aic->sample_size) { + av_log(s, AV_LOG_ERROR, "could not compute sample size\n"); + return -1; + } + aic->samples_per_frame = samples_per_frame; + aic->samples = aic->samples_per_frame; + + av_fifo_init(&aic->fifo, 100 * *aic->samples); + } + } + + return 0; +} + static int mxf_write_header(AVFormatContext *s) { MXFContext *mxf = s->priv_data; int i; uint8_t present[FF_ARRAY_ELEMS(mxf_essence_container_uls)] = {0}; + const int *samples_per_frame = NULL; for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; @@ -793,11 +838,24 @@ if (!sc) return AVERROR(ENOMEM); st->priv_data = sc; - // set pts information - if (st->codec->codec_type == CODEC_TYPE_VIDEO) - av_set_pts_info(st, 64, 1, st->codec->time_base.den); - else if (st->codec->codec_type == CODEC_TYPE_AUDIO) - av_set_pts_info(st, 64, 1, st->codec->sample_rate); + + if (st->codec->codec_type == CODEC_TYPE_VIDEO) { + if (!av_cmp_q(st->codec->time_base, (AVRational){ 1, 25 })) { + samples_per_frame = PAL_samples_per_frame; + mxf->time_base = (AVRational){ 1, 25 }; + } else if (!av_cmp_q(st->codec->time_base, (AVRational){ 1001, 30000 })) { + samples_per_frame = NTSC_samples_per_frame; + mxf->time_base = (AVRational){ 1001, 30000 }; + } else { + av_log(s, AV_LOG_ERROR, "unsupported video frame rate\n"); + return -1; + } + } else if (st->codec->codec_type == CODEC_TYPE_AUDIO) { + if (st->codec->sample_rate != 48000) { + av_log(s, AV_LOG_ERROR, "only 48khz is implemented\n"); + return -1; + } + } sc->duration = -1; sc->index = mxf_get_essence_container_ul_index(st->codec->codec_id); @@ -832,6 +890,17 @@ PRINT_KEY(s, "track essence element key", sc->track_essence_element_key); } + for (i = 0; i < s->nb_streams; i++) { + MXFStreamContext *sc = s->streams[i]->priv_data; + av_set_pts_info(s->streams[i], 64, mxf->time_base.num, mxf->time_base.den); + // update element count + sc->track_essence_element_key[13] = present[sc->index]; + sc->order = AV_RB32(sc->track_essence_element_key+12); + } + + if (ff_audio_interleave_init(s, samples_per_frame) < 0) + return -1; + mxf_write_partition(s, 1, header_open_partition_key, 1); return 0; @@ -868,6 +937,118 @@ return 0; } +static int mxf_interleave_new_audio_packet(AVFormatContext *s, AVPacket *pkt, + int stream_index, int flush) +{ + AVStream *st = s->streams[stream_index]; + AudioInterleaveContext *aic = st->priv_data; + + int size = FFMIN(av_fifo_size(&aic->fifo), *aic->samples * aic->sample_size); + if (!size || (!flush && size == av_fifo_size(&aic->fifo))) + return 0; + + av_new_packet(pkt, size); + av_fifo_read(&aic->fifo, pkt->data, size); + + pkt->dts = pkt->pts = aic->dts; + pkt->duration = av_rescale_q(*aic->samples, + (AVRational){ 1, st->codec->sample_rate }, + st->time_base); + pkt->stream_index = stream_index; + aic->dts += pkt->duration; + + aic->samples++; + if (!*aic->samples) + aic->samples = aic->samples_per_frame; + + return size; +} + +static int mxf_interleave_get_packet(AVFormatContext *s, AVPacket *out, int flush) +{ + AVPacketList *pktl; + int stream_count = 0; + int streams[MAX_STREAMS]; + + memset(streams, 0, sizeof(streams)); + pktl = s->packet_buffer; + while (pktl) { + //av_log(s, AV_LOG_DEBUG, "show st:%d dts:%lld\n", pktl->pkt.stream_index, pktl->pkt.dts); + if (!streams[pktl->pkt.stream_index]) + stream_count++; + streams[pktl->pkt.stream_index]++; + pktl = pktl->next; + } + + if (stream_count && (s->nb_streams == stream_count || flush)) { + pktl = s->packet_buffer; + *out = pktl->pkt; + //av_log(s, AV_LOG_DEBUG, "out st:%d dts:%lld\n", (*out).stream_index, (*out).dts); + s->packet_buffer = pktl->next; + av_freep(&pktl); + + if (flush && stream_count < s->nb_streams) { + // purge packet queue + pktl = s->packet_buffer; + while (pktl) { + AVPacketList *next = pktl->next; + av_free_packet(&pktl->pkt); + av_freep(&pktl); + pktl = next; + } + s->packet_buffer = NULL; + } + + return 1; + } else { + av_init_packet(out); + return 0; + } +} + +static int mxf_compare_timestamps(AVFormatContext *s, AVPacket *next, AVPacket *pkt) +{ + AVStream *st = s->streams[pkt ->stream_index]; + AVStream *st2 = s->streams[next->stream_index]; + MXFStreamContext *sc = st ->priv_data; + MXFStreamContext *sc2 = st2->priv_data; + + int64_t left = st2->time_base.num * (int64_t)st ->time_base.den; + int64_t right = st ->time_base.num * (int64_t)st2->time_base.den; + + return next->dts * left > pkt->dts * right || // FIXME this can overflow + (next->dts * left == pkt->dts * right && sc->order < sc2->order); +} + +static int mxf_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) +{ + int i; + + if (pkt) { + AVStream *st = s->streams[pkt->stream_index]; + AudioInterleaveContext *aic = st->priv_data; + if (st->codec->codec_type == CODEC_TYPE_AUDIO) { + av_fifo_generic_write(&aic->fifo, pkt->data, pkt->size, NULL); + } else { + // rewrite pts and dts to be decoded time line position + pkt->pts = pkt->dts = aic->dts; + aic->dts += pkt->duration; + ff_interleave_add_packet(s, pkt, mxf_compare_timestamps); + } + } + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_type == CODEC_TYPE_AUDIO) { + AVPacket new_pkt; + while (mxf_interleave_new_audio_packet(s, &new_pkt, i, flush)) + ff_interleave_add_packet(s, &new_pkt, mxf_compare_timestamps); + } + } + + return mxf_interleave_get_packet(s, out, flush); +} + AVOutputFormat mxf_muxer = { "mxf", NULL_IF_CONFIG_SMALL("Material eXchange Format"), @@ -879,6 +1060,9 @@ mxf_write_header, mxf_write_packet, mxf_write_footer, + 0, + NULL, + mxf_interleave, };