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
2017-12-06 05:16:52 +08:00
zm_packetqueue : : zm_packetqueue ( int video_image_count , int p_video_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 ;
2017-05-17 00:04:56 +08:00
}
zm_packetqueue : : ~ zm_packetqueue ( ) {
2017-08-01 03:49:27 +08:00
clearQueue ( ) ;
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 ) {
2017-12-09 02:46:02 +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 ) ) {
2017-12-08 23:39:24 +08:00
Debug ( 2 , " Increasing analysis_it " ) ;
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 ) {
Debug ( 2 , " Descreasing video_packet_count (%d) " , video_packet_count ) ;
video_packet_count - = 1 ;
} 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 {
Error ( " SHould have found the packet! " ) ;
}
} 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
2017-05-17 00:04:56 +08:00
pktQueue . push_back ( zm_packet ) ;
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 )
2017-12-01 20:26:34 +08:00
clearQueue ( max_video_packet_count , video_stream_id ) ;
}
2017-05-17 00:04:56 +08:00
2017-12-01 03:37:36 +08:00
if ( analysis_it = = pktQueue . end ( ) ) {
// ANalsys_it should only point to end when queue it empty
Debug ( 2 , " pointing analysis_it to begining " ) ;
analysis_it = pktQueue . begin ( ) ;
}
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 )
analysis_it + + ;
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
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 ;
}
}
2017-05-17 00:04:56 +08:00
return packet ;
}
unsigned int zm_packetqueue : : clearQueue ( unsigned int frames_to_keep , int stream_id ) {
2017-06-01 08:44:43 +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 ( ) ) {
Debug ( 3 , " Queue is 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 ;
for ( it = pktQueue . rbegin ( ) ; it ! = pktQueue . rend ( ) & & frames_to_keep ; + + it ) {
ZMPacket * zm_packet = * it ;
AVPacket * av_packet = & ( zm_packet - > packet ) ;
2017-11-28 00:28:36 +08:00
Debug ( 4 , " Looking at packet with stream index (%d) with keyframe(%d), frames_to_keep is (%d) " ,
2017-12-01 20:26:34 +08:00
av_packet - > stream_index , zm_packet - > keyframe , 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
// Make sure we start on a keyframe
for ( ; it ! = pktQueue . rend ( ) ; + + it ) {
ZMPacket * zm_packet = * it ;
AVPacket * av_packet = & ( zm_packet - > packet ) ;
2018-01-22 10:27:44 +08:00
Debug ( 5 , " Looking for 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
// 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-01-17 23:01:48 +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-01-17 23:01:48 +08:00
Debug ( 3 , " Hit end of queue, still need (%d) video frames " , frames_to_keep ) ;
}
if ( it ! = pktQueue . rend ( ) ) {
// We want to keep this packet, so advance to the next
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 ) {
Debug ( 4 , " Deleting packets from the front, count is (%d) " , packets_to_delete ) ;
while ( - - packets_to_delete ) {
Debug ( 4 , " Deleting a packet from the front, count is (%d), queue size is %d " , delete_count , pktQueue . size ( ) ) ;
packet = pktQueue . front ( ) ;
if ( * analysis_it = = packet )
analysis_it + + ;
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
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-02-16 03:08:21 +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 ;
while ( ! pktQueue . empty ( ) ) {
packet = pktQueue . front ( ) ;
pktQueue . pop_front ( ) ;
2017-11-28 00:28:36 +08:00
if ( packet - > image_index = = - 1 )
delete packet ;
2017-05-17 00:04:56 +08:00
}
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
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 ;
next_it + + ;
if ( next_it = = pktQueue . end ( ) ) {
return false ;
}
analysis_it = next_it ;
return true ;
} // end bool zm_packetqueue::increment_analysis_it( )