Skip to content

Commit 67f4190

Browse files
committed
h 265 video compatible
Signed-off-by: stfnylim <stfnylim@gmail.com>
1 parent 163cd04 commit 67f4190

2 files changed

Lines changed: 113 additions & 3 deletions

File tree

src/lib/image/MovieFFMpeg/MovieFFMpeg.cpp

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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);

src/lib/image/MovieFFMpeg/MovieFFMpeg/MovieFFMpeg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ namespace TwkMovie
198198
//
199199

200200
void collectPlaybackTiming(std::vector<bool> heroVideoTracks, std::vector<bool> heroAudioTracks);
201+
int64_t countHeroVideoPacketsAndReopen(const std::vector<bool>& heroVideoTracks);
201202
int64_t getFirstFrame(AVRational rate);
202203
void snagMetadata(AVDictionary* dict, std::string source, FrameBuffer* fb);
203204
bool snagColr(AVCodecContext* videoCodecContext, VideoTrack* track);

0 commit comments

Comments
 (0)