Apple's HLS Solution uses the FFMPEG transcoded TS stream, which will gradually become non-synchronous during playback. The solution of Sohu source is to add the discontinue tag every five minutes, however, this tag causes the native player to restart, with a bit of cards. In this case, it is better to rebuild the player without restarting and directly read the next stream. However, there are other problems with FFMPEG processing HLS playback: 1. Each time a discontinue occurs, the displayed playback duration is cleared; 2. You can only drag and drop the content before the first discontinue. In this article, FFmpeg is transformed to adapt HLS sources more elegantly.
Modify the HLS. c file. You can use ffplay for comparison before and after verification.
1. Add a function
Static int find_timestamp_in_seq_no (struct playlist * PLS, int64_t * timestamp, int seq_no)
{
Int I;
* Timestamp = 0;
For (I = 0; I <seq_no; I ++ ){
* Timestamp + = PLS-> segments [I]-> duration;
}
Return 0;
}
2. Modify the function:
Struct segment {
Int64_t duration;
Int64_t url_offset;
Int64_t size;
Char * URL;
Char * key;
Enum keytype key_type;
Uint8_t IV [16];
Int64_t first_dts; // added in this article
};
Static int hls_read_packet (avformatcontext * s, avpacket * Pkt)
{
Hlscontext * c = s-> priv_data;
Int ret, I, minplaylist =-1;
Int64_t timestamp = av_nopts_value;
Static int64_t lastdts [2] = {av_nopts_value };
Static int64_t lastdts_fix [2] = {av_nopts_value };
Static int64_t interdts [2] = {av_nopts_value };
Static int64_t whole_dts [2] = {av_nopts_value}; // accumulated DTS
Recheck_discard_flags (S, C-> first_packet );
For (I = 0; I <c-> n_playlists; I ++ ){
Struct playlist * PLS = C-> playlists [I];
/* Make sure we 've got one buffered packet from each open playlist
* Stream */
Avrational TB;
TB = get_timebase (PLS );
Find_timestamp_in_seq_no (PLS, & timestamp, pls-> cur_seq_no );
If (PLS-> needed &&! Pls-> Pkt. Data ){
While (1 ){
Int64_t ts_diff;
Ret = av_read_frame (PLS-> CTX, & PLS-> Pkt );
If (Ret <0 ){
If (! Url_feof (& PLS-> Pb) & RET! = Averror_eof)
Return ret;
Reset_packet (& PLS-> Pkt );
Break;
} Else {
// Save the first timestamp of each segments and use it as the seek to the new discontinue. It is not required to be accurate.
If (PLS-> segments [PLS-> cur_seq_no]-> first_dts = av_nopts_value)
{
Pls-> segments [PLS-> cur_seq_no]-> first_dts = PLS-> Pkt. DTS;
// Av_log (S, av_log_error, "seq_no = % d, DTS = % LLD \ n", pls-> cur_seq_no, pls-> Pkt. DTS );
}
/* Stream_index check prevents matching picture attachments etc .*/
If (PLS-> is_id3_timestamped & PLS-> Pkt. stream_index = 0 ){
/* Audio elementary streams are ID3 timestamped */
Fill_timing_for_id3_timestamped_stream (PLS );
}
If (c-> first_timestamp = av_nopts_value &&
Pls-> Pkt. DTS! = Av_nopts_value)
C-> first_timestamp = av_rescale_q (PLS-> Pkt. DTS,
Get_timebase (PLS), av_time_base_q );
}
If (PLS-> seek_timestamp = av_nopts_value)
{
// When the current timestamp is normal, it is assigned to lastdts
If (PLS-> Pkt. PTs> lastdts [PLS-> Pkt. stream_index])
{
// Save the last timestamp as the right value accumulated next time. The PTS of the last frame is the same as that of DTs, so it can be shared.
Lastdts [PLS-> Pkt. stream_index] = PLS-> Pkt. DTS;
}
Else // when the discontinue is encountered, it is accumulated to whole_dts.
{
Whole_dts [PLS-> Pkt. stream_index] + = lastdts [PLS-> Pkt. stream_index];
Lastdts [PLS-> Pkt. stream_index] = PLS-> Pkt. DTS;
Av_log (S, av_log_error, "DTS = % LLD, Index = % d \ n", whole_dts [PLS-> Pkt. stream_index], pls-> Pkt. stream_index );
}
// When whole_dts has a value, add it to the data transmission in read packet to obtain the incremental data transmission.
If (whole_dts [PLS-> Pkt. stream_index]! = Av_nopts_value)
{
// Av_log (S, av_log_error, "lastdts = % LLD, % LLD \ n", lastdts, pls-> Pkt. DTS );
Pls-> Pkt. DTS + = interdts [PLS-> Pkt. stream_index] + whole_dts [PLS-> Pkt. stream_index];
Pls-> Pkt. PTS + = interdts [PLS-> Pkt. stream_index] + whole_dts [PLS-> Pkt. stream_index];
}
// Calculate the audio/video interval value, which is subject to DTS
If (interdts [PLS-> Pkt. stream_index] = av_nopts_value &&
Lastdts [PLS-> Pkt. stream_index]! = Av_nopts_value)
{
Interdts [PLS-> Pkt. stream_index] = PLS-> Pkt. DTS-lastdts [PLS-> Pkt. stream_index];
Av_log (S, av_log_error, "Inter DTS = % LLD, Index = % d \ n", interdts, pls-> Pkt. stream_index );
}
Break;
}
Else // seek, you need to save the lastdts of the previous discontinue
{
// Whole_dts [0] + = lastdts [PLS-> Pkt. stream_index];
// Whole_dts [1] + = lastdts [PLS-> Pkt. stream_index];
}
If (PLS-> seek_stream_index <0 |
Pls-> seek_stream_index = PLS-> Pkt. stream_index ){
If (PLS-> Pkt. DTS = av_nopts_value ){
Pls-> seek_timestamp = av_nopts_value;
Break;
}
Ts_diff = timestamp + av_rescale_rnd (PLS-> Pkt. DTS-pls-> segments [PLS-> cur_seq_no]-> first_dts, av_time_base,
TB. Den, av_round_down)-pls-> seek_timestamp;
If (ts_diff> = 0 & (PLS-> seek_flags & avseek_flag_any |
Pls-> Pkt. Flags & av_pkt_flag_key )){
Whole_dts [0] = (PLS-> seek_timestamp + ts_diff) * TB. den/(Tb. Num * av_time_base)-pls-> Pkt. DTS;
Whole_dts [1] = (PLS-> seek_timestamp + ts_diff) * TB. den/(Tb. Num * av_time_base)-pls-> Pkt. DTS;
Av_log (S, av_log_error, "whold DTS = % LLD, DTS = % LLD \ n", whole_dts [0], pls-> Pkt. DTS );
Pls-> seek_timestamp = av_nopts_value;
Break;
}
}
Av_free_packet (& PLS-> Pkt );
Reset_packet (& PLS-> Pkt );
}
}
/* Check if this stream has the packet with the lowest DTS */
If (PLS-> Pkt. Data ){
Struct playlist * minpls = minplaylist <0?
Null: C-> playlists [minplaylist];
If (minplaylist <0 ){
Minplaylist = I;
} Else {
Int64_t DTS = PLS-> Pkt. DTS;
Int64_t mindts = minpls-> Pkt. DTS;
If (DTS = av_nopts_value |
(Mindts! = Av_nopts_value & compare_ts_with_wrapdetect (DTS, PLS, mindts, minpls) <0 ))
Minplaylist = I;
}
}
}
/* If we got a packet, return it */
If (minplaylist> = 0 ){
Struct playlist * PLS = C-> playlists [minplaylist];
* Pkt = PLS-> Pkt;
Pkt-> stream_index + = PLS-> stream_offset;
Reset_packet (& C-> playlists [minplaylist]-> Pkt );
If (Pkt-> DTS! = Av_nopts_value)
C-> cur_timestamp = av_rescale_q (Pkt-> DTS,
Pls-> CTX-> streams [PLS-> Pkt. stream_index]-> time_base,
Av_time_base_q );
Return 0;
}
Return averror_eof;
}
This article is from the "A Jun" blog. For more information, contact the author!
FFmpeg solves the HLS drag-and-drop problem