Bug 1322070: P7. Construct VideoInfo object from Theora CodecState. r?gerald
MozReview-Commit-ID: E4gR2Ff7qo
--- a/dom/media/ogg/OggCodecState.cpp
+++ b/dom/media/ogg/OggCodecState.cpp
@@ -142,16 +142,34 @@ OggCodecState::AddVorbisComment(Metadata
if (!IsUTF8(value)) {
LOG(LogLevel::Debug, ("Skipping comment: invalid UTF-8 in value"));
return false;
}
aTags->Put(key, value);
return true;
}
+bool
+OggCodecState::SetCodecSpecificConfig(MediaByteBuffer* aBuffer,
+ OggPacketQueue& aHeaders)
+{
+ nsTArray<const unsigned char*> headers;
+ nsTArray<size_t> headerLens;
+ for (size_t i = 0; i < aHeaders.Length(); i++) {
+ headers.AppendElement(aHeaders[i]->packet);
+ headerLens.AppendElement(aHeaders[i]->bytes);
+ }
+ // Save header packets for the decoder
+ if (!XiphHeadersToExtradata(aBuffer, headers, headerLens)) {
+ return false;
+ }
+ aHeaders.Erase();
+ return true;
+}
+
void
VorbisState::RecordVorbisPacketSamples(ogg_packet* aPacket, long aSamples)
{
#ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
mVorbisPacketSamples[aPacket] = aSamples;
#endif
}
@@ -314,67 +332,83 @@ OggCodecState::PacketOutUntilGranulepos(
}
return NS_OK;
}
TheoraState::TheoraState(ogg_page* aBosPage)
: OggCodecState(aBosPage, true)
, mSetup(0)
, mCtx(0)
- , mPixelAspectRatio(0)
{
MOZ_COUNT_CTOR(TheoraState);
- th_info_init(&mInfo);
+ th_info_init(&mTheoraInfo);
th_comment_init(&mComment);
}
TheoraState::~TheoraState()
{
MOZ_COUNT_DTOR(TheoraState);
th_setup_free(mSetup);
th_decode_free(mCtx);
th_comment_clear(&mComment);
- th_info_clear(&mInfo);
+ th_info_clear(&mTheoraInfo);
+ Reset();
}
bool
TheoraState::Init()
{
if (!mActive) {
return false;
}
- int64_t n = mInfo.aspect_numerator;
- int64_t d = mInfo.aspect_denominator;
+ int64_t n = mTheoraInfo.aspect_numerator;
+ int64_t d = mTheoraInfo.aspect_denominator;
- mPixelAspectRatio = (n == 0 || d == 0)
+ float aspectRatio = (n == 0 || d == 0)
? 1.0f : static_cast<float>(n) / static_cast<float>(d);
// Ensure the frame and picture regions aren't larger than our prescribed
// maximum, or zero sized.
- nsIntSize frame(mInfo.frame_width, mInfo.frame_height);
- nsIntRect picture(mInfo.pic_x, mInfo.pic_y, mInfo.pic_width, mInfo.pic_height);
- if (!IsValidVideoRegion(frame, picture, frame)) {
+ nsIntSize frame(mTheoraInfo.frame_width, mTheoraInfo.frame_height);
+ nsIntRect picture(mTheoraInfo.pic_x, mTheoraInfo.pic_y, mTheoraInfo.pic_width, mTheoraInfo.pic_height);
+ nsIntSize display(mTheoraInfo.pic_width, mTheoraInfo.pic_height);
+ ScaleDisplayByAspectRatio(display, aspectRatio);
+ if (!IsValidVideoRegion(frame, picture, display)) {
return mActive = false;
}
- mCtx = th_decode_alloc(&mInfo, mSetup);
+ mCtx = th_decode_alloc(&mTheoraInfo, mSetup);
if (!mCtx) {
return mActive = false;
}
- return true;
+ // Video track's frame sizes will not overflow. Activate the video track.
+ mInfo.mMimeType = NS_LITERAL_CSTRING("video/theora");
+ mInfo.mDisplay = display;
+ mInfo.mImage = frame;
+ mInfo.SetImageRect(picture);
+ mKeyframe_granule_shift = mTheoraInfo.keyframe_granule_shift;
+
+ return mActive = SetCodecSpecificConfig(mInfo.mCodecSpecificConfig, mHeaders);
+}
+
+nsresult
+TheoraState::Reset()
+{
+ mHeaders.Erase();
+ return OggCodecState::Reset();
}
bool
TheoraState::DecodeHeader(ogg_packet* aPacket)
{
- nsAutoRef<ogg_packet> autoRelease(aPacket);
+ mHeaders.Append(aPacket);
mPacketCount++;
- int ret = th_decode_headerin(&mInfo,
+ int ret = th_decode_headerin(&mTheoraInfo,
&mComment,
&mSetup,
aPacket);
// We must determine when we've read the last header packet.
// th_decode_headerin() does not tell us when it's read the last header, so
// we must keep track of the headers externally.
//
@@ -402,17 +436,17 @@ TheoraState::DecodeHeader(ogg_packet* aP
}
int64_t
TheoraState::Time(int64_t granulepos)
{
if (!mActive) {
return -1;
}
- return TheoraState::Time(&mInfo, granulepos);
+ return TheoraState::Time(&mTheoraInfo, granulepos);
}
bool
TheoraState::IsHeader(ogg_packet* aPacket)
{
return th_packet_isheader(aPacket);
}
@@ -439,54 +473,55 @@ TheoraState::Time(th_info* aInfo, int64_
return -1;
}
t /= aInfo->fps_numerator;
return t.isValid() ? t.value() : -1;
}
int64_t TheoraState::StartTime(int64_t granulepos)
{
- if (granulepos < 0 || !mActive || mInfo.fps_numerator == 0) {
+ if (granulepos < 0 || !mActive || mTheoraInfo.fps_numerator == 0) {
return -1;
}
CheckedInt64 t =
(CheckedInt64(th_granule_frame(mCtx, granulepos)) * USECS_PER_S)
- * mInfo.fps_denominator;
+ * mTheoraInfo.fps_denominator;
if (!t.isValid()) {
return -1;
}
- return t.value() / mInfo.fps_numerator;
+ return t.value() / mTheoraInfo.fps_numerator;
}
int64_t
TheoraState::PacketDuration(ogg_packet* aPacket)
{
- if (!mActive || mInfo.fps_numerator == 0) {
+ if (!mActive || mTheoraInfo.fps_numerator == 0) {
return -1;
}
- CheckedInt64 t =
- SaferMultDiv(mInfo.fps_denominator, USECS_PER_S, mInfo.fps_numerator);
+ CheckedInt64 t = SaferMultDiv(mTheoraInfo.fps_denominator, USECS_PER_S,
+ mTheoraInfo.fps_numerator);
return t.isValid() ? t.value() : -1;
}
int64_t
TheoraState::MaxKeyframeOffset()
{
// Determine the maximum time in microseconds by which a key frame could
// offset for the theora bitstream. Theora granulepos encode time as:
// ((key_frame_number << granule_shift) + frame_offset).
// Therefore the maximum possible time by which any frame could be offset
// from a keyframe is the duration of (1 << granule_shift) - 1) frames.
int64_t frameDuration;
// Max number of frames keyframe could possibly be offset.
- int64_t keyframeDiff = (1 << mInfo.keyframe_granule_shift) - 1;
+ int64_t keyframeDiff = (1 << mTheoraInfo.keyframe_granule_shift) - 1;
// Length of frame in usecs.
- frameDuration = (mInfo.fps_denominator * USECS_PER_S) / mInfo.fps_numerator;
+ frameDuration =
+ (mTheoraInfo.fps_denominator * USECS_PER_S) / mTheoraInfo.fps_numerator;
// Total time in usecs keyframe can be offset from any given frame.
return frameDuration * keyframeDiff;
}
bool
TheoraState::IsKeyframe(ogg_packet* pkt)
{
@@ -548,18 +583,18 @@ TheoraState::ReconstructTheoraGranulepos
}
ogg_int64_t lastGranulepos = mUnstamped[mUnstamped.Length() - 1]->granulepos;
NS_ASSERTION(lastGranulepos != -1, "Must know last granulepos");
// Reconstruct the granulepos (and thus timestamps) of the decoded
// frames. Granulepos are stored as ((keyframe<<shift)+offset). We
// know the granulepos of the last frame in the list, so we can infer
// the granulepos of the intermediate frames using their frame numbers.
- ogg_int64_t shift = mInfo.keyframe_granule_shift;
- ogg_int64_t version_3_2_1 = TheoraVersion(&mInfo,3,2,1);
+ ogg_int64_t shift = mTheoraInfo.keyframe_granule_shift;
+ ogg_int64_t version_3_2_1 = TheoraVersion(&mTheoraInfo,3,2,1);
ogg_int64_t lastFrame = th_granule_frame(mCtx,
lastGranulepos) + version_3_2_1;
ogg_int64_t firstFrame = lastFrame - mUnstamped.Length() + 1;
// Until we encounter a keyframe, we'll assume that the "keyframe"
// segment of the granulepos is the first frame, or if that causes
// the "offset" segment to overflow, we assume the required
// keyframe is maximumally offset. Until we encounter a keyframe
--- a/dom/media/ogg/OggCodecState.h
+++ b/dom/media/ogg/OggCodecState.h
@@ -262,16 +262,18 @@ protected:
// can be pushed over to mPackets. Used by PageIn() implementations in
// subclasses.
nsresult PacketOutUntilGranulepos(bool& aFoundGranulepos);
// Temporary buffer in which to store packets while we're reading packets
// in order to capture granulepos.
nsTArray<ogg_packet*> mUnstamped;
+ bool SetCodecSpecificConfig(MediaByteBuffer* aBuffer, OggPacketQueue& aHeaders);
+
private:
bool InternalInit();
};
class VorbisState : public OggCodecState
{
public:
explicit VorbisState(ogg_page* aBosPage);
@@ -355,35 +357,39 @@ public:
virtual ~TheoraState();
CodecType GetType() override { return TYPE_THEORA; }
bool DecodeHeader(ogg_packet* aPacket) override;
int64_t Time(int64_t granulepos) override;
int64_t StartTime(int64_t granulepos) override;
int64_t PacketDuration(ogg_packet* aPacket) override;
bool Init() override;
+ nsresult Reset() override;
bool IsHeader(ogg_packet* aPacket) override;
bool IsKeyframe(ogg_packet* aPacket) override;
nsresult PageIn(ogg_page* aPage) override;
+ const TrackInfo* GetInfo() const override { return &mInfo; }
// Returns the maximum number of microseconds which a keyframe can be offset
// from any given interframe.
int64_t MaxKeyframeOffset();
+ // Public access for mTheoraInfo.keyframe_granule_shift
+ int32_t mKeyframe_granule_shift;
+private:
// Returns the end time that a granulepos represents.
static int64_t Time(th_info* aInfo, int64_t aGranulePos);
- th_info mInfo;
+ th_info mTheoraInfo;
th_comment mComment;
th_setup_info* mSetup;
th_dec_ctx* mCtx;
- float mPixelAspectRatio;
-
-private:
+ VideoInfo mInfo;
+ OggPacketQueue mHeaders;
// Reconstructs the granulepos of Theora packets stored in the
// mUnstamped array. mUnstamped must be filled with consecutive packets from
// the stream, with the last packet having a known granulepos. Using this
// known granulepos, and the known frame numbers, we recover the granulepos
// of all frames in the array. This enables us to determine their timestamps.
void ReconstructTheoraGranulepos();
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -139,17 +139,16 @@ OggDemuxer::OggDemuxer(MediaResource* aR
, mOpusSerial(0)
, mTheoraSerial(0)
, mFlacSerial(0)
, mIsChained(false)
, mTimedMetadataEvent(nullptr)
, mOnSeekableEvent(nullptr)
{
MOZ_COUNT_CTOR(OggDemuxer);
- PodZero(&mTheoraInfo);
}
OggDemuxer::~OggDemuxer()
{
MOZ_COUNT_DTOR(OggDemuxer);
Reset(TrackInfo::kAudioTrack);
Reset(TrackInfo::kVideoTrack);
if (HasAudio() || HasVideo()) {
@@ -374,49 +373,19 @@ OggDemuxer::BuildSerialList(nsTArray<uin
void
OggDemuxer::SetupTargetTheora(TheoraState* aTheoraState, OggHeaders& aHeaders)
{
if (mTheoraState) {
mTheoraState->Reset();
}
- nsIntRect picture = nsIntRect(aTheoraState->mInfo.pic_x,
- aTheoraState->mInfo.pic_y,
- aTheoraState->mInfo.pic_width,
- aTheoraState->mInfo.pic_height);
-
- nsIntSize displaySize = nsIntSize(aTheoraState->mInfo.pic_width,
- aTheoraState->mInfo.pic_height);
-
- // Apply the aspect ratio to produce the intrinsic display size we report
- // to the element.
- ScaleDisplayByAspectRatio(displaySize, aTheoraState->mPixelAspectRatio);
-
- nsIntSize frameSize(aTheoraState->mInfo.frame_width,
- aTheoraState->mInfo.frame_height);
- if (IsValidVideoRegion(frameSize, picture, displaySize)) {
- // Video track's frame sizes will not overflow. Activate the video track.
- mInfo.mVideo.mMimeType = "video/theora";
- mInfo.mVideo.mDisplay = displaySize;
- mInfo.mVideo.mImage = frameSize;
- mInfo.mVideo.SetImageRect(picture);
-
- // Copy Theora info data for time computations on other threads.
- memcpy(&mTheoraInfo, &aTheoraState->mInfo, sizeof(mTheoraInfo));
-
- // Save header packets for the decoder
- if (!XiphHeadersToExtradata(mInfo.mVideo.mCodecSpecificConfig,
- aHeaders.mHeaders, aHeaders.mHeaderLens)) {
- return;
- }
-
- mTheoraState = aTheoraState;
- mTheoraSerial = aTheoraState->mSerial;
- }
+ mInfo.mVideo = *aTheoraState->GetInfo()->GetAsVideoInfo();
+ mTheoraState = aTheoraState;
+ mTheoraSerial = aTheoraState->mSerial;
}
void
OggDemuxer::SetupTargetVorbis(VorbisState* aVorbisState, OggHeaders& aHeaders)
{
if (mVorbisState) {
mVorbisState->Reset();
}
@@ -497,32 +466,21 @@ OggDemuxer::SetupMediaTracksInfo(const n
}
if (codecState->GetType() == OggCodecState::TYPE_THEORA) {
TheoraState* theoraState = static_cast<TheoraState*>(codecState);
if (!(mTheoraState && mTheoraState->mSerial == theoraState->mSerial)) {
continue;
}
+ mInfo.mVideo = *theoraState->GetInfo()->GetAsVideoInfo();
+
if (msgInfo) {
InitTrack(msgInfo, &mInfo.mVideo, mTheoraState == theoraState);
}
-
- nsIntRect picture = nsIntRect(theoraState->mInfo.pic_x,
- theoraState->mInfo.pic_y,
- theoraState->mInfo.pic_width,
- theoraState->mInfo.pic_height);
- nsIntSize displaySize = nsIntSize(theoraState->mInfo.pic_width,
- theoraState->mInfo.pic_height);
- nsIntSize frameSize(theoraState->mInfo.frame_width,
- theoraState->mInfo.frame_height);
- ScaleDisplayByAspectRatio(displaySize, theoraState->mPixelAspectRatio);
- if (IsValidVideoRegion(frameSize, picture, displaySize)) {
- mInfo.mVideo.mDisplay = displaySize;
- }
} else if (codecState->GetType() == OggCodecState::TYPE_VORBIS) {
VorbisState* vorbisState = static_cast<VorbisState*>(codecState);
if (!(mVorbisState && mVorbisState->mSerial == vorbisState->mSerial)) {
continue;
}
mInfo.mAudio = *vorbisState->GetInfo()->GetAsAudioInfo();
@@ -1090,17 +1048,17 @@ OggDemuxer::GetBuffered(TrackInfo::Track
startTime = mOpusState->Time(granulepos);
NS_ASSERTION(startTime > 0, "Must have positive start time");
} else if (aType == TrackInfo::kAudioTrack && mFlacState &&
serial == mFlacSerial) {
startTime = mFlacState->Time(granulepos);
NS_ASSERTION(startTime > 0, "Must have positive start time");
} else if (aType == TrackInfo::kVideoTrack && mTheoraState &&
serial == mTheoraSerial) {
- startTime = TheoraState::Time(&mTheoraInfo, granulepos);
+ startTime = mTheoraState->Time(granulepos);
NS_ASSERTION(startTime > 0, "Must have positive start time");
} else if (mCodecStore.Contains(serial)) {
// Stream is not the theora or vorbis stream we're playing,
// but is one that we have header data for.
startOffset += page.header_len + page.body_len;
continue;
} else {
// Page is for a stream we don't know about (possibly a chained
@@ -1828,21 +1786,22 @@ OggDemuxer::SeekInBufferedRange(TrackInf
// We have an active Theora bitstream. Peek the next Theora frame, and
// extract its keyframe's time.
DemuxUntilPacketAvailable(aType, mTheoraState);
ogg_packet* packet = mTheoraState->PacketPeek();
if (packet && !mTheoraState->IsKeyframe(packet)) {
// First post-seek frame isn't a keyframe, seek back to previous keyframe,
// otherwise we'll get visual artifacts.
NS_ASSERTION(packet->granulepos != -1, "Must have a granulepos");
- int shift = mTheoraState->mInfo.keyframe_granule_shift;
+ int shift = mTheoraState->mKeyframe_granule_shift;
int64_t keyframeGranulepos = (packet->granulepos >> shift) << shift;
int64_t keyframeTime = mTheoraState->StartTime(keyframeGranulepos);
- SEEK_LOG(LogLevel::Debug, ("Keyframe for %lld is at %lld, seeking back to it",
- frameTime, keyframeTime));
+ SEEK_LOG(LogLevel::Debug,
+ ("Keyframe for %lld is at %lld, seeking back to it", frameTime,
+ keyframeTime));
aAdjustedTarget = std::min(aAdjustedTarget, keyframeTime);
}
}
nsresult res = NS_OK;
if (aAdjustedTarget < aTarget) {
SeekRange k = SelectSeekRange(aType,
aRanges,
--- a/dom/media/ogg/OggDemuxer.h
+++ b/dom/media/ogg/OggDemuxer.h
@@ -303,18 +303,16 @@ private:
// data in the codec states due to threading issues. You must check the
// associated mTheoraState or mVorbisState pointer is non-null before
// using this codec data.
uint32_t mVorbisSerial;
uint32_t mOpusSerial;
uint32_t mTheoraSerial;
uint32_t mFlacSerial;
- th_info mTheoraInfo;
-
Maybe<int64_t> mStartTime;
// Booleans to indicate if we have audio and/or video data
bool HasVideo() const;
bool HasAudio() const;
bool HasSkeleton() const
{
return mSkeletonState != 0 && mSkeletonState->mActive;