@@ -79,6 +79,21 @@ namespace TwkMovie
7979#define RV_OUTPUT_VIDEO_CODEC " mjpeg"
8080#define RV_OUTPUT_AUDIO_CODEC " pcm_s16be"
8181
82+ static bool isHEVCElementaryStream (const AVFormatContext* formatContext, const AVStream* stream, const string& filename)
83+ {
84+ if (!stream || !stream->codecpar || stream->codecpar ->codec_id != AV_CODEC_ID_H265 )
85+ return false ;
86+
87+ if (formatContext && formatContext->iformat && formatContext->iformat ->name && !std::strcmp (formatContext->iformat ->name , " hevc" ))
88+ {
89+ return true ;
90+ }
91+
92+ string ext = boost::filesystem::path (filename).extension ().string ();
93+ boost::algorithm::to_lower (ext);
94+ return ext == " .265" || ext == " .h265" || ext == " .hevc" ;
95+ }
96+
8297#if 0
8398#define DB_LOG_LEVEL AV_LOG_ERROR
8499#define DB_GENERAL 0x001
@@ -281,6 +296,7 @@ namespace TwkMovie
281296 , videoFrame(0 )
282297 , lastDecodedVideo(-1 )
283298 , lastEncodedVideo(0 )
299+ , nextSyntheticTS(0 )
284300 , imgConvertContext(0 )
285301 , isOpen(false )
286302 , useOpenJPH(false )
@@ -329,6 +345,7 @@ namespace TwkMovie
329345 useAppleProRes = t->useAppleProRes ;
330346 number = t->number ;
331347 rotate = t->rotate ;
348+ nextSyntheticTS = t->nextSyntheticTS ;
332349#if defined(RV_USE_APPLE_PRORES_SDK)
333350 appleProResCtx = t->appleProResCtx ;
334351#endif
@@ -344,6 +361,7 @@ namespace TwkMovie
344361 bool useOpenJPH;
345362 bool useAppleProRes;
346363 set<int64_t > tsSet;
364+ int64_t nextSyntheticTS;
347365 FrameBuffer fb;
348366 struct SwsContext * imgConvertContext;
349367 AVPacket* videoPacket;
@@ -1345,6 +1363,34 @@ namespace TwkMovie
13451363 return true ;
13461364 }
13471365
1366+ int64_t MovieFFMpegReader::countHeroVideoPacketsAndReopen (const vector<bool >& heroVideoTracks)
1367+ {
1368+ int64_t frames = 0 ;
1369+ AVPacket* pkt = av_packet_alloc ();
1370+ if (!pkt)
1371+ TWK_THROW_EXC_STREAM (" Unable to allocate packet while counting frames: " << m_filename);
1372+
1373+ while (av_read_frame (m_avFormatContext, pkt) >= 0 )
1374+ {
1375+ const int streamIndex = pkt->stream_index ;
1376+ if (streamIndex >= 0 )
1377+ {
1378+ const size_t heroIndex = static_cast <size_t >(streamIndex);
1379+ if (heroIndex < heroVideoTracks.size () && heroVideoTracks[heroIndex])
1380+ ++frames;
1381+ }
1382+ av_packet_unref (pkt);
1383+ }
1384+
1385+ av_packet_free (&pkt);
1386+
1387+ avformat_close_input (&m_avFormatContext);
1388+ openAVFormat ();
1389+ findStreamInfo ();
1390+
1391+ return frames;
1392+ }
1393+
13481394 void MovieFFMpegReader::trackFromStreamIndex (int index, VideoTrack*& vTrack, AudioTrack*& aTrack)
13491395 {
13501396 vTrack = 0 ;
@@ -2845,6 +2891,7 @@ namespace TwkMovie
28452891 //
28462892
28472893 AVRational rate = av_d2q (0.0 , INT_MAX );
2894+ AVRational fallbackVideoRate = av_d2q (0.0 , INT_MAX );
28482895 int64_t frames = 0 ;
28492896 Time audioLength = 0 ;
28502897 double timeDuration = 0 ;
@@ -2889,6 +2936,12 @@ namespace TwkMovie
28892936 DBL (DB_TIMESTAMPS , " stm: " << i << " fps: " << codecFPS << " frames: " << frames << " frameDur: " << frameDur
28902937 << " timeDuration: " << timeDuration << " start_time: " << tsStream->start_time );
28912938 }
2939+ else if (tsStream->codecpar ->codec_type == AVMEDIA_TYPE_VIDEO && heroVideoTracks[i] && codecFPS > 0
2940+ && isHEVCElementaryStream (m_avFormatContext, tsStream, m_filename))
2941+ {
2942+ AVRational tsRate = (realFPS > 0 ) ? tsStream->r_frame_rate : tsStream->avg_frame_rate ;
2943+ fallbackVideoRate = (av_q2d (tsRate) > av_q2d (fallbackVideoRate)) ? tsRate : fallbackVideoRate;
2944+ }
28922945 // check if we are reading an image instead of a video
28932946 else if (tsStream->codecpar ->codec_type == AVMEDIA_TYPE_VIDEO && heroVideoTracks[i]
28942947 && isImageFormat (m_avFormatContext->iformat ->name ))
@@ -2929,7 +2982,20 @@ namespace TwkMovie
29292982 }
29302983 if (timeDuration <= 0 )
29312984 {
2932- TWK_THROW_EXC_STREAM (" Could not determine playback timing: " << m_filename);
2985+ const double fallbackFPS = av_q2d (fallbackVideoRate);
2986+ if (fallbackFPS > 0 )
2987+ {
2988+ frames = countHeroVideoPacketsAndReopen (heroVideoTracks);
2989+ if (frames > 0 )
2990+ {
2991+ rate = fallbackVideoRate;
2992+ timeDuration = double (frames) / fallbackFPS;
2993+ }
2994+ }
2995+ if (timeDuration <= 0 )
2996+ {
2997+ TWK_THROW_EXC_STREAM (" Could not determine playback timing: " << m_filename);
2998+ }
29332999 }
29343000 if (frames == 0 )
29353001 {
@@ -3543,10 +3609,28 @@ namespace TwkMovie
35433609 }
35443610
35453611 avcodec_flush_buffers (track->avCodecContext );
3546- if (av_seek_frame (m_avFormatContext, track->number , seekTarget, AVSEEK_FLAG_BACKWARD ) < 0 )
3612+
3613+ const bool useSyntheticTimestamps = isHEVCElementaryStream (m_avFormatContext, videoStream, m_filename) && videoStream->duration <= 0
3614+ && videoStream->nb_frames <= 0 && m_info.fps > 0 ;
3615+ bool seekFailed = false ;
3616+ if (useSyntheticTimestamps && m_avFormatContext->pb )
3617+ {
3618+ seekFailed = avio_seek (m_avFormatContext->pb , 0 , SEEK_SET ) < 0 ;
3619+ if (!seekFailed)
3620+ {
3621+ avformat_flush (m_avFormatContext);
3622+ }
3623+ }
3624+ else
3625+ {
3626+ seekFailed = av_seek_frame (m_avFormatContext, track->number , seekTarget, AVSEEK_FLAG_BACKWARD ) < 0 ;
3627+ }
3628+
3629+ if (seekFailed)
35473630 {
35483631 // Try from the start if targeted seek fails
3549- if (av_seek_frame (m_avFormatContext, -1 , m_avFormatContext->start_time , 0 ) < 0 )
3632+ const int64_t startTime = (m_avFormatContext->start_time == AV_NOPTS_VALUE ) ? 0 : m_avFormatContext->start_time ;
3633+ if (av_seek_frame (m_avFormatContext, -1 , startTime, 0 ) < 0 )
35503634 {
35513635 TWK_THROW_EXC_STREAM (" av_seek_frame failed in video stream." );
35523636 }
@@ -3558,6 +3642,7 @@ namespace TwkMovie
35583642 //
35593643 track->lastDecodedVideo = -1 ;
35603644 track->tsSet .clear ();
3645+ track->nextSyntheticTS = useSyntheticTimestamps ? 0 : seekTarget;
35613646
35623647#if DB_TIMING & DB_LEVEL
35633648 double seekDuration = m_timingDetails->pauseTimer (" seek" );
@@ -3597,6 +3682,25 @@ namespace TwkMovie
35973682#endif
35983683 } while ((track->videoPacket ->stream_index != track->number ) && !finalPacket);
35993684
3685+ AVStream* stream = m_avFormatContext->streams [track->number ];
3686+ if (!finalPacket && track->videoPacket ->pts == AV_NOPTS_VALUE && track->videoPacket ->dts == AV_NOPTS_VALUE && m_info.fps > 0
3687+ && isHEVCElementaryStream (m_avFormatContext, stream, m_filename))
3688+ {
3689+ int64_t packetDuration = track->videoPacket ->duration ;
3690+ if (packetDuration <= 0 )
3691+ {
3692+ const double timeBase = av_q2d (stream->time_base );
3693+ if (timeBase > 0 )
3694+ {
3695+ packetDuration = int64_t (1.0 / (timeBase * m_info.fps ) + 0.5 );
3696+ }
3697+ }
3698+ packetDuration = std::max<int64_t >(1 , packetDuration);
3699+ track->videoPacket ->pts = track->nextSyntheticTS ;
3700+ track->videoPacket ->dts = track->nextSyntheticTS ;
3701+ track->nextSyntheticTS += packetDuration;
3702+ }
3703+
36003704 // If we get a valid timestamp here then we should add it to
36013705 // the track's list of nearby timestamps.
36023706 if (track->videoPacket ->pts != AV_NOPTS_VALUE )
@@ -6060,10 +6164,12 @@ namespace TwkMovie
60606164 unsigned int audcap = MovieIO::MovieReadAudio | MovieIO::MovieWriteAudio | MovieIO::AttributeRead | MovieIO::AttributeWrite;
60616165
60626166 unsigned int vidcap = MovieIO::MovieRead | MovieIO::MovieWrite | audcap;
6167+ unsigned int vidReadCap = MovieIO::MovieRead | MovieIO::AttributeRead;
60636168
60646169 if (this ->bruteForce ())
60656170 {
60666171 vidcap |= MovieIO::MovieBruteForceIO;
6172+ vidReadCap |= MovieIO::MovieBruteForceIO;
60676173 audcap |= MovieIO::MovieBruteForceIO;
60686174 }
60696175
@@ -6073,6 +6179,9 @@ namespace TwkMovie
60736179 formats[" avi" ] = make_pair (" Audio Video Interleave" , vidcap);
60746180 formats[" flv" ] = make_pair (" Flash Video" , vidcap);
60756181 formats[" gif" ] = make_pair (" Graphics Interchange Format" , vidcap);
6182+ formats[" 265" ] = make_pair (" HEVC/H.265 Elementary Stream" , vidReadCap);
6183+ formats[" h265" ] = make_pair (" HEVC/H.265 Elementary Stream" , vidReadCap);
6184+ formats[" hevc" ] = make_pair (" HEVC/H.265 Elementary Stream" , vidReadCap);
60766185 formats[" m3u8" ] = make_pair (" M3U8 Stream Metadata" , vidcap);
60776186 formats[" m4v" ] = make_pair (" iTunes Video Format (from MPEG-4)" , vidcap);
60786187 formats[" mkv" ] = make_pair (" Matroska Video" , vidcap);
0 commit comments