From d1a3a3d4b2856c7cf116668900b00b9a0eba3350 Mon Sep 17 00:00:00 2001 From: Jindrich Makovicka Date: Fri, 1 Jun 2012 11:44:56 +0200 Subject: [PATCH 1/4] mpegtsenc: use AVERROR() for return codes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jindrich Makovicka Signed-off-by: Martin Storsjö --- libavformat/mpegtsenc.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index f60594abeb..90d4f3a7df 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -459,6 +459,7 @@ static int mpegts_write_header(AVFormatContext *s) const char *service_name; const char *provider_name; int *pids; + int ret; if (s->max_delay < 0) /* Not set by the caller */ s->max_delay = 0; @@ -499,12 +500,16 @@ static int mpegts_write_header(AVFormatContext *s) st = s->streams[i]; avpriv_set_pts_info(st, 33, 1, 90000); ts_st = av_mallocz(sizeof(MpegTSWriteStream)); - if (!ts_st) + if (!ts_st) { + ret = AVERROR(ENOMEM); goto fail; + } st->priv_data = ts_st; ts_st->payload = av_mallocz(ts->pes_payload_size); - if (!ts_st->payload) + if (!ts_st->payload) { + ret = AVERROR(ENOMEM); goto fail; + } ts_st->service = service; /* MPEG pid values < 16 are reserved. Applications which set st->id in * this range are assigned a calculated pid. */ @@ -514,15 +519,18 @@ static int mpegts_write_header(AVFormatContext *s) ts_st->pid = st->id; } else { av_log(s, AV_LOG_ERROR, "Invalid stream id %d, must be less than 8191\n", st->id); + ret = AVERROR(EINVAL); goto fail; } if (ts_st->pid == service->pmt.pid) { av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", ts_st->pid); + ret = AVERROR(EINVAL); goto fail; } for (j = 0; j < i; j++) if (pids[j] == ts_st->pid) { av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", ts_st->pid); + ret = AVERROR(EINVAL); goto fail; } pids[i] = ts_st->pid; @@ -615,7 +623,7 @@ static int mpegts_write_header(AVFormatContext *s) } av_freep(&st->priv_data); } - return -1; + return ret; } /* send SDT, PAT and PMT tables regulary */ @@ -959,7 +967,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) if (ts_st->first_pts_check && pts == AV_NOPTS_VALUE) { av_log(s, AV_LOG_ERROR, "first pts value must set\n"); - return -1; + return AVERROR(EINVAL); } ts_st->first_pts_check = 0; @@ -970,7 +978,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001) { av_log(s, AV_LOG_ERROR, "H.264 bitstream malformed, " "no startcode found, use -bsf h264_mp4toannexb\n"); - return -1; + return AVERROR(EINVAL); } do { @@ -982,7 +990,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) if ((state & 0x1f) != 9) { // AUD NAL data = av_malloc(pkt->size+6); if (!data) - return -1; + return AVERROR(ENOMEM); memcpy(data+6, pkt->data, pkt->size); AV_WB32(data, 0x00000001); data[4] = 0x09; From b1c56eabe84b8a808dcf691205f3ba26619d1fa0 Mon Sep 17 00:00:00 2001 From: Jindrich Makovicka Date: Fri, 1 Jun 2012 12:42:20 +0200 Subject: [PATCH 2/4] mpegtsenc: use AVFormatContext for AAC packetization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the dependency on adts.c internals, and simplifies adding other packetization formats. Signed-off-by: Jindrich Makovicka Signed-off-by: Martin Storsjö --- configure | 1 + libavformat/Makefile | 2 +- libavformat/mpegtsenc.c | 113 +++++++++++++++++++++++++++++----------- 3 files changed, 85 insertions(+), 31 deletions(-) diff --git a/configure b/configure index 67371c8102..ece3814855 100755 --- a/configure +++ b/configure @@ -1457,6 +1457,7 @@ matroska_demuxer_suggest="zlib bzlib" mov_demuxer_suggest="zlib" mp3_demuxer_select="mpegaudio_parser" mp4_muxer_select="mov_muxer" +mpegts_muxer_select="adts_muxer" mpegtsraw_demuxer_select="mpegts_demuxer" mxf_d10_muxer_select="mxf_muxer" ogg_demuxer_select="golomb" diff --git a/libavformat/Makefile b/libavformat/Makefile index 0e041d106a..ca4f7a02e1 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -158,7 +158,7 @@ OBJS-$(CONFIG_MPEG1VIDEO_MUXER) += rawenc.o OBJS-$(CONFIG_MPEG2VIDEO_MUXER) += rawenc.o OBJS-$(CONFIG_MPEGPS_DEMUXER) += mpeg.o OBJS-$(CONFIG_MPEGTS_DEMUXER) += mpegts.o isom.o -OBJS-$(CONFIG_MPEGTS_MUXER) += mpegtsenc.o adtsenc.o +OBJS-$(CONFIG_MPEGTS_MUXER) += mpegtsenc.o OBJS-$(CONFIG_MPEGVIDEO_DEMUXER) += mpegvideodec.o rawdec.o OBJS-$(CONFIG_MPJPEG_MUXER) += mpjpeg.o OBJS-$(CONFIG_MSNWC_TCP_DEMUXER) += msnwc_tcp.o diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index 90d4f3a7df..5c6d8db99d 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -28,7 +28,6 @@ #include "avformat.h" #include "internal.h" #include "mpegts.h" -#include "adts.h" #define PCR_TIME_BASE 27000000 @@ -214,7 +213,11 @@ typedef struct MpegTSWriteStream { int64_t payload_dts; int payload_flags; uint8_t *payload; - ADTSContext *adts; + + uint8_t *adata; + int adata_pos; + int adata_size; + AVFormatContext *amux; } MpegTSWriteStream; static void mpegts_write_pat(AVFormatContext *s) @@ -448,6 +451,19 @@ static void section_write_packet(MpegTSSection *s, const uint8_t *packet) avio_write(ctx->pb, packet, TS_PACKET_SIZE); } +/* Write callback for audio packetizer */ +static int mpegts_audio_write(void *opaque, uint8_t *buf, int size) +{ + MpegTSWriteStream *ts_st = (MpegTSWriteStream *)opaque; + if (ts_st->adata_pos + size > ts_st->adata_size) + return AVERROR(EIO); + + memcpy(ts_st->adata + ts_st->adata_pos, buf, size); + ts_st->adata_pos += size; + + return 0; +} + static int mpegts_write_header(AVFormatContext *s) { MpegTSWrite *ts = s->priv_data; @@ -545,12 +561,35 @@ static int mpegts_write_header(AVFormatContext *s) pcr_st = st; } if (st->codec->codec_id == CODEC_ID_AAC && - st->codec->extradata_size > 0) { - ts_st->adts = av_mallocz(sizeof(*ts_st->adts)); - if (!ts_st->adts) + st->codec->extradata_size > 0) + { + AVStream *ast; + uint8_t *buffer; + int buffer_size = 32768; + ts_st->amux = avformat_alloc_context(); + if (!ts_st->amux) { + ret = AVERROR(ENOMEM); goto fail; - if (ff_adts_decode_extradata(s, ts_st->adts, st->codec->extradata, - st->codec->extradata_size) < 0) + } + buffer = av_malloc(buffer_size); + if (!buffer) { + ret = AVERROR(ENOMEM); + goto fail; + } + ts_st->amux->pb = avio_alloc_context(buffer, buffer_size, AVIO_FLAG_WRITE, + ts_st, NULL, mpegts_audio_write, NULL); + if (!ts_st->amux->pb) { + av_free(buffer); + ret = AVERROR(ENOMEM); + goto fail; + } + ts_st->amux->oformat = av_guess_format("adts", NULL, NULL); + ast = avformat_new_stream(ts_st->amux, NULL); + ret = avcodec_copy_context(ast->codec, st->codec); + if (ret != 0) + goto fail; + ret = avformat_write_header(ts_st->amux, NULL); + if (ret < 0) goto fail; } } @@ -619,7 +658,11 @@ static int mpegts_write_header(AVFormatContext *s) ts_st = st->priv_data; if (ts_st) { av_freep(&ts_st->payload); - av_freep(&ts_st->adts); + if (ts_st->amux) { + av_free(ts_st->amux->pb->buffer); + av_free(ts_st->amux->pb); + avformat_free_context(ts_st->amux); + } } av_freep(&st->priv_data); } @@ -999,35 +1042,41 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) size = pkt->size+6; } } else if (st->codec->codec_id == CODEC_ID_AAC) { - if (pkt->size < 2) - return -1; + if (pkt->size < 2) { + av_log(s, AV_LOG_ERROR, "AAC packet too short\n"); + return AVERROR(EINVAL); + } if ((AV_RB16(pkt->data) & 0xfff0) != 0xfff0) { - ADTSContext *adts = ts_st->adts; - int new_size, err; - if (!adts) { - av_log(s, AV_LOG_ERROR, "aac bitstream not in adts format " + int ret; + AVPacket pkt2; + + if (!ts_st->amux) { + av_log(s, AV_LOG_ERROR, "AAC bitstream not in ADTS format " "and extradata missing\n"); - return -1; + return AVERROR(EINVAL); } - new_size = ADTS_HEADER_SIZE+adts->pce_size+pkt->size; - if ((unsigned)new_size >= INT_MAX) - return -1; - data = av_malloc(new_size); + + av_init_packet(&pkt2); + pkt2.data = pkt->data; + pkt2.size = pkt->size; + ts_st->adata_size = 1024 + pkt->size; + ts_st->adata = data = av_malloc(ts_st->adata_size); + ts_st->adata_pos = 0; if (!data) return AVERROR(ENOMEM); - err = ff_adts_write_frame_header(adts, data, pkt->size, - adts->pce_size); - if (err < 0) { + + ret = av_write_frame(ts_st->amux, &pkt2); + if (ret < 0) { av_free(data); - return err; + return ret; } - if (adts->pce_size) { - memcpy(data+ADTS_HEADER_SIZE, adts->pce_data, adts->pce_size); - adts->pce_size = 0; + avio_flush(ts_st->amux->pb); + if (ts_st->amux->pb->error < 0) { + av_free(data); + return ts_st->amux->pb->error; } - memcpy(data+ADTS_HEADER_SIZE+adts->pce_size, pkt->data, pkt->size); - buf = data; - size = new_size; + buf = ts_st->adata; + size = ts_st->adata_pos; } } @@ -1107,7 +1156,11 @@ static int mpegts_write_end(AVFormatContext *s) AVStream *st = s->streams[i]; MpegTSWriteStream *ts_st = st->priv_data; av_freep(&ts_st->payload); - av_freep(&ts_st->adts); + if (ts_st->amux) { + av_free(ts_st->amux->pb->buffer); + av_free(ts_st->amux->pb); + avformat_free_context(ts_st->amux); + } } for(i = 0; i < ts->nb_services; i++) { From 485d3ea064c60d0344e4919a7ba611ebe0367508 Mon Sep 17 00:00:00 2001 From: Jindrich Makovicka Date: Tue, 29 May 2012 14:48:38 +0200 Subject: [PATCH 3/4] adtsenc: Don't expose the muxer internals to the rest of lavf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This isn't required any longer, when the mpegts muxer uses it as a proper chained muxer. Signed-off-by: Jindrich Makovicka Signed-off-by: Martin Storsjö --- libavformat/adts.h | 45 ------------------------------------------- libavformat/adtsenc.c | 22 +++++++++++++++------ 2 files changed, 16 insertions(+), 51 deletions(-) delete mode 100644 libavformat/adts.h diff --git a/libavformat/adts.h b/libavformat/adts.h deleted file mode 100644 index 78b42c7101..0000000000 --- a/libavformat/adts.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * ADTS muxer. - * Copyright (c) 2006 Baptiste Coudurier - * Mans Rullgard - * - * This file is part of Libav. - * - * Libav is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * Libav is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef AVFORMAT_ADTS_H -#define AVFORMAT_ADTS_H - -#include "avformat.h" -#include "libavcodec/mpeg4audio.h" - -#define ADTS_HEADER_SIZE 7 - -typedef struct { - int write_adts; - int objecttype; - int sample_rate_index; - int channel_conf; - int pce_size; - uint8_t pce_data[MAX_PCE_SIZE]; -} ADTSContext; - -int ff_adts_write_frame_header(ADTSContext *ctx, uint8_t *buf, - int size, int pce_size); -int ff_adts_decode_extradata(AVFormatContext *s, ADTSContext *adts, - uint8_t *buf, int size); - -#endif /* AVFORMAT_ADTS_H */ diff --git a/libavformat/adtsenc.c b/libavformat/adtsenc.c index ef3d8e2c21..1a9f879aac 100644 --- a/libavformat/adtsenc.c +++ b/libavformat/adtsenc.c @@ -25,11 +25,21 @@ #include "libavcodec/avcodec.h" #include "libavcodec/mpeg4audio.h" #include "avformat.h" -#include "adts.h" + +#define ADTS_HEADER_SIZE 7 + +typedef struct { + int write_adts; + int objecttype; + int sample_rate_index; + int channel_conf; + int pce_size; + uint8_t pce_data[MAX_PCE_SIZE]; +} ADTSContext; #define ADTS_MAX_FRAME_BYTES ((1 << 13) - 1) -int ff_adts_decode_extradata(AVFormatContext *s, ADTSContext *adts, uint8_t *buf, int size) +static int adts_decode_extradata(AVFormatContext *s, ADTSContext *adts, uint8_t *buf, int size) { GetBitContext gb; PutBitContext pb; @@ -84,14 +94,14 @@ static int adts_write_header(AVFormatContext *s) AVCodecContext *avc = s->streams[0]->codec; if (avc->extradata_size > 0 && - ff_adts_decode_extradata(s, adts, avc->extradata, avc->extradata_size) < 0) + adts_decode_extradata(s, adts, avc->extradata, avc->extradata_size) < 0) return -1; return 0; } -int ff_adts_write_frame_header(ADTSContext *ctx, - uint8_t *buf, int size, int pce_size) +static int adts_write_frame_header(ADTSContext *ctx, + uint8_t *buf, int size, int pce_size) { PutBitContext pb; @@ -137,7 +147,7 @@ static int adts_write_packet(AVFormatContext *s, AVPacket *pkt) if (!pkt->size) return 0; if (adts->write_adts) { - int err = ff_adts_write_frame_header(adts, buf, pkt->size, + int err = adts_write_frame_header(adts, buf, pkt->size, adts->pce_size); if (err < 0) return err; From 2439bd86817374362ac8daa701fdb567e14cbe50 Mon Sep 17 00:00:00 2001 From: Jindrich Makovicka Date: Fri, 1 Jun 2012 10:42:48 +0200 Subject: [PATCH 4/4] mpegtsenc: Support LATM packetization for AAC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds the avoption mpegts_flags and converts the existing resend_headers option into a flag, keeping the old option as fallback for now. Signed-off-by: Jindrich Makovicka Signed-off-by: Martin Storsjö --- configure | 2 +- libavformat/mpegtsenc.c | 33 ++++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/configure b/configure index ece3814855..a1dbe9f9a7 100755 --- a/configure +++ b/configure @@ -1457,7 +1457,7 @@ matroska_demuxer_suggest="zlib bzlib" mov_demuxer_suggest="zlib" mp3_demuxer_select="mpegaudio_parser" mp4_muxer_select="mov_muxer" -mpegts_muxer_select="adts_muxer" +mpegts_muxer_select="adts_muxer latm_muxer" mpegtsraw_demuxer_select="mpegts_demuxer" mxf_d10_muxer_select="mxf_muxer" ogg_demuxer_select="golomb" diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index 5c6d8db99d..f3520c0504 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -76,7 +76,11 @@ typedef struct MpegTSWrite { int pmt_start_pid; int start_pid; - int reemit_pat_pmt; + int reemit_pat_pmt; // backward compatibility + +#define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01 +#define MPEGTS_FLAG_AAC_LATM 0x02 + int flags; } MpegTSWrite; /* a PES packet header is generated every DEFAULT_PES_HEADER_FREQ packets */ @@ -97,6 +101,15 @@ static const AVOption options[] = { { "muxrate", NULL, offsetof(MpegTSWrite, mux_rate), AV_OPT_TYPE_INT, {1}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { "pes_payload_size", "Minimum PES packet payload in bytes", offsetof(MpegTSWrite, pes_payload_size), AV_OPT_TYPE_INT, {DEFAULT_PES_PAYLOAD_SIZE}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "mpegts_flags", "MPEG-TS muxing flags", offsetof(MpegTSWrite, flags), AV_OPT_TYPE_FLAGS, {.dbl = 0}, 0, INT_MAX, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, + { "resend_headers", "Reemit PAT/PMT before writing the next packet", + 0, AV_OPT_TYPE_CONST, {.dbl = MPEGTS_FLAG_REEMIT_PAT_PMT}, 0, INT_MAX, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags"}, + { "latm", "Use LATM packetization for AAC", + 0, AV_OPT_TYPE_CONST, {.dbl = MPEGTS_FLAG_AAC_LATM}, 0, INT_MAX, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags"}, + // backward compatibility { "resend_headers", "Reemit PAT/PMT before writing the next packet", offsetof(MpegTSWrite, reemit_pat_pmt), AV_OPT_TYPE_INT, {0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { NULL }, @@ -239,7 +252,7 @@ static void mpegts_write_pat(AVFormatContext *s) static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) { - // MpegTSWrite *ts = s->priv_data; + MpegTSWrite *ts = s->priv_data; uint8_t data[1012], *q, *desc_length_ptr, *program_info_length_ptr; int val, stream_type, i; @@ -278,7 +291,7 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) stream_type = STREAM_TYPE_AUDIO_MPEG1; break; case CODEC_ID_AAC: - stream_type = STREAM_TYPE_AUDIO_AAC; + stream_type = (ts->flags & MPEGTS_FLAG_AAC_LATM) ? STREAM_TYPE_AUDIO_AAC_LATM : STREAM_TYPE_AUDIO_AAC; break; case CODEC_ID_AAC_LATM: stream_type = STREAM_TYPE_AUDIO_AAC_LATM; @@ -583,7 +596,11 @@ static int mpegts_write_header(AVFormatContext *s) ret = AVERROR(ENOMEM); goto fail; } - ts_st->amux->oformat = av_guess_format("adts", NULL, NULL); + ts_st->amux->oformat = av_guess_format((ts->flags & MPEGTS_FLAG_AAC_LATM) ? "latm" : "adts", NULL, NULL); + if (!ts_st->amux->oformat) { + ret = AVERROR(EINVAL); + goto fail; + } ast = avformat_new_stream(ts_st->amux, NULL); ret = avcodec_copy_context(ast->codec, st->codec); if (ret != 0) @@ -998,9 +1015,15 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) int64_t dts = AV_NOPTS_VALUE, pts = AV_NOPTS_VALUE; if (ts->reemit_pat_pmt) { + av_log(s, AV_LOG_WARNING, "resend_headers option is deprecated, use -mpegts_flags resend_headers\n"); + ts->reemit_pat_pmt = 0; + ts->flags |= MPEGTS_FLAG_REEMIT_PAT_PMT; + } + + if (ts->flags & MPEGTS_FLAG_REEMIT_PAT_PMT) { ts->pat_packet_count = ts->pat_packet_period - 1; ts->sdt_packet_count = ts->sdt_packet_period - 1; - ts->reemit_pat_pmt = 0; + ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT; } if (pkt->pts != AV_NOPTS_VALUE)