From 88227730fefc14d51570dd0e943f932e17fe2299 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 15 Feb 2017 12:33:57 +0100 Subject: [PATCH] Added Annex B H264 output --- CMakeLists.txt | 1 + src/output/output_h264.cpp | 86 ++++++++++++++++++++++++++++++++++++++ src/output/output_h264.h | 17 ++++++++ 3 files changed, 104 insertions(+) create mode 100644 src/output/output_h264.cpp create mode 100644 src/output/output_h264.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 95848092..aabd5449 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -401,6 +401,7 @@ makeOutput(FLV progressive_flv http) makeOutput(HTTPMinimalServer http_minimalserver http) makeOutput(MP4 progressive_mp4 http) makeOutput(MP3 progressive_mp3 http) +makeOutput(H264 h264 http) makeOutput(HSS hss http) makeOutput(HDS hds http) makeOutput(SRT srt http) diff --git a/src/output/output_h264.cpp b/src/output/output_h264.cpp new file mode 100644 index 00000000..921fe083 --- /dev/null +++ b/src/output/output_h264.cpp @@ -0,0 +1,86 @@ +#include "output_h264.h" +#include +#include + +namespace Mist{ + OutH264::OutH264(Socket::Connection &conn) : HTTPOutput(conn){ + if (config->getString("target").size()){ + if (!streamName.size()){ + WARN_MSG("Recording unconnected H264 output to file! Cancelled."); + conn.close(); + return; + } + if (config->getString("target") == "-"){ + parseData = true; + wantRequest = false; + INFO_MSG("Outputting %s to stdout in H264 format", streamName.c_str()); + return; + } + if (connectToFile(config->getString("target"))){ + parseData = true; + wantRequest = false; + INFO_MSG("Recording %s to %s in H264 format", streamName.c_str(), config->getString("target").c_str()); + }else{ + conn.close(); + } + } + } + + void OutH264::init(Util::Config *cfg){ + HTTPOutput::init(cfg); + capa["name"] = "H264"; + capa["desc"] = "Enables HTTP protocol H264 Annex B streaming"; + capa["url_rel"] = "/$.h264"; + capa["url_match"] = "/$.h264"; + capa["codecs"][0u][0u].append("H264"); + + JSON::Value opt; + opt["arg"] = "string"; + opt["default"] = ""; + opt["arg_num"] = 1ll; + opt["help"] = "Target filename to store H264 file as, or - for stdout."; + cfg->addOption("target", opt); + } + + bool OutH264::isRecording(){return config->getString("target").size();} + + void OutH264::sendNext(){ + char *dataPointer = 0; + unsigned int len = 0; + thisPacket.getString("data", dataPointer, len); + + unsigned int i = 0; + while (i + 4 < len){ + uint32_t ThisNaluSize = Bit::btohl(dataPointer+i); + myConn.SendNow("\000\000\000\001", 4); + myConn.SendNow(dataPointer + i + 4, ThisNaluSize); + i += ThisNaluSize+4; + } + } + + void OutH264::sendHeader(){ + MP4::AVCC avccbox; + unsigned int mainTrack = getMainSelectedTrack(); + if (mainTrack && myMeta.tracks.count(mainTrack)){ + avccbox.setPayload(myMeta.tracks[mainTrack].init); + myConn.SendNow(avccbox.asAnnexB()); + } + sentHeader = true; + } + + void OutH264::onHTTP(){ + std::string method = H.method; + H.Clean(); + H.SetHeader("Content-Type", "video/H264"); + H.protocol = "HTTP/1.0"; + H.setCORSHeaders(); + if (method == "OPTIONS" || method == "HEAD"){ + H.SendResponse("200", "OK", myConn); + return; + } + H.SendResponse("200", "OK", myConn); + parseData = true; + wantRequest = false; + } +} + diff --git a/src/output/output_h264.h b/src/output/output_h264.h new file mode 100644 index 00000000..6241d5c0 --- /dev/null +++ b/src/output/output_h264.h @@ -0,0 +1,17 @@ +#include "output_http.h" + +namespace Mist{ + class OutH264 : public HTTPOutput{ + public: + OutH264(Socket::Connection &conn); + static void init(Util::Config *cfg); + void onHTTP(); + void sendNext(); + void sendHeader(); + + private: + bool isRecording(); + }; +} + +typedef Mist::OutH264 mistOut;