author | Randy Lin <rlin@mozilla.com> |
Wed, 15 Jan 2014 14:21:14 +0800 | |
changeset 165375 | a331400db013bd863d738236fe054fc62aec415f |
parent 165374 | faecbc4a1b4b78c02f5745a938180f24e7768668 |
child 165376 | 4680e4f0354b44bb710aefcb8b7541c494a29ff3 |
push id | 26088 |
push user | ryanvm@gmail.com |
push date | Mon, 27 Jan 2014 20:10:48 +0000 |
treeherder | mozilla-central@07021aa58289 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | giles, gps, rillian |
bugs | 891705, 950567, 883749, 881840 |
milestone | 29.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/confvars.sh +++ b/browser/confvars.sh @@ -59,11 +59,12 @@ if test "$OS_TARGET" = "WINNT" -o "$OS_T MOZ_FOLD_LIBS=1 fi MOZ_WEBGL_CONFORMANT=1 # Enable navigator.mozPay MOZ_PAY=1 # Enable activities. These are used for FxOS developers currently. MOZ_ACTIVITIES=1 MOZ_JSDOWNLOADS=1 +MOZ_WEBM_ENCODER=1 # Enable exact rooting on desktop. JSGC_USE_EXACT_ROOTING=1
--- a/config/external/moz.build +++ b/config/external/moz.build @@ -22,16 +22,19 @@ if CONFIG['MOZ_TREMOR']: external_dirs += ['media/libtremor'] if CONFIG['MOZ_OPUS']: external_dirs += ['media/libopus'] if CONFIG['MOZ_WEBM']: external_dirs += ['media/libnestegg'] +if CONFIG['MOZ_WEBM_ENCODER']: + external_dirs += ['media/libmkv'] + if CONFIG['MOZ_VPX'] and not CONFIG['MOZ_NATIVE_LIBVPX']: external_dirs += ['media/libvpx'] if CONFIG['MOZ_OGG']: external_dirs += ['media/libogg', 'media/libtheora'] if not CONFIG['MOZ_NATIVE_PNG']: external_dirs += ['media/libpng']
--- a/configure.in +++ b/configure.in @@ -5541,16 +5541,21 @@ fi if test -n "$MOZ_TREMOR"; then AC_DEFINE(MOZ_TREMOR) fi if test -n "$MOZ_OPUS"; then AC_DEFINE(MOZ_OPUS) fi +if test -n "$MOZ_WEBM_ENCODER"; then + AC_DEFINE(MOZ_WEBM_ENCODER) +fi +AC_SUBST(MOZ_WEBM_ENCODER) + dnl ================================== dnl = Check alsa availability on Linux dnl ================================== dnl If using Linux, ensure that the alsa library is available if test "$OS_TARGET" = "Linux"; then MOZ_ALSA=1 fi
--- a/content/media/encoder/MediaEncoder.cpp +++ b/content/media/encoder/MediaEncoder.cpp @@ -9,16 +9,21 @@ #include "prlog.h" #include "mozilla/Preferences.h" #ifdef MOZ_OGG #include "OggWriter.h" #endif #ifdef MOZ_OPUS #include "OpusTrackEncoder.h" + +#endif + +#ifdef MOZ_VORBIS +#include "VorbisTrackEncoder.h" #endif #ifdef MOZ_WEBM_ENCODER #include "VorbisTrackEncoder.h" #include "VP8TrackEncoder.h" #include "WebMWriter.h" #endif #ifdef MOZ_OMX_ENCODER #include "OmxTrackEncoder.h"
--- a/content/media/encoder/TrackEncoder.cpp +++ b/content/media/encoder/TrackEncoder.cpp @@ -123,16 +123,30 @@ AudioTrackEncoder::InterleaveTrackData(A aChunk.mVolume, aOutputChannels, aOutput); } else { InterleaveAndConvertBuffer(aChunk.mChannelData.Elements(), aChunk.mBufferFormat, aDuration, aChunk.mVolume, aOutputChannels, aOutput); } } +/*static*/ +void +AudioTrackEncoder::DeInterleaveTrackData(AudioDataValue* aInput, + int32_t aDuration, + int32_t aChannels, + AudioDataValue* aOutput) +{ + for (int32_t i = 0; i < aChannels; ++i) { + for(int32_t j = 0; j < aDuration; ++j) { + aOutput[i * aDuration + j] = aInput[i + j * aChannels]; + } + } +} + void VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID, TrackRate aTrackRate, TrackTicks aTrackOffset, uint32_t aTrackEvents, const MediaSegment& aQueuedMedia) {
--- a/content/media/encoder/TrackEncoder.h +++ b/content/media/encoder/TrackEncoder.h @@ -153,16 +153,23 @@ public: * to up-mix or down-mix the channel data if the channels number of this chunk * is different from aOutputChannels. The channel data from aChunk might be * modified by up-mixing. */ static void InterleaveTrackData(AudioChunk& aChunk, int32_t aDuration, uint32_t aOutputChannels, AudioDataValue* aOutput); + /** + * De-interleaves the aInput data and stores the result into aOutput. + * No up-mix or down-mix operations inside. + */ + static void DeInterleaveTrackData(AudioDataValue* aInput, int32_t aDuration, + int32_t aChannels, AudioDataValue* aOutput); + protected: /** * Number of samples per channel in a pcm buffer. This is also the value of * frame size required by audio encoder, and mReentrantMonitor will be * notified when at least this much data has been added to mRawSegment. */ virtual int GetPacketDuration() { return 0; }
--- a/content/media/encoder/TrackMetadataBase.h +++ b/content/media/encoder/TrackMetadataBase.h @@ -13,16 +13,17 @@ namespace mozilla { // A class represent meta data for various codec format. Only support one track information. class TrackMetadataBase { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackMetadataBase) enum MetadataKind { METADATA_OPUS, // Represent the Opus metadata METADATA_VP8, + METADATA_VORBIS, METADATA_AVC, METADATA_AAC, METADATA_UNKNOWN // Metadata Kind not set }; virtual ~TrackMetadataBase() {} // Return the specific metadata kind virtual MetadataKind GetKind() const = 0; };
new file mode 100644 --- /dev/null +++ b/content/media/encoder/VP8TrackEncoder.cpp @@ -0,0 +1,472 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "VP8TrackEncoder.h" +#include "vpx/vp8cx.h" +#include "vpx/vpx_encoder.h" +#include "VideoUtils.h" +#include "prsystem.h" +#include "WebMWriter.h" + +namespace mozilla { + +#ifdef PR_LOGGING +PRLogModuleInfo* gVP8TrackEncoderLog; +#define VP8LOG(msg, ...) PR_LOG(gVP8TrackEncoderLog, PR_LOG_DEBUG, \ + (msg, ##__VA_ARGS__)) +// Debug logging macro with object pointer and class name. +#else +#define VP8LOG(msg, ...) +#endif + +#define DEFAULT_BITRATE 2500 // in kbit/s +#define DEFAULT_ENCODE_FRAMERATE 30 + +VP8TrackEncoder::VP8TrackEncoder() + : VideoTrackEncoder() + , mEncodedFrameDuration(0) + , mEncodedTimestamp(0) + , mRemainingTicks(0) + , mVPXContext(new vpx_codec_ctx_t()) + , mVPXImageWrapper(new vpx_image_t()) +{ + MOZ_COUNT_CTOR(VP8TrackEncoder); +#ifdef PR_LOGGING + if (!gVP8TrackEncoderLog) { + gVP8TrackEncoderLog = PR_NewLogModule("VP8TrackEncoder"); + } +#endif +} + +VP8TrackEncoder::~VP8TrackEncoder() +{ + if (mInitialized) { + vpx_codec_destroy(mVPXContext); + } + + if (mVPXImageWrapper) { + vpx_img_free(mVPXImageWrapper); + } + MOZ_COUNT_DTOR(VP8TrackEncoder); +} + +nsresult +VP8TrackEncoder::Init(int32_t aWidth, int32_t aHeight, TrackRate aTrackRate) +{ + if (aWidth < 1 || aHeight < 1 || aTrackRate <= 0) { + return NS_ERROR_FAILURE; + } + + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + + mTrackRate = aTrackRate; + mEncodedFrameRate = DEFAULT_ENCODE_FRAMERATE; + mEncodedFrameDuration = mTrackRate / mEncodedFrameRate; + mFrameWidth = aWidth; + mFrameHeight = aHeight; + + // Encoder configuration structure. + vpx_codec_enc_cfg_t config; + memset(&config, 0, sizeof(vpx_codec_enc_cfg_t)); + if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config, 0)) { + return NS_ERROR_FAILURE; + } + + // Creating a wrapper to the image - setting image data to NULL. Actual + // pointer will be set in encode. Setting align to 1, as it is meaningless + // (actual memory is not allocated). + vpx_img_wrap(mVPXImageWrapper, IMG_FMT_I420, + mFrameWidth, mFrameHeight, 1, nullptr); + + config.g_w = mFrameWidth; + config.g_h = mFrameHeight; + // TODO: Maybe we should have various aFrameRate bitrate pair for each devices? + // or for different platform + config.rc_target_bitrate = DEFAULT_BITRATE; // in kbit/s + + // Setting the time base of the codec + config.g_timebase.num = 1; + config.g_timebase.den = mTrackRate; + + config.g_error_resilient = 0; + + config.g_lag_in_frames = 0; // 0- no frame lagging + + int32_t number_of_cores = PR_GetNumberOfProcessors(); + if (mFrameWidth * mFrameHeight > 1280 * 960 && number_of_cores >= 6) { + config.g_threads = 3; // 3 threads for 1080p. + } else if (mFrameWidth * mFrameHeight > 640 * 480 && number_of_cores >= 3) { + config.g_threads = 2; // 2 threads for qHD/HD. + } else { + config.g_threads = 1; // 1 thread for VGA or less + } + + // rate control settings + config.rc_dropframe_thresh = 0; + config.rc_end_usage = VPX_CBR; + config.g_pass = VPX_RC_ONE_PASS; + config.rc_resize_allowed = 1; + config.rc_undershoot_pct = 100; + config.rc_overshoot_pct = 15; + config.rc_buf_initial_sz = 500; + config.rc_buf_optimal_sz = 600; + config.rc_buf_sz = 1000; + + config.kf_mode = VPX_KF_AUTO; + // Ensure that we can output one I-frame per second. + config.kf_max_dist = mEncodedFrameRate; + + vpx_codec_flags_t flags = 0; + flags |= VPX_CODEC_USE_OUTPUT_PARTITION; + if (vpx_codec_enc_init(mVPXContext, vpx_codec_vp8_cx(), &config, flags)) { + return NS_ERROR_FAILURE; + } + + vpx_codec_control(mVPXContext, VP8E_SET_STATIC_THRESHOLD, 1); + vpx_codec_control(mVPXContext, VP8E_SET_CPUUSED, -6); + vpx_codec_control(mVPXContext, VP8E_SET_TOKEN_PARTITIONS, + VP8_ONE_TOKENPARTITION); + + mInitialized = true; + mon.NotifyAll(); + + return NS_OK; +} + +already_AddRefed<TrackMetadataBase> +VP8TrackEncoder::GetMetadata() +{ + { + // Wait if mEncoder is not initialized. + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + while (!mCanceled && !mInitialized) { + mon.Wait(); + } + } + + if (mCanceled || mEncodingComplete) { + return nullptr; + } + + nsRefPtr<VP8Metadata> meta = new VP8Metadata(); + meta->mWidth = mFrameWidth; + meta->mHeight = mFrameHeight; + meta->mEncodedFrameRate = mEncodedFrameRate; + + return meta.forget(); +} + +nsresult +VP8TrackEncoder::GetEncodedPartitions(EncodedFrameContainer& aData) +{ + vpx_codec_iter_t iter = nullptr; + EncodedFrame::FrameType frameType = EncodedFrame::P_FRAME; + nsTArray<uint8_t> frameData; + const vpx_codec_cx_pkt_t *pkt = nullptr; + while ((pkt = vpx_codec_get_cx_data(mVPXContext, &iter)) != nullptr) { + switch (pkt->kind) { + case VPX_CODEC_CX_FRAME_PKT: { + // Copy the encoded data from libvpx to frameData + frameData.AppendElements((uint8_t*)pkt->data.frame.buf, + pkt->data.frame.sz); + break; + } + default: { + break; + } + } + // End of frame + if ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) { + if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) { + frameType = EncodedFrame::I_FRAME; + } + break; + } + } + + if (!frameData.IsEmpty() && + (pkt->data.frame.pts == mEncodedTimestamp)) { + // Copy the encoded data to aData. + EncodedFrame* videoData = new EncodedFrame(); + videoData->SetFrameType(frameType); + // Convert the timestamp and duration to Usecs. + CheckedInt64 timestamp = FramesToUsecs(mEncodedTimestamp, mTrackRate); + if (timestamp.isValid()) { + videoData->SetTimeStamp( + (uint64_t)FramesToUsecs(mEncodedTimestamp, mTrackRate).value()); + } + CheckedInt64 duration = FramesToUsecs(pkt->data.frame.duration, mTrackRate); + if (duration.isValid()) { + videoData->SetDuration( + (uint64_t)FramesToUsecs(pkt->data.frame.duration, mTrackRate).value()); + } + videoData->SetFrameData(&frameData); + VP8LOG("GetEncodedPartitions TimeStamp %lld Duration %lld\n", + videoData->GetTimeStamp(), videoData->GetDuration()); + VP8LOG("frameType %d\n", videoData->GetFrameType()); + aData.AppendEncodedFrame(videoData); + } + + return NS_OK; +} + +void VP8TrackEncoder::PrepareMutedFrame() +{ + if (mMuteFrame.IsEmpty()) { + CreateMutedFrame(&mMuteFrame); + } + + uint32_t yPlanSize = mFrameWidth * mFrameHeight; + uint32_t halfWidth = (mFrameWidth + 1) / 2; + uint32_t halfHeight = (mFrameHeight + 1) / 2; + uint32_t uvPlanSize = halfWidth * halfHeight; + + MOZ_ASSERT(mMuteFrame.Length() >= (yPlanSize + uvPlanSize)); + uint8_t *y = mMuteFrame.Elements(); + uint8_t *cb = mMuteFrame.Elements() + yPlanSize; + uint8_t *cr = mMuteFrame.Elements() + yPlanSize + uvPlanSize; + + mVPXImageWrapper->planes[PLANE_Y] = y; + mVPXImageWrapper->planes[PLANE_U] = cb; + mVPXImageWrapper->planes[PLANE_V] = cr; + mVPXImageWrapper->stride[VPX_PLANE_Y] = mFrameWidth; + mVPXImageWrapper->stride[VPX_PLANE_U] = halfWidth; + mVPXImageWrapper->stride[VPX_PLANE_V] = halfWidth; +} + +nsresult VP8TrackEncoder::PrepareRawFrame(VideoChunk &aChunk) +{ + if (aChunk.mFrame.GetForceBlack()) { + PrepareMutedFrame(); + } else { + layers::Image* img = aChunk.mFrame.GetImage(); + if (NS_WARN_IF(!img)) { + return NS_ERROR_NULL_POINTER; + } + ImageFormat format = img->GetFormat(); + if (format != PLANAR_YCBCR) { + VP8LOG("Unsupported video format\n"); + return NS_ERROR_FAILURE; + } + + // Cast away constness b/c some of the accessors are non-const + layers::PlanarYCbCrImage* yuv = + const_cast<layers::PlanarYCbCrImage *>(static_cast<const layers::PlanarYCbCrImage *>(img)); + // Big-time assumption here that this is all contiguous data coming + // from getUserMedia or other sources. + MOZ_ASSERT(yuv); + const layers::PlanarYCbCrImage::Data *data = yuv->GetData(); + + mVPXImageWrapper->planes[PLANE_Y] = data->mYChannel; + mVPXImageWrapper->planes[PLANE_U] = data->mCbChannel; + mVPXImageWrapper->planes[PLANE_V] = data->mCrChannel; + mVPXImageWrapper->stride[VPX_PLANE_Y] = data->mYStride; + mVPXImageWrapper->stride[VPX_PLANE_U] = data->mCbCrStride; + mVPXImageWrapper->stride[VPX_PLANE_V] = data->mCbCrStride; + } + return NS_OK; +} + +// These two define value used in GetNextEncodeOperation to determine the +// EncodeOperation for next target frame. +#define I_FRAME_RATIO (0.5) +#define SKIP_FRAME_RATIO (0.75) + +/** + * Compares the elapsed time from the beginning of GetEncodedTrack and + * the processed frame duration in mSourceSegment + * in order to set the nextEncodeOperation for next target frame. + */ +VP8TrackEncoder::EncodeOperation +VP8TrackEncoder::GetNextEncodeOperation(TimeDuration aTimeElapsed, + TrackTicks aProcessedDuration) +{ + int64_t durationInUsec = + FramesToUsecs(aProcessedDuration + mEncodedFrameDuration, + mTrackRate).value(); + if (aTimeElapsed.ToMicroseconds() > (durationInUsec * SKIP_FRAME_RATIO)) { + // The encoder is too slow. + // We should skip next frame to consume the mSourceSegment. + return SKIP_FRAME; + } else if (aTimeElapsed.ToMicroseconds() > (durationInUsec * I_FRAME_RATIO)) { + // The encoder is a little slow. + // We force the encoder to encode an I-frame to accelerate. + return ENCODE_I_FRAME; + } else { + return ENCODE_NORMAL_FRAME; + } +} + +TrackTicks +VP8TrackEncoder::CalculateRemainingTicks(TrackTicks aDurationCopied, + TrackTicks aEncodedDuration) +{ + return mRemainingTicks + aEncodedDuration - aDurationCopied; +} + +// Try to extend the encodedDuration as long as possible if the target frame +// has a long duration. +TrackTicks +VP8TrackEncoder::CalculateEncodedDuration(TrackTicks aDurationCopied) +{ + TrackTicks temp64 = aDurationCopied; + TrackTicks encodedDuration = mEncodedFrameDuration; + temp64 -= mRemainingTicks; + while (temp64 > mEncodedFrameDuration) { + temp64 -= mEncodedFrameDuration; + encodedDuration += mEncodedFrameDuration; + } + return encodedDuration; +} + +/** + * Encoding flow in GetEncodedTrack(): + * 1: Check the mInitialized state and the packet duration. + * 2: Move the data from mRawSegment to mSourceSegment. + * 3: Encode the video chunks in mSourceSegment in a for-loop. + * 3.1: Pick the video chunk by mRemainingTicks. + * 3.2: Calculate the encoding duration for the parameter of vpx_codec_encode(). + * The encoding duration is a multiple of mEncodedFrameDuration. + * 3.3: Setup the video chunk to mVPXImageWrapper by PrepareRawFrame(). + * 3.4: Send frame into vp8 encoder by vpx_codec_encode(). + * 3.5: Get the output frame from encoder by calling GetEncodedPartitions(). + * 3.6: Calculate the mRemainingTicks for next target frame. + * 3.7: Set the nextEncodeOperation for the next target frame. + * There is a heuristic: If the frame duration we have processed in + * mSourceSegment is 100ms, means that we can't spend more than 100ms to + * encode it. + * 4. Remove the encoded chunks in mSourceSegment after for-loop. + * + * Ex1: Input frame rate is 100 => input frame duration is 10ms for each. + * mEncodedFrameRate is 30 => output frame duration is 33ms. + * In this case, the frame duration in mSourceSegment will be: + * 1st : 0~10ms + * 2nd : 10~20ms + * 3rd : 20~30ms + * 4th : 30~40ms + * ... + * The VP8 encoder will take the 1st and 4th frames to encode. At beginning + * mRemainingTicks is 0 for 1st frame, then the mRemainingTicks is set + * to 23 to pick the 4th frame. (mEncodedFrameDuration - 1st frame duration) + * + * Ex2: Input frame rate is 25 => frame duration is 40ms for each. + * mEncodedFrameRate is 30 => output frame duration is 33ms. + * In this case, the frame duration in mSourceSegment will be: + * 1st : 0~40ms + * 2nd : 40~80ms + * 3rd : 80~120ms + * 4th : 120~160ms + * ... + * Because the input frame duration is 40ms larger than 33ms, so the first + * encoded frame duration will be 66ms by calling CalculateEncodedDuration. + * And the mRemainingTicks will be set to 26 + * (CalculateRemainingTicks 0+66-40) in order to pick the next frame(2nd) + * in mSourceSegment. + */ +nsresult +VP8TrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData) +{ + { + // Move all the samples from mRawSegment to mSourceSegment. We only hold + // the monitor in this block. + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + // Wait if mEncoder is not initialized, or when not enough raw data, but is + // not the end of stream nor is being canceled. + while (!mCanceled && (!mInitialized || + (mRawSegment.GetDuration() + mSourceSegment.GetDuration() < + mEncodedFrameDuration && !mEndOfStream))) { + mon.Wait(); + } + if (mCanceled || mEncodingComplete) { + return NS_ERROR_FAILURE; + } + mSourceSegment.AppendFrom(&mRawSegment); + } + + VideoSegment::ChunkIterator iter(mSourceSegment); + TrackTicks durationCopied = 0; + TrackTicks totalProcessedDuration = 0; + TimeStamp timebase = TimeStamp::Now(); + EncodeOperation nextEncodeOperation = ENCODE_NORMAL_FRAME; + + for (; !iter.IsEnded(); iter.Next()) { + VideoChunk &chunk = *iter; + // Accumulate chunk's duration to durationCopied until it reaches + // mRemainingTicks. + durationCopied += chunk.GetDuration(); + MOZ_ASSERT(mRemainingTicks <= mEncodedFrameDuration); + VP8LOG("durationCopied %lld mRemainingTicks %lld\n", + durationCopied, mRemainingTicks); + if (durationCopied >= mRemainingTicks) { + VP8LOG("nextEncodeOperation is %d\n",nextEncodeOperation); + // Calculate encodedDuration for this target frame. + TrackTicks encodedDuration = CalculateEncodedDuration(durationCopied); + + // Encode frame. + if (nextEncodeOperation != SKIP_FRAME) { + nsresult rv = PrepareRawFrame(chunk); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + + // Encode the data with VP8 encoder + int flags = (nextEncodeOperation == ENCODE_NORMAL_FRAME) ? + 0 : VPX_EFLAG_FORCE_KF; + if (vpx_codec_encode(mVPXContext, mVPXImageWrapper, mEncodedTimestamp, + (unsigned long)encodedDuration, flags, + VPX_DL_REALTIME)) { + return NS_ERROR_FAILURE; + } + // Get the encoded data from VP8 encoder. + GetEncodedPartitions(aData); + } else { + // SKIP_FRAME + // Extend the duration of the last encoded data in aData + // because this frame will be skip. + nsRefPtr<EncodedFrame> last = nullptr; + last = aData.GetEncodedFrames().LastElement(); + if (last) { + last->SetDuration(last->GetDuration() + encodedDuration); + } + } + // Move forward the mEncodedTimestamp. + mEncodedTimestamp += encodedDuration; + totalProcessedDuration += durationCopied; + // Calculate mRemainingTicks for next target frame. + mRemainingTicks = CalculateRemainingTicks(durationCopied, + encodedDuration); + + // Check the remain data is enough for next target frame. + if (mSourceSegment.GetDuration() - totalProcessedDuration + >= mEncodedFrameDuration) { + TimeDuration elapsedTime = TimeStamp::Now() - timebase; + nextEncodeOperation = GetNextEncodeOperation(elapsedTime, + totalProcessedDuration); + // Reset durationCopied for next iteration. + durationCopied = 0; + } else { + // Process done, there is no enough data left for next iteration, + // break the for-loop. + break; + } + } + } + // Remove the chunks we have processed. + mSourceSegment.RemoveLeading(totalProcessedDuration); + VP8LOG("RemoveLeading %lld\n",totalProcessedDuration); + + // End of stream, pull the rest frames in encoder. + if (mEndOfStream) { + VP8LOG("mEndOfStream is true\n"); + mEncodingComplete = true; + if (vpx_codec_encode(mVPXContext, nullptr, mEncodedTimestamp, + mEncodedFrameDuration, 0, VPX_DL_REALTIME)) { + return NS_ERROR_FAILURE; + } + GetEncodedPartitions(aData); + } + + return NS_OK ; +} + +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/content/media/encoder/VP8TrackEncoder.h @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef VP8TrackEncoder_h_ +#define VP8TrackEncoder_h_ + +#include "TrackEncoder.h" +#include "vpx/vpx_codec.h" + +namespace mozilla { + +typedef struct vpx_codec_ctx vpx_codec_ctx_t; +typedef struct vpx_codec_enc_cfg vpx_codec_enc_cfg_t; +typedef struct vpx_image vpx_image_t; + +/** + * VP8TrackEncoder implements VideoTrackEncoder by using libvpx library. + * We implement a realtime and fixed FPS encoder. In order to achieve that, + * there is a pick target frame and drop frame encoding policy implemented in + * GetEncodedTrack. + */ +class VP8TrackEncoder : public VideoTrackEncoder +{ + enum EncodeOperation { + ENCODE_NORMAL_FRAME, // VP8 track encoder works normally. + ENCODE_I_FRAME, // The next frame will be encoded as I-Frame. + SKIP_FRAME, // Skip the next frame. + }; +public: + VP8TrackEncoder(); + virtual ~VP8TrackEncoder(); + + already_AddRefed<TrackMetadataBase> GetMetadata() MOZ_FINAL MOZ_OVERRIDE; + + nsresult GetEncodedTrack(EncodedFrameContainer& aData) MOZ_FINAL MOZ_OVERRIDE; + +protected: + nsresult Init(int32_t aWidth, int32_t aHeight, + TrackRate aTrackRate) MOZ_FINAL MOZ_OVERRIDE; + +private: + // Calculate the target frame's encoded duration. + TrackTicks CalculateEncodedDuration(TrackTicks aDurationCopied); + + // Calculate the mRemainingTicks for next target frame. + TrackTicks CalculateRemainingTicks(TrackTicks aDurationCopied, + TrackTicks aEncodedDuration); + + // Get the EncodeOperation for next target frame. + EncodeOperation GetNextEncodeOperation(TimeDuration aTimeElapsed, + TrackTicks aProcessedDuration); + + // Get the encoded data from encoder to aData. + nsresult GetEncodedPartitions(EncodedFrameContainer& aData); + + // Prepare the input data to the mVPXImageWrapper for encoding. + nsresult PrepareRawFrame(VideoChunk &aChunk); + + // Prepare the muted frame data to the mVPXImageWrapper for encoding. + void PrepareMutedFrame(); + + // Output frame rate. + uint32_t mEncodedFrameRate; + // Duration for the output frame, reciprocal to mEncodedFrameRate. + TrackTicks mEncodedFrameDuration; + // Encoded timestamp. + TrackTicks mEncodedTimestamp; + // Duration to the next encode frame. + TrackTicks mRemainingTicks; + + // Muted frame, we only create it once. + nsTArray<uint8_t> mMuteFrame; + + /** + * A local segment queue which takes the raw data out from mRawSegment in the + * call of GetEncodedTrack(). Since we implement the fixed FPS encoding + * policy, it needs to be global in order to store the leftover segments + * taken from mRawSegment. + */ + VideoSegment mSourceSegment; + + // VP8 relative members. + // Codec context structure. + nsAutoPtr<vpx_codec_ctx_t> mVPXContext; + // Image Descriptor. + nsAutoPtr<vpx_image_t> mVPXImageWrapper; +}; + +} // namespace mozilla + +#endif
new file mode 100644 --- /dev/null +++ b/content/media/encoder/VorbisTrackEncoder.cpp @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "VorbisTrackEncoder.h" +#include <ogg/ogg.h> +#include <vorbis/vorbisenc.h> +#include "WebMWriter.h" + +// One actually used: Encoding using a VBR quality mode. The usable range is -.1 +// (lowest quality, smallest file) to 1. (highest quality, largest file). +// Example quality mode .4: 44kHz stereo coupled, roughly 128kbps VBR +// ret = vorbis_encode_init_vbr(&vi,2,44100,.4); +static const float BASE_QUALITY = 0.4f; + +namespace mozilla { + +#undef LOG +#ifdef PR_LOGGING +PRLogModuleInfo* gVorbisTrackEncoderLog; +#define VORBISLOG(msg, ...) PR_LOG(gVorbisTrackEncoderLog, PR_LOG_DEBUG, \ + (msg, ##__VA_ARGS__)) +#else +#define VORBISLOG(msg, ...) +#endif + +VorbisTrackEncoder::VorbisTrackEncoder() + : AudioTrackEncoder() +{ + MOZ_COUNT_CTOR(VorbisTrackEncoder); +#ifdef PR_LOGGING + if (!gVorbisTrackEncoderLog) { + gVorbisTrackEncoderLog = PR_NewLogModule("VorbisTrackEncoder"); + } +#endif +} + +VorbisTrackEncoder::~VorbisTrackEncoder() +{ + MOZ_COUNT_DTOR(VorbisTrackEncoder); + if (mInitialized) { + vorbis_block_clear(&mVorbisBlock); + vorbis_dsp_clear(&mVorbisDsp); + vorbis_info_clear(&mVorbisInfo); + } +} + +nsresult +VorbisTrackEncoder::Init(int aChannels, int aSamplingRate) +{ + if (aChannels <= 0 || aChannels > 8) { + VORBISLOG("aChannels <= 0 || aChannels > 8"); + return NS_ERROR_INVALID_ARG; + } + + // This monitor is used to wake up other methods that are waiting for encoder + // to be completely initialized. + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + mChannels = aChannels; + mSamplingRate = aSamplingRate; + + int ret = 0; + vorbis_info_init(&mVorbisInfo); + + ret = vorbis_encode_init_vbr(&mVorbisInfo, mChannels, mSamplingRate, + BASE_QUALITY); + + mInitialized = (ret == 0); + + if (mInitialized) { + // Set up the analysis state and auxiliary encoding storage + vorbis_analysis_init(&mVorbisDsp, &mVorbisInfo); + vorbis_block_init(&mVorbisDsp, &mVorbisBlock); + } + + mon.NotifyAll(); + + return ret == 0 ? NS_OK : NS_ERROR_FAILURE; +} + +void VorbisTrackEncoder::WriteLacing(nsTArray<uint8_t> *aOutput, int32_t aLacing) +{ + while (aLacing > 255) { + aLacing -= 255; + aOutput->AppendElement(255); + } + aOutput->AppendElement((uint8_t)aLacing); +} + +already_AddRefed<TrackMetadataBase> +VorbisTrackEncoder::GetMetadata() +{ + { + // Wait if encoder is not initialized. + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + while (!mCanceled && !mInitialized) { + mon.Wait(); + } + } + + if (mCanceled || mEncodingComplete) { + return nullptr; + } + + // Vorbis codec specific data + // http://matroska.org/technical/specs/codecid/index.html + nsRefPtr<VorbisMetadata> meta = new VorbisMetadata(); + meta->mBitDepth = 32; // float for desktop + meta->mChannels = mChannels; + meta->mSamplingFrequency = mSamplingRate; + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + // Add comment + vorbis_comment vorbisComment; + vorbis_comment_init(&vorbisComment); + vorbis_comment_add_tag(&vorbisComment, "ENCODER", + NS_LITERAL_CSTRING("Mozilla VorbisTrackEncoder " MOZ_APP_UA_VERSION).get()); + vorbis_analysis_headerout(&mVorbisDsp, &vorbisComment, + &header,&header_comm, &header_code); + vorbis_comment_clear(&vorbisComment); + // number of distinct packets - 1 + meta->mData.AppendElement(2); + // Xiph-style lacing header.bytes, header_comm.bytes + WriteLacing(&(meta->mData), header.bytes); + WriteLacing(&(meta->mData), header_comm.bytes); + + // Append the three packets + meta->mData.AppendElements(header.packet, header.bytes); + meta->mData.AppendElements(header_comm.packet, header_comm.bytes); + meta->mData.AppendElements(header_code.packet, header_code.bytes); + + return meta.forget(); +} + +void +VorbisTrackEncoder::GetEncodedFrames(EncodedFrameContainer& aData) +{ + // vorbis does some data preanalysis, then divvies up blocks for + // more involved (potentially parallel) processing. Get a single + // block for encoding now. + while (vorbis_analysis_blockout(&mVorbisDsp, &mVorbisBlock) == 1) { + ogg_packet oggPacket; + if (vorbis_analysis(&mVorbisBlock, &oggPacket) == 0) { + VORBISLOG("vorbis_analysis_blockout block size %d", oggPacket.bytes); + EncodedFrame* audiodata = new EncodedFrame(); + audiodata->SetFrameType(EncodedFrame::AUDIO_FRAME); + nsTArray<uint8_t> frameData; + frameData.AppendElements(oggPacket.packet, oggPacket.bytes); + audiodata->SetFrameData(&frameData); + aData.AppendEncodedFrame(audiodata); + } + } +} + +nsresult +VorbisTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData) +{ + if (mEosSetInEncoder) { + return NS_OK; + } + + nsAutoPtr<AudioSegment> sourceSegment; + sourceSegment = new AudioSegment(); + { + // Move all the samples from mRawSegment to sourceSegment. We only hold + // the monitor in this block. + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + + // Wait if mEncoder is not initialized, or when not enough raw data, but is + // not the end of stream nor is being canceled. + while (!mCanceled && mRawSegment.GetDuration() < GetPacketDuration() && + !mEndOfStream) { + mon.Wait(); + } + VORBISLOG("GetEncodedTrack passes wait, duration is %lld\n", + mRawSegment.GetDuration()); + if (mCanceled || mEncodingComplete) { + return NS_ERROR_FAILURE; + } + + sourceSegment->AppendFrom(&mRawSegment); + } + + if (mEndOfStream && (sourceSegment->GetDuration() == 0) + && !mEosSetInEncoder) { + mEncodingComplete = true; + mEosSetInEncoder = true; + VORBISLOG("[Vorbis] Done encoding."); + vorbis_analysis_wrote(&mVorbisDsp, 0); + GetEncodedFrames(aData); + + return NS_OK; + } + + // Start encoding data. + AudioSegment::ChunkIterator iter(*sourceSegment); + + AudioDataValue **vorbisBuffer = + vorbis_analysis_buffer(&mVorbisDsp, (int)sourceSegment->GetDuration()); + + int framesCopied = 0; + nsAutoTArray<AudioDataValue, 9600> interleavedPcm; + nsAutoTArray<AudioDataValue, 9600> nonInterleavedPcm; + interleavedPcm.SetLength(sourceSegment->GetDuration() * mChannels); + nonInterleavedPcm.SetLength(sourceSegment->GetDuration() * mChannels); + while (!iter.IsEnded()) { + AudioChunk chunk = *iter; + int frameToCopy = chunk.GetDuration(); + if (!chunk.IsNull()) { + InterleaveTrackData(chunk, frameToCopy, mChannels, + interleavedPcm.Elements() + framesCopied * mChannels); + } else { // empty data + memset(interleavedPcm.Elements() + framesCopied * mChannels, 0, + frameToCopy * mChannels * sizeof(AudioDataValue)); + } + framesCopied += frameToCopy; + iter.Next(); + } + // De-interleave the interleavedPcm. + DeInterleaveTrackData(interleavedPcm.Elements(), framesCopied, mChannels, + nonInterleavedPcm.Elements()); + // Copy the nonInterleavedPcm to vorbis buffer. + for(uint8_t i = 0; i < mChannels; ++i) { + memcpy(vorbisBuffer[i], nonInterleavedPcm.Elements() + framesCopied * i, + framesCopied * sizeof(AudioDataValue)); + } + + // Now the vorbisBuffer contain the all data in non-interleaved. + // Tell the library how much we actually submitted. + vorbis_analysis_wrote(&mVorbisDsp, framesCopied); + VORBISLOG("vorbis_analysis_wrote framesCopied %d\n", framesCopied); + GetEncodedFrames(aData); + + return NS_OK; +} + +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/content/media/encoder/VorbisTrackEncoder.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef VorbisTrackEncoder_h_ +#define VorbisTrackEncoder_h_ + +#include "TrackEncoder.h" +#include "nsCOMPtr.h" +#include <vorbis/codec.h> + +namespace mozilla { + +class VorbisTrackEncoder : public AudioTrackEncoder +{ +public: + VorbisTrackEncoder(); + virtual ~VorbisTrackEncoder(); + + already_AddRefed<TrackMetadataBase> GetMetadata() MOZ_FINAL MOZ_OVERRIDE; + + nsresult GetEncodedTrack(EncodedFrameContainer& aData) MOZ_FINAL MOZ_OVERRIDE; + +protected: + /** + * http://xiph.org/vorbis/doc/libvorbis/vorbis_analysis_buffer.html + * We use 1024 samples for the write buffer; libvorbis will construct packets + * with the appropriate duration for the encoding mode internally. + */ + int GetPacketDuration() MOZ_FINAL MOZ_OVERRIDE { + return 1024; + } + + nsresult Init(int aChannels, int aSamplingRate) MOZ_FINAL MOZ_OVERRIDE; + +private: + // Write Xiph-style lacing to aOutput. + void WriteLacing(nsTArray<uint8_t> *aOutput, int32_t aLacing); + + // Get the encoded data from vorbis encoder and append into aData. + void GetEncodedFrames(EncodedFrameContainer& aData); + + // vorbis codec members + // Struct that stores all the static vorbis bitstream settings. + vorbis_info mVorbisInfo; + // Central working state for the PCM->packet encoder. + vorbis_dsp_state mVorbisDsp; + // Local working space for PCM->packet encode. + vorbis_block mVorbisBlock; +}; + +} +#endif
--- a/content/media/encoder/moz.build +++ b/content/media/encoder/moz.build @@ -15,21 +15,29 @@ EXPORTS += [ 'TrackMetadataBase.h', ] UNIFIED_SOURCES += [ 'MediaEncoder.cpp', 'TrackEncoder.cpp', ] +if CONFIG['MOZ_OMX_ENCODER']: + EXPORTS += ['OmxTrackEncoder.h'] + UNIFIED_SOURCES += ['OmxTrackEncoder.cpp'] + if CONFIG['MOZ_OPUS']: EXPORTS += ['OpusTrackEncoder.h'] UNIFIED_SOURCES += ['OpusTrackEncoder.cpp'] -if CONFIG['MOZ_OMX_ENCODER']: - EXPORTS += ['OmxTrackEncoder.h'] - UNIFIED_SOURCES += ['OmxTrackEncoder.cpp'] +if CONFIG['MOZ_WEBM_ENCODER']: + EXPORTS += ['VorbisTrackEncoder.h', + 'VP8TrackEncoder.h', + ] + UNIFIED_SOURCES += ['VorbisTrackEncoder.cpp', + 'VP8TrackEncoder.cpp', + ] FAIL_ON_WARNINGS = True FINAL_LIBRARY = 'gklayout' include('/ipc/chromium/chromium-config.mozbuild')
new file mode 100644 --- /dev/null +++ b/content/media/webm/EbmlComposer.cpp @@ -0,0 +1,177 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "EbmlComposer.h" +#include "libmkv/EbmlIDs.h" +#include "libmkv/EbmlWriter.h" +#include "libmkv/WebMElement.h" +#include "prtime.h" + +namespace mozilla { + +// Timecode scale in nanoseconds +static const unsigned long TIME_CODE_SCALE = 1000000; +// The WebM header size without audio CodecPrivateData +static const int32_t DEFAULT_HEADER_SIZE = 1024; + +void EbmlComposer::GenerateHeader() +{ + // Write the EBML header. + EbmlGlobal ebml; + // The WEbM header default size usually smaller than 1k. + nsAutoArrayPtr<uint8_t> buffer(new uint8_t[DEFAULT_HEADER_SIZE + + mCodecPrivateData.Length()]); + ebml.buf = buffer.get(); + ebml.offset = 0; + writeHeader(&ebml); + { + EbmlLoc segEbmlLoc, ebmlLocseg, ebmlLoc; + Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment); + { + Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead); + // Todo: We don't know the exact sizes of encoded data and ignore this section. + Ebml_EndSubElement(&ebml, &ebmlLocseg); + writeSegmentInformation(&ebml, &ebmlLoc, TIME_CODE_SCALE, 0); + { + EbmlLoc trackLoc; + Ebml_StartSubElement(&ebml, &trackLoc, Tracks); + { + char cid_string[8]; + // Video + if (mWidth > 0 && mHeight > 0) { + strcpy(cid_string, "V_VP8"); + writeVideoTrack(&ebml, 0x1, 0, cid_string, + mWidth, mHeight, mFrameRate); + } + // Audio + if (mCodecPrivateData.Length() > 0) { + strcpy(cid_string, "A_VORBIS"); + writeAudioTrack(&ebml, 0x2, 0x0, cid_string, mSampleFreq, + mChannels, mCodecPrivateData.Elements(), + mCodecPrivateData.Length()); + } + } + Ebml_EndSubElement(&ebml, &trackLoc); + } + } + // The Recording length is unknow and ignore write the whole Segment element size + } + MOZ_ASSERT_IF(ebml.offset > DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(), + "write more data > EBML_BUFFER_SIZE"); + mClusterBuffs.AppendElement(); + mClusterBuffs.LastElement().SetLength(ebml.offset); + memcpy(mClusterBuffs.LastElement().Elements(), ebml.buf, ebml.offset); +} + +void EbmlComposer::FinishCluster() +{ + MOZ_ASSERT(mClusterLengthLoc > 0 ); + MOZ_ASSERT(mClusterHeaderIndex > 0); + for (uint32_t i = 0; i < mClusterBuffs.Length(); i ++ ) { + mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]); + } + mClusterBuffs.Clear(); + EbmlGlobal ebml; + EbmlLoc ebmlLoc; + ebmlLoc.offset = mClusterLengthLoc; + ebml.offset = mClusterCanFlushBuffs[mClusterHeaderIndex].Length(); + ebml.buf = mClusterCanFlushBuffs[mClusterHeaderIndex].Elements(); + Ebml_EndSubElement(&ebml, &ebmlLoc); + mClusterHeaderIndex = 0; + mClusterLengthLoc = 0; +} + +void +EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame) +{ + EbmlGlobal ebml; + ebml.offset = 0; + + if (aFrame->GetFrameType() == EncodedFrame::FrameType::I_FRAME && mClusterHeaderIndex > 0) { + FinishCluster(); + } + + mClusterBuffs.AppendElement(); + mClusterBuffs.LastElement().SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE); + ebml.buf = mClusterBuffs.LastElement().Elements(); + + if (aFrame->GetFrameType() == EncodedFrame::FrameType::I_FRAME) { + EbmlLoc ebmlLoc; + Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster); + mClusterHeaderIndex = mClusterBuffs.Length() - 1; // current cluster header array index + mClusterLengthLoc = ebmlLoc.offset; + if (aFrame->GetFrameType() != EncodedFrame::FrameType::AUDIO_FRAME) { + mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC; + } + Ebml_SerializeUnsigned(&ebml, Timecode, mClusterTimecode); + } + + if (aFrame->GetFrameType() != EncodedFrame::FrameType::AUDIO_FRAME) { + short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC - mClusterTimecode; + writeSimpleBlock(&ebml, 0x1, timeCode, aFrame->GetFrameType() == + EncodedFrame::FrameType::I_FRAME, + 0, 0, (unsigned char*)aFrame->GetFrameData().Elements(), + aFrame->GetFrameData().Length()); + } else { + writeSimpleBlock(&ebml, 0x2, 0, false, + 0, 0, (unsigned char*)aFrame->GetFrameData().Elements(), + aFrame->GetFrameData().Length()); + } + MOZ_ASSERT_IF(ebml.offset > DEFAULT_HEADER_SIZE + aFrame->GetFrameData().Length(), + "write more data > EBML_BUFFER_SIZE"); + mClusterBuffs.LastElement().SetLength(ebml.offset); +} + +void +EbmlComposer::SetVideoConfig(uint32_t aWidth, uint32_t aHeight, + float aFrameRate) +{ + MOZ_ASSERT(aWidth > 0, "Width should > 0"); + MOZ_ASSERT(aHeight > 0, "Height should > 0"); + MOZ_ASSERT(aFrameRate > 0, "FrameRate should > 0"); + mWidth = aWidth; + mHeight = aHeight; + mFrameRate = aFrameRate; +} + +void +EbmlComposer::SetAudioConfig(uint32_t aSampleFreq, uint32_t aChannels, + uint32_t aBitDepth) +{ + MOZ_ASSERT(aSampleFreq > 0, "SampleFreq should > 0"); + MOZ_ASSERT(aBitDepth > 0, "BitDepth should > 0"); + MOZ_ASSERT(aChannels > 0, "Channels should > 0"); + mSampleFreq = aSampleFreq; + mBitDepth = aBitDepth; + mChannels = aChannels; +} + +void +EbmlComposer::ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs, + uint32_t aFlag) +{ + if ((aFlag & ContainerWriter::FLUSH_NEEDED) && mClusterHeaderIndex > 0) { + FinishCluster(); + } + // aDestBufs may have some element + for (uint32_t i = 0; i < mClusterCanFlushBuffs.Length(); i ++ ) { + aDestBufs->AppendElement()->SwapElements(mClusterCanFlushBuffs[i]); + } + mClusterCanFlushBuffs.Clear(); +} + +EbmlComposer::EbmlComposer() + : mClusterHeaderIndex(0) + , mClusterLengthLoc(0) + , mClusterTimecode(0) + , mWidth(0) + , mHeight(0) + , mFrameRate(0) + , mSampleFreq(0) + , mBitDepth(0) + , mChannels(0) +{} + +}
new file mode 100644 --- /dev/null +++ b/content/media/webm/EbmlComposer.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef EbmlComposer_h_ +#define EbmlComposer_h_ +#include "nsTArray.h" +#include "ContainerWriter.h" + +namespace mozilla { + +/* + * A WebM muxer helper for package the valid WebM format. + */ +class EbmlComposer { +public: + EbmlComposer(); + /* + * Assign the parameter which header required. + */ + void SetVideoConfig(uint32_t aWidth, uint32_t aHeight, float aFrameRate); + + void SetAudioConfig(uint32_t aSampleFreq, uint32_t aChannels, + uint32_t bitDepth); + /* + * Set the CodecPrivateData for writing in header. + */ + void SetAudioCodecPrivateData(nsTArray<uint8_t>& aBufs) + { + mCodecPrivateData.AppendElements(aBufs); + } + /* + * Generate the whole WebM header and output to mBuff. + */ + void GenerateHeader(); + /* + * Insert media encoded buffer into muxer and it would be package + * into SimpleBlock. If no cluster is opened, new cluster will start for writing. + */ + void WriteSimpleBlock(EncodedFrame* aFrame); + /* + * Get valid cluster data. + */ + void ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs, + uint32_t aFlag = 0); +private: + // Close current cluster and move data to mClusterCanFlushBuffs. + void FinishCluster(); + // The temporary storage for cluster data. + nsTArray<nsTArray<uint8_t> > mClusterBuffs; + // The storage which contain valid cluster data. + nsTArray<nsTArray<uint8_t> > mClusterCanFlushBuffs; + // Indicate the header index in mClusterBuffs. + uint32_t mClusterHeaderIndex; + // The cluster length position. + uint64_t mClusterLengthLoc; + // Audio codec specific header data. + nsTArray<uint8_t> mCodecPrivateData; + + // The timecode of the cluster. + uint64_t mClusterTimecode; + + // Video configuration + int mWidth; + int mHeight; + float mFrameRate; + // Audio configuration + float mSampleFreq; + int mBitDepth; + int mChannels; +}; + +} +#endif
new file mode 100644 --- /dev/null +++ b/content/media/webm/WebMWriter.cpp @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WebMWriter.h" +#include "EbmlComposer.h" + +namespace mozilla { + +WebMWriter::WebMWriter(uint32_t aTrackTypes) : ContainerWriter() +{ + mMetadataRequiredFlag = aTrackTypes; + mEbmlComposer = new EbmlComposer(); +} + +nsresult +WebMWriter::WriteEncodedTrack(const EncodedFrameContainer& aData, + uint32_t aFlags) +{ + for (uint32_t i = 0 ; i < aData.GetEncodedFrames().Length(); i++) { + mEbmlComposer->WriteSimpleBlock(aData.GetEncodedFrames().ElementAt(i).get()); + } + return NS_OK; +} + +nsresult +WebMWriter::GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs, + uint32_t aFlags) +{ + mEbmlComposer->ExtractBuffer(aOutputBufs, aFlags); + if (aFlags & ContainerWriter::FLUSH_NEEDED) { + mIsWritingComplete = true; + } + return NS_OK; +} + +nsresult +WebMWriter::SetMetadata(TrackMetadataBase* aMetadata) +{ + MOZ_ASSERT(aMetadata); + if (aMetadata->GetKind() == TrackMetadataBase::METADATA_VP8) { + VP8Metadata* meta = static_cast<VP8Metadata*>(aMetadata); + MOZ_ASSERT(meta, "Cannot find vp8 encoder metadata"); + mEbmlComposer->SetVideoConfig(meta->mWidth, meta->mHeight, + meta->mEncodedFrameRate); + mMetadataRequiredFlag = mMetadataRequiredFlag & ~ContainerWriter::HAS_VIDEO; + } + + if (aMetadata->GetKind() == TrackMetadataBase::METADATA_VORBIS) { + VorbisMetadata* meta = static_cast<VorbisMetadata*>(aMetadata); + MOZ_ASSERT(meta, "Cannot find vorbis encoder metadata"); + mEbmlComposer->SetAudioConfig(meta->mSamplingFrequency, meta->mChannels, meta->mBitDepth); + mEbmlComposer->SetAudioCodecPrivateData(meta->mData); + mMetadataRequiredFlag = mMetadataRequiredFlag & ~ContainerWriter::HAS_AUDIO; + } + + if (!mMetadataRequiredFlag) { + mEbmlComposer->GenerateHeader(); + } + return NS_OK; +} + +} // mozilla namespace
new file mode 100644 --- /dev/null +++ b/content/media/webm/WebMWriter.h @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WebMWriter_h_ +#define WebMWriter_h_ + +#include "ContainerWriter.h" + +namespace mozilla { + +class EbmlComposer; + +// Vorbis meta data structure +class VorbisMetadata : public TrackMetadataBase +{ +public: + nsTArray<uint8_t> mData; + int32_t mChannels; + int32_t mBitDepth; + float mSamplingFrequency; + MetadataKind GetKind() const MOZ_OVERRIDE { return METADATA_VORBIS; } +}; + +// VP8 meta data structure +class VP8Metadata : public TrackMetadataBase +{ +public: + int32_t mWidth; + int32_t mHeight; + int32_t mEncodedFrameRate; + MetadataKind GetKind() const MOZ_OVERRIDE { return METADATA_VP8; } +}; + +/** + * WebM writer helper + * This class accepts encoder to set audio or video meta data or + * encoded data to ebml Composer, and get muxing data through GetContainerData. + * The ctor/dtor run in the MediaRecorder thread, others run in MediaEncoder thread. + */ +class WebMWriter : public ContainerWriter +{ +public: + // aTrackTypes indicate this muxer should multiplex into Video only or A/V foramt. + // Run in MediaRecorder thread + WebMWriter(uint32_t aTrackTypes); + // WriteEncodedTrack inserts raw packets into WebM stream. + nsresult WriteEncodedTrack(const EncodedFrameContainer &aData, + uint32_t aFlags = 0) MOZ_OVERRIDE; + + // GetContainerData outputs multiplexing data. + // aFlags indicates the muxer should enter into finished stage and flush out + // queue data. + nsresult GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs, + uint32_t aFlags = 0) MOZ_OVERRIDE; + + // Assign metadata into muxer + nsresult SetMetadata(TrackMetadataBase* aMetadata) MOZ_OVERRIDE; + +private: + nsAutoPtr<EbmlComposer> mEbmlComposer; + + // Indicate what kind of meta data needed in the writer. + // If this value become 0, it means writer can start to generate header. + uint8_t mMetadataRequiredFlag; +}; + +} +#endif
--- a/content/media/webm/moz.build +++ b/content/media/webm/moz.build @@ -10,11 +10,17 @@ EXPORTS += [ ] UNIFIED_SOURCES += [ 'WebMBufferedParser.cpp', 'WebMDecoder.cpp', 'WebMReader.cpp', ] +if CONFIG['MOZ_WEBM_ENCODER']: + EXPORTS += ['WebMWriter.h'] + UNIFIED_SOURCES += ['EbmlComposer.cpp', + 'WebMWriter.cpp', + ] + FAIL_ON_WARNINGS = True FINAL_LIBRARY = 'gklayout'
--- a/layout/media/symbols.def.in +++ b/layout/media/symbols.def.in @@ -24,16 +24,27 @@ nestegg_track_count nestegg_get_cue_point nestegg_track_seek nestegg_track_type nestegg_track_video_params nestegg_tstamp_scale nestegg_has_cues nestegg_sniff #endif +#ifdef MOZ_WEBM_ENCODER +writeSimpleBlock +writeHeader +writeSegmentInformation +writeVideoTrack +writeAudioTrack +Ebml_Serialize +Ebml_SerializeUnsigned +Ebml_StartSubElement +Ebml_EndSubElement +#endif #ifdef MOZ_VPX #ifndef MOZ_NATIVE_LIBVPX vpx_codec_control_ vpx_codec_dec_init_ver vpx_codec_decode vpx_codec_destroy vpx_codec_get_frame vpx_codec_peek_stream_info
new file mode 100644 --- /dev/null +++ b/media/libmkv/AUTHORS @@ -0,0 +1,100 @@ +# This file is automatically generated from the git commit history +# by tools/gen_authors.sh. + +Aaron Watry <awatry@gmail.com> +Abo Talib Mahfoodh <ab.mahfoodh@gmail.com> +Adrian Grange <agrange@google.com> +Ahmad Sharif <asharif@google.com> +Alexander Voronov <avoronov@graphics.cs.msu.ru> +Alex Converse <alex.converse@gmail.com> +Alexis Ballier <aballier@gentoo.org> +Alok Ahuja <waveletcoeff@gmail.com> +Alpha Lam <hclam@google.com> +A.Mahfoodh <ab.mahfoodh@gmail.com> +Ami Fischman <fischman@chromium.org> +Andoni Morales Alastruey <ylatuya@gmail.com> +Andres Mejia <mcitadel@gmail.com> +Aron Rosenberg <arosenberg@logitech.com> +Attila Nagy <attilanagy@google.com> +changjun.yang <changjun.yang@intel.com> +chm <chm@rock-chips.com> +Christian Duvivier <cduvivier@google.com> +Daniel Kang <ddkang@google.com> +Deb Mukherjee <debargha@google.com> +Dmitry Kovalev <dkovalev@google.com> +Dragan Mrdjan <dmrdjan@mips.com> +Erik Niemeyer <erik.a.niemeyer@gmail.com> +Fabio Pedretti <fabio.ped@libero.it> +Frank Galligan <fgalligan@google.com> +Fredrik Söderquist <fs@opera.com> +Fritz Koenig <frkoenig@google.com> +Gaute Strokkenes <gaute.strokkenes@broadcom.com> +Giuseppe Scrivano <gscrivano@gnu.org> +Guillaume Martres <gmartres@google.com> +Guillermo Ballester Valor <gbvalor@gmail.com> +Hangyu Kuang <hkuang@google.com> +Henrik Lundin <hlundin@google.com> +Hui Su <huisu@google.com> +Ivan Maltz <ivanmaltz@google.com> +James Berry <jamesberry@google.com> +James Zern <jzern@google.com> +Jan Kratochvil <jan.kratochvil@redhat.com> +Janne Salonen <jsalonen@google.com> +Jeff Faust <jfaust@google.com> +Jeff Muizelaar <jmuizelaar@mozilla.com> +Jeff Petkau <jpet@chromium.org> +Jim Bankoski <jimbankoski@google.com> +Jingning Han <jingning@google.com> +Johann Koenig <johannkoenig@google.com> +John Koleszar <jkoleszar@google.com> +Joshua Bleecher Snyder <josh@treelinelabs.com> +Joshua Litt <joshualitt@google.com> +Justin Clift <justin@salasaga.org> +Justin Lebar <justin.lebar@gmail.com> +KO Myung-Hun <komh@chollian.net> +Lou Quillio <louquillio@google.com> +Luca Barbato <lu_zero@gentoo.org> +Makoto Kato <makoto.kt@gmail.com> +Mans Rullgard <mans@mansr.com> +Marco Paniconi <marpan@google.com> +Mark Mentovai <mark@chromium.org> +Martin Ettl <ettl.martin78@googlemail.com> +Martin Storsjo <martin@martin.st> +Matthew Heaney <matthewjheaney@chromium.org> +Michael Kohler <michaelkohler@live.com> +Mike Frysinger <vapier@chromium.org> +Mike Hommey <mhommey@mozilla.com> +Mikhal Shemer <mikhal@google.com> +Morton Jonuschat <yabawock@gmail.com> +Parag Salasakar <img.mips1@gmail.com> +Pascal Massimino <pascal.massimino@gmail.com> +Patrik Westin <patrik.westin@gmail.com> +Paul Wilkins <paulwilkins@google.com> +Pavol Rusnak <stick@gk2.sk> +Paweł Hajdan <phajdan@google.com> +Philip Jägenstedt <philipj@opera.com> +Priit Laes <plaes@plaes.org> +Rafael Ávila de Espíndola <rafael.espindola@gmail.com> +Rafaël Carré <funman@videolan.org> +Ralph Giles <giles@xiph.org> +Rob Bradford <rob@linux.intel.com> +Ronald S. Bultje <rbultje@google.com> +Sami Pietilä <samipietila@google.com> +Scott Graham <scottmg@chromium.org> +Scott LaVarnway <slavarnway@google.com> +Shimon Doodkin <helpmepro1@gmail.com> +Stefan Holmer <holmer@google.com> +Suman Sunkara <sunkaras@google.com> +Taekhyun Kim <takim@nvidia.com> +Takanori MATSUURA <t.matsuu@gmail.com> +Tamar Levy <tamar.levy@intel.com> +Tero Rintaluoma <teror@google.com> +Thijs Vermeir <thijsvermeir@gmail.com> +Timothy B. Terriberry <tterribe@xiph.org> +Tom Finegan <tomfinegan@google.com> +Vignesh Venkatasubramanian <vigneshv@google.com> +Yaowu Xu <yaowu@google.com> +Yunqing Wang <yunqingwang@google.com> +Google Inc. +The Mozilla Foundation +The Xiph.Org Foundation
new file mode 100644 --- /dev/null +++ b/media/libmkv/EbmlBufferWriter.c @@ -0,0 +1,79 @@ +// #include <strmif.h> +#include "EbmlBufferWriter.h" +#include "EbmlWriter.h" +// #include <cassert> +// #include <limits> +// #include <malloc.h> //_alloca +#include <stdlib.h> +#include <wchar.h> +#include <string.h> + +void +Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, int buffer_size, unsigned long len) +{ + /* buffer_size: + * 1 - int8_t; + * 2 - int16_t; + * 3 - int32_t; + * 4 - int64_t; + */ + long i; + for(i = len-1; i >= 0; i--) { + unsigned char x; + if (buffer_size == 1) { + x = (char)(*(const int8_t *)buffer_in >> (i * 8)); + } else if (buffer_size == 2) { + x = (char)(*(const int16_t *)buffer_in >> (i * 8)); + } else if (buffer_size == 4) { + x = (char)(*(const int32_t *)buffer_in >> (i * 8)); + } else if (buffer_size == 8) { + x = (char)(*(const int64_t *)buffer_in >> (i * 8)); + } + Ebml_Write(glob, &x, 1); + } +} + +void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len) { + unsigned char *src = glob->buf; + src += glob->offset; + memcpy(src, buffer_in, len); + glob->offset += len; +} + +static void _Serialize(EbmlGlobal *glob, const unsigned char *p, const unsigned char *q) { + while (q != p) { + --q; + + memcpy(&(glob->buf[glob->offset]), q, 1); + glob->offset++; + } +} + +/* +void Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, unsigned long len) { + // assert(buf); + + const unsigned char *const p = (const unsigned char *)(buffer_in); + const unsigned char *const q = p + len; + + _Serialize(glob, p, q); +} +*/ + +void Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, unsigned long class_id) { + unsigned long long unknownLen = 0x01FFFFFFFFFFFFFFLL; + Ebml_WriteID(glob, class_id); + ebmlLoc->offset = glob->offset; + // todo this is always taking 8 bytes, this may need later optimization + Ebml_Serialize(glob, (void *)&unknownLen,sizeof(unknownLen), 8); // this is a key that says lenght unknown +} + +void Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc) { + unsigned long long size = glob->offset - ebmlLoc->offset - 8; + unsigned long long curOffset = glob->offset; + glob->offset = ebmlLoc->offset; + size |= 0x0100000000000000LL; + Ebml_Serialize(glob, &size,sizeof(size), 8); + glob->offset = curOffset; +} +
new file mode 100644 --- /dev/null +++ b/media/libmkv/EbmlBufferWriter.h @@ -0,0 +1,20 @@ +#ifndef EBMLBUFFERWRITER_HPP +#define EBMLBUFFERWRITER_HPP + +typedef struct { + unsigned long long offset; +} EbmlLoc; + +typedef struct { + unsigned char *buf; + unsigned int length; + unsigned int offset; +} EbmlGlobal; + +void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len); +void Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, + int buffer_size, unsigned long len); +void Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, unsigned long class_id); +void Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc); + +#endif
new file mode 100644 --- /dev/null +++ b/media/libmkv/EbmlIDs.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef MKV_DEFS_HPP +#define MKV_DEFS_HPP 1 + +/* Commenting out values not available in webm, but available in matroska */ + +enum mkv { + EBML = 0x1A45DFA3, + EBMLVersion = 0x4286, + EBMLReadVersion = 0x42F7, + EBMLMaxIDLength = 0x42F2, + EBMLMaxSizeLength = 0x42F3, + DocType = 0x4282, + DocTypeVersion = 0x4287, + DocTypeReadVersion = 0x4285, +/* CRC_32 = 0xBF, */ + Void = 0xEC, + SignatureSlot = 0x1B538667, + SignatureAlgo = 0x7E8A, + SignatureHash = 0x7E9A, + SignaturePublicKey = 0x7EA5, + Signature = 0x7EB5, + SignatureElements = 0x7E5B, + SignatureElementList = 0x7E7B, + SignedElement = 0x6532, + /* segment */ + Segment = 0x18538067, + /* Meta Seek Information */ + SeekHead = 0x114D9B74, + Seek = 0x4DBB, + SeekID = 0x53AB, + SeekPosition = 0x53AC, + /* Segment Information */ + Info = 0x1549A966, +/* SegmentUID = 0x73A4, */ +/* SegmentFilename = 0x7384, */ +/* PrevUID = 0x3CB923, */ +/* PrevFilename = 0x3C83AB, */ +/* NextUID = 0x3EB923, */ +/* NextFilename = 0x3E83BB, */ +/* SegmentFamily = 0x4444, */ +/* ChapterTranslate = 0x6924, */ +/* ChapterTranslateEditionUID = 0x69FC, */ +/* ChapterTranslateCodec = 0x69BF, */ +/* ChapterTranslateID = 0x69A5, */ + TimecodeScale = 0x2AD7B1, + Segment_Duration = 0x4489, + DateUTC = 0x4461, +/* Title = 0x7BA9, */ + MuxingApp = 0x4D80, + WritingApp = 0x5741, + /* Cluster */ + Cluster = 0x1F43B675, + Timecode = 0xE7, +/* SilentTracks = 0x5854, */ +/* SilentTrackNumber = 0x58D7, */ +/* Position = 0xA7, */ + PrevSize = 0xAB, + BlockGroup = 0xA0, + Block = 0xA1, +/* BlockVirtual = 0xA2, */ + BlockAdditions = 0x75A1, + BlockMore = 0xA6, + BlockAddID = 0xEE, + BlockAdditional = 0xA5, + BlockDuration = 0x9B, +/* ReferencePriority = 0xFA, */ + ReferenceBlock = 0xFB, +/* ReferenceVirtual = 0xFD, */ +/* CodecState = 0xA4, */ +/* Slices = 0x8E, */ +/* TimeSlice = 0xE8, */ + LaceNumber = 0xCC, +/* FrameNumber = 0xCD, */ +/* BlockAdditionID = 0xCB, */ +/* MkvDelay = 0xCE, */ +/* Cluster_Duration = 0xCF, */ + SimpleBlock = 0xA3, +/* EncryptedBlock = 0xAF, */ + /* Track */ + Tracks = 0x1654AE6B, + TrackEntry = 0xAE, + TrackNumber = 0xD7, + TrackUID = 0x73C5, + TrackType = 0x83, + FlagEnabled = 0xB9, + FlagDefault = 0x88, + FlagForced = 0x55AA, + FlagLacing = 0x9C, +/* MinCache = 0x6DE7, */ +/* MaxCache = 0x6DF8, */ + DefaultDuration = 0x23E383, +/* TrackTimecodeScale = 0x23314F, */ +/* TrackOffset = 0x537F, */ + MaxBlockAdditionID = 0x55EE, + Name = 0x536E, + Language = 0x22B59C, + CodecID = 0x86, + CodecPrivate = 0x63A2, + CodecName = 0x258688, +/* AttachmentLink = 0x7446, */ +/* CodecSettings = 0x3A9697, */ +/* CodecInfoURL = 0x3B4040, */ +/* CodecDownloadURL = 0x26B240, */ +/* CodecDecodeAll = 0xAA, */ +/* TrackOverlay = 0x6FAB, */ +/* TrackTranslate = 0x6624, */ +/* TrackTranslateEditionUID = 0x66FC, */ +/* TrackTranslateCodec = 0x66BF, */ +/* TrackTranslateTrackID = 0x66A5, */ + /* video */ + Video = 0xE0, + FlagInterlaced = 0x9A, + WEBM_StereoMode = 0x53B8, + AlphaMode = 0x53C0, + PixelWidth = 0xB0, + PixelHeight = 0xBA, + PixelCropBottom = 0x54AA, + PixelCropTop = 0x54BB, + PixelCropLeft = 0x54CC, + PixelCropRight = 0x54DD, + DisplayWidth = 0x54B0, + DisplayHeight = 0x54BA, + DisplayUnit = 0x54B2, + AspectRatioType = 0x54B3, +/* ColourSpace = 0x2EB524, */ +/* GammaValue = 0x2FB523, */ + FrameRate = 0x2383E3, + /* end video */ + /* audio */ + Audio = 0xE1, + SamplingFrequency = 0xB5, + OutputSamplingFrequency = 0x78B5, + Channels = 0x9F, +/* ChannelPositions = 0x7D7B, */ + BitDepth = 0x6264, + /* end audio */ + /* content encoding */ +/* ContentEncodings = 0x6d80, */ +/* ContentEncoding = 0x6240, */ +/* ContentEncodingOrder = 0x5031, */ +/* ContentEncodingScope = 0x5032, */ +/* ContentEncodingType = 0x5033, */ +/* ContentCompression = 0x5034, */ +/* ContentCompAlgo = 0x4254, */ +/* ContentCompSettings = 0x4255, */ +/* ContentEncryption = 0x5035, */ +/* ContentEncAlgo = 0x47e1, */ +/* ContentEncKeyID = 0x47e2, */ +/* ContentSignature = 0x47e3, */ +/* ContentSigKeyID = 0x47e4, */ +/* ContentSigAlgo = 0x47e5, */ +/* ContentSigHashAlgo = 0x47e6, */ + /* end content encoding */ + /* Cueing Data */ + Cues = 0x1C53BB6B, + CuePoint = 0xBB, + CueTime = 0xB3, + CueTrackPositions = 0xB7, + CueTrack = 0xF7, + CueClusterPosition = 0xF1, + CueBlockNumber = 0x5378 +/* CueCodecState = 0xEA, */ +/* CueReference = 0xDB, */ +/* CueRefTime = 0x96, */ +/* CueRefCluster = 0x97, */ +/* CueRefNumber = 0x535F, */ +/* CueRefCodecState = 0xEB, */ + /* Attachment */ +/* Attachments = 0x1941A469, */ +/* AttachedFile = 0x61A7, */ +/* FileDescription = 0x467E, */ +/* FileName = 0x466E, */ +/* FileMimeType = 0x4660, */ +/* FileData = 0x465C, */ +/* FileUID = 0x46AE, */ +/* FileReferral = 0x4675, */ + /* Chapters */ +/* Chapters = 0x1043A770, */ +/* EditionEntry = 0x45B9, */ +/* EditionUID = 0x45BC, */ +/* EditionFlagHidden = 0x45BD, */ +/* EditionFlagDefault = 0x45DB, */ +/* EditionFlagOrdered = 0x45DD, */ +/* ChapterAtom = 0xB6, */ +/* ChapterUID = 0x73C4, */ +/* ChapterTimeStart = 0x91, */ +/* ChapterTimeEnd = 0x92, */ +/* ChapterFlagHidden = 0x98, */ +/* ChapterFlagEnabled = 0x4598, */ +/* ChapterSegmentUID = 0x6E67, */ +/* ChapterSegmentEditionUID = 0x6EBC, */ +/* ChapterPhysicalEquiv = 0x63C3, */ +/* ChapterTrack = 0x8F, */ +/* ChapterTrackNumber = 0x89, */ +/* ChapterDisplay = 0x80, */ +/* ChapString = 0x85, */ +/* ChapLanguage = 0x437C, */ +/* ChapCountry = 0x437E, */ +/* ChapProcess = 0x6944, */ +/* ChapProcessCodecID = 0x6955, */ +/* ChapProcessPrivate = 0x450D, */ +/* ChapProcessCommand = 0x6911, */ +/* ChapProcessTime = 0x6922, */ +/* ChapProcessData = 0x6933, */ + /* Tagging */ +/* Tags = 0x1254C367, */ +/* Tag = 0x7373, */ +/* Targets = 0x63C0, */ +/* TargetTypeValue = 0x68CA, */ +/* TargetType = 0x63CA, */ +/* Tagging_TrackUID = 0x63C5, */ +/* Tagging_EditionUID = 0x63C9, */ +/* Tagging_ChapterUID = 0x63C4, */ +/* AttachmentUID = 0x63C6, */ +/* SimpleTag = 0x67C8, */ +/* TagName = 0x45A3, */ +/* TagLanguage = 0x447A, */ +/* TagDefault = 0x4484, */ +/* TagString = 0x4487, */ +/* TagBinary = 0x4485, */ +}; +#endif
new file mode 100644 --- /dev/null +++ b/media/libmkv/EbmlWriter.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "EbmlWriter.h" +#include <stdlib.h> +#include <wchar.h> +#include <string.h> +#include <limits.h> +#include "EbmlBufferWriter.h" +#if defined(_MSC_VER) +#define LITERALU64(n) n +#else +#define LITERALU64(n) n##LLU +#endif + +void Ebml_WriteLen(EbmlGlobal *glob, int64_t val) { + /* TODO check and make sure we are not > than 0x0100000000000000LLU */ + unsigned char size = 8; /* size in bytes to output */ + + /* mask to compare for byte size */ + int64_t minVal = 0xff; + + for (size = 1; size < 8; size ++) { + if (val < minVal) + break; + + minVal = (minVal << 7); + } + + val |= (((uint64_t)0x80) << ((size - 1) * 7)); + + Ebml_Serialize(glob, (void *) &val, sizeof(val), size); +} + +void Ebml_WriteString(EbmlGlobal *glob, const char *str) { + const size_t size_ = strlen(str); + const uint64_t size = size_; + Ebml_WriteLen(glob, size); + /* TODO: it's not clear from the spec whether the nul terminator + * should be serialized too. For now we omit the null terminator. + */ + Ebml_Write(glob, str, (unsigned long)size); +} + +void Ebml_WriteUTF8(EbmlGlobal *glob, const wchar_t *wstr) { + const size_t strlen = wcslen(wstr); + + /* TODO: it's not clear from the spec whether the nul terminator + * should be serialized too. For now we include it. + */ + const uint64_t size = strlen; + + Ebml_WriteLen(glob, size); + Ebml_Write(glob, wstr, (unsigned long)size); +} + +void Ebml_WriteID(EbmlGlobal *glob, unsigned long class_id) { + int len; + + if (class_id >= 0x01000000) + len = 4; + else if (class_id >= 0x00010000) + len = 3; + else if (class_id >= 0x00000100) + len = 2; + else + len = 1; + + Ebml_Serialize(glob, (void *)&class_id, sizeof(class_id), len); +} + +void Ebml_SerializeUnsigned32(EbmlGlobal *glob, unsigned long class_id, uint32_t ui) { + unsigned char sizeSerialized = 8 | 0x80; + Ebml_WriteID(glob, class_id); + Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1); + Ebml_Serialize(glob, &ui, sizeof(ui), 4); +} + +void Ebml_SerializeUnsigned64(EbmlGlobal *glob, unsigned long class_id, uint64_t ui) { + unsigned char sizeSerialized = 8 | 0x80; + Ebml_WriteID(glob, class_id); + Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1); + Ebml_Serialize(glob, &ui, sizeof(ui), 8); +} + +void Ebml_SerializeUnsigned(EbmlGlobal *glob, unsigned long class_id, unsigned long ui) { + unsigned char size = 8; /* size in bytes to output */ + unsigned char sizeSerialized = 0; + unsigned long minVal; + + Ebml_WriteID(glob, class_id); + minVal = 0x7fLU; /* mask to compare for byte size */ + + for (size = 1; size < 4; size ++) { + if (ui < minVal) { + break; + } + + minVal <<= 7; + } + + sizeSerialized = 0x80 | size; + Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1); + Ebml_Serialize(glob, &ui, sizeof(ui), size); +} +/* TODO: perhaps this is a poor name for this id serializer helper function */ +void Ebml_SerializeBinary(EbmlGlobal *glob, unsigned long class_id, unsigned long bin) { + int size; + for (size = 4; size > 1; size--) { + if (bin & (unsigned int)0x000000ff << ((size - 1) * 8)) + break; + } + Ebml_WriteID(glob, class_id); + Ebml_WriteLen(glob, size); + Ebml_WriteID(glob, bin); +} + +void Ebml_SerializeFloat(EbmlGlobal *glob, unsigned long class_id, double d) { + unsigned char len = 0x88; + + Ebml_WriteID(glob, class_id); + Ebml_Serialize(glob, &len, sizeof(len), 1); + Ebml_Serialize(glob, &d, sizeof(d), 8); +} + +void Ebml_WriteSigned16(EbmlGlobal *glob, short val) { + signed long out = ((val & 0x003FFFFF) | 0x00200000) << 8; + Ebml_Serialize(glob, &out, sizeof(out), 3); +} + +void Ebml_SerializeString(EbmlGlobal *glob, unsigned long class_id, const char *s) { + Ebml_WriteID(glob, class_id); + Ebml_WriteString(glob, s); +} + +void Ebml_SerializeUTF8(EbmlGlobal *glob, unsigned long class_id, wchar_t *s) { + Ebml_WriteID(glob, class_id); + Ebml_WriteUTF8(glob, s); +} + +void Ebml_SerializeData(EbmlGlobal *glob, unsigned long class_id, unsigned char *data, unsigned long data_length) { + Ebml_WriteID(glob, class_id); + Ebml_WriteLen(glob, data_length); + Ebml_Write(glob, data, data_length); +} + +void Ebml_WriteVoid(EbmlGlobal *glob, unsigned long vSize) { + unsigned char tmp = 0; + unsigned long i = 0; + + Ebml_WriteID(glob, 0xEC); + Ebml_WriteLen(glob, vSize); + + for (i = 0; i < vSize; i++) { + Ebml_Write(glob, &tmp, 1); + } +} + +/* TODO Serialize Date */
new file mode 100644 --- /dev/null +++ b/media/libmkv/EbmlWriter.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef EBMLWRITER_HPP +#define EBMLWRITER_HPP +#include <stddef.h> +#include "vpx/vpx_integer.h" +#include "EbmlBufferWriter.h" + +/* note: you must define write and serialize functions as well as your own + * EBML_GLOBAL + * + * These functions MUST be implemented + */ + +// typedef struct EbmlGlobal EbmlGlobal; +// void Ebml_Serialize(EbmlGlobal *glob, const void *, int, unsigned long); +// void Ebml_Write(EbmlGlobal *glob, const void *, unsigned long); + +/*****/ + +void Ebml_WriteLen(EbmlGlobal *glob, int64_t val); +void Ebml_WriteString(EbmlGlobal *glob, const char *str); +void Ebml_WriteUTF8(EbmlGlobal *glob, const wchar_t *wstr); +void Ebml_WriteID(EbmlGlobal *glob, unsigned long class_id); +void Ebml_SerializeUnsigned32(EbmlGlobal *glob, unsigned long class_id, uint32_t ui); +void Ebml_SerializeUnsigned64(EbmlGlobal *glob, unsigned long class_id, uint64_t ui); +void Ebml_SerializeUnsigned(EbmlGlobal *glob, unsigned long class_id, unsigned long ui); +void Ebml_SerializeBinary(EbmlGlobal *glob, unsigned long class_id, unsigned long ui); +void Ebml_SerializeFloat(EbmlGlobal *glob, unsigned long class_id, double d); +/* TODO make this more generic to signed */ +void Ebml_WriteSigned16(EbmlGlobal *glob, short val); +void Ebml_SerializeString(EbmlGlobal *glob, unsigned long class_id, const char *s); +void Ebml_SerializeUTF8(EbmlGlobal *glob, unsigned long class_id, wchar_t *s); +void Ebml_SerializeData(EbmlGlobal *glob, unsigned long class_id, unsigned char *data, unsigned long data_length); +void Ebml_WriteVoid(EbmlGlobal *glob, unsigned long vSize); +/* TODO need date function */ +#endif + +#ifdef __cplusplus +} +#endif
new file mode 100644 --- /dev/null +++ b/media/libmkv/LICENSE @@ -0,0 +1,31 @@ +Copyright (c) 2010, The WebM Project authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google, nor the WebM Project, nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +
new file mode 100644 --- /dev/null +++ b/media/libmkv/README @@ -0,0 +1,139 @@ +vpx Multi-Format Codec SDK +README - 1 August 2013 + +Welcome to the WebM VP8/VP9 Codec SDK! + +COMPILING THE APPLICATIONS/LIBRARIES: + The build system used is similar to autotools. Building generally consists of + "configuring" with your desired build options, then using GNU make to build + the application. + + 1. Prerequisites + + * All x86 targets require the Yasm[1] assembler be installed. + * All Windows builds require that Cygwin[2] be installed. + * Building the documentation requires PHP[3] and Doxygen[4]. If you do not + have these packages, you must pass --disable-install-docs to the + configure script. + * Downloading the data for the unit tests requires curl[5] and sha1sum. + sha1sum is provided via the GNU coreutils, installed by default on + many *nix platforms, as well as MinGW and Cygwin. If coreutils is not + available, a compatible version of sha1sum can be built from + source[6]. These requirements are optional if not running the unit + tests. + + [1]: http://www.tortall.net/projects/yasm + [2]: http://www.cygwin.com + [3]: http://php.net + [4]: http://www.doxygen.org + [5]: http://curl.haxx.se + [6]: http://www.microbrew.org/tools/md5sha1sum/ + + 2. Out-of-tree builds + Out of tree builds are a supported method of building the application. For + an out of tree build, the source tree is kept separate from the object + files produced during compilation. For instance: + + $ mkdir build + $ cd build + $ ../libvpx/configure <options> + $ make + + 3. Configuration options + The 'configure' script supports a number of options. The --help option can be + used to get a list of supported options: + $ ../libvpx/configure --help + + 4. Cross development + For cross development, the most notable option is the --target option. The + most up-to-date list of supported targets can be found at the bottom of the + --help output of the configure script. As of this writing, the list of + available targets is: + + armv5te-android-gcc + armv5te-linux-rvct + armv5te-linux-gcc + armv5te-none-rvct + armv6-darwin-gcc + armv6-linux-rvct + armv6-linux-gcc + armv6-none-rvct + armv7-android-gcc + armv7-darwin-gcc + armv7-linux-rvct + armv7-linux-gcc + armv7-none-rvct + armv7-win32-vs11 + armv7-win32-vs12 + mips32-linux-gcc + ppc32-darwin8-gcc + ppc32-darwin9-gcc + ppc32-linux-gcc + ppc64-darwin8-gcc + ppc64-darwin9-gcc + ppc64-linux-gcc + sparc-solaris-gcc + x86-android-gcc + x86-darwin8-gcc + x86-darwin8-icc + x86-darwin9-gcc + x86-darwin9-icc + x86-darwin10-gcc + x86-darwin11-gcc + x86-darwin12-gcc + x86-darwin13-gcc + x86-linux-gcc + x86-linux-icc + x86-os2-gcc + x86-solaris-gcc + x86-win32-gcc + x86-win32-vs7 + x86-win32-vs8 + x86-win32-vs9 + x86-win32-vs10 + x86-win32-vs11 + x86-win32-vs12 + x86_64-darwin9-gcc + x86_64-darwin10-gcc + x86_64-darwin11-gcc + x86_64-darwin12-gcc + x86_64-darwin13-gcc + x86_64-linux-gcc + x86_64-linux-icc + x86_64-solaris-gcc + x86_64-win64-gcc + x86_64-win64-vs8 + x86_64-win64-vs9 + x86_64-win64-vs10 + x86_64-win64-vs11 + x86_64-win64-vs12 + universal-darwin8-gcc + universal-darwin9-gcc + universal-darwin10-gcc + universal-darwin11-gcc + universal-darwin12-gcc + universal-darwin13-gcc + generic-gnu + + The generic-gnu target, in conjunction with the CROSS environment variable, + can be used to cross compile architectures that aren't explicitly listed, if + the toolchain is a cross GNU (gcc/binutils) toolchain. Other POSIX toolchains + will likely work as well. For instance, to build using the mipsel-linux-uclibc + toolchain, the following command could be used (note, POSIX SH syntax, adapt + to your shell as necessary): + + $ CROSS=mipsel-linux-uclibc- ../libvpx/configure + + In addition, the executables to be invoked can be overridden by specifying the + environment variables: CC, AR, LD, AS, STRIP, NM. Additional flags can be + passed to these executables with CFLAGS, LDFLAGS, and ASFLAGS. + + 5. Configuration errors + If the configuration step fails, the first step is to look in the error log. + This defaults to config.log. This should give a good indication of what went + wrong. If not, contact us for support. + +SUPPORT + This library is an open source project supported by its community. Please + please email webm-discuss@webmproject.org for help. +
new file mode 100644 --- /dev/null +++ b/media/libmkv/README_MOZILLA @@ -0,0 +1,6 @@ +The source from this directory was copied from the libvpx/third_party/libmkv +git repository using the update.sh script. + +The libvpx git repository is: http://git.chromium.org/webm/libvpx.git + +The git commit ID used was c5aaf923d80e9f71e0c93d7d99dc1e2f83d7acbf.
new file mode 100644 --- /dev/null +++ b/media/libmkv/WebMElement.c @@ -0,0 +1,219 @@ +// Copyright (c) 2010 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#include "EbmlIDs.h" +#include "WebMElement.h" +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <time.h> + +#define kVorbisPrivateMaxSize 4000 +#define UInt64 uint64_t + +void writeHeader(EbmlGlobal *glob) { + EbmlLoc start; + Ebml_StartSubElement(glob, &start, EBML); + Ebml_SerializeUnsigned(glob, EBMLVersion, 1); + Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1); // EBML Read Version + Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4); // EBML Max ID Length + Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8); // EBML Max Size Length + Ebml_SerializeString(glob, DocType, "webm"); // Doc Type + Ebml_SerializeUnsigned(glob, DocTypeVersion, 2); // Doc Type Version + Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2); // Doc Type Read Version + Ebml_EndSubElement(glob, &start); +} + +void writeSimpleBlock(EbmlGlobal *glob, unsigned char trackNumber, short timeCode, + int isKeyframe, unsigned char lacingFlag, int discardable, + unsigned char *data, unsigned long dataLength) { + unsigned long blockLength = 4 + dataLength; + unsigned char flags = 0x00 | (isKeyframe ? 0x80 : 0x00) | (lacingFlag << 1) | discardable; + Ebml_WriteID(glob, SimpleBlock); + blockLength |= 0x10000000; // TODO check length < 0x0FFFFFFFF + Ebml_Serialize(glob, &blockLength, sizeof(blockLength), 4); + trackNumber |= 0x80; // TODO check track nubmer < 128 + Ebml_Write(glob, &trackNumber, 1); + // Ebml_WriteSigned16(glob, timeCode,2); //this is 3 bytes + Ebml_Serialize(glob, &timeCode, sizeof(timeCode), 2); + flags = 0x00 | (isKeyframe ? 0x80 : 0x00) | (lacingFlag << 1) | discardable; + Ebml_Write(glob, &flags, 1); + Ebml_Write(glob, data, dataLength); +} + +static UInt64 generateTrackID(unsigned int trackNumber) { + UInt64 t = time(NULL) * trackNumber; + UInt64 r = rand(); + r = r << 32; + r += rand(); +// UInt64 rval = t ^ r; + return t ^ r; +} + +void writeVideoTrack(EbmlGlobal *glob, unsigned int trackNumber, int flagLacing, + char *codecId, unsigned int pixelWidth, unsigned int pixelHeight, + double frameRate) { + EbmlLoc start; + UInt64 trackID; + Ebml_StartSubElement(glob, &start, TrackEntry); + Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); + trackID = generateTrackID(trackNumber); + Ebml_SerializeUnsigned(glob, TrackUID, trackID); + Ebml_SerializeString(glob, CodecName, "VP8"); // TODO shouldn't be fixed + + Ebml_SerializeUnsigned(glob, TrackType, 1); // video is always 1 + Ebml_SerializeString(glob, CodecID, codecId); + { + EbmlLoc videoStart; + Ebml_StartSubElement(glob, &videoStart, Video); + Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth); + Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight); + Ebml_SerializeFloat(glob, FrameRate, frameRate); + Ebml_EndSubElement(glob, &videoStart); // Video + } + Ebml_EndSubElement(glob, &start); // Track Entry +} +void writeAudioTrack(EbmlGlobal *glob, unsigned int trackNumber, int flagLacing, + char *codecId, double samplingFrequency, unsigned int channels, + unsigned char *private, unsigned long privateSize) { + EbmlLoc start; + UInt64 trackID; + Ebml_StartSubElement(glob, &start, TrackEntry); + Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); + trackID = generateTrackID(trackNumber); + Ebml_SerializeUnsigned(glob, TrackUID, trackID); + Ebml_SerializeUnsigned(glob, TrackType, 2); // audio is always 2 + // I am using defaults for thesed required fields + /* Ebml_SerializeUnsigned(glob, FlagEnabled, 1); + Ebml_SerializeUnsigned(glob, FlagDefault, 1); + Ebml_SerializeUnsigned(glob, FlagForced, 1); + Ebml_SerializeUnsigned(glob, FlagLacing, flagLacing);*/ + Ebml_SerializeString(glob, CodecID, codecId); + Ebml_SerializeData(glob, CodecPrivate, private, privateSize); + + Ebml_SerializeString(glob, CodecName, "VORBIS"); // fixed for now + { + EbmlLoc AudioStart; + Ebml_StartSubElement(glob, &AudioStart, Audio); + Ebml_SerializeFloat(glob, SamplingFrequency, samplingFrequency); + Ebml_SerializeUnsigned(glob, Channels, channels); + Ebml_EndSubElement(glob, &AudioStart); + } + Ebml_EndSubElement(glob, &start); +} +void writeSegmentInformation(EbmlGlobal *ebml, EbmlLoc *startInfo, unsigned long timeCodeScale, double duration) { + Ebml_StartSubElement(ebml, startInfo, Info); + Ebml_SerializeUnsigned(ebml, TimecodeScale, timeCodeScale); + Ebml_SerializeFloat(ebml, Segment_Duration, duration * 1000.0); // Currently fixed to using milliseconds + Ebml_SerializeString(ebml, 0x4D80, "QTmuxingAppLibWebM-0.0.1"); + Ebml_SerializeString(ebml, 0x5741, "QTwritingAppLibWebM-0.0.1"); + Ebml_EndSubElement(ebml, startInfo); +} + +/* +void Mkv_InitializeSegment(Ebml& ebml_out, EbmlLoc& ebmlLoc) +{ + Ebml_StartSubElement(ebml_out, ebmlLoc, 0x18538067); +} + +void Mkv_InitializeSeek(Ebml& ebml_out, EbmlLoc& ebmlLoc) +{ + Ebml_StartSubElement(ebml_out, ebmlLoc, 0x114d9b74); +} +void Mkv_WriteSeekInformation(Ebml& ebml_out, SeekStruct& seekInformation) +{ + EbmlLoc ebmlLoc; + Ebml_StartSubElement(ebml_out, ebmlLoc, 0x4dbb); + Ebml_SerializeString(ebml_out, 0x53ab, seekInformation.SeekID); + Ebml_SerializeUnsigned(ebml_out, 0x53ac, seekInformation.SeekPosition); + Ebml_EndSubElement(ebml_out, ebmlLoc); +} + +void Mkv_WriteSegmentInformation(Ebml& ebml_out, SegmentInformationStruct& segmentInformation) +{ + Ebml_SerializeUnsigned(ebml_out, 0x73a4, segmentInformation.segmentUID); + if (segmentInformation.filename != 0) + Ebml_SerializeString(ebml_out, 0x7384, segmentInformation.filename); + Ebml_SerializeUnsigned(ebml_out, 0x2AD7B1, segmentInformation.TimecodeScale); + Ebml_SerializeUnsigned(ebml_out, 0x4489, segmentInformation.Duration); + // TODO date + Ebml_SerializeWString(ebml_out, 0x4D80, L"MKVMUX"); + Ebml_SerializeWString(ebml_out, 0x5741, segmentInformation.WritingApp); +} + +void Mkv_InitializeTrack(Ebml& ebml_out, EbmlLoc& ebmlLoc) +{ + Ebml_StartSubElement(ebml_out, ebmlLoc, 0x1654AE6B); +} + +static void Mkv_WriteGenericTrackData(Ebml& ebml_out, TrackStruct& track) +{ + Ebml_SerializeUnsigned(ebml_out, 0xD7, track.TrackNumber); + Ebml_SerializeUnsigned(ebml_out, 0x73C5, track.TrackUID); + Ebml_SerializeUnsigned(ebml_out, 0x83, track.TrackType); + Ebml_SerializeUnsigned(ebml_out, 0xB9, track.FlagEnabled ? 1 :0); + Ebml_SerializeUnsigned(ebml_out, 0x88, track.FlagDefault ? 1 :0); + Ebml_SerializeUnsigned(ebml_out, 0x55AA, track.FlagForced ? 1 :0); + if (track.Language != 0) + Ebml_SerializeString(ebml_out, 0x22B59C, track.Language); + if (track.CodecID != 0) + Ebml_SerializeString(ebml_out, 0x86, track.CodecID); + if (track.CodecPrivate != 0) + Ebml_SerializeData(ebml_out, 0x63A2, track.CodecPrivate, track.CodecPrivateLength); + if (track.CodecName != 0) + Ebml_SerializeWString(ebml_out, 0x258688, track.CodecName); +} + +void Mkv_WriteVideoTrack(Ebml& ebml_out, TrackStruct & track, VideoTrackStruct& video) +{ + EbmlLoc trackHeadLoc, videoHeadLoc; + Ebml_StartSubElement(ebml_out, trackHeadLoc, 0xAE); // start Track + Mkv_WriteGenericTrackData(ebml_out, track); + Ebml_StartSubElement(ebml_out, videoHeadLoc, 0xE0); // start Video + Ebml_SerializeUnsigned(ebml_out, 0x9A, video.FlagInterlaced ? 1 :0); + Ebml_SerializeUnsigned(ebml_out, 0xB0, video.PixelWidth); + Ebml_SerializeUnsigned(ebml_out, 0xBA, video.PixelHeight); + Ebml_SerializeUnsigned(ebml_out, 0x54B0, video.PixelDisplayWidth); + Ebml_SerializeUnsigned(ebml_out, 0x54BA, video.PixelDisplayHeight); + Ebml_SerializeUnsigned(ebml_out, 0x54B2, video.displayUnit); + Ebml_SerializeFloat(ebml_out, 0x2383E3, video.FrameRate); + Ebml_EndSubElement(ebml_out, videoHeadLoc); + Ebml_EndSubElement(ebml_out, trackHeadLoc); + +} + +void Mkv_WriteAudioTrack(Ebml& ebml_out, TrackStruct & track, AudioTrackStruct& video) +{ + EbmlLoc trackHeadLoc, audioHeadLoc; + Ebml_StartSubElement(ebml_out, trackHeadLoc, 0xAE); + Mkv_WriteGenericTrackData(ebml_out, track); + Ebml_StartSubElement(ebml_out, audioHeadLoc, 0xE0); // start Audio + Ebml_SerializeFloat(ebml_out, 0xB5, video.SamplingFrequency); + Ebml_SerializeUnsigned(ebml_out, 0x9F, video.Channels); + Ebml_SerializeUnsigned(ebml_out, 0x6264, video.BitDepth); + Ebml_EndSubElement(ebml_out, audioHeadLoc); // end audio + Ebml_EndSubElement(ebml_out, trackHeadLoc); +} + +void Mkv_WriteEbmlClusterHead(Ebml& ebml_out, EbmlLoc& ebmlLoc, ClusterHeadStruct & clusterHead) +{ + Ebml_StartSubElement(ebml_out, ebmlLoc, 0x1F43B675); + Ebml_SerializeUnsigned(ebml_out, 0x6264, clusterHead.TimeCode); +} + +void Mkv_WriteSimpleBlockHead(Ebml& ebml_out, EbmlLoc& ebmlLoc, SimpleBlockStruct& block) +{ + Ebml_StartSubElement(ebml_out, ebmlLoc, 0xA3); + Ebml_Write1UInt(ebml_out, block.TrackNumber); + Ebml_WriteSigned16(ebml_out,block.TimeCode); + unsigned char flags = 0x00 | (block.iskey ? 0x80:0x00) | (block.lacing << 1) | block.discardable; + Ebml_Write1UInt(ebml_out, flags); // TODO this may be the wrong function + Ebml_Serialize(ebml_out, block.data, block.dataLength); + Ebml_EndSubElement(ebml_out,ebmlLoc); +} +*/
new file mode 100644 --- /dev/null +++ b/media/libmkv/WebMElement.h @@ -0,0 +1,37 @@ +// Copyright (c) 2010 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MKV_CONTEXT_HPP +#define MKV_CONTEXT_HPP 1 + +#include "EbmlWriter.h" + +// these are helper functions +void writeHeader(EbmlGlobal *ebml); +void writeSegmentInformation(EbmlGlobal *ebml, EbmlLoc *startInfo, unsigned long timeCodeScale, double duration); +// this function is a helper only, it assumes a lot of defaults +void writeVideoTrack(EbmlGlobal *ebml, unsigned int trackNumber, int flagLacing, + char *codecId, unsigned int pixelWidth, unsigned int pixelHeight, + double frameRate); +void writeAudioTrack(EbmlGlobal *glob, unsigned int trackNumber, int flagLacing, + char *codecId, double samplingFrequency, unsigned int channels, + unsigned char *private_, unsigned long privateSize); + +void writeSimpleBlock(EbmlGlobal *ebml, unsigned char trackNumber, short timeCode, + int isKeyframe, unsigned char lacingFlag, int discardable, + unsigned char *data, unsigned long dataLength); + +#endif + +#ifdef __cplusplus +} +#endif
new file mode 100644 --- /dev/null +++ b/media/libmkv/gecko_fix.patch @@ -0,0 +1,133 @@ +diff --git a/EbmlBufferWriter.h b/EbmlBufferWriter.h +index c135f29..d5116ce 100644 +--- a/EbmlBufferWriter.h ++++ b/EbmlBufferWriter.h +@@ -11,6 +11,9 @@ typedef struct { + unsigned int offset; + } EbmlGlobal; + ++void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len); ++void Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, ++ int buffer_size, unsigned long len); + void Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, unsigned long class_id); + void Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc); + +diff --git a/EbmlIDs.h b/EbmlIDs.h +index 44d4385..3b5da19 100644 +--- a/EbmlIDs.h ++++ b/EbmlIDs.h +@@ -119,7 +119,7 @@ enum mkv { + /* video */ + Video = 0xE0, + FlagInterlaced = 0x9A, +- StereoMode = 0x53B8, ++ WEBM_StereoMode = 0x53B8, + AlphaMode = 0x53C0, + PixelWidth = 0xB0, + PixelHeight = 0xBA, +diff --git a/EbmlWriter.c b/EbmlWriter.c +index ebefc1a..087e817 100644 +--- a/EbmlWriter.c ++++ b/EbmlWriter.c +@@ -12,6 +12,7 @@ + #include <wchar.h> + #include <string.h> + #include <limits.h> ++#include "EbmlBufferWriter.h" + #if defined(_MSC_VER) + #define LITERALU64(n) n + #else +diff --git a/EbmlWriter.h b/EbmlWriter.h +index a0a848b..3aee2b3 100644 +--- a/EbmlWriter.h ++++ b/EbmlWriter.h +@@ -7,10 +7,16 @@ + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ + #ifndef EBMLWRITER_HPP + #define EBMLWRITER_HPP + #include <stddef.h> + #include "vpx/vpx_integer.h" ++#include "EbmlBufferWriter.h" + + /* note: you must define write and serialize functions as well as your own + * EBML_GLOBAL +@@ -18,9 +24,9 @@ + * These functions MUST be implemented + */ + +-typedef struct EbmlGlobal EbmlGlobal; +-void Ebml_Serialize(EbmlGlobal *glob, const void *, int, unsigned long); +-void Ebml_Write(EbmlGlobal *glob, const void *, unsigned long); ++// typedef struct EbmlGlobal EbmlGlobal; ++// void Ebml_Serialize(EbmlGlobal *glob, const void *, int, unsigned long); ++// void Ebml_Write(EbmlGlobal *glob, const void *, unsigned long); + + /*****/ + +@@ -41,3 +47,7 @@ void Ebml_SerializeData(EbmlGlobal *glob, unsigned long class_id, unsigned char + void Ebml_WriteVoid(EbmlGlobal *glob, unsigned long vSize); + /* TODO need date function */ + #endif ++ ++#ifdef __cplusplus ++} ++#endif +diff --git a/WebMElement.c b/WebMElement.c +index 02eefa4..0d5056d 100644 +--- a/WebMElement.c ++++ b/WebMElement.c +@@ -6,8 +6,6 @@ + // in the file PATENTS. All contributing project authors may + // be found in the AUTHORS file in the root of the source tree. + +- +-#include "EbmlBufferWriter.h" + #include "EbmlIDs.h" + #include "WebMElement.h" + #include <stdio.h> +diff --git a/WebMElement.h b/WebMElement.h +index d9ad0a0..987582a 100644 +--- a/WebMElement.h ++++ b/WebMElement.h +@@ -6,10 +6,15 @@ + // in the file PATENTS. All contributing project authors may + // be found in the AUTHORS file in the root of the source tree. + ++#ifdef __cplusplus ++extern "C" { ++#endif + + #ifndef MKV_CONTEXT_HPP + #define MKV_CONTEXT_HPP 1 + ++#include "EbmlWriter.h" ++ + void writeSimpleBock(EbmlGlobal *ebml, unsigned char trackNumber, unsigned short timeCode, + int isKeyframe, unsigned char lacingFlag, int discardable, + unsigned char *data, unsigned long dataLength); +@@ -24,12 +29,14 @@ void writeVideoTrack(EbmlGlobal *ebml, unsigned int trackNumber, int flagLacing, + double frameRate); + void writeAudioTrack(EbmlGlobal *glob, unsigned int trackNumber, int flagLacing, + char *codecId, double samplingFrequency, unsigned int channels, +- unsigned char *private, unsigned long privateSize); ++ unsigned char *private_, unsigned long privateSize); + + void writeSimpleBlock(EbmlGlobal *ebml, unsigned char trackNumber, short timeCode, + int isKeyframe, unsigned char lacingFlag, int discardable, + unsigned char *data, unsigned long dataLength); + ++#endif + +- +-#endif +\ No newline at end of file ++#ifdef __cplusplus ++} ++#endif
new file mode 100644 --- /dev/null +++ b/media/libmkv/moz.build @@ -0,0 +1,27 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS.libmkv += [ + 'EbmlBufferWriter.h', + 'EbmlIDs.h', + 'EbmlWriter.h', + 'WebMElement.h', +] + +UNIFIED_SOURCES += [ +] + +# These files can't be unified because of function redefinitions. +SOURCES += [ + 'EbmlBufferWriter.c', + 'EbmlWriter.c', + 'WebMElement.c', +] + +if CONFIG['GKMEDIAS_SHARED_LIBRARY']: + NO_VISIBILITY_FLAGS = True + +FINAL_LIBRARY = 'gkmedias'
new file mode 100644 --- /dev/null +++ b/media/libmkv/source_fix.patch @@ -0,0 +1,193 @@ +diff --git a/EbmlBufferWriter.c b/EbmlBufferWriter.c +index 574e478..8c26e80 100644 +--- a/EbmlBufferWriter.c ++++ b/EbmlBufferWriter.c +@@ -8,6 +8,31 @@ + #include <wchar.h> + #include <string.h> + ++void ++Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, int buffer_size, unsigned long len) ++{ ++ /* buffer_size: ++ * 1 - int8_t; ++ * 2 - int16_t; ++ * 3 - int32_t; ++ * 4 - int64_t; ++ */ ++ long i; ++ for(i = len-1; i >= 0; i--) { ++ unsigned char x; ++ if (buffer_size == 1) { ++ x = (char)(*(const int8_t *)buffer_in >> (i * 8)); ++ } else if (buffer_size == 2) { ++ x = (char)(*(const int16_t *)buffer_in >> (i * 8)); ++ } else if (buffer_size == 4) { ++ x = (char)(*(const int32_t *)buffer_in >> (i * 8)); ++ } else if (buffer_size == 8) { ++ x = (char)(*(const int64_t *)buffer_in >> (i * 8)); ++ } ++ Ebml_Write(glob, &x, 1); ++ } ++} ++ + void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len) { + unsigned char *src = glob->buf; + src += glob->offset; +@@ -19,12 +44,12 @@ static void _Serialize(EbmlGlobal *glob, const unsigned char *p, const unsigned + while (q != p) { + --q; + +- unsigned long cbWritten; + memcpy(&(glob->buf[glob->offset]), q, 1); + glob->offset++; + } + } + ++/* + void Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, unsigned long len) { + // assert(buf); + +@@ -33,22 +58,22 @@ void Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, unsigned long len) + + _Serialize(glob, p, q); + } +- ++*/ + + void Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, unsigned long class_id) { ++ unsigned long long unknownLen = 0x01FFFFFFFFFFFFFFLL; + Ebml_WriteID(glob, class_id); + ebmlLoc->offset = glob->offset; + // todo this is always taking 8 bytes, this may need later optimization +- unsigned long long unknownLen = 0x01FFFFFFFFFFFFFFLLU; +- Ebml_Serialize(glob, (void *)&unknownLen, 8); // this is a key that says lenght unknown ++ Ebml_Serialize(glob, (void *)&unknownLen,sizeof(unknownLen), 8); // this is a key that says lenght unknown + } + + void Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc) { + unsigned long long size = glob->offset - ebmlLoc->offset - 8; + unsigned long long curOffset = glob->offset; + glob->offset = ebmlLoc->offset; +- size |= 0x0100000000000000LLU; +- Ebml_Serialize(glob, &size, 8); ++ size |= 0x0100000000000000LL; ++ Ebml_Serialize(glob, &size,sizeof(size), 8); + glob->offset = curOffset; + } + +diff --git a/EbmlBufferWriter.h b/EbmlBufferWriter.h +index acd5c2a..c135f29 100644 +--- a/EbmlBufferWriter.h ++++ b/EbmlBufferWriter.h +@@ -11,9 +11,7 @@ typedef struct { + unsigned int offset; + } EbmlGlobal; + +- + void Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, unsigned long class_id); + void Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc); + +- + #endif +diff --git a/EbmlWriter.c b/EbmlWriter.c +index 27cfe86..ebefc1a 100644 +--- a/EbmlWriter.c ++++ b/EbmlWriter.c +@@ -74,6 +74,13 @@ void Ebml_WriteID(EbmlGlobal *glob, unsigned long class_id) { + Ebml_Serialize(glob, (void *)&class_id, sizeof(class_id), len); + } + ++void Ebml_SerializeUnsigned32(EbmlGlobal *glob, unsigned long class_id, uint32_t ui) { ++ unsigned char sizeSerialized = 8 | 0x80; ++ Ebml_WriteID(glob, class_id); ++ Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1); ++ Ebml_Serialize(glob, &ui, sizeof(ui), 4); ++} ++ + void Ebml_SerializeUnsigned64(EbmlGlobal *glob, unsigned long class_id, uint64_t ui) { + unsigned char sizeSerialized = 8 | 0x80; + Ebml_WriteID(glob, class_id); +diff --git a/EbmlWriter.h b/EbmlWriter.h +index b94f757..a0a848b 100644 +--- a/EbmlWriter.h ++++ b/EbmlWriter.h +@@ -28,6 +28,7 @@ void Ebml_WriteLen(EbmlGlobal *glob, int64_t val); + void Ebml_WriteString(EbmlGlobal *glob, const char *str); + void Ebml_WriteUTF8(EbmlGlobal *glob, const wchar_t *wstr); + void Ebml_WriteID(EbmlGlobal *glob, unsigned long class_id); ++void Ebml_SerializeUnsigned32(EbmlGlobal *glob, unsigned long class_id, uint32_t ui); + void Ebml_SerializeUnsigned64(EbmlGlobal *glob, unsigned long class_id, uint64_t ui); + void Ebml_SerializeUnsigned(EbmlGlobal *glob, unsigned long class_id, unsigned long ui); + void Ebml_SerializeBinary(EbmlGlobal *glob, unsigned long class_id, unsigned long ui); +diff --git a/WebMElement.c b/WebMElement.c +index 2f79a3c..02eefa4 100644 +--- a/WebMElement.c ++++ b/WebMElement.c +@@ -11,8 +11,12 @@ + #include "EbmlIDs.h" + #include "WebMElement.h" + #include <stdio.h> ++#include <stdint.h> ++#include <stdlib.h> ++#include <time.h> + + #define kVorbisPrivateMaxSize 4000 ++#define UInt64 uint64_t + + void writeHeader(EbmlGlobal *glob) { + EbmlLoc start; +@@ -30,15 +34,16 @@ void writeHeader(EbmlGlobal *glob) { + void writeSimpleBlock(EbmlGlobal *glob, unsigned char trackNumber, short timeCode, + int isKeyframe, unsigned char lacingFlag, int discardable, + unsigned char *data, unsigned long dataLength) { +- Ebml_WriteID(glob, SimpleBlock); + unsigned long blockLength = 4 + dataLength; ++ unsigned char flags = 0x00 | (isKeyframe ? 0x80 : 0x00) | (lacingFlag << 1) | discardable; ++ Ebml_WriteID(glob, SimpleBlock); + blockLength |= 0x10000000; // TODO check length < 0x0FFFFFFFF + Ebml_Serialize(glob, &blockLength, sizeof(blockLength), 4); + trackNumber |= 0x80; // TODO check track nubmer < 128 + Ebml_Write(glob, &trackNumber, 1); + // Ebml_WriteSigned16(glob, timeCode,2); //this is 3 bytes + Ebml_Serialize(glob, &timeCode, sizeof(timeCode), 2); +- unsigned char flags = 0x00 | (isKeyframe ? 0x80 : 0x00) | (lacingFlag << 1) | discardable; ++ flags = 0x00 | (isKeyframe ? 0x80 : 0x00) | (lacingFlag << 1) | discardable; + Ebml_Write(glob, &flags, 1); + Ebml_Write(glob, data, dataLength); + } +@@ -48,17 +53,18 @@ static UInt64 generateTrackID(unsigned int trackNumber) { + UInt64 r = rand(); + r = r << 32; + r += rand(); +- UInt64 rval = t ^ r; +- return rval; ++// UInt64 rval = t ^ r; ++ return t ^ r; + } + + void writeVideoTrack(EbmlGlobal *glob, unsigned int trackNumber, int flagLacing, + char *codecId, unsigned int pixelWidth, unsigned int pixelHeight, + double frameRate) { + EbmlLoc start; ++ UInt64 trackID; + Ebml_StartSubElement(glob, &start, TrackEntry); + Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); +- UInt64 trackID = generateTrackID(trackNumber); ++ trackID = generateTrackID(trackNumber); + Ebml_SerializeUnsigned(glob, TrackUID, trackID); + Ebml_SerializeString(glob, CodecName, "VP8"); // TODO shouldn't be fixed + +@@ -78,9 +84,10 @@ void writeAudioTrack(EbmlGlobal *glob, unsigned int trackNumber, int flagLacing, + char *codecId, double samplingFrequency, unsigned int channels, + unsigned char *private, unsigned long privateSize) { + EbmlLoc start; ++ UInt64 trackID; + Ebml_StartSubElement(glob, &start, TrackEntry); + Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); +- UInt64 trackID = generateTrackID(trackNumber); ++ trackID = generateTrackID(trackNumber); + Ebml_SerializeUnsigned(glob, TrackUID, trackID); + Ebml_SerializeUnsigned(glob, TrackType, 2); // audio is always 2 + // I am using defaults for thesed required fields +
new file mode 100644 --- /dev/null +++ b/media/libmkv/update.sh @@ -0,0 +1,33 @@ +# Usage: sh update.sh <upstream_src_directory> +set -e +echo "copy source from libvpx" + +cp $1/third_party/libmkv/EbmlBufferWriter.c . +cp $1/third_party/libmkv/WebMElement.c . +cp $1/third_party/libmkv/EbmlWriter.c . +cp $1/third_party/libmkv/EbmlWriter.h . +cp $1/third_party/libmkv/EbmlBufferWriter.h . +cp $1/third_party/libmkv/WebMElement.h . +cp $1/third_party/libmkv/EbmlIDs.h . + +cp $1/LICENSE . +cp $1/README . +cp $1/AUTHORS . +if [ -d $1/.git ]; then + rev=$(cd $1 && git rev-parse --verify HEAD) + dirty=$(cd $1 && git diff-index --name-only HEAD) +fi + +if [ -n "$rev" ]; then + version=$rev + if [ -n "$dirty" ]; then + version=$version-dirty + echo "WARNING: updating from a dirty git repository." + fi + sed -i.bak -e "/The git commit ID used was/ s/[0-9a-f]\{40\}\(-dirty\)\{0,1\}\./$version./" README_MOZILLA + rm README_MOZILLA.bak +else + echo "Remember to update README_MOZILLA with the version details." +fi + +echo "please apply source_fix.patch and gecko_fix.patch"