Skip to content

Commit ab518a2

Browse files
committed
support 1) non transcode dashcastx 2) non-annex B streams for mp4 mux and 3) fix #47
1 parent a34a057 commit ab518a2

File tree

7 files changed

+110
-50
lines changed

7 files changed

+110
-50
lines changed

doc/build.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,3 @@ $ PKG_CONFIG_PATH=$PWD/extra/x86_64-w64-mingw32/lib/pkgconfig CXX=x86_64-w64-min
9292

9393
# Run
9494
The binaries are in generated in ```bin/```including a sample player, the dashcastx application, and all the unit test apps.
95-
96-
Note: For dashcastx, for live inputs with FFmpeg demux, please increase the fifo size: protocol://ip_address:port?fifo_size=1000000&overrun_nonfatal=1

src/apps/dashcastx/options.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,14 @@ const option::Descriptor usage[] = {
6969
{
7070
UNKNOWN, 0, "", "", Arg::None,
7171
"\nExamples:\n"
72-
" dashcastx file.ts\n"
73-
" dashcastx udp://226.0.0.1:1234?fifo_size=1000000&overrun_nonfatal=1\n"
72+
"No transcode:\n"
7473
" dashcastx -l -s 10000 file.mp4\n"
74+
" dashcastx file.ts\n"
75+
" dashcastx udp://226.0.0.1:1234?overrun_nonfatal=1\n"
76+
"Transcode:\n"
7577
" dashcastx --live --seg-dur 10000 --video 320x180:50000 -v 640x360:300000 http://server.com/file.mp4\n"
7678
" dashcastx --live -v 1280x720:100000 webcam:video=/dev/video0:audio=/dev/audio1\n"
79+
" dashcastx --live -v 1280x720:100000 -v 640x360:300000 webcam:video=/dev/video0:audio=/dev/audio1\n"
7780
},
7881
{ 0, 0, 0, 0, 0, 0 }
7982
};
@@ -132,8 +135,5 @@ dashcastXOptions processArgs(int argc, char const* argv[]) {
132135
}
133136
}
134137

135-
if (!opt.v.size())
136-
opt.v.push_back(Video(Modules::VIDEO_RESOLUTION, 300000));
137-
138138
return opt;
139139
}

src/apps/dashcastx/pipeliner.cpp

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ void declarePipeline(Pipeline &pipeline, const dashcastXOptions &opt) {
5353
auto dasher = pipeline.addModule(new Modules::Stream::MPEG_DASH("dashcastx.mpd",
5454
opt.isLive ? Modules::Stream::MPEG_DASH::Live : Modules::Stream::MPEG_DASH::Static, opt.segmentDuration));
5555

56+
const bool transcode = opt.v.size() > 0 ? true : false;
57+
if (!transcode) {
58+
Log::msg(Log::Warning, "[DashcastX] No transcode. Make passthru.");
59+
}
60+
5661
int numDashInputs = 0;
5762
for (size_t i = 0; i < demux->getNumOutputs(); ++i) {
5863
auto const metadata = getMetadataFromOutput<MetadataPktLibav>(demux->getOutput(i));
@@ -61,32 +66,41 @@ void declarePipeline(Pipeline &pipeline, const dashcastXOptions &opt) {
6166
break;
6267
}
6368

64-
auto decode = pipeline.addModule(new Decode::LibavDecode(*metadata));
65-
pipeline.connect(demux, i, decode, 0);
66-
69+
Pipelines::PipelinedModule *decode = nullptr;
70+
if (transcode) {
71+
decode = pipeline.addModule(new Decode::LibavDecode(*metadata));
72+
pipeline.connect(demux, i, decode, 0);
6773
#ifdef DEBUG_MONITOR
68-
auto webcamPreview = pipeline.addModule(new Render::SDLVideo());
69-
connect(decode, webcamPreview);
74+
auto webcamPreview = pipeline.addModule(new Render::SDLVideo());
75+
connect(decode, webcamPreview);
7076
#endif
77+
}
7178

72-
auto const numRes = metadata->isVideo() ? opt.v.size() : 1;
79+
auto const numRes = metadata->isVideo() ? std::max<size_t>(opt.v.size(), 1) : 1;
7380
for (size_t r = 0; r < numRes; ++r) {
74-
auto converter = pipeline.addModule(createConverter(metadata, opt.v[r].res));
75-
if (!converter)
76-
continue;
81+
Pipelines::PipelinedModule *encoder = nullptr;
82+
if (transcode) {
83+
auto converter = pipeline.addModule(createConverter(metadata, opt.v[r].res));
84+
if (!converter)
85+
continue;
7786

78-
connect(decode, converter);
87+
connect(decode, converter);
7988

80-
auto encoder = pipeline.addModule(createEncoder(metadata, opt, r));
81-
if (!encoder)
82-
continue;
89+
encoder = pipeline.addModule(createEncoder(metadata, opt, r));
90+
if (!encoder)
91+
continue;
8392

84-
connect(converter, encoder);
93+
connect(converter, encoder);
94+
}
8595

8696
std::stringstream filename;
8797
filename << numDashInputs;
8898
auto muxer = pipeline.addModule(new Mux::GPACMuxMP4(filename.str(), opt.segmentDuration, true));
89-
connect(encoder, muxer);
99+
if (transcode) {
100+
connect(encoder, muxer);
101+
} else {
102+
pipeline.connect(demux, i, muxer, 0);
103+
}
90104

91105
pipeline.connect(muxer, 0, dasher, numDashInputs);
92106
numDashInputs++;

src/lib_media/demux/libav_demux.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ LibavDemux::LibavDemux(const std::string &url) {
9090
throw std::runtime_error("Couldn't find stream info.");
9191
}
9292

93-
restamp = uptr(new Transform::Restamp(Transform::Restamp::Passthru));
93+
restamp = uptr(new Transform::Restamp(Transform::Restamp::Reset));
9494
}
9595

9696
for (unsigned i = 0; i<m_formatCtx->nb_streams; i++) {

src/lib_media/mux/gpac_mux_mp4.cpp

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ static GF_Err avc_import_ffextradata(const u8 *extradata, const u64 extradataSiz
3434
}
3535
if (gf_bs_read_u32(bs) != 0x00000001) {
3636
gf_bs_del(bs);
37-
return GF_BAD_PARAM;
37+
return GF_NON_COMPLIANT_BITSTREAM;
3838
}
3939

4040
//SPS
@@ -151,7 +151,7 @@ static GF_Err hevc_import_ffextradata(const u8 *extradata, const u64 extradata_s
151151

152152
if (gf_bs_read_u32(bs) != 0x00000001) {
153153
gf_bs_del(bs);
154-
return GF_BAD_PARAM;
154+
return GF_NON_COMPLIANT_BITSTREAM;
155155
}
156156
NALStart = gf_bs_get_position(bs);
157157
NALSize = gf_media_nalu_next_start_code_bs(bs);
@@ -314,6 +314,7 @@ void fillVideoSampleData(const u8 *bufPtr, u32 bufLen, GF_ISOSample &sample) {
314314
bufPtr += (NALUSize + scSize);
315315
bufLen -= (NALUSize + scSize);
316316
}
317+
317318
while (bufLen) {
318319
NALUSize = gf_media_nalu_next_start_code(bufPtr, bufLen, &scSize);
319320
if (NALUSize != 0) {
@@ -570,22 +571,6 @@ void GPACMuxMP4::declareStreamAudio(std::shared_ptr<const MetadataPktLibavAudio>
570571
}
571572

572573
void GPACMuxMP4::declareStreamVideo(std::shared_ptr<const MetadataPktLibavVideo> metadata) {
573-
GF_AVCConfig *avccfg = gf_odf_avc_cfg_new();
574-
if (!avccfg) {
575-
Log::msg(Log::Warning, "Cannot create AVCConfig");
576-
throw std::runtime_error("[GPACMuxMP4] Container format import failed");
577-
}
578-
579-
const uint8_t *extradata;
580-
size_t extradataSize;
581-
metadata->getExtradata(extradata, extradataSize);
582-
GF_Err e = avc_import_ffextradata(extradata, extradataSize, avccfg);
583-
if (e) {
584-
Log::msg(Log::Warning, "Cannot parse H264 SPS/PPS");
585-
gf_odf_avc_cfg_del(avccfg);
586-
throw std::runtime_error("[GPACMuxMP4] Container format import failed");
587-
}
588-
589574
u32 trackNum = gf_isom_new_track(m_iso, 0, GF_ISOM_MEDIA_VISUAL, metadata->getTimeScale() * TIMESCALE_MUL);
590575
if (!trackNum) {
591576
Log::msg(Log::Warning, "Cannot create new track");
@@ -594,20 +579,74 @@ void GPACMuxMP4::declareStreamVideo(std::shared_ptr<const MetadataPktLibavVideo>
594579

595580
m_trackId = gf_isom_get_track_id(m_iso, trackNum);
596581

597-
e = gf_isom_set_track_enabled(m_iso, trackNum, GF_TRUE);
582+
GF_Err e = gf_isom_set_track_enabled(m_iso, trackNum, GF_TRUE);
598583
if (e != GF_OK) {
599584
Log::msg(Log::Warning, "%s: gf_isom_set_track_enabled", gf_error_to_string(e));
600585
throw std::runtime_error("[GPACMuxMP4] Cannot enable track");
601586
}
602587

588+
const uint8_t *extradata;
589+
size_t extradataSize;
590+
metadata->getExtradata(extradata, extradataSize);
591+
603592
u32 di;
604-
e = gf_isom_avc_config_new(m_iso, trackNum, avccfg, nullptr, nullptr, &di);
605-
if (e != GF_OK) {
606-
Log::msg(Log::Warning, "%s: gf_isom_avc_config_new", gf_error_to_string(e));
607-
throw std::runtime_error("[GPACMuxMP4] Cannot create AVC config");
593+
if (metadata->getAVCodecContext()->codec_id == CODEC_ID_H264) {
594+
GF_AVCConfig *avccfg = gf_odf_avc_cfg_new();
595+
if (!avccfg) {
596+
Log::msg(Log::Warning, "Cannot create AVCConfig");
597+
throw std::runtime_error("[GPACMuxMP4] Container format import failed (AVC)");
598+
}
599+
e = avc_import_ffextradata(extradata, extradataSize, avccfg);
600+
if (e == GF_OK) {
601+
e = gf_isom_avc_config_new(m_iso, trackNum, avccfg, nullptr, nullptr, &di);
602+
if (e != GF_OK) {
603+
Log::msg(Log::Warning, "%s: gf_isom_avc_config_new", gf_error_to_string(e));
604+
throw std::runtime_error("[GPACMuxMP4] Cannot create AVC config");
605+
}
606+
gf_odf_avc_cfg_del(avccfg);
607+
}
608+
} else if (metadata->getAVCodecContext()->codec_id == AV_CODEC_ID_H265) {
609+
GF_HEVCConfig *hevccfg = gf_odf_hevc_cfg_new();
610+
if (!hevccfg) {
611+
Log::msg(Log::Warning, "Cannot create HEVCConfig");
612+
throw std::runtime_error("[GPACMuxMP4] Container format import failed (HEVC)");
613+
}
614+
e = hevc_import_ffextradata(extradata, extradataSize, hevccfg);
615+
if (e == GF_OK) {
616+
e = gf_isom_hevc_config_new(m_iso, trackNum, hevccfg, nullptr, nullptr, &di);
617+
if (e != GF_OK) {
618+
Log::msg(Log::Warning, "%s: gf_isom_avc_config_new", gf_error_to_string(e));
619+
throw std::runtime_error("[GPACMuxMP4] Cannot create AVC config");
620+
}
621+
gf_odf_hevc_cfg_del(hevccfg);
622+
}
623+
} else {
624+
throw std::runtime_error("[GPACMuxMP4] Unknown codec");
625+
}
626+
if (e) {
627+
if (e == GF_NON_COMPLIANT_BITSTREAM) {
628+
/*non Annex B: assume this is AVCC*/
629+
GF_ESD *esd = (GF_ESD *)gf_odf_desc_esd_new(0);
630+
esd->ESID = 1; /*FIXME: only one track: set trackID?*/
631+
esd->decoderConfig->streamType = GF_STREAM_VISUAL;
632+
esd->decoderConfig->avgBitrate = esd->decoderConfig->maxBitrate = 0;
633+
esd->decoderConfig->objectTypeIndication = metadata->getAVCodecContext()->codec_id == CODEC_ID_H264 ? GPAC_OTI_VIDEO_AVC : GPAC_OTI_VIDEO_HEVC;
634+
esd->decoderConfig->decoderSpecificInfo->dataLength = (u32)extradataSize;
635+
esd->decoderConfig->decoderSpecificInfo->data = (char*)gf_malloc(extradataSize);
636+
memcpy(esd->decoderConfig->decoderSpecificInfo->data, extradata, extradataSize);
637+
esd->slConfig->predefined = SLPredef_MP4;
638+
639+
e = gf_isom_new_mpeg4_description(m_iso, trackNum, esd, nullptr, nullptr, &di);
640+
if (e != GF_OK) {
641+
Log::msg(Log::Warning, "%s: gf_isom_new_mpeg4_description", gf_error_to_string(e));
642+
throw std::runtime_error("[GPACMuxMP4] Cannot create MPEG-4 config");
643+
}
644+
gf_odf_desc_del((GF_Descriptor*)esd);
645+
isAnnexB = false;
646+
} else {
647+
throw std::runtime_error("[GPACMuxMP4] Container format import failed");
648+
}
608649
}
609-
610-
gf_odf_avc_cfg_del(avccfg);
611650

612651
auto const res = metadata->getResolution();
613652
gf_isom_set_visual_info(m_iso, trackNum, di, res.width, res.height);
@@ -752,7 +791,13 @@ void GPACMuxMP4::process() {
752791

753792
u32 mediaType = gf_isom_get_media_type(m_iso, 1);
754793
if (mediaType == GF_ISOM_MEDIA_VISUAL) {
755-
fillVideoSampleData(bufPtr, bufLen, sample);
794+
if (isAnnexB) {
795+
fillVideoSampleData(bufPtr, bufLen, sample);
796+
} else {
797+
sample.data = (char*)bufPtr;
798+
sample.dataLength = bufLen;
799+
sample.setDataOwnership(false);
800+
}
756801
} else if (mediaType == GF_ISOM_MEDIA_AUDIO) {
757802
sample.data = (char*)bufPtr;
758803
sample.dataLength = bufLen;

src/lib_media/mux/gpac_mux_mp4.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class GPACMuxMP4 : public ModuleDynI {
2929
GF_ISOFile *m_iso;
3030
uint32_t m_trackId;
3131
uint64_t m_DTS = 0, m_lastInputTimeIn180k = 0;
32+
bool isAnnexB = true;
3233

3334
//fragments
3435
void setupFragments();

src/lib_media/stream/mpeg_dash.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ void MPEG_DASH::ensureMPD() {
125125
void MPEG_DASH::generateMPD() {
126126
ensureMPD();
127127
for (size_t i = 0; i < getNumInputs() - 1; ++i) {
128-
qualities[i].rep->starts_with_sap = (qualities[i].rep->starts_with_sap == GF_TRUE && qualities[i].meta->getStartsWithRAP()) ? GF_TRUE : GF_FALSE;
128+
if (qualities[i].rep->width) { /*video only*/
129+
qualities[i].rep->starts_with_sap = (qualities[i].rep->starts_with_sap == GF_TRUE && qualities[i].meta->getStartsWithRAP()) ? GF_TRUE : GF_FALSE;
130+
}
129131
}
130132
if (!mpd->mpd->availabilityStartTime) {
131133
mpd->mpd->availabilityStartTime = gf_net_get_utc() - segDurationInMs;

0 commit comments

Comments
 (0)