2017-05-17 00:04:56 +08:00
|
|
|
//ZoneMinder Packet Queue Implementation Class
|
|
|
|
//Copyright 2016 Steve Gilvarry
|
|
|
|
//
|
|
|
|
//This file is part of ZoneMinder.
|
|
|
|
//
|
|
|
|
//ZoneMinder is free software: you can redistribute it and/or modify
|
|
|
|
//it under the terms of the GNU General Public License as published by
|
|
|
|
//the Free Software Foundation, either version 3 of the License, or
|
|
|
|
//(at your option) any later version.
|
|
|
|
//
|
|
|
|
//ZoneMinder is distributed in the hope that it will be useful,
|
|
|
|
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
//GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
//You should have received a copy of the GNU General Public License
|
|
|
|
//along with ZoneMinder. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
|
|
#include "zm_packetqueue.h"
|
|
|
|
#include "zm_ffmpeg.h"
|
2017-08-24 03:05:44 +08:00
|
|
|
#include <sys/time.h>
|
2017-05-17 00:04:56 +08:00
|
|
|
|
2018-10-21 05:31:14 +08:00
|
|
|
zm_packetqueue::zm_packetqueue( int video_image_count, int p_video_stream_id, int p_audio_stream_id ) {
|
2017-12-01 20:26:34 +08:00
|
|
|
video_stream_id = p_video_stream_id;
|
2017-12-06 05:16:52 +08:00
|
|
|
max_video_packet_count = video_image_count-1;
|
2017-11-22 00:58:15 +08:00
|
|
|
video_packet_count = 0;
|
2017-12-01 03:37:36 +08:00
|
|
|
analysis_it = pktQueue.begin();
|
2017-12-08 23:39:24 +08:00
|
|
|
first_video_packet_index = -1;
|
2018-10-21 05:31:14 +08:00
|
|
|
|
|
|
|
max_stream_id = p_video_stream_id > p_audio_stream_id ? p_video_stream_id : p_audio_stream_id;
|
2018-10-15 22:51:56 +08:00
|
|
|
packet_counts = new int[max_stream_id+1];
|
|
|
|
for ( int i=0; i <= max_stream_id; ++i )
|
2018-10-15 23:13:10 +08:00
|
|
|
packet_counts[i] = 0;
|
2017-05-17 00:04:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
zm_packetqueue::~zm_packetqueue() {
|
2017-08-01 03:49:27 +08:00
|
|
|
clearQueue();
|
2018-10-15 22:51:56 +08:00
|
|
|
delete[] packet_counts;
|
|
|
|
packet_counts = NULL;
|
2017-05-17 00:04:56 +08:00
|
|
|
}
|
|
|
|
|
2018-08-18 04:06:03 +08:00
|
|
|
/* Enqueues the given packet. Will maintain the analysis_it pointer and image packet counts.
|
|
|
|
* If we have reached our max image packet count, it will pop off as many packets as are needed.
|
|
|
|
* Thus it will ensure that the same packet never gets queued twice.
|
|
|
|
*/
|
|
|
|
|
2017-05-17 00:04:56 +08:00
|
|
|
bool zm_packetqueue::queuePacket( ZMPacket* zm_packet ) {
|
2017-12-08 23:39:24 +08:00
|
|
|
|
|
|
|
if ( zm_packet->image_index != -1 ) {
|
|
|
|
// If we can never queue the same packet, then they can never go past
|
|
|
|
if ( zm_packet->image_index == first_video_packet_index ) {
|
2018-08-18 04:06:03 +08:00
|
|
|
Debug(2, "queuing packet that is already on the queue(%d)", zm_packet->image_index);
|
2018-03-09 22:03:19 +08:00
|
|
|
ZMPacket *p = NULL;;
|
2017-12-09 02:46:02 +08:00
|
|
|
while ( pktQueue.size() && (p = pktQueue.front()) && ( p->image_index != zm_packet->image_index ) ) {
|
|
|
|
if ( ( analysis_it != pktQueue.end() ) && ( *analysis_it == p ) ) {
|
2018-08-18 04:06:03 +08:00
|
|
|
Debug(2, "Increasing analysis_it, meaning analysis is not keeping up");
|
2017-12-09 02:46:02 +08:00
|
|
|
++analysis_it;
|
2017-12-08 23:39:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pktQueue.pop_front();
|
|
|
|
if ( p->codec_type == AVMEDIA_TYPE_VIDEO ) {
|
2018-08-18 04:06:03 +08:00
|
|
|
Debug(2, "Decreasing video_packet_count (%d), popped (%d)",
|
|
|
|
video_packet_count, p->image_index);
|
2017-12-08 23:39:24 +08:00
|
|
|
video_packet_count -= 1;
|
2018-08-18 04:06:03 +08:00
|
|
|
first_video_packet_index += 1;
|
|
|
|
first_video_packet_index %= max_video_packet_count;
|
|
|
|
|
2017-12-08 23:39:24 +08:00
|
|
|
} else {
|
2017-12-09 02:46:02 +08:00
|
|
|
Debug(2, "Deleteing audio frame(%d)", p->image_index);
|
2017-12-08 23:39:24 +08:00
|
|
|
delete p;
|
2017-12-09 02:46:02 +08:00
|
|
|
p = NULL;
|
2017-12-08 23:39:24 +08:00
|
|
|
}
|
2017-12-09 02:46:02 +08:00
|
|
|
Debug(2,"pktQueue.size(%d)", pktQueue.size() );
|
2017-12-08 23:39:24 +08:00
|
|
|
} // end while there are packets at the head of the queue that are not this one
|
|
|
|
|
2017-12-09 02:46:02 +08:00
|
|
|
if ( p && ( p->image_index == zm_packet->image_index ) ) {
|
2017-12-08 23:39:24 +08:00
|
|
|
// it should
|
|
|
|
video_packet_count -= 1;
|
|
|
|
pktQueue.pop_front();
|
|
|
|
first_video_packet_index += 1;
|
|
|
|
first_video_packet_index %= max_video_packet_count;
|
|
|
|
|
|
|
|
} else {
|
2018-08-18 04:06:03 +08:00
|
|
|
Error("SHould have found the packet! front packet index was %d, new packet index is %d ",
|
|
|
|
p->image_index, zm_packet->image_index
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if ( analysis_it == pktQueue.end() ) {
|
|
|
|
// Analsys_it should only point to end when queue is empty
|
|
|
|
Debug(2,"pointing analysis_it to begining");
|
|
|
|
analysis_it = pktQueue.begin();
|
2017-12-08 23:39:24 +08:00
|
|
|
}
|
|
|
|
} else if ( first_video_packet_index == -1 ) {
|
|
|
|
// Initialize the first_video_packet indicator
|
|
|
|
first_video_packet_index = zm_packet->image_index;
|
|
|
|
} // end if
|
|
|
|
} // end if queuing a video packet
|
|
|
|
|
2018-08-18 04:06:03 +08:00
|
|
|
pktQueue.push_back(zm_packet);
|
2018-10-15 22:51:56 +08:00
|
|
|
packet_counts[zm_packet->packet.stream_index] += 1;
|
2018-08-18 04:06:03 +08:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
// This code should not be neccessary. Taken care of by the above code that ensure that no packet appears twice
|
2017-12-01 20:26:34 +08:00
|
|
|
if ( zm_packet->codec_type == AVMEDIA_TYPE_VIDEO ) {
|
2017-11-22 00:58:15 +08:00
|
|
|
video_packet_count += 1;
|
2017-12-06 05:16:52 +08:00
|
|
|
if ( video_packet_count >= max_video_packet_count )
|
2018-08-18 04:06:03 +08:00
|
|
|
clearQueue(max_video_packet_count, video_stream_id);
|
2017-12-01 20:26:34 +08:00
|
|
|
}
|
2018-08-18 04:06:03 +08:00
|
|
|
#endif
|
2017-05-17 00:04:56 +08:00
|
|
|
|
|
|
|
|
|
|
|
return true;
|
2017-12-08 23:39:24 +08:00
|
|
|
} // end bool zm_packetqueue::queuePacket(ZMPacket* zm_packet)
|
2017-05-17 00:04:56 +08:00
|
|
|
|
|
|
|
ZMPacket* zm_packetqueue::popPacket( ) {
|
|
|
|
if ( pktQueue.empty() ) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ZMPacket *packet = pktQueue.front();
|
2017-12-01 03:37:36 +08:00
|
|
|
if ( *analysis_it == packet )
|
2018-08-13 22:01:16 +08:00
|
|
|
++analysis_it;
|
2017-12-01 03:37:36 +08:00
|
|
|
|
2017-05-17 00:04:56 +08:00
|
|
|
pktQueue.pop_front();
|
2017-12-08 23:39:24 +08:00
|
|
|
if ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) {
|
2017-11-22 00:58:15 +08:00
|
|
|
video_packet_count -= 1;
|
2017-12-08 23:39:24 +08:00
|
|
|
if ( video_packet_count ) {
|
2017-12-09 02:46:02 +08:00
|
|
|
// There is another video packet, so it must be the next one
|
2018-08-18 04:06:03 +08:00
|
|
|
Debug(2,"Incrementing first video packet index was (%d)", first_video_packet_index);
|
2017-12-08 23:39:24 +08:00
|
|
|
first_video_packet_index += 1;
|
|
|
|
first_video_packet_index %= max_video_packet_count;
|
|
|
|
} else {
|
|
|
|
first_video_packet_index = -1;
|
|
|
|
}
|
|
|
|
}
|
2018-10-15 22:51:56 +08:00
|
|
|
packet_counts[packet->packet.stream_index] -= 1;
|
2017-05-17 00:04:56 +08:00
|
|
|
|
|
|
|
return packet;
|
|
|
|
}
|
|
|
|
|
2018-10-15 22:51:56 +08:00
|
|
|
unsigned int zm_packetqueue::clearQueue(unsigned int frames_to_keep, int stream_id) {
|
2017-05-17 00:04:56 +08:00
|
|
|
|
2018-08-18 04:06:03 +08:00
|
|
|
Debug(3, "Clearing all but %d frames, queue has %d", frames_to_keep, pktQueue.size());
|
2017-05-17 00:04:56 +08:00
|
|
|
|
|
|
|
if ( pktQueue.empty() ) {
|
|
|
|
return 0;
|
|
|
|
}
|
2018-02-16 03:08:21 +08:00
|
|
|
frames_to_keep += 1;
|
|
|
|
if ( pktQueue.size() <= frames_to_keep ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int packets_to_delete = pktQueue.size();
|
2017-05-17 00:04:56 +08:00
|
|
|
|
2017-08-24 03:05:44 +08:00
|
|
|
std::list<ZMPacket *>::reverse_iterator it;
|
2017-05-17 00:04:56 +08:00
|
|
|
ZMPacket *packet = NULL;
|
|
|
|
|
2018-08-18 04:06:03 +08:00
|
|
|
for ( it = pktQueue.rbegin(); frames_to_keep && (it != pktQueue.rend()); ++it ) {
|
2017-05-17 00:04:56 +08:00
|
|
|
ZMPacket *zm_packet = *it;
|
|
|
|
AVPacket *av_packet = &(zm_packet->packet);
|
|
|
|
|
2018-08-18 04:06:03 +08:00
|
|
|
Debug(4, "Looking at packet with stream index (%d) with keyframe(%d), Image_index(%d) frames_to_keep is (%d)",
|
|
|
|
av_packet->stream_index, zm_packet->keyframe, zm_packet->image_index, frames_to_keep );
|
2017-05-17 00:04:56 +08:00
|
|
|
|
|
|
|
// Want frames_to_keep video keyframes. Otherwise, we may not have enough
|
2017-11-22 00:58:15 +08:00
|
|
|
if ( av_packet->stream_index == stream_id ) {
|
2017-05-17 00:04:56 +08:00
|
|
|
frames_to_keep --;
|
2018-02-16 03:08:21 +08:00
|
|
|
packets_to_delete --;
|
2017-05-17 00:04:56 +08:00
|
|
|
}
|
|
|
|
}
|
2018-01-08 02:39:13 +08:00
|
|
|
|
2018-08-18 04:06:03 +08:00
|
|
|
// Make sure we start on a keyframe
|
2018-01-08 02:39:13 +08:00
|
|
|
for ( ; it != pktQueue.rend(); ++it ) {
|
|
|
|
ZMPacket *zm_packet = *it;
|
|
|
|
AVPacket *av_packet = &(zm_packet->packet);
|
|
|
|
|
2018-08-18 04:06:03 +08:00
|
|
|
Debug(5, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), image_index(%d) frames_to_keep is (%d)",
|
|
|
|
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), zm_packet->image_index, frames_to_keep );
|
2018-01-08 02:39:13 +08:00
|
|
|
|
|
|
|
// Want frames_to_keep video keyframes. Otherwise, we may not have enough
|
|
|
|
if ( ( av_packet->stream_index == stream_id) && ( av_packet->flags & AV_PKT_FLAG_KEY ) ) {
|
2018-08-18 04:06:03 +08:00
|
|
|
Debug(4, "Found keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)",
|
|
|
|
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
2018-01-08 02:39:13 +08:00
|
|
|
break;
|
2017-11-22 00:58:15 +08:00
|
|
|
}
|
2018-02-16 03:08:21 +08:00
|
|
|
packets_to_delete--;
|
2017-06-01 08:44:43 +08:00
|
|
|
}
|
|
|
|
if ( frames_to_keep ) {
|
2018-08-18 04:06:03 +08:00
|
|
|
Debug(3, "Hit end of queue, still need (%d) video frames", frames_to_keep);
|
2018-01-17 23:01:48 +08:00
|
|
|
}
|
|
|
|
if ( it != pktQueue.rend() ) {
|
|
|
|
// We want to keep this packet, so advance to the next
|
2018-08-18 04:06:03 +08:00
|
|
|
++it;
|
2018-02-16 03:08:21 +08:00
|
|
|
packets_to_delete--;
|
2017-06-01 08:44:43 +08:00
|
|
|
}
|
2018-02-16 03:08:21 +08:00
|
|
|
int delete_count = 0;
|
2017-05-17 00:04:56 +08:00
|
|
|
|
2018-02-16 03:08:21 +08:00
|
|
|
if ( packets_to_delete > 0 ) {
|
2018-08-18 04:06:03 +08:00
|
|
|
Debug(4, "Deleting packets from the front, count is (%d)", packets_to_delete);
|
2018-02-16 03:08:21 +08:00
|
|
|
while ( --packets_to_delete ) {
|
2018-08-18 04:06:03 +08:00
|
|
|
Debug(4, "Deleting a packet from the front, count is (%d), queue size is %d",
|
|
|
|
delete_count, pktQueue.size());
|
2018-02-16 03:08:21 +08:00
|
|
|
|
|
|
|
packet = pktQueue.front();
|
|
|
|
if ( *analysis_it == packet )
|
2018-08-18 04:06:03 +08:00
|
|
|
++analysis_it;
|
2018-02-16 03:08:21 +08:00
|
|
|
if ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) {
|
|
|
|
video_packet_count -= 1;
|
|
|
|
if ( video_packet_count ) {
|
|
|
|
// There is another video packet, so it must be the next one
|
|
|
|
first_video_packet_index += 1;
|
|
|
|
first_video_packet_index %= max_video_packet_count;
|
|
|
|
} else {
|
|
|
|
first_video_packet_index = -1;
|
|
|
|
}
|
2017-12-09 02:46:02 +08:00
|
|
|
}
|
2018-02-16 03:08:21 +08:00
|
|
|
pktQueue.pop_front();
|
|
|
|
if ( packet->image_index == -1 )
|
|
|
|
delete packet;
|
2017-05-17 00:04:56 +08:00
|
|
|
|
2018-02-16 03:08:21 +08:00
|
|
|
delete_count += 1;
|
|
|
|
} // while our iterator is not the first packet
|
|
|
|
} // end if have packet_delete_count
|
2018-10-15 22:51:56 +08:00
|
|
|
packet = NULL; // tidy up for valgrind
|
|
|
|
Debug(3, "Deleted %d packets, %d remaining", delete_count, pktQueue.size());
|
2017-12-09 02:46:02 +08:00
|
|
|
|
|
|
|
#if 0
|
2017-12-08 23:39:24 +08:00
|
|
|
if ( pktQueue.size() ) {
|
|
|
|
packet = pktQueue.front();
|
|
|
|
first_video_packet_index = packet->image_index;
|
|
|
|
} else {
|
|
|
|
first_video_packet_index = -1;
|
|
|
|
}
|
2017-12-09 02:46:02 +08:00
|
|
|
#endif
|
2017-12-08 23:39:24 +08:00
|
|
|
|
2018-08-18 04:06:03 +08:00
|
|
|
Debug(3, "Deleted packets, resulting size is %d", pktQueue.size());
|
2017-05-17 00:04:56 +08:00
|
|
|
return delete_count;
|
|
|
|
} // end unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream_id )
|
|
|
|
|
|
|
|
void zm_packetqueue::clearQueue() {
|
2017-12-08 23:39:24 +08:00
|
|
|
mutex.lock();
|
2017-05-17 00:04:56 +08:00
|
|
|
ZMPacket *packet = NULL;
|
2018-09-14 04:18:28 +08:00
|
|
|
int delete_count = 0;
|
2017-05-17 00:04:56 +08:00
|
|
|
while(!pktQueue.empty()) {
|
|
|
|
packet = pktQueue.front();
|
2018-10-15 22:51:56 +08:00
|
|
|
packet_counts[packet->packet.stream_index] -= 1;
|
2017-05-17 00:04:56 +08:00
|
|
|
pktQueue.pop_front();
|
2017-11-28 00:28:36 +08:00
|
|
|
if ( packet->image_index == -1 )
|
|
|
|
delete packet;
|
2018-09-14 04:18:28 +08:00
|
|
|
delete_count += 1;
|
2017-05-17 00:04:56 +08:00
|
|
|
}
|
2018-09-14 04:18:28 +08:00
|
|
|
Debug(3, "Deleted (%d) packets", delete_count );
|
2017-11-22 00:58:15 +08:00
|
|
|
video_packet_count = 0;
|
2017-12-08 23:39:24 +08:00
|
|
|
first_video_packet_index = -1;
|
2017-12-01 03:37:36 +08:00
|
|
|
analysis_it = pktQueue.begin();
|
2017-12-08 23:39:24 +08:00
|
|
|
mutex.unlock();
|
2017-05-17 00:04:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int zm_packetqueue::size() {
|
|
|
|
return pktQueue.size();
|
|
|
|
}
|
|
|
|
|
2017-11-22 08:55:40 +08:00
|
|
|
unsigned int zm_packetqueue::get_video_packet_count() {
|
2017-11-22 00:58:15 +08:00
|
|
|
return video_packet_count;
|
|
|
|
}
|
2017-05-17 00:04:56 +08:00
|
|
|
|
2018-10-15 22:51:56 +08:00
|
|
|
int zm_packetqueue::packet_count( int stream_id ) {
|
|
|
|
return packet_counts[stream_id];
|
|
|
|
} // end int zm_packetqueue::packet_count( int stream_id )
|
2017-05-17 00:04:56 +08:00
|
|
|
|
2017-12-01 03:37:36 +08:00
|
|
|
// Returns a packet to analyse or NULL
|
|
|
|
ZMPacket *zm_packetqueue::get_analysis_packet() {
|
|
|
|
|
|
|
|
if ( ! pktQueue.size() )
|
|
|
|
return NULL;
|
|
|
|
if ( analysis_it == pktQueue.end() )
|
|
|
|
return NULL;
|
|
|
|
|
2017-12-09 02:46:02 +08:00
|
|
|
//Debug(2, "Distance from head: (%d)", std::distance( pktQueue.begin(), analysis_it ) );
|
|
|
|
//Debug(2, "Distance from end: (%d)", std::distance( analysis_it, pktQueue.end() ) );
|
2017-12-01 03:37:36 +08:00
|
|
|
|
|
|
|
return *analysis_it;
|
|
|
|
} // end ZMPacket *zm_packetqueue::get_analysis_packet()
|
|
|
|
|
|
|
|
// The idea is that analsys_it will only be == end() if the queue is empty
|
|
|
|
// probvlem here is that we don't want to analyse a packet twice. Maybe we can flag the packet analysed
|
|
|
|
bool zm_packetqueue::increment_analysis_it( ) {
|
|
|
|
// We do this instead of distance becuase distance will traverse the entire list in the worst case
|
|
|
|
std::list<ZMPacket *>::iterator next_it = analysis_it;
|
2018-04-15 05:04:00 +08:00
|
|
|
++ next_it;
|
2017-12-01 03:37:36 +08:00
|
|
|
if ( next_it == pktQueue.end() ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
analysis_it = next_it;
|
|
|
|
return true;
|
|
|
|
} // end bool zm_packetqueue::increment_analysis_it( )
|