mirror of
https://github.com/tuxbox-neutrino/neutrino.git
synced 2025-08-26 15:02:56 +02:00
second try to fix record m3u8 format with separate audio
This commit is contained in:
@@ -80,6 +80,7 @@ class CStreamRec : public CRecordInstance, OpenThreads::Thread
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
AVFormatContext *ifcx;
|
AVFormatContext *ifcx;
|
||||||
|
AVFormatContext *ifcx2;
|
||||||
AVFormatContext *ofcx;
|
AVFormatContext *ofcx;
|
||||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 100)
|
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 100)
|
||||||
AVBitStreamFilterContext *bsfc;
|
AVBitStreamFilterContext *bsfc;
|
||||||
@@ -91,6 +92,11 @@ class CStreamRec : public CRecordInstance, OpenThreads::Thread
|
|||||||
time_t time_started;
|
time_t time_started;
|
||||||
int stream_index;
|
int stream_index;
|
||||||
|
|
||||||
|
int videoindex_v;
|
||||||
|
int videoindex_out;
|
||||||
|
int audioindex_a;
|
||||||
|
int audioindex_out;
|
||||||
|
bool have2url;
|
||||||
void GetPids(CZapitChannel * channel);
|
void GetPids(CZapitChannel * channel);
|
||||||
void FillMovieInfo(CZapitChannel * channel, APIDList & apid_list);
|
void FillMovieInfo(CZapitChannel * channel, APIDList & apid_list);
|
||||||
bool Start();
|
bool Start();
|
||||||
@@ -98,6 +104,7 @@ class CStreamRec : public CRecordInstance, OpenThreads::Thread
|
|||||||
void Close();
|
void Close();
|
||||||
bool Open(CZapitChannel * channel);
|
bool Open(CZapitChannel * channel);
|
||||||
void run();
|
void run();
|
||||||
|
void run2url();
|
||||||
void WriteHeader(uint32_t duration);
|
void WriteHeader(uint32_t duration);
|
||||||
public:
|
public:
|
||||||
CStreamRec(const CTimerd::RecordingInfo * const eventinfo, std::string &dir, bool timeshift = false, bool stream_vtxt_pid = false, bool stream_pmt_pid = false, bool stream_subtitle_pids = false);
|
CStreamRec(const CTimerd::RecordingInfo * const eventinfo, std::string &dir, bool timeshift = false, bool stream_vtxt_pid = false, bool stream_pmt_pid = false, bool stream_subtitle_pids = false);
|
||||||
@@ -1977,28 +1984,16 @@ bool CRecordManager::MountDirectory(const char *recordingDir)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if 0 // not used, saved in case we needed it
|
|
||||||
extern bool autoshift_delete;
|
|
||||||
bool CRecordManager::LinkTimeshift()
|
|
||||||
{
|
|
||||||
if(autoshift) {
|
|
||||||
char buf[512];
|
|
||||||
autoshift = false;
|
|
||||||
sprintf(buf, "ln %s/* %s", timeshiftDir, g_settings.network_nfs_recordingdir);
|
|
||||||
system(buf);
|
|
||||||
autoshift_delete = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CStreamRec::CStreamRec(const CTimerd::RecordingInfo * const eventinfo, std::string &dir, bool timeshift, bool stream_vtxt_pid, bool stream_pmt_pid, bool stream_subtitle_pids)
|
CStreamRec::CStreamRec(const CTimerd::RecordingInfo * const eventinfo, std::string &dir, bool timeshift, bool stream_vtxt_pid, bool stream_pmt_pid, bool stream_subtitle_pids)
|
||||||
: CRecordInstance(eventinfo, dir, timeshift, stream_vtxt_pid, stream_pmt_pid, stream_subtitle_pids)
|
: CRecordInstance(eventinfo, dir, timeshift, stream_vtxt_pid, stream_pmt_pid, stream_subtitle_pids)
|
||||||
{
|
{
|
||||||
ifcx = NULL;
|
ifcx = NULL;
|
||||||
|
ifcx2 = NULL;
|
||||||
ofcx = NULL;
|
ofcx = NULL;
|
||||||
|
bsfc = NULL;
|
||||||
stopped = true;
|
stopped = true;
|
||||||
interrupt = false;
|
interrupt = false;
|
||||||
bsfc = NULL;
|
have2url = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CStreamRec::~CStreamRec()
|
CStreamRec::~CStreamRec()
|
||||||
@@ -2012,6 +2007,9 @@ void CStreamRec::Close()
|
|||||||
if (ifcx) {
|
if (ifcx) {
|
||||||
avformat_close_input(&ifcx);
|
avformat_close_input(&ifcx);
|
||||||
}
|
}
|
||||||
|
if (ifcx2) {
|
||||||
|
avformat_close_input(&ifcx2);
|
||||||
|
}
|
||||||
if (ofcx) {
|
if (ofcx) {
|
||||||
if (ofcx->pb) {
|
if (ofcx->pb) {
|
||||||
avio_close(ofcx->pb);
|
avio_close(ofcx->pb);
|
||||||
@@ -2027,6 +2025,7 @@ void CStreamRec::Close()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
ifcx = NULL;
|
ifcx = NULL;
|
||||||
|
ifcx2 = NULL;
|
||||||
ofcx = NULL;
|
ofcx = NULL;
|
||||||
bsfc = NULL;
|
bsfc = NULL;
|
||||||
}
|
}
|
||||||
@@ -2210,12 +2209,13 @@ bool CStreamRec::Open(CZapitChannel * channel)
|
|||||||
if (url.empty())
|
if (url.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::string pretty_name,headers,dumb;
|
std::string pretty_name,headers,url2;
|
||||||
if (!CMoviePlayerGui::getInstance(true).getLiveUrl(channel->getUrl(), channel->getScriptName(), url, pretty_name, recMovieInfo->epgInfo1, recMovieInfo->epgInfo2,headers,dumb)) {
|
if (!CMoviePlayerGui::getInstance(true).getLiveUrl(channel->getUrl(), channel->getScriptName(), url, pretty_name, recMovieInfo->epgInfo1, recMovieInfo->epgInfo2,headers,url2)) {
|
||||||
printf("%s: getLiveUrl() [%s] failed!\n", __FUNCTION__, url.c_str());
|
printf("%s: getLiveUrl() [%s] failed!\n", __FUNCTION__, url.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if(!url2.empty())
|
||||||
|
have2url = true;
|
||||||
//av_log_set_level(AV_LOG_VERBOSE);
|
//av_log_set_level(AV_LOG_VERBOSE);
|
||||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
|
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
|
||||||
av_register_all();
|
av_register_all();
|
||||||
@@ -2234,6 +2234,12 @@ bool CStreamRec::Open(CZapitChannel * channel)
|
|||||||
av_dict_set(&options, "reconnect", "1", 0);
|
av_dict_set(&options, "reconnect", "1", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(have2url) {
|
||||||
|
if (avformat_open_input(&ifcx2, url2.c_str(), NULL, &options) != 0) {
|
||||||
|
printf("%s: Cannot open input2 [%s]!\n", __FUNCTION__, url2.c_str());
|
||||||
|
have2url = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (avformat_open_input(&ifcx, url.c_str(), NULL, &options) != 0) {
|
if (avformat_open_input(&ifcx, url.c_str(), NULL, &options) != 0) {
|
||||||
printf("%s: Cannot open input [%s]!\n", __FUNCTION__, url.c_str());
|
printf("%s: Cannot open input [%s]!\n", __FUNCTION__, url.c_str());
|
||||||
if (!headers.empty())
|
if (!headers.empty())
|
||||||
@@ -2297,6 +2303,7 @@ bool CStreamRec::Open(CZapitChannel * channel)
|
|||||||
ofcx->url = av_strdup(!tsfile.empty() ? tsfile.c_str() : "");
|
ofcx->url = av_strdup(!tsfile.empty() ? tsfile.c_str() : "");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
videoindex_v = -1, videoindex_out = -1;
|
||||||
stream_index = -1;
|
stream_index = -1;
|
||||||
int stid = 0x200;
|
int stid = 0x200;
|
||||||
for (unsigned i = 0; i < ifcx->nb_streams; i++) {
|
for (unsigned i = 0; i < ifcx->nb_streams; i++) {
|
||||||
@@ -2312,11 +2319,38 @@ bool CStreamRec::Open(CZapitChannel * channel)
|
|||||||
av_dict_copy(&ost->metadata, ifcx->streams[i]->metadata, 0);
|
av_dict_copy(&ost->metadata, ifcx->streams[i]->metadata, 0);
|
||||||
ost->time_base = ifcx->streams[i]->time_base;
|
ost->time_base = ifcx->streams[i]->time_base;
|
||||||
ost->id = stid++;
|
ost->id = stid++;
|
||||||
|
videoindex_out = ost->index;
|
||||||
if (iccx->codec_type == AVMEDIA_TYPE_VIDEO) {
|
if (iccx->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||||
stream_index = i;
|
stream_index = i;
|
||||||
|
videoindex_v = i;
|
||||||
} else if (stream_index < 0)
|
} else if (stream_index < 0)
|
||||||
stream_index = i;
|
stream_index = i;
|
||||||
}
|
}
|
||||||
|
if(have2url) {
|
||||||
|
audioindex_a = -1, audioindex_out = -1;
|
||||||
|
for (unsigned i = 0; i < ifcx2->nb_streams; i++) {
|
||||||
|
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(57, 25, 101)
|
||||||
|
AVCodecContext * iccx = ifcx2->streams[i]->codec;
|
||||||
|
AVStream *ost = avformat_new_stream(ofcx, iccx->codec);
|
||||||
|
avcodec_copy_context(ost->codec, iccx);
|
||||||
|
#else
|
||||||
|
AVCodecParameters * iccx = ifcx2->streams[i]->codecpar;
|
||||||
|
AVStream *ost = avformat_new_stream(ofcx, NULL);
|
||||||
|
avcodec_parameters_copy(ost->codecpar, iccx);
|
||||||
|
#endif
|
||||||
|
av_dict_copy(&ost->metadata, ifcx2->streams[i]->metadata, 0);
|
||||||
|
ost->time_base = ifcx2->streams[i]->time_base;
|
||||||
|
ost->id = stid++;
|
||||||
|
audioindex_out = ost->index;
|
||||||
|
|
||||||
|
if (iccx->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||||
|
audioindex_a = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(audioindex_a == -1)
|
||||||
|
have2url = false;
|
||||||
|
}
|
||||||
av_log_set_level(AV_LOG_VERBOSE);
|
av_log_set_level(AV_LOG_VERBOSE);
|
||||||
#if (LIBAVFORMAT_VERSION_MAJOR < 58)
|
#if (LIBAVFORMAT_VERSION_MAJOR < 58)
|
||||||
av_dump_format(ofcx, 0, ofcx->filename, 1);
|
av_dump_format(ofcx, 0, ofcx->filename, 1);
|
||||||
@@ -2352,8 +2386,11 @@ static void get_packet_defaults(AVPacket *pkt)
|
|||||||
|
|
||||||
void CStreamRec::run()
|
void CStreamRec::run()
|
||||||
{
|
{
|
||||||
|
if(have2url) {
|
||||||
|
run2url();
|
||||||
|
return;
|
||||||
|
}
|
||||||
AVPacket pkt;
|
AVPacket pkt;
|
||||||
|
|
||||||
time_t now = 0;
|
time_t now = 0;
|
||||||
time_t tstart = time_monotonic();
|
time_t tstart = time_monotonic();
|
||||||
time_started = tstart;
|
time_started = tstart;
|
||||||
@@ -2428,6 +2465,132 @@ void CStreamRec::run()
|
|||||||
printf("%s: Stopped.\n", __FUNCTION__);
|
printf("%s: Stopped.\n", __FUNCTION__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CStreamRec::run2url()
|
||||||
|
{
|
||||||
|
printf("%s: Start.\n", __FUNCTION__);
|
||||||
|
AVPacket pkt;
|
||||||
|
int64_t cur_pts_v = 0, cur_pts_a = 0;
|
||||||
|
int frame_index = 0;
|
||||||
|
time_t now = 0;
|
||||||
|
time_t tstart = time_monotonic();
|
||||||
|
time_started = tstart;
|
||||||
|
start_time = time(0);
|
||||||
|
if (avformat_write_header(ofcx, NULL) < 0) {
|
||||||
|
printf("%s: avformat_write_header failed\n", __FUNCTION__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double total = 0;
|
||||||
|
while (1) {
|
||||||
|
AVFormatContext *ifmt_ctx;
|
||||||
|
int _stream_index = 0;
|
||||||
|
AVStream *in_stream, *out_stream;
|
||||||
|
|
||||||
|
|
||||||
|
// Get an AVPacket
|
||||||
|
if (av_compare_ts(cur_pts_v, ifcx->streams[videoindex_v]->time_base, cur_pts_a, ifcx2->streams[audioindex_a]->time_base) <= 0) {
|
||||||
|
ifmt_ctx = ifcx;
|
||||||
|
_stream_index = videoindex_out;
|
||||||
|
if (av_read_frame(ifmt_ctx, &pkt) >= 0) {
|
||||||
|
do {
|
||||||
|
if (pkt.stream_index == videoindex_v) {
|
||||||
|
cur_pts_v = pkt.pts;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (av_read_frame(ifmt_ctx, &pkt) >= 0);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ifmt_ctx = ifcx2;
|
||||||
|
_stream_index = audioindex_out;
|
||||||
|
if (av_read_frame(ifmt_ctx, &pkt) >= 0) {
|
||||||
|
do {
|
||||||
|
if (pkt.stream_index == audioindex_a) {
|
||||||
|
cur_pts_a = pkt.pts;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (av_read_frame(ifmt_ctx, &pkt) >= 0);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
av_packet_make_refcounted(&pkt);
|
||||||
|
|
||||||
|
in_stream = ifmt_ctx->streams[pkt.stream_index];
|
||||||
|
out_stream = ofcx->streams[_stream_index];
|
||||||
|
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(57, 25, 101)
|
||||||
|
AVCodecContext *codec = ifcx->streams[pkt.stream_index]->codec;
|
||||||
|
#else
|
||||||
|
AVCodecParameters *codec = ifcx->streams[pkt.stream_index]->codecpar;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (bsfc && codec->codec_id == AV_CODEC_ID_H264) {
|
||||||
|
AVPacket newpkt = pkt;
|
||||||
|
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 100)
|
||||||
|
if (av_bitstream_filter_filter(bsfc, codec, NULL, &newpkt.data, &newpkt.size, pkt.data, pkt.size, pkt.flags & AV_PKT_FLAG_KEY) >= 0) {
|
||||||
|
av_packet_unref(&pkt);
|
||||||
|
newpkt.buf = av_buffer_create(newpkt.data, newpkt.size, av_buffer_default_free, NULL, 0);
|
||||||
|
pkt = newpkt;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int ret = av_bsf_send_packet(bsfc, &pkt);
|
||||||
|
if (ret < 0){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret = av_bsf_receive_packet(bsfc, &newpkt);
|
||||||
|
if (ret == AVERROR(EAGAIN)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(ret != AVERROR_EOF){
|
||||||
|
av_packet_unref(&pkt);
|
||||||
|
pkt = newpkt;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (pkt.pts == AV_NOPTS_VALUE) {
|
||||||
|
//Write PTS
|
||||||
|
AVRational time_base1 = in_stream->time_base;
|
||||||
|
//Duration between 2 frames (us)
|
||||||
|
int64_t calc_duration = (double) AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
|
||||||
|
//Parameters
|
||||||
|
pkt.pts = (double) (frame_index * calc_duration) / (double) (av_q2d(time_base1) * AV_TIME_BASE);
|
||||||
|
pkt.dts = pkt.pts;
|
||||||
|
pkt.duration = (double) calc_duration / (double) (av_q2d(time_base1) * AV_TIME_BASE);
|
||||||
|
frame_index++;
|
||||||
|
}
|
||||||
|
/* copy packet */
|
||||||
|
// Convert PTS/DTS
|
||||||
|
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (enum AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
|
||||||
|
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (enum AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
|
||||||
|
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
|
||||||
|
pkt.pos = -1;
|
||||||
|
pkt.stream_index = _stream_index;
|
||||||
|
|
||||||
|
// Write
|
||||||
|
if (av_interleaved_write_frame(ofcx, &pkt) < 0) {
|
||||||
|
printf("Error muxing packet\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
av_packet_unref(&pkt);
|
||||||
|
|
||||||
|
if (now == 0)
|
||||||
|
WriteHeader(1000);
|
||||||
|
now = time_monotonic();
|
||||||
|
if (now - tstart > 1) {
|
||||||
|
tstart = now;
|
||||||
|
WriteHeader(total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
av_read_pause(ifcx);
|
||||||
|
av_read_pause(ifcx2);
|
||||||
|
av_write_trailer(ofcx);
|
||||||
|
WriteHeader(total);
|
||||||
|
printf("%s: Stopped.\n", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct pvr_file_info
|
typedef struct pvr_file_info
|
||||||
{
|
{
|
||||||
uint32_t uDuration; /* Time duration in Ms */
|
uint32_t uDuration; /* Time duration in Ms */
|
||||||
@@ -2451,3 +2614,17 @@ void CStreamRec::WriteHeader(uint32_t duration)
|
|||||||
} else
|
} else
|
||||||
perror(tsfile.c_str());
|
perror(tsfile.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0 // not used, saved in case we needed it
|
||||||
|
extern bool autoshift_delete;
|
||||||
|
bool CRecordManager::LinkTimeshift()
|
||||||
|
{
|
||||||
|
if(autoshift) {
|
||||||
|
char buf[512];
|
||||||
|
autoshift = false;
|
||||||
|
sprintf(buf, "ln %s/* %s", timeshiftDir, g_settings.network_nfs_recordingdir);
|
||||||
|
system(buf);
|
||||||
|
autoshift_delete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Reference in New Issue
Block a user