stream-deploy/ZLM/3rdpart/media-server/librtsp/test/media/mp4-file-source.cpp

429 lines
10 KiB
C++
Raw Normal View History

#include "mp4-file-source.h"
#include "mov-format.h"
#include "rtp.h"
#include "rtp-profile.h"
#include "rtp-payload.h"
#include "sys/system.h"
#include "sys/path.h"
#include <assert.h>
extern "C" const struct mov_buffer_t* mov_file_buffer(void);
MP4FileSource::MP4FileSource(const char *file)
{
m_speed = 1.0;
m_status = 0;
m_clock = 0;
m_count = 0;
m_dts = -1;
m_fp = fopen(file, "rb");
m_reader = mov_reader_create(mov_file_buffer(), m_fp);
if (m_reader)
{
struct mov_reader_trackinfo_t info = { MP4OnVideo, MP4OnAudio };
mov_reader_getinfo(m_reader, &info, this);
}
for (int i = 0; i < m_count; i++)
{
struct media_t* m = &m_media[i];
rtp_set_info(m->rtp.rtp, "RTSPServer", path_basename(file));
}
}
MP4FileSource::~MP4FileSource()
{
for (int i = 0; i < m_count; i++)
{
struct media_t* m = &m_media[i];
rtp_sender_destroy(&m->rtp);
if (m->pkts)
{
avpacket_queue_destroy(m->pkts);
m->pkts = NULL;
}
}
if (m_reader)
{
mov_reader_destroy(m_reader);
m_reader = NULL;
}
if (m_fp)
fclose(m_fp);
}
int MP4FileSource::SetTransport(const char* track, std::shared_ptr<IRTPTransport> transport)
{
int t = atoi(track + 5/*track*/);
for (int i = 0; i < m_count; i++)
{
struct media_t* m = &m_media[i];
if(t != m->track)
continue;
m->transport = transport;
return 0;
}
return -1;
}
struct MP4FileSource::media_t* MP4FileSource::FetchNextPacket()
{
struct media_t* best = NULL;
int64_t timestamp = 0;
for (int i = 0; i < m_count; i++)
{
struct media_t* m = &m_media[i];
while (0 == avpacket_queue_count(m->pkts))
{
int r = mov_reader_read(m_reader, m_packet, sizeof(m_packet), MP4OnRead, this);
if (r == 0)
{
// 0-EOF
break;
}
else if (r < 0)
{
// error
return NULL;
}
}
if(0 == avpacket_queue_count(m->pkts))
continue;
std::shared_ptr<struct avpacket_t> cur(avpacket_queue_front(m->pkts), avpacket_release);
if (NULL == best || cur->dts < timestamp || cur->pts < timestamp)
{
best = m;
timestamp = cur->dts < cur->pts ? cur->dts : cur->pts;
}
}
//m_status = 3;
//SendBye();
return best;
}
int MP4FileSource::Play()
{
bool sendframe = false;
if (3 == m_status)
return 0;
struct media_t* m = FetchNextPacket();
if (NULL == m)
{
// 0-EOF
m_status = 3;
SendBye();
return 0;
}
m_status = 1;
int bytes = 0;
uint64_t clock = system_time();
std::shared_ptr<struct avpacket_t> pkt(avpacket_queue_front(m->pkts), avpacket_release);
int64_t dts = pkt->dts < pkt->pts ? pkt->dts : pkt->pts;
SendRTCP(clock);
if (0 == m_clock || m_clock > clock)
m_clock = clock;
if (-1 == m_dts)
m_dts = dts;
if (int64_t(clock - m_clock) + m_dts >= dts)
{
if (0 == strcmp("H264", m->rtp.encoding))
{
// AVC1 -> H.264 byte stream
bytes = h264_mp4toannexb(&m_avc, pkt->data, pkt->size, m_packet, sizeof(m_packet));
//printf("[V] pts: %lld, dts: %lld, clock: %llu\n", m_frame.pts, m_frame.dts, clock);
}
else if (0 == strcmp("H265", m->rtp.encoding))
{
// HVC1 -> H.264 byte stream
bytes = h265_mp4toannexb(&m_hevc, pkt->data, pkt->size, m_packet, sizeof(m_packet));
//printf("[V] pts: %lld, dts: %lld, clock: %llu\n", m_frame.pts, m_frame.dts, clock);
}
else if (0 == strcmp("MP4A-LATM", m->rtp.encoding) || 0 == strcmp("MPEG4-GENERIC", m->rtp.encoding))
{
// add ADTS header
memcpy(m_packet, pkt->data, pkt->size);
bytes = pkt->size;
//printf("[A] pts: %lld, dts: %lld, clock: %llu\n", m_frame.pts, m_frame.dts, clock);
}
else
{
assert(0);
}
if (-1 == m->dts_first)
m->dts_first = pkt->pts;
m->dts_last = pkt->pts;
uint32_t timestamp = m->rtp.timestamp + (uint32_t)((m->dts_last - m->dts_first) * (m->rtp.frequency / 1000) /*kHz*/);
//printf("[%s] pts: %lld, dts: %lld, timestamp: %u(%u)\n", m->rtp.encoding, pkt->pts, pkt->dts, (unsigned int)timestamp, (unsigned int)m->timestamp);
rtp_payload_encode_input(m->rtp.encoder, m_packet, bytes, timestamp);
avpacket_queue_pop(m->pkts);
sendframe = 1;
// preload next packet
FetchNextPacket();
}
return sendframe ? 1 : 0;
}
int MP4FileSource::Pause()
{
m_status = 2;
m_clock = 0;
m_dts = -1;
return 0;
}
int MP4FileSource::Seek(int64_t pos)
{
// update timestamp
for (int i = 0; i < m_count; i++)
{
//m_media[i].dts = -1;
//m_media[i].timestamp += 1;
if (-1 != m_media[i].dts_first)
m_media[i].rtp.timestamp += (uint32_t)(m_media[i].dts_last - m_media[i].dts_first + 1);
m_media[i].dts_first = -1;
avpacket_queue_clear(m_media[i].pkts); // clear buffered frame
}
m_dts = pos;
m_clock = 0;
return mov_reader_seek(m_reader, &m_dts);
}
int MP4FileSource::SetSpeed(double speed)
{
m_speed = speed;
return 0;
}
int MP4FileSource::GetDuration(int64_t& duration) const
{
if (m_reader)
{
duration = (int64_t)mov_reader_getduration(m_reader);
return 0;
}
return -1;
}
int MP4FileSource::GetSDPMedia(std::string& sdp) const
{
sdp = m_sdp;
return m_reader ? 0 : -1;
}
int MP4FileSource::GetRTPInfo(const char* uri, char *rtpinfo, size_t bytes) const
{
int n = 0;
uint16_t seq;
uint32_t timestamp;
// RTP-Info: url=rtsp://foo.com/bar.avi/streamid=0;seq=45102,
// url=rtsp://foo.com/bar.avi/streamid=1;seq=30211
for (int i = 0; i < m_count; i++)
{
const struct media_t* m = &m_media[i];
rtp_payload_encode_getinfo(m->rtp.encoder, &seq, &timestamp);
if (i > 0)
rtpinfo[n++] = ',';
n += snprintf(rtpinfo + n, bytes - n, "url=%s/track%d;seq=%hu;rtptime=%u", uri, m->track, seq, (unsigned int)m->rtp.timestamp);
}
return 0;
}
void MP4FileSource::MP4OnVideo(void* param, uint32_t track, uint8_t object, int /*width*/, int /*height*/, const void* extra, size_t bytes)
{
assert(track > 0);
int n = 0;
MP4FileSource* self = (MP4FileSource*)param;
struct media_t* m = &self->m_media[self->m_count++];
memset(&m->rtp, 0, sizeof(m->rtp));
m->pkts = avpacket_queue_create(100);
m->track = track;
m->rtcp_clock = 0;
m->dts_first = -1;
m->dts_last = -1;
if (MOV_OBJECT_H264 == object)
{
mpeg4_avc_decoder_configuration_record_load((const uint8_t*)extra, bytes, &self->m_avc);
n = rtp_sender_init_video(&m->rtp, "RTP/AVP", 0, RTP_PAYLOAD_H264, "H264", 90000, extra, bytes);
}
else if (MOV_OBJECT_HEVC == object)
{
mpeg4_hevc_decoder_configuration_record_load((const uint8_t*)extra, bytes, &self->m_hevc);
n = rtp_sender_init_video(&m->rtp, "RTP/AVP", 0, RTP_PAYLOAD_H265, "H265", 90000, extra, bytes);
}
else if (MOV_OBJECT_MP4V == object)
{
n = rtp_sender_init_video(&m->rtp, "RTP/AVP", 0, RTP_PAYLOAD_MP4V, "MP4V-ES", 90000, extra, bytes);
}
else
{
assert(0);
return;
}
m->rtp.onpacket = OnRTPPacket;
m->rtp.param = m;
n = snprintf((char*)self->m_packet, sizeof(self->m_packet), "%.*sa=control:track%d\n", n, m->rtp.buffer, m->track);
self->m_sdp += (const char*)self->m_packet;
}
void MP4FileSource::MP4OnAudio(void* param, uint32_t track, uint8_t object, int channel_count, int /*bit_per_sample*/, int sample_rate, const void* extra, size_t bytes)
{
assert(track > 0);
int n = 0;
MP4FileSource* self = (MP4FileSource*)param;
struct media_t* m = &self->m_media[self->m_count++];
memset(&m->rtp, 0, sizeof(m->rtp));
m->pkts = avpacket_queue_create(100);
m->track = track;
m->rtcp_clock = 0;
m->dts_first = -1;
m->dts_last = -1;
if (MOV_OBJECT_AAC == object || MOV_OBJECT_AAC_LOW == object)
{
mpeg4_aac_audio_specific_config_load((const uint8_t*)extra, bytes, &self->m_aac);
if (1)
{
// RFC 6416
n = rtp_sender_init_audio(&m->rtp, "RTP/AVP", 0, RTP_PAYLOAD_LATM, "MP4A-LATM", sample_rate, self->m_aac.channel_configuration, extra, bytes);
}
else
{
// RFC 3640 3.3.1. General (p21)
n = rtp_sender_init_audio(&m->rtp, "RTP/AVP", 0, RTP_PAYLOAD_MP4A, "MP4A-GENERIC", sample_rate, channel_count, extra, bytes);
}
}
else if (MOV_OBJECT_OPUS == object)
{
// RFC7587 RTP Payload Format for the Opus Speech and Audio Codec
n = rtp_sender_init_audio(&m->rtp, "RTP/AVP", 0, RTP_PAYLOAD_OPUS, "opus", sample_rate, channel_count, extra, bytes);
}
else if (MOV_OBJECT_G711u == object)
{
n = rtp_sender_init_audio(&m->rtp, "RTP/AVP", 0, RTP_PAYLOAD_PCMU, "PCMU", 8000, 1, extra, bytes);
}
else if (MOV_OBJECT_G711a == object)
{
n = rtp_sender_init_audio(&m->rtp, "RTP/AVP", 0, RTP_PAYLOAD_PCMA, "PCMA", 8000, 1, extra, bytes);
}
else
{
assert(0);
return;
}
m->rtp.param = m;
m->rtp.onpacket = OnRTPPacket;
n = snprintf((char*)self->m_packet, sizeof(self->m_packet), "%.*sa=control:track%d\n", n, m->rtp.buffer, m->track);
self->m_sdp += (const char*)self->m_packet;
}
void MP4FileSource::MP4OnRead(void* param, uint32_t track, const void* buffer, size_t bytes, int64_t pts, int64_t dts, int /*flags*/)
{
MP4FileSource *self = (MP4FileSource *)param;
std::shared_ptr<struct avpacket_t> pkt(avpacket_alloc((int)bytes), avpacket_release);
memcpy(pkt->data, buffer, bytes);
//pkt->codecid = track;
pkt->pts = pts;
pkt->dts = dts;
for (int i = 0; i < self->m_count; i++)
{
struct media_t* m = &self->m_media[i];
if (m->track == (int)track)
{
// TODO: overflow
assert(0 == avpacket_queue_push(m->pkts, pkt.get()));
//assert(0 == avpacket_queue_push_wait(m->pkts, &pkt, 1000000));
break;
}
}
}
void MP4FileSource::OnRTCPEvent(const struct rtcp_msg_t* msg)
{
msg;
}
void MP4FileSource::OnRTCPEvent(void* param, const struct rtcp_msg_t* msg)
{
MP4FileSource *self = (MP4FileSource *)param;
self->OnRTCPEvent(msg);
}
int MP4FileSource::SendBye()
{
char rtcp[1024] = { 0 };
for (int i = 0; i < m_count; i++)
{
struct media_t* m = &m_media[i];
size_t n = rtp_rtcp_bye(m->rtp.rtp, rtcp, sizeof(rtcp));
// send RTCP packet
m->transport->Send(true, rtcp, n);
}
return 0;
}
int MP4FileSource::SendRTCP(uint64_t clock)
{
char rtcp[1024] = { 0 };
// make sure have sent RTP packet
for (int i = 0; i < m_count; i++)
{
struct media_t* m = &m_media[i];
int interval = rtp_rtcp_interval(m->rtp.rtp);
if (0 == m->rtcp_clock || m->rtcp_clock + interval < clock)
{
size_t n = rtp_rtcp_report(m->rtp.rtp, rtcp, sizeof(rtcp));
// send RTCP packet
m->transport->Send(true, rtcp, n);
m->rtcp_clock = clock;
}
}
return 0;
}
int MP4FileSource::OnRTPPacket(void* param, const void *packet, int bytes, uint32_t /*timestamp*/, int /*flags*/)
{
struct media_t* m = (struct media_t*)param;
int r = m->transport->Send(false, packet, bytes);
if (r != bytes)
{
assert(0);
return -1;
}
return rtp_onsend(m->rtp.rtp, packet, bytes/*, time*/);
}