This source file includes following definitions.
- dc_gpac_audio_moov_create
- dc_gpac_audio_isom_open_seg
- dc_gpac_audio_isom_write
- dc_gpac_audio_isom_close_seg
- dc_gpac_audio_isom_close
- dc_ffmpeg_audio_muxer_open
- dc_ffmpeg_audio_muxer_write
- dc_ffmpeg_audio_muxer_close
- dc_audio_muxer_init
- dc_audio_muxer_free
- dc_audio_muxer_open
- dc_audio_muxer_write
- dc_audio_muxer_close
#include "audio_muxer.h"
#include "libavformat/avio.h"
#ifndef GPAC_DISABLE_ISOM
int dc_gpac_audio_moov_create(AudioOutputFile *audio_output_file, char *filename)
{
GF_Err ret;
u32 di, track;
u8 bpsample;
GF_ESD *esd;
#ifndef GPAC_DISABLE_AV_PARSERS
GF_M4ADecSpecInfo acfg;
#endif
AVCodecContext *audio_codec_ctx = audio_output_file->codec_ctx;
audio_output_file->isof = gf_isom_open(filename, GF_ISOM_OPEN_WRITE, NULL);
if (!audio_output_file->isof) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot open iso file %s\n", filename));
return -1;
}
esd = gf_odf_desc_esd_new(2);
if (!esd) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot create GF_ESD\n"));
return -1;
}
esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG);
esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG);
esd->decoderConfig->streamType = GF_STREAM_AUDIO;
if (!strcmp(audio_output_file->codec_ctx->codec->name, "aac")) {
esd->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_AAC_MPEG4;
esd->decoderConfig->bufferSizeDB = 20;
esd->slConfig->timestampResolution = audio_codec_ctx->sample_rate;
esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
esd->ESID = 1;
#ifndef GPAC_DISABLE_AV_PARSERS
memset(&acfg, 0, sizeof(GF_M4ADecSpecInfo));
acfg.base_object_type = GF_M4A_AAC_LC;
acfg.base_sr = audio_codec_ctx->sample_rate;
acfg.nb_chan = audio_codec_ctx->channels;
acfg.sbr_object_type = 0;
acfg.audioPL = gf_m4a_get_profile(&acfg);
ret = gf_m4a_write_config(&acfg, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength);
assert(ret == GF_OK);
#endif
} else {
if (strcmp(audio_output_file->codec_ctx->codec->name, "mp2")) {
GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("Unlisted codec, setting GPAC_OTI_AUDIO_MPEG1 descriptor.\n"));
}
esd->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_MPEG1;
esd->decoderConfig->bufferSizeDB = 20;
esd->slConfig->timestampResolution = audio_codec_ctx->sample_rate;
esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
esd->ESID = 1;
#ifndef GPAC_DISABLE_AV_PARSERS
memset(&acfg, 0, sizeof(GF_M4ADecSpecInfo));
acfg.base_object_type = GF_M4A_LAYER2;
acfg.base_sr = audio_codec_ctx->sample_rate;
acfg.nb_chan = audio_codec_ctx->channels;
acfg.sbr_object_type = 0;
acfg.audioPL = gf_m4a_get_profile(&acfg);
ret = gf_m4a_write_config(&acfg, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength);
assert(ret == GF_OK);
#endif
}
track = gf_isom_new_track(audio_output_file->isof, esd->ESID, GF_ISOM_MEDIA_AUDIO, audio_codec_ctx->sample_rate);
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("TimeScale: %d \n", audio_codec_ctx->time_base.den));
if (!track) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot create new track\n"));
return -1;
}
ret = gf_isom_set_track_enabled(audio_output_file->isof, track, 1);
if (ret != GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_set_track_enabled\n", gf_error_to_string(ret)));
return -1;
}
ret = gf_isom_new_mpeg4_description(audio_output_file->isof, track, esd, NULL, NULL, &di);
if (ret != GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_new_mpeg4_description\n", gf_error_to_string(ret)));
return -1;
}
gf_odf_desc_del((GF_Descriptor *) esd);
esd = NULL;
bpsample = av_get_bytes_per_sample(audio_output_file->codec_ctx->sample_fmt) * 8;
ret = gf_isom_set_audio_info(audio_output_file->isof, track, di, audio_codec_ctx->sample_rate, audio_output_file->codec_ctx->channels, bpsample);
if (ret != GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_set_audio_info\n", gf_error_to_string(ret)));
return -1;
}
#ifndef GPAC_DISABLE_AV_PARSERS
ret = gf_isom_set_pl_indication(audio_output_file->isof, GF_ISOM_PL_AUDIO, acfg.audioPL);
if (ret != GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_set_pl_indication\n", gf_error_to_string(ret)));
return -1;
}
#endif
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("time scale: %d sample dur: %d \n", audio_codec_ctx->time_base.den, audio_output_file->codec_ctx->frame_size));
ret = gf_isom_setup_track_fragment(audio_output_file->isof, track, 1, audio_output_file->codec_ctx->frame_size, 0, 0, 0, 0);
if (ret != GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_setup_track_fragment\n", gf_error_to_string(ret)));
return -1;
}
ret = gf_isom_finalize_for_fragment(audio_output_file->isof, 1);
if (ret != GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_finalize_for_fragment\n", gf_error_to_string(ret)));
return -1;
}
ret = gf_media_get_rfc_6381_codec_name(audio_output_file->isof, track, audio_output_file->audio_data_conf->codec6381, GF_FALSE, GF_FALSE);
if (ret != GF_OK) return -1;
return 0;
}
int dc_gpac_audio_isom_open_seg(AudioOutputFile *audio_output_file, char *filename)
{
GF_Err ret;
ret = gf_isom_start_segment(audio_output_file->isof, filename, GF_TRUE);
if (ret != GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_start_segment\n", gf_error_to_string(ret)));
return -1;
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DashCast] Audio segment %s started at "LLU"\n", filename, gf_net_get_utc() ));
audio_output_file->dts = 0;
return 0;
}
int dc_gpac_audio_isom_write(AudioOutputFile *audio_output_file)
{
GF_Err ret;
audio_output_file->sample->data = (char *) audio_output_file->packet.data;
audio_output_file->sample->dataLength = audio_output_file->packet.size;
audio_output_file->sample->DTS = audio_output_file->dts;
audio_output_file->sample->IsRAP = RAP;
ret = gf_isom_fragment_add_sample(audio_output_file->isof, 1, audio_output_file->sample, 1, audio_output_file->codec_ctx->frame_size, 0, 0, 0);
audio_output_file->dts += audio_output_file->codec_ctx->frame_size;
if (ret != GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_fragment_add_sample\n", gf_error_to_string(ret)));
return -1;
}
return 0;
}
int dc_gpac_audio_isom_close_seg(AudioOutputFile *audio_output_file)
{
GF_Err ret;
ret = gf_isom_close_segment(audio_output_file->isof, 0, 0,0, 0, 0, 0, 1, audio_output_file->seg_marker, NULL, NULL);
if (ret != GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_close_segment\n", gf_error_to_string(ret)));
return -1;
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DashCast] Audio segment closed at "LLU"\n", gf_net_get_utc() ));
return 0;
}
int dc_gpac_audio_isom_close(AudioOutputFile *audio_output_file)
{
GF_Err ret;
ret = gf_isom_close(audio_output_file->isof);
if (ret != GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_close\n", gf_error_to_string(ret)));
return -1;
}
return 0;
}
#endif
int dc_ffmpeg_audio_muxer_open(AudioOutputFile *audio_output_file, char *filename)
{
AVStream *audio_stream;
AVOutputFormat *output_fmt;
AVDictionary *opts = NULL;
AVCodecContext *audio_codec_ctx = audio_output_file->codec_ctx;
audio_output_file->av_fmt_ctx = NULL;
output_fmt = av_guess_format(NULL, filename, NULL);
if (!output_fmt) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot find suitable output format\n"));
return -1;
}
audio_output_file->av_fmt_ctx = avformat_alloc_context();
if (!audio_output_file->av_fmt_ctx) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot allocate memory for pOutVideoFormatCtx\n"));
return -1;
}
audio_output_file->av_fmt_ctx->oformat = output_fmt;
strcpy(audio_output_file->av_fmt_ctx->filename, filename);
if (!(output_fmt->flags & AVFMT_NOFILE)) {
if (avio_open(&audio_output_file->av_fmt_ctx->pb, filename, URL_WRONLY) < 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot not open '%s'\n", filename));
return -1;
}
}
audio_stream = avformat_new_stream(audio_output_file->av_fmt_ctx, audio_output_file->codec);
if (!audio_stream) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot create output video stream\n"));
return -1;
}
audio_stream->codec->codec_id = audio_output_file->codec->id;
audio_stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
audio_stream->codec->bit_rate = audio_codec_ctx->bit_rate;
audio_stream->codec->sample_rate = audio_codec_ctx->sample_rate;
audio_stream->codec->channels = audio_codec_ctx->channels;
assert(audio_codec_ctx->codec->sample_fmts);
audio_stream->codec->sample_fmt = audio_codec_ctx->codec->sample_fmts[0];
av_dict_set(&opts, "strict", "experimental", 0);
if (avcodec_open2(audio_stream->codec, audio_output_file->codec, &opts) < 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot open output video codec\n"));
av_dict_free(&opts);
return -1;
}
av_dict_free(&opts);
avformat_write_header(audio_output_file->av_fmt_ctx, NULL);
return 0;
}
int dc_ffmpeg_audio_muxer_write(AudioOutputFile *audio_output_file)
{
AVStream *audio_stream = audio_output_file->av_fmt_ctx->streams[audio_output_file->astream_idx];
AVCodecContext *audio_codec_ctx = audio_stream->codec;
audio_output_file->packet.stream_index = audio_stream->index;
if (audio_output_file->packet.pts != AV_NOPTS_VALUE)
audio_output_file->packet.pts = av_rescale_q(audio_output_file->packet.pts, audio_codec_ctx->time_base, audio_stream->time_base);
if (audio_output_file->packet.duration > 0)
audio_output_file->packet.duration = (int)av_rescale_q(audio_output_file->packet.duration, audio_codec_ctx->time_base, audio_stream->time_base);
audio_output_file->packet.flags |= AV_PKT_FLAG_KEY;
if (av_interleaved_write_frame(audio_output_file->av_fmt_ctx, &audio_output_file->packet) != 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Writing frame is not successful\n"));
av_free_packet(&audio_output_file->packet);
return -1;
}
av_free_packet(&audio_output_file->packet);
return 0;
}
int dc_ffmpeg_audio_muxer_close(AudioOutputFile *audio_output_file)
{
u32 i;
av_write_trailer(audio_output_file->av_fmt_ctx);
avio_close(audio_output_file->av_fmt_ctx->pb);
for (i = 0; i < audio_output_file->av_fmt_ctx->nb_streams; i++) {
avcodec_close(audio_output_file->av_fmt_ctx->streams[i]->codec);
av_freep(&audio_output_file->av_fmt_ctx->streams[i]->info);
}
avformat_free_context(audio_output_file->av_fmt_ctx);
return 0;
}
int dc_audio_muxer_init(AudioOutputFile *audio_output_file, AudioDataConf *audio_data_conf, AudioMuxerType muxer_type, int frame_per_seg, int frame_per_frag, u32 seg_marker)
{
char name[GF_MAX_PATH];
snprintf(name, sizeof(name), "audio encoder %s", audio_data_conf->filename);
dc_consumer_init(&audio_output_file->consumer, AUDIO_CB_SIZE, name);
#ifndef GPAC_DISABLE_ISOM
audio_output_file->sample = gf_isom_sample_new();
audio_output_file->isof = NULL;
#endif
audio_output_file->muxer_type = muxer_type;
audio_output_file->frame_per_seg = frame_per_seg;
audio_output_file->frame_per_frag = frame_per_frag;
audio_output_file->seg_marker = seg_marker;
return 0;
}
void dc_audio_muxer_free(AudioOutputFile *audio_output_file)
{
#ifndef GPAC_DISABLE_ISOM
if (audio_output_file->isof != NULL) {
gf_isom_close(audio_output_file->isof);
}
#endif
}
GF_Err dc_audio_muxer_open(AudioOutputFile *audio_output_file, char *directory, char *id_name, int seg)
{
GF_Err ret = GF_NOT_SUPPORTED;
char name[GF_MAX_PATH];
switch (audio_output_file->muxer_type) {
case FFMPEG_AUDIO_MUXER:
snprintf(name, sizeof(name), "%s/%s_%d_ffmpeg.mp4", directory, id_name, seg);
return dc_ffmpeg_audio_muxer_open(audio_output_file, name);
#ifndef GPAC_DISABLE_ISOM
case GPAC_AUDIO_MUXER:
snprintf(name, sizeof(name), "%s/%s_%d_gpac.mp4", directory, id_name, seg);
dc_gpac_audio_moov_create(audio_output_file, name);
return dc_gpac_audio_isom_open_seg(audio_output_file, NULL);
case GPAC_INIT_AUDIO_MUXER:
if (seg == 1) {
snprintf(name, sizeof(name), "%s/%s_init_gpac.mp4", directory, id_name);
dc_gpac_audio_moov_create(audio_output_file, name);
audio_output_file->first_dts = 0;
}
snprintf(name, sizeof(name), "%s/%s_%d_gpac.m4s", directory, id_name, seg);
ret = dc_gpac_audio_isom_open_seg(audio_output_file, name);
return ret;
#endif
default:
ret = GF_BAD_PARAM;
break;
}
return ret;
}
int dc_audio_muxer_write(AudioOutputFile *audio_output_file, int frame_nb, Bool insert_ntp)
{
switch (audio_output_file->muxer_type) {
case FFMPEG_AUDIO_MUXER:
return dc_ffmpeg_audio_muxer_write(audio_output_file);
#ifndef GPAC_DISABLE_ISOM
case GPAC_AUDIO_MUXER:
case GPAC_INIT_AUDIO_MUXER:
if (frame_nb % audio_output_file->frame_per_frag == 0) {
gf_isom_start_fragment(audio_output_file->isof, 1);
if (insert_ntp) {
gf_isom_set_fragment_reference_time(audio_output_file->isof, 1, audio_output_file->frame_ntp, audio_output_file->first_dts * audio_output_file->codec_ctx->frame_size);
}
gf_isom_set_traf_base_media_decode_time(audio_output_file->isof, 1, audio_output_file->first_dts * audio_output_file->codec_ctx->frame_size);
audio_output_file->first_dts += audio_output_file->frame_per_frag;
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DashCast] Audio start fragment first DTS %u at "LLU"\n", audio_output_file->first_dts, gf_net_get_utc() ));
}
dc_gpac_audio_isom_write(audio_output_file);
if (frame_nb % audio_output_file->frame_per_frag == audio_output_file->frame_per_frag - 1) {
gf_isom_flush_fragments(audio_output_file->isof, 1);
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DashCast] Audio flush fragment first DTS %u at "LLU"\n", audio_output_file->first_dts, gf_net_get_utc() ));
}
if (frame_nb + 1 == audio_output_file->frame_per_seg) {
return 1;
}
return 0;
#endif
default:
return GF_BAD_PARAM;
}
return GF_BAD_PARAM;
}
int dc_audio_muxer_close(AudioOutputFile *audio_output_file)
{
switch (audio_output_file->muxer_type) {
case FFMPEG_AUDIO_MUXER:
return dc_ffmpeg_audio_muxer_close(audio_output_file);
#ifndef GPAC_DISABLE_ISOM
case GPAC_AUDIO_MUXER:
dc_gpac_audio_isom_close_seg(audio_output_file);
return dc_gpac_audio_isom_close(audio_output_file);
case GPAC_INIT_AUDIO_MUXER:
return dc_gpac_audio_isom_close_seg(audio_output_file);
#endif
default:
return GF_BAD_PARAM;
}
return GF_BAD_PARAM;
}