Bug 1195723: [flac] P12. Add sniffer for streaming flac. r?kamidphish
MozReview-Commit-ID: P62v6vsXzs
--- a/dom/media/flac/FlacDemuxer.cpp
+++ b/dom/media/flac/FlacDemuxer.cpp
@@ -231,22 +231,52 @@ const uint8_t FrameHeader::CRC8Table[256
0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};
// flac::Frame - Frame meta container used to parse and hold a frame
// header and side info.
class Frame {
public:
- // Find the next frame start in the current resource.
- // On exit return true, offset is set and resource points to the frame found.
// The FLAC signature is made of 14 bits set to 1; however the 15th bit is
// mandatorily set to 0, so we need to find either of 0xfffc or 0xfffd 2-bytes
// signature. We first use a bitmask to see if 0xfc or 0xfd is present. And if
// so we check for the whole signature.
+ // aData must be pointing to a buffer at least
+ // aLength + FLAC_MAX_FRAME_HEADER_SIZE bytes.
+ int64_t FindNext(const uint8_t* aData, const uint32_t aLength)
+ {
+ uint32_t modOffset = aLength % 4;
+ uint32_t i, j;
+
+ for (i = 0; i < modOffset; i++) {
+ if ((BigEndian::readUint16(aData + i) & 0xfffe) == 0xfff8) {
+ if (mHeader.Parse(aData + i)) {
+ return i;
+ }
+ }
+ }
+
+ for (; i < aLength; i += 4) {
+ uint32_t x = BigEndian::readUint32(aData + i);
+ if (((x & ~(x + 0x01010101)) & 0x80808080)) {
+ for (j = 0; j < 4; j++) {
+ if ((BigEndian::readUint16(aData + i + j) & 0xfffe) == 0xfff8) {
+ if (mHeader.Parse(aData + i + j)) {
+ return i + j;
+ }
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+ // Find the next frame start in the current resource.
+ // On exit return true, offset is set and resource points to the frame found.
bool FindNext(MediaResourceIndex& aResource)
{
static const int BUFFER_SIZE = 4096;
Reset();
nsTArray<char> buffer;
int64_t originalOffset = aResource.Tell();
@@ -256,51 +286,32 @@ public:
do {
uint32_t read = 0;
buffer.SetLength(BUFFER_SIZE + innerOffset);
nsresult rv =
aResource.Read(buffer.Elements() + innerOffset, BUFFER_SIZE, &read);
if (NS_FAILED(rv)) {
return false;
}
+
if (read < FLAC_MAX_FRAME_HEADER_SIZE) {
// Assume that we can't have a valid frame in such small content, we
// must have reached EOS.
// So we're done.
mEOS = true;
return false;
}
const size_t bufSize = read + innerOffset - FLAC_MAX_FRAME_HEADER_SIZE;
-
- const uint8_t* buf = reinterpret_cast<uint8_t*>(buffer.Elements());
- uint32_t modOffset = bufSize % 4;
- uint32_t i, j;
+ int64_t foundOffset =
+ FindNext(reinterpret_cast<uint8_t*>(buffer.Elements()), bufSize);
- for (i = 0; i < modOffset; i++) {
- if ((BigEndian::readUint16(buf + i) & 0xfffe) == 0xfff8) {
- if (mHeader.Parse(buf + i)) {
- SetOffset(aResource, offset + i);
- return true;
- }
- }
- }
-
- for (; i < bufSize; i += 4) {
- uint32_t x = BigEndian::readUint32(buf + i);
- if (((x & ~(x + 0x01010101)) & 0x80808080)) {
- for (j = 0; j < 4; j++) {
- if ((BigEndian::readUint16(buf + i + j) & 0xfffe) == 0xfff8) {
- if (mHeader.Parse(buf + i + j)) {
- SetOffset(aResource, offset + i + j);
- return true;
- }
- }
- }
- }
+ if (foundOffset >= 0) {
+ SetOffset(aResource, foundOffset + offset);
+ return true;
}
// Scan the next block;
offset += bufSize;
buffer.RemoveElementsAt(0, bufSize);
innerOffset = buffer.Length();
} while (offset - originalOffset < FLAC_MAX_FRAME_SIZE);
@@ -1034,9 +1045,20 @@ FlacTrackDemuxer::TimeAtEnd()
// Update our current progress stats.
mParsedFramesDuration =
previousTime + previousDuration - mParser->FirstFrame().Time();
mTotalFrameLen = streamLen - mParser->FirstFrame().Offset();
return mParsedFramesDuration;
}
+/* static */ bool
+FlacDemuxer::FlacSniffer(const uint8_t* aData, const uint32_t aLength)
+{
+ if (aLength < FLAC_MAX_FRAME_HEADER_SIZE) {
+ return false;
+ }
+
+ flac::Frame frame;
+ return frame.FindNext(aData, aLength - FLAC_MAX_FRAME_HEADER_SIZE) >= 0;
+}
+
} // namespace mozilla
--- a/dom/media/flac/FlacDemuxer.h
+++ b/dom/media/flac/FlacDemuxer.h
@@ -25,16 +25,19 @@ public:
explicit FlacDemuxer(MediaResource* aSource);
RefPtr<InitPromise> Init() override;
bool HasTrackType(TrackInfo::TrackType aType) const override;
uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
bool IsSeekable() const override;
+ // Return true if a valid flac frame header could be found.
+ static bool FlacSniffer(const uint8_t* aData, const uint32_t aLength);
+
private:
bool InitInternal();
RefPtr<MediaResource> mSource;
RefPtr<FlacTrackDemuxer> mTrackDemuxer;
};
class FlacTrackDemuxer : public MediaTrackDemuxer {
--- a/toolkit/components/mediasniffer/nsMediaSniffer.cpp
+++ b/toolkit/components/mediasniffer/nsMediaSniffer.cpp
@@ -7,16 +7,17 @@
#include "nsMediaSniffer.h"
#include "nsIHttpChannel.h"
#include "nsString.h"
#include "nsMimeTypes.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/ModuleUtils.h"
#include "mp3sniff.h"
#include "nestegg/nestegg.h"
+#include "FlacDemuxer.h"
#include "nsIClassInfoImpl.h"
#include <algorithm>
// The minimum number of bytes that are needed to attempt to sniff an mp4 file.
static const unsigned MP4_MIN_BYTES_COUNT = 12;
// The maximum number of bytes to consider when attempting to sniff a file.
static const uint32_t MAX_BYTES_SNIFFED = 512;
@@ -115,16 +116,21 @@ static bool MatchesWebM(const uint8_t* a
// This function implements mp3 sniffing based on parsing
// packet headers and looking for expected boundaries.
static bool MatchesMP3(const uint8_t* aData, const uint32_t aLength)
{
return mp3_sniff(aData, (long)aLength);
}
+static bool MatchesFLAC(const uint8_t* aData, const uint32_t aLength)
+{
+ return mozilla::FlacDemuxer::FlacSniffer(aData, aLength);
+}
+
NS_IMETHODIMP
nsMediaSniffer::GetMIMETypeFromContent(nsIRequest* aRequest,
const uint8_t* aData,
const uint32_t aLength,
nsACString& aSniffedType)
{
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
if (channel) {
@@ -174,13 +180,21 @@ nsMediaSniffer::GetMIMETypeFromContent(n
}
// Bug 950023: 512 bytes are often not enough to sniff for mp3.
if (MatchesMP3(aData, std::min(aLength, MAX_BYTES_SNIFFED_MP3))) {
aSniffedType.AssignLiteral(AUDIO_MP3);
return NS_OK;
}
+ // Flac frames are generally big, often in excess of 24kB.
+ // Using a size of MAX_BYTES_SNIFFED effectively means that we will only
+ // recognize flac content if it starts with a frame.
+ if (MatchesFLAC(aData, clampedLength)) {
+ aSniffedType.AssignLiteral(AUDIO_FLAC);
+ return NS_OK;
+ }
+
// Could not sniff the media type, we are required to set it to
// application/octet-stream.
aSniffedType.AssignLiteral(APPLICATION_OCTET_STREAM);
return NS_ERROR_NOT_AVAILABLE;
}