Merge "SoftAVCEnc: Modified the code for runtime change in params to be generic" into nyc-dev
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index eacb32e..ca68722 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -33,6 +33,7 @@
#include <binder/ProcessState.h>
#include <media/ICrypto.h>
#include <media/IMediaHTTPService.h>
+#include <media/IMediaCodecService.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ALooper.h>
@@ -904,8 +905,8 @@
if (listComponents) {
sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.player"));
- sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+ sp<IBinder> binder = sm->getService(String16("media.codec"));
+ sp<IMediaCodecService> service = interface_cast<IMediaCodecService>(binder);
CHECK(service.get() != NULL);
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index c47a4e7..521557d 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -19,6 +19,7 @@
#include <cutils/sched_policy.h>
#include <media/AudioSystem.h>
+#include <media/AudioTimestamp.h>
#include <media/IAudioRecord.h>
#include <media/Modulo.h>
#include <utils/threads.h>
@@ -314,6 +315,17 @@
*/
status_t getPosition(uint32_t *position) const;
+ /* Return the record timestamp.
+ *
+ * Parameters:
+ * timestamp: A pointer to the timestamp to be filled.
+ *
+ * Returned status (from utils/Errors.h) can be:
+ * - NO_ERROR: successful operation
+ * - BAD_VALUE: timestamp is NULL
+ */
+ status_t getTimestamp(ExtendedTimestamp *timestamp);
+
/* Returns a handle on the audio input used by this AudioRecord.
*
* Parameters:
@@ -571,6 +583,11 @@
size_t mReqFrameCount; // frame count to request the first or next time
// a new IAudioRecord is needed, non-decreasing
+ int64_t mFramesRead; // total frames read. reset to zero after
+ // the start() following stop(). It is not
+ // changed after restoring the track.
+ int64_t mFramesReadServerOffset; // An offset to server frames read due to
+ // restoring AudioRecord, or stop/start.
// constant after constructor or set()
uint32_t mSampleRate;
audio_format_t mFormat;
diff --git a/include/media/AudioTimestamp.h b/include/media/AudioTimestamp.h
index 99e9c3e..e6ca225 100644
--- a/include/media/AudioTimestamp.h
+++ b/include/media/AudioTimestamp.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_AUDIO_TIMESTAMP_H
#define ANDROID_AUDIO_TIMESTAMP_H
+#include <string>
+#include <sstream>
#include <time.h>
namespace android {
@@ -32,6 +34,85 @@
struct timespec mTime; // corresponding CLOCK_MONOTONIC when frame is expected to present
};
+struct ExtendedTimestamp {
+ enum Location {
+ LOCATION_CLIENT, // timestamp of last read frame from client-server track buffer
+ LOCATION_SERVER, // timestamp of newest frame from client-server track buffer
+ LOCATION_KERNEL, // timestamp of newest frame in the kernel (alsa) buffer.
+ LOCATION_MAX // for sizing arrays only
+ };
+
+ // This needs to be kept in sync with android.media.AudioTimestamp
+ enum Timebase {
+ TIMEBASE_MONOTONIC, // Clock monotonic offset (generally 0)
+ TIMEBASE_BOOTTIME,
+ TIMEBASE_MAX,
+ };
+
+ ExtendedTimestamp() {
+ clear();
+ }
+
+ // mPosition is expressed in frame units.
+ // It is generally nonnegative, though we keep this signed for
+ // to potentially express algorithmic latency at the start of the stream
+ // and to prevent unintentional unsigned integer underflow.
+ int64_t mPosition[LOCATION_MAX];
+
+ // mTimeNs is in nanoseconds for the default timebase, monotonic.
+ // If this value is -1, then both time and position are invalid.
+ // If this value is 0, then the time is not valid but the position is valid.
+ int64_t mTimeNs[LOCATION_MAX];
+
+ // mTimebaseOffset is the offset in ns from monotonic when the
+ // timestamp was taken. This may vary due to suspend time
+ // or NTP adjustment.
+ int64_t mTimebaseOffset[TIMEBASE_MAX];
+
+ void clear() {
+ memset(mPosition, 0, sizeof(mPosition)); // actually not necessary if time is -1
+ for (int i = 0; i < LOCATION_MAX; ++i) {
+ mTimeNs[i] = -1;
+ }
+ memset(mTimebaseOffset, 0, sizeof(mTimebaseOffset));
+ }
+
+ // Returns the best timestamp as judged from the closest-to-hw stage in the
+ // pipeline with a valid timestamp.
+ int getBestTimestamp(int64_t *position, int64_t *time, int timebase) {
+ if (position == nullptr || time == nullptr
+ || timebase < 0 || timebase >= TIMEBASE_MAX) {
+ return BAD_VALUE;
+ }
+ // look for the closest-to-hw stage in the pipeline with a valid timestamp.
+ // We omit LOCATION_CLIENT as we prefer at least LOCATION_SERVER based accuracy
+ // when getting the best timestamp.
+ for (int i = LOCATION_MAX - 1; i >= LOCATION_SERVER; --i) {
+ if (mTimeNs[i] > 0) {
+ *position = mPosition[i];
+ *time = mTimeNs[i] + mTimebaseOffset[timebase];
+ return OK;
+ }
+ }
+ return INVALID_OPERATION;
+ }
+
+ // convert fields to a printable string
+ std::string toString() {
+ std::stringstream ss;
+
+ ss << "BOOTTIME offset " << mTimebaseOffset[TIMEBASE_BOOTTIME] << "\n";
+ for (int i = 0; i < LOCATION_MAX; ++i) {
+ ss << "ExtendedTimestamp[" << i << "] position: "
+ << mPosition[i] << " time: " << mTimeNs[i] << "\n";
+ }
+ return ss.str();
+ }
+ // TODO:
+ // Consider adding buffer status:
+ // size, available, algorithmic latency
+};
+
} // namespace
#endif // ANDROID_AUDIO_TIMESTAMP_H
diff --git a/include/media/IMediaCodecService.h b/include/media/IMediaCodecService.h
new file mode 100644
index 0000000..984a0fd
--- /dev/null
+++ b/include/media/IMediaCodecService.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IMEDIACODECSERVICE_H
+#define ANDROID_IMEDIACODECSERVICE_H
+
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <media/IDataSource.h>
+#include <include/OMX.h>
+
+namespace android {
+
+class IMediaCodecService: public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(MediaCodecService);
+
+ virtual sp<IOMX> getOMX() = 0;
+};
+
+class BnMediaCodecService: public BnInterface<IMediaCodecService>
+{
+public:
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0);
+};
+
+} // namespace android
+
+#endif // ANDROID_IMEDIACODECSERVICE_H
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index a316ce2..99ca6f0 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -51,7 +51,6 @@
virtual sp<IMediaMetadataRetriever> createMetadataRetriever() = 0;
virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, int audioSessionId = 0)
= 0;
-
virtual sp<IOMX> getOMX() = 0;
virtual sp<ICrypto> makeCrypto() = 0;
virtual sp<IDrm> makeDrm() = 0;
diff --git a/include/media/MediaCodecInfo.h b/include/media/MediaCodecInfo.h
index 4067b47..26dc1ce 100644
--- a/include/media/MediaCodecInfo.h
+++ b/include/media/MediaCodecInfo.h
@@ -33,7 +33,6 @@
struct AMessage;
class Parcel;
-struct CodecCapabilities;
typedef KeyedVector<AString, AString> CodecSettings;
@@ -44,12 +43,23 @@
};
struct Capabilities : public RefBase {
+ enum {
+ // decoder flags
+ kFlagSupportsAdaptivePlayback = 1 << 0,
+ kFlagSupportsSecurePlayback = 1 << 1,
+ kFlagSupportsTunneledPlayback = 1 << 2,
+
+ // encoder flags
+ kFlagSupportsIntraRefresh = 1 << 0,
+
+ };
+
void getSupportedProfileLevels(Vector<ProfileLevel> *profileLevels) const;
void getSupportedColorFormats(Vector<uint32_t> *colorFormats) const;
uint32_t getFlags() const;
const sp<AMessage> getDetails() const;
- private:
+ protected:
Vector<ProfileLevel> mProfileLevels;
Vector<uint32_t> mColorFormats;
uint32_t mFlags;
@@ -57,6 +67,7 @@
Capabilities();
+ private:
// read object from parcel even if object creation fails
static sp<Capabilities> FromParcel(const Parcel &parcel);
status_t writeToParcel(Parcel *parcel) const;
@@ -66,6 +77,14 @@
friend class MediaCodecInfo;
};
+ // Use a subclass to allow setting fields on construction without allowing
+ // to do the same throughout the framework.
+ struct CapabilitiesBuilder : public Capabilities {
+ void addProfileLevel(uint32_t profile, uint32_t level);
+ void addColorFormat(uint32_t format);
+ void addFlags(uint32_t flags);
+ };
+
bool isEncoder() const;
bool hasQuirk(const char *name) const;
void getSupportedMimes(Vector<AString> *mimes) const;
@@ -107,7 +126,9 @@
void addQuirk(const char *name);
status_t addMime(const char *mime);
status_t updateMime(const char *mime);
- status_t initializeCapabilities(const CodecCapabilities &caps);
+
+ // after this call |caps| will be owned by MediaCodecInfo, which may modify it
+ status_t setCapabilitiesFromCodec(const sp<Capabilities> &caps);
void addDetail(const AString &key, const AString &value);
void addFeature(const AString &key, int32_t value);
void addFeature(const AString &key, const char *value);
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 5db70a6..349db64 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -50,6 +50,10 @@
virtual void initiateStart();
virtual void initiateShutdown(bool keepComponentAllocated = false);
+ virtual status_t queryCapabilities(
+ const AString &name, const AString &mime, bool isEncoder,
+ sp<MediaCodecInfo::Capabilities> *caps);
+
virtual status_t setSurface(const sp<Surface> &surface);
virtual void signalFlush();
@@ -312,6 +316,10 @@
ssize_t *index = NULL);
status_t setComponentRole(bool isEncoder, const char *mime);
+ static const char *getComponentRole(bool isEncoder, const char *mime);
+ static status_t setComponentRole(
+ const sp<IOMX> &omx, IOMX::node_id node, const char *role);
+
status_t configureCodec(const char *mime, const sp<AMessage> &msg);
status_t configureTunneledVideoPlayback(int32_t audioHwSync,
diff --git a/include/media/stagefright/ClockEstimator.h b/include/media/stagefright/ClockEstimator.h
deleted file mode 100644
index 1455b7f..0000000
--- a/include/media/stagefright/ClockEstimator.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-**
-** Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#ifndef CLOCK_ESTIMATOR_H_
-
-#define CLOCK_ESTIMATOR_H_
-
-#include "foundation/ABase.h"
-#include <utils/RefBase.h>
-#include <utils/Vector.h>
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-struct ClockEstimator : RefBase {
- virtual double estimate(double x, double y) = 0;
- virtual void reset() = 0;
-};
-
-struct WindowedLinearFitEstimator : ClockEstimator {
- struct LinearFit {
- /**
- * Fit y = a * x + b, where each input has a weight
- */
- double mX; // sum(w_i * x_i)
- double mXX; // sum(w_i * x_i^2)
- double mY; // sum(w_i * y_i)
- double mYY; // sum(w_i * y_i^2)
- double mXY; // sum(w_i * x_i * y_i)
- double mW; // sum(w_i)
-
- LinearFit();
- void reset();
- void combine(const LinearFit &lf);
- void add(double x, double y, double w);
- void scale(double w);
- double interpolate(double x);
- double size() const;
-
- DISALLOW_EVIL_CONSTRUCTORS(LinearFit);
- };
-
- /**
- * Estimator for f(x) = y' where input y' is noisy, but
- * theoretically linear:
- *
- * y' =~ y = a * x + b
- *
- * It uses linear fit regression over a tapering rolling window
- * to get an estimate for y (from the current and past inputs
- * (x, y')).
- *
- * ____________
- * /| |\
- * / | | \
- * / | | \ <--- new data (x, y')
- * / | main | \
- * <--><----------><-->
- * tail head
- *
- * weight is 1 under the main window, tapers exponentially by
- * the factors given in the head and the tail.
- *
- * Assuming that x and y' are monotonic, that x is somewhat
- * evenly sampled, and that a =~ 1, the estimated y is also
- * going to be monotonic.
- */
- WindowedLinearFitEstimator(
- size_t headLength = 5, double headFactor = 0.5,
- size_t mainLength = 0, double tailFactor = 0.99);
-
- virtual void reset();
-
- // add a new sample (x -> y') and return an estimated value for the true y
- virtual double estimate(double x, double y);
-
-private:
- Vector<double> mXHistory; // circular buffer
- Vector<double> mYHistory; // circular buffer
- LinearFit mHead;
- LinearFit mMain;
- LinearFit mTail;
- double mHeadFactorInv;
- double mTailFactor;
- double mFirstWeight;
- size_t mHistoryLength;
- size_t mHeadLength;
- size_t mNumSamples;
- size_t mSampleIx;
-
- DISALLOW_EVIL_CONSTRUCTORS(WindowedLinearFitEstimator);
-};
-
-}; // namespace android
-
-#endif
diff --git a/include/media/stagefright/CodecBase.h b/include/media/stagefright/CodecBase.h
index 542db71..01b744b 100644
--- a/include/media/stagefright/CodecBase.h
+++ b/include/media/stagefright/CodecBase.h
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <media/IOMX.h>
+#include <media/MediaCodecInfo.h>
#include <media/stagefright/foundation/AHandler.h>
namespace android {
@@ -59,6 +60,10 @@
// require an explicit message handler
virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
+ virtual status_t queryCapabilities(
+ const AString &name, const AString &mime, bool isEncoder,
+ sp<MediaCodecInfo::Capabilities> *caps /* nonnull */) { return INVALID_OPERATION; }
+
virtual status_t setSurface(const sp<Surface> &surface) { return INVALID_OPERATION; }
virtual void signalFlush() = 0;
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index d4b8e4a..ea708e3 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -20,6 +20,7 @@
#include <gui/IGraphicBufferProducer.h>
#include <media/hardware/CryptoAPI.h>
+#include <media/MediaCodecInfo.h>
#include <media/MediaResource.h>
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/FrameRenderTracker.h>
@@ -73,6 +74,11 @@
static sp<PersistentSurface> CreatePersistentInputSurface();
+ // utility method to query capabilities
+ static status_t QueryCapabilities(
+ const AString &name, const AString &mime, bool isEncoder,
+ sp<MediaCodecInfo::Capabilities> *caps /* nonnull */);
+
status_t configure(
const sp<AMessage> &format,
const sp<Surface> &nativeWindow,
@@ -341,6 +347,8 @@
MediaCodec(const sp<ALooper> &looper, pid_t pid);
+ static sp<CodecBase> GetCodecBase(const AString &name, bool nameIsType = false);
+
static status_t PostAndAwaitResponse(
const sp<AMessage> &msg, sp<AMessage> *response);
diff --git a/include/media/stagefright/TimeSource.h b/include/media/stagefright/TimeSource.h
deleted file mode 100644
index 8f11e14..0000000
--- a/include/media/stagefright/TimeSource.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TIME_SOURCE_H_
-
-#define TIME_SOURCE_H_
-
-#include <stdint.h>
-
-namespace android {
-
-class TimeSource {
-public:
- TimeSource() {}
- virtual ~TimeSource() {}
-
- virtual int64_t getRealTimeUs() = 0;
-
-private:
- TimeSource(const TimeSource &);
- TimeSource &operator=(const TimeSource &);
-};
-
-class SystemTimeSource : public TimeSource {
-public:
- SystemTimeSource();
-
- virtual int64_t getRealTimeUs();
-
-private:
- int64_t mStartTimeUs;
-};
-
-} // namespace android
-
-#endif // TIME_SOURCE_H_
diff --git a/include/media/stagefright/timedtext/TimedTextDriver.h b/include/media/stagefright/timedtext/TimedTextDriver.h
deleted file mode 100644
index 6e699a6..0000000
--- a/include/media/stagefright/timedtext/TimedTextDriver.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TIMED_TEXT_DRIVER_H_
-#define TIMED_TEXT_DRIVER_H_
-
-#include <media/IMediaSource.h>
-#include <media/stagefright/foundation/ABase.h> // for DISALLOW_* macro
-#include <utils/Errors.h> // for status_t
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-
-namespace android {
-
-struct ALooper;
-struct IMediaHTTPService;
-class MediaPlayerBase;
-class Parcel;
-class TimedTextPlayer;
-class TimedTextSource;
-class DataSource;
-
-class TimedTextDriver {
-public:
- TimedTextDriver(
- const wp<MediaPlayerBase> &listener,
- const sp<IMediaHTTPService> &httpService);
-
- ~TimedTextDriver();
-
- status_t start();
- status_t pause();
- status_t selectTrack(size_t index);
- status_t unselectTrack(size_t index);
-
- status_t seekToAsync(int64_t timeUs);
-
- status_t addInBandTextSource(
- size_t trackIndex, const sp<IMediaSource>& source);
-
- status_t addOutOfBandTextSource(
- size_t trackIndex, const char *uri, const char *mimeType);
-
- // Caller owns the file desriptor and caller is responsible for closing it.
- status_t addOutOfBandTextSource(
- size_t trackIndex, int fd, off64_t offset,
- off64_t length, const char *mimeType);
-
- void getExternalTrackInfo(Parcel *parcel);
- size_t countExternalTracks() const;
-
-private:
- Mutex mLock;
-
- enum State {
- UNINITIALIZED,
- PREPARED,
- PLAYING,
- PAUSED,
- };
-
- enum TextSourceType {
- TEXT_SOURCE_TYPE_IN_BAND = 0,
- TEXT_SOURCE_TYPE_OUT_OF_BAND,
- };
-
- sp<ALooper> mLooper;
- sp<TimedTextPlayer> mPlayer;
- wp<MediaPlayerBase> mListener;
- sp<IMediaHTTPService> mHTTPService;
-
- // Variables to be guarded by mLock.
- State mState;
- size_t mCurrentTrackIndex;
- KeyedVector<size_t, sp<TimedTextSource> > mTextSourceVector;
- Vector<TextSourceType> mTextSourceTypeVector;
-
- // -- End of variables to be guarded by mLock
-
- status_t selectTrack_l(size_t index);
-
- status_t createOutOfBandTextSource(
- size_t trackIndex, const char* mimeType,
- const sp<DataSource>& dataSource);
-
- DISALLOW_EVIL_CONSTRUCTORS(TimedTextDriver);
-};
-
-} // namespace android
-
-#endif // TIMED_TEXT_DRIVER_H_
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 770f007..2270c85 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -26,6 +26,7 @@
#include <utils/RefBase.h>
#include <audio_utils/roundup.h>
#include <media/AudioResamplerPublic.h>
+#include <media/AudioTimestamp.h>
#include <media/Modulo.h>
#include <media/SingleStateQueue.h>
@@ -118,6 +119,8 @@
typedef SingleStateQueue<AudioPlaybackRate> PlaybackRateQueue;
+typedef SingleStateQueue<ExtendedTimestamp> ExtendedTimestampQueue;
+
// ----------------------------------------------------------------------------
// Important: do not add any virtual methods, including ~
@@ -171,6 +174,8 @@
uint16_t mPad2; // unused
+ // server write-only, client read
+ ExtendedTimestampQueue::Shared mExtendedTimestampQueue;
public:
volatile int32_t mFlags; // combinations of CBLK_*
@@ -426,8 +431,39 @@
AudioRecordClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
size_t frameSize)
: ClientProxy(cblk, buffers, frameCount, frameSize,
- false /*isOut*/, false /*clientInServer*/) { }
+ false /*isOut*/, false /*clientInServer*/)
+ , mTimestampObserver(&cblk->mExtendedTimestampQueue) { }
~AudioRecordClientProxy() { }
+
+ status_t getTimestamp(ExtendedTimestamp *timestamp) {
+ if (timestamp == nullptr) {
+ return BAD_VALUE;
+ }
+ (void) mTimestampObserver.poll(mTimestamp);
+ *timestamp = mTimestamp;
+ return OK;
+ }
+
+ void clearTimestamp() {
+ mTimestamp.clear();
+ }
+
+ // Advances the client read pointer to the server write head pointer
+ // effectively flushing the client read buffer. The effect is
+ // instantaneous. Returns the number of frames flushed.
+ uint32_t flush() {
+ int32_t rear = android_atomic_acquire_load(&mCblk->u.mStreaming.mRear);
+ int32_t front = mCblk->u.mStreaming.mFront;
+ android_atomic_release_store(rear, &mCblk->u.mStreaming.mFront);
+ return (Modulo<int32_t>(rear) - front).unsignedValue();
+ }
+
+private:
+ // The shared buffer contents referred to by the timestamp observer
+ // is initialized when the server proxy created. A local zero timestamp
+ // is initialized by the client constructor.
+ ExtendedTimestampQueue::Observer mTimestampObserver;
+ ExtendedTimestamp mTimestamp; // initialized by constructor
};
// ----------------------------------------------------------------------------
@@ -476,6 +512,7 @@
protected:
size_t mAvailToClient; // estimated frames available to client prior to releaseBuffer()
int32_t mFlush; // our copy of cblk->u.mStreaming.mFlush, for streaming output only
+ int64_t mReleased; // our copy of cblk->mServer, at 64 bit resolution
};
// Proxy used by AudioFlinger for servicing AudioTrack
@@ -520,7 +557,7 @@
virtual uint32_t getUnderrunFrames() const { return mCblk->u.mStreaming.mUnderrunFrames; }
// Return the total number of frames that AudioFlinger has obtained and released
- virtual size_t framesReleased() const { return mCblk->mServer; }
+ virtual size_t framesReleased() const { return mReleased; }
// Return the playback speed and pitch read atomically. Not multi-thread safe on server side.
AudioPlaybackRate getPlaybackRate();
@@ -574,9 +611,20 @@
public:
AudioRecordServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
size_t frameSize, bool clientInServer)
- : ServerProxy(cblk, buffers, frameCount, frameSize, false /*isOut*/, clientInServer) { }
+ : ServerProxy(cblk, buffers, frameCount, frameSize, false /*isOut*/, clientInServer)
+ , mTimestampMutator(&cblk->mExtendedTimestampQueue) { }
+
+ // Return the total number of frames that AudioFlinger has obtained and released
+ virtual int64_t framesReleased() const { return mReleased; }
+
+ // Expose timestamp to client proxy. Should only be called by a single thread.
+ virtual void setExtendedTimestamp(const ExtendedTimestamp ×tamp) {
+ mTimestampMutator.push(timestamp);
+ }
protected:
virtual ~AudioRecordServerProxy() { }
+
+ ExtendedTimestampQueue::Mutator mTimestampMutator;
};
// ----------------------------------------------------------------------------
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 285c33e..c095724 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -30,6 +30,7 @@
AudioSystem.cpp \
mediaplayer.cpp \
IMediaCodecList.cpp \
+ IMediaCodecService.cpp \
IMediaHTTPConnection.cpp \
IMediaHTTPService.cpp \
IMediaLogService.cpp \
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 1c0d904..ec57d96 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -284,6 +284,8 @@
mSequence = 1;
mObservedSequence = mSequence;
mInOverrun = false;
+ mFramesRead = 0;
+ mFramesReadServerOffset = 0;
return NO_ERROR;
}
@@ -299,6 +301,12 @@
return NO_ERROR;
}
+ // discard data in buffer
+ const uint32_t framesFlushed = mProxy->flush();
+ mFramesReadServerOffset -= mFramesRead + framesFlushed;
+ mFramesRead = 0;
+ mProxy->clearTimestamp(); // timestamp is invalid until next server push
+
// reset current position as seen by client to 0
mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
// force refresh of remaining frames by processAudioBuffer() as last
@@ -449,6 +457,27 @@
return AudioSystem::getInputFramesLost(getInputPrivate());
}
+status_t AudioRecord::getTimestamp(ExtendedTimestamp *timestamp)
+{
+ if (timestamp == nullptr) {
+ return BAD_VALUE;
+ }
+ AutoMutex lock(mLock);
+ status_t status = mProxy->getTimestamp(timestamp);
+ if (status == OK) {
+ timestamp->mPosition[ExtendedTimestamp::LOCATION_CLIENT] = mFramesRead;
+ timestamp->mTimeNs[ExtendedTimestamp::LOCATION_CLIENT] = 0;
+ // server side frame offset in case AudioRecord has been restored.
+ for (int i = ExtendedTimestamp::LOCATION_SERVER;
+ i < ExtendedTimestamp::LOCATION_MAX; ++i) {
+ if (timestamp->mTimeNs[i] >= 0) {
+ timestamp->mPosition[i] += mFramesReadServerOffset;
+ }
+ }
+ }
+ return status;
+}
+
// ---- Explicit Routing ---------------------------------------------------
status_t AudioRecord::setInputDevice(audio_port_handle_t deviceId) {
AutoMutex lock(mLock);
@@ -837,7 +866,10 @@
releaseBuffer(&audioBuffer);
}
-
+ if (read > 0) {
+ mFramesRead += read / mFrameSize;
+ // mFramesReadTime = systemTime(SYSTEM_TIME_MONOTONIC); // not provided at this time.
+ }
return read;
}
@@ -988,6 +1020,7 @@
requested = &timeout;
}
+ size_t readFrames = 0;
while (mRemainingFrames > 0) {
Buffer audioBuffer;
@@ -1049,6 +1082,7 @@
}
releaseBuffer(&audioBuffer);
+ readFrames += releasedFrames;
// FIXME here is where we would repeat EVENT_MORE_DATA again on same advanced buffer
// if callback doesn't like to accept the full chunk
@@ -1072,6 +1106,11 @@
#endif
}
+ if (readFrames > 0) {
+ AutoMutex lock(mLock);
+ mFramesRead += readFrames;
+ // mFramesReadTime = systemTime(SYSTEM_TIME_MONOTONIC); // not provided at this time.
+ }
mRemainingFrames = notificationFrames;
mRetryOnPartialBuffer = true;
@@ -1096,6 +1135,7 @@
// FIXME this fails if we have a new AudioFlinger instance
result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0);
}
+ mFramesReadServerOffset = mFramesRead; // server resets to zero so we need an offset.
}
if (result != NO_ERROR) {
ALOGW("restoreRecord_l() failed status %d", result);
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index d75ad87..988386e 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -597,7 +597,7 @@
ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
size_t frameSize, bool isOut, bool clientInServer)
: Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer),
- mAvailToClient(0), mFlush(0)
+ mAvailToClient(0), mFlush(0), mReleased(0)
{
}
@@ -733,6 +733,7 @@
}
cblk->mServer += stepCount;
+ mReleased += stepCount;
size_t half = mFrameCount / 2;
if (half == 0) {
@@ -1033,6 +1034,8 @@
mFramesReadySafe = clampToSize(mFramesReady);
cblk->mServer += stepCount;
+ mReleased += stepCount;
+
// This may overflow, but client is not supposed to rely on it
StaticAudioTrackPosLoop posLoop;
posLoop.mBufferPosition = mState.mPosition;
diff --git a/media/libmedia/IMediaCodecService.cpp b/media/libmedia/IMediaCodecService.cpp
new file mode 100644
index 0000000..dcf2b27
--- /dev/null
+++ b/media/libmedia/IMediaCodecService.cpp
@@ -0,0 +1,72 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "IMediaCodecService"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/Parcel.h>
+#include <media/IMediaCodecService.h>
+
+namespace android {
+
+enum {
+ GET_OMX = IBinder::FIRST_CALL_TRANSACTION
+};
+
+class BpMediaCodecService : public BpInterface<IMediaCodecService>
+{
+public:
+ BpMediaCodecService(const sp<IBinder>& impl)
+ : BpInterface<IMediaCodecService>(impl)
+ {
+ }
+
+ virtual sp<IOMX> getOMX() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaCodecService::getInterfaceDescriptor());
+ remote()->transact(GET_OMX, data, &reply);
+ return interface_cast<IOMX>(reply.readStrongBinder());
+ }
+
+};
+
+IMPLEMENT_META_INTERFACE(MediaCodecService, "android.media.IMediaCodecService");
+
+// ----------------------------------------------------------------------
+
+status_t BnMediaCodecService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+
+ case GET_OMX: {
+ CHECK_INTERFACE(IMediaCodecService, data, reply);
+ sp<IOMX> omx = getOMX();
+ reply->writeStrongBinder(IInterface::asBinder(omx));
+ return NO_ERROR;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+} // namespace android
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 8d3fa7b..8351af6 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -26,8 +26,6 @@
#include <media/stagefright/foundation/AMessage.h>
#include <binder/Parcel.h>
-#include <media/stagefright/OMXCodec.h>
-
namespace android {
void MediaCodecInfo::Capabilities::getSupportedProfileLevels(
@@ -101,6 +99,21 @@
return OK;
}
+void MediaCodecInfo::CapabilitiesBuilder::addProfileLevel(uint32_t profile, uint32_t level) {
+ ProfileLevel profileLevel;
+ profileLevel.mProfile = profile;
+ profileLevel.mLevel = level;
+ mProfileLevels.push_back(profileLevel);
+}
+
+void MediaCodecInfo::CapabilitiesBuilder::addColorFormat(uint32_t format) {
+ mColorFormats.push(format);
+}
+
+void MediaCodecInfo::CapabilitiesBuilder::addFlags(uint32_t flags) {
+ mFlags |= flags;
+}
+
bool MediaCodecInfo::isEncoder() const {
return mIsEncoder;
}
@@ -225,26 +238,12 @@
}
}
-status_t MediaCodecInfo::initializeCapabilities(const CodecCapabilities &caps) {
- mCurrentCaps->mProfileLevels.clear();
- mCurrentCaps->mColorFormats.clear();
-
- for (size_t i = 0; i < caps.mProfileLevels.size(); ++i) {
- const CodecProfileLevel &src = caps.mProfileLevels.itemAt(i);
-
- ProfileLevel profileLevel;
- profileLevel.mProfile = src.mProfile;
- profileLevel.mLevel = src.mLevel;
- mCurrentCaps->mProfileLevels.push_back(profileLevel);
+status_t MediaCodecInfo::setCapabilitiesFromCodec(const sp<Capabilities> &caps) {
+ if (mCurrentCaps != NULL) {
+ // keep current capabilities map
+ caps->mDetails = mCurrentCaps->mDetails;
}
-
- for (size_t i = 0; i < caps.mColorFormats.size(); ++i) {
- mCurrentCaps->mColorFormats.push_back(caps.mColorFormats.itemAt(i));
- }
-
- mCurrentCaps->mFlags = caps.mFlags;
- mCurrentCaps->mDetails = new AMessage;
-
+ mCurrentCaps = caps;
return OK;
}
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index c5f53cb..bb24403 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -346,6 +346,7 @@
}
sp<IOMX> MediaPlayerService::getOMX() {
+ ALOGI("MediaPlayerService::getOMX");
Mutex::Autolock autoLock(mLock);
if (mOMX.get() == NULL) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 765ceff..2528777 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -736,6 +736,7 @@
if (err == OK) {
if (rate.mSpeed == 0.f) {
onPause();
+ mPausedByClient = true;
// save all other settings (using non-paused speed)
// so we can restore them on start
AudioPlaybackRate newRate = rate;
@@ -743,6 +744,7 @@
mPlaybackSettings = newRate;
} else { /* rate.mSpeed != 0.f */
onResume();
+ mPausedByClient = false;
mPlaybackSettings = rate;
}
}
@@ -1208,6 +1210,8 @@
break;
}
+ mPendingBufferingFlag = PENDING_BUFFERING_FLAG_NONE;
+
mDeferredActions.push_back(
new FlushDecoderAction(FLUSH_CMD_FLUSH /* audio */,
FLUSH_CMD_FLUSH /* video */));
@@ -1908,6 +1912,7 @@
}
mPreviousSeekTimeUs = seekTimeUs;
mSource->seekTo(seekTimeUs);
+ mPendingBufferingFlag = PENDING_BUFFERING_FLAG_NONE;
++mTimedTextGeneration;
// everything's flushed, continue playback.
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 7535934..cd3ba53 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -69,6 +69,9 @@
// is closed to allow the audio DSP to power down.
static const int64_t kOffloadPauseMaxUs = 10000000ll;
+// Maximum allowed delay from AudioSink, 1.5 seconds.
+static const int64_t kMaxAllowedAudioSinkDelayUs = 1500000ll;
+
// static
const NuPlayer::Renderer::PcmInfo NuPlayer::Renderer::AUDIO_PCMINFO_INITIALIZER = {
AUDIO_CHANNEL_NONE,
@@ -1038,10 +1041,25 @@
AudioTimestamp ts;
status_t res = mAudioSink->getTimestamp(ts);
+ int64_t nowUs = ALooper::GetNowUs();
if (res == OK) {
- int64_t nowUs = ALooper::GetNowUs();
int64_t nowMediaUs = mediaTimeUs - getPendingAudioPlayoutDurationUs(nowUs);
mMediaClock->updateAnchor(nowMediaUs, nowUs, mediaTimeUs);
+ } else {
+ int64_t unused;
+ if ((mMediaClock->getMediaTime(nowUs, &unused) != OK)
+ && (getDurationUsIfPlayedAtSampleRate(mNumFramesWritten)
+ > kMaxAllowedAudioSinkDelayUs)) {
+ // Enough data has been sent to AudioSink, but AudioSink has not rendered
+ // any data yet. Something is wrong with AudioSink, e.g., the device is not
+ // connected to audio out.
+ // Switch to system clock. This essentially creates a virtual AudioSink with
+ // initial latenty of getDurationUsIfPlayedAtSampleRate(mNumFramesWritten).
+ // This virtual AudioSink renders audio data starting from the very first sample
+ // and it's paced by system clock.
+ ALOGW("AudioSink stuck. ARE YOU CONNECTED TO AUDIO OUT? Switching to system clock.");
+ mMediaClock->updateAnchor(mAudioFirstAnchorTimeMediaUs, nowUs, mediaTimeUs);
+ }
}
mAnchorNumFramesWritten = mNumFramesWritten;
mAnchorTimeMediaUs = mediaTimeUs;
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index c861fd1..ec33478 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -385,10 +385,8 @@
case MyHandler::kWhatSeekDone:
{
mState = CONNECTED;
- if (mSeekReplyID != NULL) {
- // Unblock seekTo here in case we attempted to seek in a live stream
- finishSeek(OK);
- }
+ // Unblock seekTo here in case we attempted to seek in a live stream
+ finishSeek(OK);
break;
}
@@ -409,12 +407,13 @@
status_t err = OK;
msg->findInt32("err", &err);
- finishSeek(err);
if (err == OK) {
int64_t timeUs;
CHECK(msg->findInt64("time", &timeUs));
mHandler->continueSeekAfterPause(timeUs);
+ } else {
+ finishSeek(err);
}
break;
}
@@ -749,7 +748,9 @@
}
void NuPlayer::RTSPSource::finishSeek(status_t err) {
- CHECK(mSeekReplyID != NULL);
+ if (mSeekReplyID == NULL) {
+ return;
+ }
sp<AMessage> seekReply = new AMessage;
seekReply->setInt32("err", err);
seekReply->postReply(mSeekReplyID);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index fa6703e..d39fffa 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -55,6 +55,10 @@
namespace android {
+enum {
+ kMaxIndicesToCheck = 32, // used when enumerating supported formats and profiles
+};
+
// OMX errors are directly mapped into status_t range if
// there is no corresponding MediaError status code.
// Use the statusFromOMXError(int32_t omxError) function.
@@ -1528,6 +1532,21 @@
status_t ACodec::setComponentRole(
bool isEncoder, const char *mime) {
+ const char *role = getComponentRole(isEncoder, mime);
+ if (role == NULL) {
+ return BAD_VALUE;
+ }
+ status_t err = setComponentRole(mOMX, mNode, role);
+ if (err != OK) {
+ ALOGW("[%s] Failed to set standard component role '%s'.",
+ mComponentName.c_str(), role);
+ }
+ return err;
+}
+
+//static
+const char *ACodec::getComponentRole(
+ bool isEncoder, const char *mime) {
struct MimeToRole {
const char *mime;
const char *decoderRole;
@@ -1594,35 +1613,27 @@
}
if (i == kNumMimeToRole) {
- return ERROR_UNSUPPORTED;
+ return NULL;
}
- const char *role =
- isEncoder ? kMimeToRole[i].encoderRole
+ return isEncoder ? kMimeToRole[i].encoderRole
: kMimeToRole[i].decoderRole;
+}
- if (role != NULL) {
- OMX_PARAM_COMPONENTROLETYPE roleParams;
- InitOMXParams(&roleParams);
+//static
+status_t ACodec::setComponentRole(
+ const sp<IOMX> &omx, IOMX::node_id node, const char *role) {
+ OMX_PARAM_COMPONENTROLETYPE roleParams;
+ InitOMXParams(&roleParams);
- strncpy((char *)roleParams.cRole,
- role, OMX_MAX_STRINGNAME_SIZE - 1);
+ strncpy((char *)roleParams.cRole,
+ role, OMX_MAX_STRINGNAME_SIZE - 1);
- roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+ roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
- status_t err = mOMX->setParameter(
- mNode, OMX_IndexParamStandardComponentRole,
- &roleParams, sizeof(roleParams));
-
- if (err != OK) {
- ALOGW("[%s] Failed to set standard component role '%s'.",
- mComponentName.c_str(), role);
-
- return err;
- }
- }
-
- return OK;
+ return omx->setParameter(
+ node, OMX_IndexParamStandardComponentRole,
+ &roleParams, sizeof(roleParams));
}
status_t ACodec::configureCodec(
@@ -2373,9 +2384,8 @@
InitOMXParams(&format);
format.nPortIndex = portIndex;
- for (OMX_U32 index = 0;; ++index) {
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
format.nIndex = index;
-
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamAudioPortFormat,
&format, sizeof(format));
@@ -2387,6 +2397,13 @@
if (format.eEncoding == desiredFormat) {
break;
}
+
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking formats after %u: %s(%x)",
+ mComponentName.c_str(), index,
+ asString(format.eEncoding), format.eEncoding);
+ return ERROR_UNSUPPORTED;
+ }
}
return mOMX->setParameter(
@@ -2806,8 +2823,7 @@
format.nIndex = 0;
bool found = false;
- OMX_U32 index = 0;
- for (;;) {
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
format.nIndex = index;
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamVideoPortFormat,
@@ -2852,7 +2868,12 @@
break;
}
- ++index;
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking formats after %u: %s(%x)/%s(%x)",
+ mComponentName.c_str(), index,
+ asString(format.eCompressionFormat), format.eCompressionFormat,
+ asString(format.eColorFormat), format.eColorFormat);
+ }
}
if (!found) {
@@ -3737,7 +3758,8 @@
InitOMXParams(¶ms);
params.nPortIndex = kPortIndexOutput;
- for (params.nProfileIndex = 0;; ++params.nProfileIndex) {
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
+ params.nProfileIndex = index;
status_t err = mOMX->getParameter(
mNode,
OMX_IndexParamVideoProfileLevelQuerySupported,
@@ -3754,7 +3776,14 @@
if (profile == supportedProfile && level <= supportedLevel) {
return OK;
}
+
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking profiles after %u: %x/%x",
+ mComponentName.c_str(), index,
+ params.eProfile, params.eLevel);
+ }
}
+ return ERROR_UNSUPPORTED;
}
status_t ACodec::configureBitrate(
@@ -7004,6 +7033,168 @@
}
}
+status_t ACodec::queryCapabilities(
+ const AString &name, const AString &mime, bool isEncoder,
+ sp<MediaCodecInfo::Capabilities> *caps) {
+ (*caps).clear();
+ const char *role = getComponentRole(isEncoder, mime.c_str());
+ if (role == NULL) {
+ return BAD_VALUE;
+ }
+
+ OMXClient client;
+ status_t err = client.connect();
+ if (err != OK) {
+ return err;
+ }
+
+ sp<IOMX> omx = client.interface();
+ sp<CodecObserver> observer = new CodecObserver;
+ IOMX::node_id node = 0;
+
+ err = omx->allocateNode(name.c_str(), observer, &node);
+ if (err != OK) {
+ client.disconnect();
+ return err;
+ }
+
+ err = setComponentRole(omx, node, role);
+ if (err != OK) {
+ omx->freeNode(node);
+ client.disconnect();
+ return err;
+ }
+
+ sp<MediaCodecInfo::CapabilitiesBuilder> builder = new MediaCodecInfo::CapabilitiesBuilder();
+ bool isVideo = mime.startsWithIgnoreCase("video/");
+
+ if (isVideo) {
+ OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
+ InitOMXParams(¶m);
+ param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
+
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
+ param.nProfileIndex = index;
+ status_t err = omx->getParameter(
+ node, OMX_IndexParamVideoProfileLevelQuerySupported,
+ ¶m, sizeof(param));
+ if (err != OK) {
+ break;
+ }
+ builder->addProfileLevel(param.eProfile, param.eLevel);
+
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking profiles after %u: %x/%x",
+ name.c_str(), index,
+ param.eProfile, param.eLevel);
+ }
+ }
+
+ // Color format query
+ // return colors in the order reported by the OMX component
+ // prefix "flexible" standard ones with the flexible equivalent
+ OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat;
+ InitOMXParams(&portFormat);
+ param.nPortIndex = isEncoder ? kPortIndexInput : kPortIndexOutput;
+ Vector<uint32_t> supportedColors; // shadow copy to check for duplicates
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
+ portFormat.nIndex = index;
+ status_t err = omx->getParameter(
+ node, OMX_IndexParamVideoPortFormat,
+ &portFormat, sizeof(portFormat));
+ if (err != OK) {
+ break;
+ }
+
+ OMX_U32 flexibleEquivalent;
+ if (isFlexibleColorFormat(
+ omx, node, portFormat.eColorFormat, false /* usingNativeWindow */,
+ &flexibleEquivalent)) {
+ bool marked = false;
+ for (size_t i = 0; i < supportedColors.size(); ++i) {
+ if (supportedColors[i] == flexibleEquivalent) {
+ marked = true;
+ break;
+ }
+ }
+ if (!marked) {
+ supportedColors.push(flexibleEquivalent);
+ builder->addColorFormat(flexibleEquivalent);
+ }
+ }
+ supportedColors.push(portFormat.eColorFormat);
+ builder->addColorFormat(portFormat.eColorFormat);
+
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking formats after %u: %s(%x)",
+ name.c_str(), index,
+ asString(portFormat.eColorFormat), portFormat.eColorFormat);
+ }
+ }
+ } else if (mime.equalsIgnoreCase(MEDIA_MIMETYPE_AUDIO_AAC)) {
+ // More audio codecs if they have profiles.
+ OMX_AUDIO_PARAM_ANDROID_PROFILETYPE param;
+ InitOMXParams(¶m);
+ param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
+ param.nProfileIndex = index;
+ status_t err = omx->getParameter(
+ node, (OMX_INDEXTYPE)OMX_IndexParamAudioProfileQuerySupported,
+ ¶m, sizeof(param));
+ if (err != OK) {
+ break;
+ }
+ // For audio, level is ignored.
+ builder->addProfileLevel(param.eProfile, 0 /* level */);
+
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking profiles after %u: %x",
+ name.c_str(), index,
+ param.eProfile);
+ }
+ }
+
+ // NOTE: Without Android extensions, OMX does not provide a way to query
+ // AAC profile support
+ if (param.nProfileIndex == 0) {
+ ALOGW("component %s doesn't support profile query.", name.c_str());
+ }
+ }
+
+ if (isVideo && !isEncoder) {
+ native_handle_t *sidebandHandle = NULL;
+ if (omx->configureVideoTunnelMode(
+ node, kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) {
+ // tunneled playback includes adaptive playback
+ builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback
+ | MediaCodecInfo::Capabilities::kFlagSupportsTunneledPlayback);
+ } else if (omx->storeMetaDataInBuffers(
+ node, kPortIndexOutput, OMX_TRUE) == OK ||
+ omx->prepareForAdaptivePlayback(
+ node, kPortIndexOutput, OMX_TRUE,
+ 1280 /* width */, 720 /* height */) == OK) {
+ builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback);
+ }
+ }
+
+ if (isVideo && isEncoder) {
+ OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE params;
+ InitOMXParams(¶ms);
+ params.nPortIndex = kPortIndexOutput;
+ // TODO: should we verify if fallback is supported?
+ if (omx->getConfig(
+ node, (OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh,
+ ¶ms, sizeof(params)) == OK) {
+ builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsIntraRefresh);
+ }
+ }
+
+ *caps = builder;
+ omx->freeNode(node);
+ client.disconnect();
+ return OK;
+}
+
// These are supposed be equivalent to the logic in
// "audio_channel_out_mask_from_count".
//static
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index f297256..2df69b4 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -13,7 +13,6 @@
CallbackDataSource.cpp \
CameraSource.cpp \
CameraSourceTimeLapse.cpp \
- ClockEstimator.cpp \
CodecBase.cpp \
DataSource.cpp \
DataURISource.cpp \
@@ -58,7 +57,6 @@
SurfaceMediaSource.cpp \
SurfaceUtils.cpp \
ThrottledSource.cpp \
- TimeSource.cpp \
Utils.cpp \
VBRISeeker.cpp \
VideoFrameScheduler.cpp \
diff --git a/media/libstagefright/ClockEstimator.cpp b/media/libstagefright/ClockEstimator.cpp
deleted file mode 100644
index 34d1e42..0000000
--- a/media/libstagefright/ClockEstimator.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
-**
-** Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "ClockEstimator"
-#include <utils/Log.h>
-
-#include <math.h>
-#include <media/stagefright/ClockEstimator.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-
-namespace android {
-
-WindowedLinearFitEstimator::WindowedLinearFitEstimator(
- size_t headLength, double headFactor, size_t mainLength, double tailFactor)
- : mHeadFactorInv(1. / headFactor),
- mTailFactor(tailFactor),
- mHistoryLength(mainLength + headLength),
- mHeadLength(headLength) {
- reset();
- mXHistory.resize(mHistoryLength);
- mYHistory.resize(mHistoryLength);
- mFirstWeight = pow(headFactor, mHeadLength);
-}
-
-WindowedLinearFitEstimator::LinearFit::LinearFit() {
- reset();
-}
-
-void WindowedLinearFitEstimator::LinearFit::reset() {
- mX = mXX = mY = mYY = mXY = mW = 0.;
-}
-
-double WindowedLinearFitEstimator::LinearFit::size() const {
- double s = mW * mW + mX * mX + mY * mY + mXX * mXX + mXY * mXY + mYY * mYY;
- if (s > 1e72) {
- // 1e72 corresponds to clock monotonic time of about 8 years
- ALOGW("estimator is overflowing: w=%g x=%g y=%g xx=%g xy=%g yy=%g",
- mW, mX, mY, mXX, mXY, mYY);
- }
- return s;
-}
-
-void WindowedLinearFitEstimator::LinearFit::add(double x, double y, double w) {
- mW += w;
- mX += w * x;
- mY += w * y;
- mXX += w * x * x;
- mXY += w * x * y;
- mYY += w * y * y;
-}
-
-void WindowedLinearFitEstimator::LinearFit::combine(const LinearFit &lf) {
- mW += lf.mW;
- mX += lf.mX;
- mY += lf.mY;
- mXX += lf.mXX;
- mXY += lf.mXY;
- mYY += lf.mYY;
-}
-
-void WindowedLinearFitEstimator::LinearFit::scale(double w) {
- mW *= w;
- mX *= w;
- mY *= w;
- mXX *= w;
- mXY *= w;
- mYY *= w;
-}
-
-double WindowedLinearFitEstimator::LinearFit::interpolate(double x) {
- double div = mW * mXX - mX * mX;
- if (fabs(div) < 1e-5 * mW * mW) {
- // this only should happen on the first value
- return x;
- // assuming a = 1, we could also return x + (mY - mX) / mW;
- }
- double a_div = (mW * mXY - mX * mY);
- double b_div = (mXX * mY - mX * mXY);
- ALOGV("a=%.4g b=%.4g in=%g out=%g",
- a_div / div, b_div / div, x, (a_div * x + b_div) / div);
- return (a_div * x + b_div) / div;
-}
-
-double WindowedLinearFitEstimator::estimate(double x, double y) {
- /*
- * TODO: We could update the head by adding the new sample to it
- * and amplifying it, but this approach can lead to unbounded
- * error. Instead, we recalculate the head at each step, which
- * is computationally more expensive. We could balance the two
- * methods by recalculating just before the error becomes
- * significant.
- */
- const bool update_head = false;
- if (update_head) {
- // add new sample to the head
- mHead.scale(mHeadFactorInv); // amplify head
- mHead.add(x, y, mFirstWeight);
- }
-
- /*
- * TRICKY: place elements into the circular buffer at decreasing
- * indices, so that we can access past elements by addition
- * (thereby avoiding potentially negative indices.)
- */
- if (mNumSamples >= mHeadLength) {
- // move last head sample from head to the main window
- size_t lastHeadIx = (mSampleIx + mHeadLength) % mHistoryLength;
- if (update_head) {
- mHead.add(mXHistory[lastHeadIx], mYHistory[lastHeadIx], -1.); // remove
- }
- mMain.add(mXHistory[lastHeadIx], mYHistory[lastHeadIx], 1.);
- if (mNumSamples >= mHistoryLength) {
- // move last main sample from main window to tail
- mMain.add(mXHistory[mSampleIx], mYHistory[mSampleIx], -1.); // remove
- mTail.add(mXHistory[mSampleIx], mYHistory[mSampleIx], 1.);
- mTail.scale(mTailFactor); // attenuate tail
- }
- }
-
- mXHistory.editItemAt(mSampleIx) = x;
- mYHistory.editItemAt(mSampleIx) = y;
- if (mNumSamples < mHistoryLength) {
- ++mNumSamples;
- }
-
- // recalculate head unless we were using the update method
- if (!update_head) {
- mHead.reset();
- double w = mFirstWeight;
- for (size_t headIx = 0; headIx < mHeadLength && headIx < mNumSamples; ++headIx) {
- size_t ix = (mSampleIx + headIx) % mHistoryLength;
- mHead.add(mXHistory[ix], mYHistory[ix], w);
- w *= mHeadFactorInv;
- }
- }
-
- if (mSampleIx > 0) {
- --mSampleIx;
- } else {
- mSampleIx = mHistoryLength - 1;
- }
-
- // return estimation result
- LinearFit total;
- total.combine(mHead);
- total.combine(mMain);
- total.combine(mTail);
- return total.interpolate(x);
-}
-
-void WindowedLinearFitEstimator::reset() {
- mHead.reset();
- mMain.reset();
- mTail.reset();
- mNumSamples = 0;
- mSampleIx = mHistoryLength - 1;
-}
-
-}; // namespace android
-
-
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 8893e9c..ce67d78 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -193,6 +193,22 @@
}
// static
+status_t MediaCodec::QueryCapabilities(
+ const AString &name, const AString &mime, bool isEncoder,
+ sp<MediaCodecInfo::Capabilities> *caps /* nonnull */) {
+ // TRICKY: this method is used by MediaCodecList/Info during its
+ // initialization. As such, we cannot create a MediaCodec instance
+ // because that requires an initialized MediaCodecList.
+
+ sp<CodecBase> codec = GetCodecBase(name);
+ if (codec == NULL) {
+ return NAME_NOT_FOUND;
+ }
+
+ return codec->queryCapabilities(name, mime, isEncoder, caps);
+}
+
+// static
sp<PersistentSurface> MediaCodec::CreatePersistentInputSurface() {
OMXClient client;
CHECK_EQ(client.connect(), (status_t)OK);
@@ -298,6 +314,18 @@
response->postReply(replyID);
}
+//static
+sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, bool nameIsType) {
+ // at this time only ACodec specifies a mime type.
+ if (nameIsType || name.startsWithIgnoreCase("omx.")) {
+ return new ACodec;
+ } else if (name.startsWithIgnoreCase("android.filter.")) {
+ return new MediaFilter;
+ } else {
+ return NULL;
+ }
+}
+
status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
mResourceManagerService->init();
@@ -311,12 +339,8 @@
// we need to invest in an extra looper to free the main event
// queue.
- if (nameIsType || !strncasecmp(name.c_str(), "omx.", 4)) {
- mCodec = new ACodec;
- } else if (!nameIsType
- && !strncasecmp(name.c_str(), "android.filter.", 15)) {
- mCodec = new MediaFilter;
- } else {
+ mCodec = GetCodecBase(name, nameIsType);
+ if (mCodec == NULL) {
return NAME_NOT_FOUND;
}
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index c049097..200796c 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -31,10 +31,10 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
#include <sys/stat.h>
#include <utils/threads.h>
@@ -752,18 +752,22 @@
ALOGV("initializeCapabilities %s:%s",
mCurrentInfo->mName.c_str(), type);
- CodecCapabilities caps;
- status_t err = QueryCodec(
- mOMX,
- mCurrentInfo->mName.c_str(),
+ sp<MediaCodecInfo::Capabilities> caps;
+ status_t err = MediaCodec::QueryCapabilities(
+ mCurrentInfo->mName,
type,
mCurrentInfo->mIsEncoder,
&caps);
if (err != OK) {
return err;
+ } else if (caps == NULL) {
+ ALOGE("MediaCodec::QueryCapabilities returned OK but no capabilities for '%s':'%s':'%s'",
+ mCurrentInfo->mName.c_str(), type,
+ mCurrentInfo->mIsEncoder ? "encoder" : "decoder");
+ return UNKNOWN_ERROR;
}
- return mCurrentInfo->initializeCapabilities(caps);
+ return mCurrentInfo->setCapabilitiesFromCodec(caps);
}
status_t MediaCodecList::addQuirk(const char **attrs) {
diff --git a/media/libstagefright/TimeSource.cpp b/media/libstagefright/TimeSource.cpp
deleted file mode 100644
index 041980f..0000000
--- a/media/libstagefright/TimeSource.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stddef.h>
-#include <sys/time.h>
-
-#include <media/stagefright/foundation/ALooper.h>
-#include <media/stagefright/TimeSource.h>
-
-namespace android {
-
-SystemTimeSource::SystemTimeSource()
- : mStartTimeUs(ALooper::GetNowUs()) {
-}
-
-int64_t SystemTimeSource::getRealTimeUs() {
- return ALooper::GetNowUs() - mStartTimeUs;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp
index ae3cb33..6132a2c 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXMaster.cpp
@@ -23,6 +23,7 @@
#include "SoftOMXPlugin.h"
#include <dlfcn.h>
+#include <fcntl.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -30,6 +31,29 @@
OMXMaster::OMXMaster()
: mVendorLibHandle(NULL) {
+
+ mProcessName[0] = 0;
+ if (mProcessName[0] == 0) {
+ pid_t pid = getpid();
+ char filename[20];
+ snprintf(filename, sizeof(filename), "/proc/%d/comm", pid);
+ int fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ ALOGW("couldn't determine process name");
+ sprintf(mProcessName, "<unknown>");
+ } else {
+ ssize_t len = read(fd, mProcessName, sizeof(mProcessName));
+ if (len < 2) {
+ ALOGW("couldn't determine process name");
+ sprintf(mProcessName, "<unknown>");
+ } else {
+ // the name is newline terminated, so erase the newline
+ mProcessName[len - 1] = 0;
+ }
+ close(fd);
+ }
+ }
+
addVendorPlugin();
addPlugin(new SoftOMXPlugin);
}
@@ -123,6 +147,7 @@
const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData,
OMX_COMPONENTTYPE **component) {
+ ALOGI("makeComponentInstance(%s) in %s process", name, mProcessName);
Mutex::Autolock autoLock(mLock);
*component = NULL;
diff --git a/media/libstagefright/omx/OMXMaster.h b/media/libstagefright/omx/OMXMaster.h
index 6069741..3f9c0ca 100644
--- a/media/libstagefright/omx/OMXMaster.h
+++ b/media/libstagefright/omx/OMXMaster.h
@@ -50,6 +50,7 @@
Vector<String8> *roles);
private:
+ char mProcessName[16];
Mutex mLock;
List<OMXPluginBase *> mPlugins;
KeyedVector<String8, OMXPluginBase *> mPluginByComponentName;
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 5159de3..6e21c14 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -27,7 +27,7 @@
#include <binder/IServiceManager.h>
#include <binder/MemoryDealer.h>
#include <media/IMediaHTTPService.h>
-#include <media/IMediaPlayerService.h>
+#include <media/IMediaCodecService.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/DataSource.h>
@@ -57,8 +57,8 @@
status_t Harness::initOMX() {
sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.player"));
- sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+ sp<IBinder> binder = sm->getService(String16("media.codec"));
+ sp<IMediaCodecService> service = interface_cast<IMediaCodecService>(binder);
mOMX = service->getOMX();
return mOMX != 0 ? OK : NO_INIT;
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 3932339..eedbb42 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -1135,6 +1135,7 @@
result, strerror(-result));
mCheckPending = false;
+ ++mCheckGeneration;
postAccessUnitTimeoutCheck();
if (result == OK) {
@@ -1286,6 +1287,7 @@
result, strerror(-result));
mCheckPending = false;
+ ++mCheckGeneration;
postAccessUnitTimeoutCheck();
if (result == OK) {
diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk
index 4d4ffba..f2c6365 100644
--- a/media/libstagefright/timedtext/Android.mk
+++ b/media/libstagefright/timedtext/Android.mk
@@ -3,11 +3,6 @@
LOCAL_SRC_FILES:= \
TextDescriptions.cpp \
- TimedTextDriver.cpp \
- TimedText3GPPSource.cpp \
- TimedTextSource.cpp \
- TimedTextSRTSource.cpp \
- TimedTextPlayer.cpp
LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
LOCAL_CLANG := true
diff --git a/media/libstagefright/timedtext/TimedText3GPPSource.cpp b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
deleted file mode 100644
index 0c8fb79..0000000
--- a/media/libstagefright/timedtext/TimedText3GPPSource.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
- /*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "TimedText3GPPSource"
-#include <utils/Log.h>
-
-#include <binder/Parcel.h>
-#include <media/stagefright/foundation/ADebug.h> // CHECK_XX macro
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-
-#include "TimedText3GPPSource.h"
-#include "TextDescriptions.h"
-
-namespace android {
-
-TimedText3GPPSource::TimedText3GPPSource(const sp<IMediaSource>& mediaSource)
- : mSource(mediaSource) {
-}
-
-TimedText3GPPSource::~TimedText3GPPSource() {
-}
-
-status_t TimedText3GPPSource::read(
- int64_t *startTimeUs, int64_t *endTimeUs, Parcel *parcel,
- const MediaSource::ReadOptions *options) {
- MediaBuffer *textBuffer = NULL;
- status_t err = mSource->read(&textBuffer, options);
- if (err != OK) {
- return err;
- }
- CHECK(textBuffer != NULL);
- textBuffer->meta_data()->findInt64(kKeyTime, startTimeUs);
- CHECK_GE(*startTimeUs, 0);
- extractAndAppendLocalDescriptions(*startTimeUs, textBuffer, parcel);
- textBuffer->release();
- // endTimeUs is a dummy parameter for 3gpp timed text format.
- // Set a negative value to it to mark it is unavailable.
- *endTimeUs = -1;
- return OK;
-}
-
-// Each text sample consists of a string of text, optionally with sample
-// modifier description. The modifier description could specify a new
-// text style for the string of text. These descriptions are present only
-// if they are needed. This method is used to extract the modifier
-// description and append it at the end of the text.
-status_t TimedText3GPPSource::extractAndAppendLocalDescriptions(
- int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel) {
- const void *data;
- size_t size = 0;
- int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS;
-
- const char *mime;
- CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
- CHECK(strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0);
-
- data = textBuffer->data();
- size = textBuffer->size();
-
- if (size > 0) {
- parcel->freeData();
- flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
- return TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, timeUs / 1000, parcel);
- }
- return OK;
-}
-
-// To extract and send the global text descriptions for all the text samples
-// in the text track or text file.
-// TODO: send error message to application via notifyListener()...?
-status_t TimedText3GPPSource::extractGlobalDescriptions(Parcel *parcel) {
- const void *data;
- size_t size = 0;
- int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS;
-
- const char *mime;
- CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
- CHECK(strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0);
-
- uint32_t type;
- // get the 'tx3g' box content. This box contains the text descriptions
- // used to render the text track
- if (!mSource->getFormat()->findData(
- kKeyTextFormatData, &type, &data, &size)) {
- return ERROR_MALFORMED;
- }
-
- if (size > 0) {
- flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
- return TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, 0, parcel);
- }
- return OK;
-}
-
-sp<MetaData> TimedText3GPPSource::getFormat() {
- return mSource->getFormat();
-}
-
-} // namespace android
diff --git a/media/libstagefright/timedtext/TimedText3GPPSource.h b/media/libstagefright/timedtext/TimedText3GPPSource.h
deleted file mode 100644
index fdc79ca..0000000
--- a/media/libstagefright/timedtext/TimedText3GPPSource.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TIMED_TEXT_3GPP_SOURCE_H_
-#define TIMED_TEXT_3GPP_SOURCE_H_
-
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-
-#include "TimedTextSource.h"
-
-namespace android {
-
-class MediaBuffer;
-class Parcel;
-
-class TimedText3GPPSource : public TimedTextSource {
-public:
- TimedText3GPPSource(const sp<IMediaSource>& mediaSource);
- virtual status_t start() { return mSource->start(); }
- virtual status_t stop() { return mSource->stop(); }
- virtual status_t read(
- int64_t *startTimeUs,
- int64_t *endTimeUs,
- Parcel *parcel,
- const MediaSource::ReadOptions *options = NULL);
- virtual status_t extractGlobalDescriptions(Parcel *parcel);
- virtual sp<MetaData> getFormat();
-
-protected:
- virtual ~TimedText3GPPSource();
-
-private:
- sp<IMediaSource> mSource;
-
- status_t extractAndAppendLocalDescriptions(
- int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel);
-
- DISALLOW_EVIL_CONSTRUCTORS(TimedText3GPPSource);
-};
-
-} // namespace android
-
-#endif // TIMED_TEXT_3GPP_SOURCE_H_
diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp
deleted file mode 100644
index fd0eebb..0000000
--- a/media/libstagefright/timedtext/TimedTextDriver.cpp
+++ /dev/null
@@ -1,287 +0,0 @@
- /*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "TimedTextDriver"
-#include <utils/Log.h>
-
-#include <binder/IPCThreadState.h>
-
-#include <media/IMediaHTTPService.h>
-#include <media/mediaplayer.h>
-#include <media/MediaPlayerInterface.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/FileSource.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/Utils.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/ALooper.h>
-#include <media/stagefright/timedtext/TimedTextDriver.h>
-
-#include "TextDescriptions.h"
-#include "TimedTextPlayer.h"
-#include "TimedTextSource.h"
-
-namespace android {
-
-TimedTextDriver::TimedTextDriver(
- const wp<MediaPlayerBase> &listener,
- const sp<IMediaHTTPService> &httpService)
- : mLooper(new ALooper),
- mListener(listener),
- mHTTPService(httpService),
- mState(UNINITIALIZED),
- mCurrentTrackIndex(UINT_MAX) {
- mLooper->setName("TimedTextDriver");
- mLooper->start();
- mPlayer = new TimedTextPlayer(listener);
- mLooper->registerHandler(mPlayer);
-}
-
-TimedTextDriver::~TimedTextDriver() {
- mTextSourceVector.clear();
- mTextSourceTypeVector.clear();
- mLooper->stop();
-}
-
-status_t TimedTextDriver::selectTrack_l(size_t index) {
- if (mCurrentTrackIndex == index) {
- return OK;
- }
- sp<TimedTextSource> source;
- source = mTextSourceVector.valueFor(index);
- mPlayer->setDataSource(source);
- if (mState == UNINITIALIZED) {
- mState = PREPARED;
- }
- mCurrentTrackIndex = index;
- return OK;
-}
-
-status_t TimedTextDriver::start() {
- Mutex::Autolock autoLock(mLock);
- switch (mState) {
- case UNINITIALIZED:
- return INVALID_OPERATION;
- case PLAYING:
- return OK;
- case PREPARED:
- mPlayer->start();
- mState = PLAYING;
- return OK;
- case PAUSED:
- mPlayer->resume();
- mState = PLAYING;
- return OK;
- default:
- TRESPASS();
- }
- return UNKNOWN_ERROR;
-}
-
-status_t TimedTextDriver::pause() {
- Mutex::Autolock autoLock(mLock);
- ALOGV("%s() is called", __FUNCTION__);
- switch (mState) {
- case UNINITIALIZED:
- return INVALID_OPERATION;
- case PLAYING:
- mPlayer->pause();
- mState = PAUSED;
- return OK;
- case PREPARED:
- return INVALID_OPERATION;
- case PAUSED:
- return OK;
- default:
- TRESPASS();
- }
- return UNKNOWN_ERROR;
-}
-
-status_t TimedTextDriver::selectTrack(size_t index) {
- status_t ret = OK;
- Mutex::Autolock autoLock(mLock);
- ALOGV("%s() is called", __FUNCTION__);
- switch (mState) {
- case UNINITIALIZED:
- case PREPARED:
- case PAUSED:
- ret = selectTrack_l(index);
- break;
- case PLAYING:
- mPlayer->pause();
- ret = selectTrack_l(index);
- if (ret != OK) {
- break;
- }
- mPlayer->start();
- break;
- default:
- TRESPASS();
- }
- return ret;
-}
-
-status_t TimedTextDriver::unselectTrack(size_t index) {
- Mutex::Autolock autoLock(mLock);
- ALOGV("%s() is called", __FUNCTION__);
- if (mCurrentTrackIndex != index) {
- return INVALID_OPERATION;
- }
- mCurrentTrackIndex = UINT_MAX;
- switch (mState) {
- case UNINITIALIZED:
- return INVALID_OPERATION;
- case PLAYING:
- mPlayer->setDataSource(NULL);
- mState = UNINITIALIZED;
- return OK;
- case PREPARED:
- case PAUSED:
- mState = UNINITIALIZED;
- return OK;
- default:
- TRESPASS();
- }
- return UNKNOWN_ERROR;
-}
-
-status_t TimedTextDriver::seekToAsync(int64_t timeUs) {
- Mutex::Autolock autoLock(mLock);
- ALOGV("%s() is called", __FUNCTION__);
- switch (mState) {
- case UNINITIALIZED:
- return INVALID_OPERATION;
- case PREPARED:
- mPlayer->seekToAsync(timeUs);
- mPlayer->pause();
- mState = PAUSED;
- return OK;
- case PAUSED:
- mPlayer->seekToAsync(timeUs);
- mPlayer->pause();
- return OK;
- case PLAYING:
- mPlayer->seekToAsync(timeUs);
- return OK;
- default:
- TRESPASS();
- }
- return UNKNOWN_ERROR;
-}
-
-status_t TimedTextDriver::addInBandTextSource(
- size_t trackIndex, const sp<IMediaSource>& mediaSource) {
- sp<TimedTextSource> source =
- TimedTextSource::CreateTimedTextSource(mediaSource);
- if (source == NULL) {
- return ERROR_UNSUPPORTED;
- }
- Mutex::Autolock autoLock(mLock);
- mTextSourceVector.add(trackIndex, source);
- mTextSourceTypeVector.add(TEXT_SOURCE_TYPE_IN_BAND);
- return OK;
-}
-
-status_t TimedTextDriver::addOutOfBandTextSource(
- size_t trackIndex, const char *uri, const char *mimeType) {
-
- // To support local subtitle file only for now
- if (strncasecmp("file://", uri, 7)) {
- ALOGE("uri('%s') is not a file", uri);
- return ERROR_UNSUPPORTED;
- }
-
- sp<DataSource> dataSource =
- DataSource::CreateFromURI(mHTTPService, uri);
- return createOutOfBandTextSource(trackIndex, mimeType, dataSource);
-}
-
-status_t TimedTextDriver::addOutOfBandTextSource(
- size_t trackIndex, int fd, off64_t offset, off64_t length, const char *mimeType) {
-
- if (fd < 0) {
- ALOGE("Invalid file descriptor: %d", fd);
- return ERROR_UNSUPPORTED;
- }
-
- sp<DataSource> dataSource = new FileSource(dup(fd), offset, length);
- return createOutOfBandTextSource(trackIndex, mimeType, dataSource);
-}
-
-status_t TimedTextDriver::createOutOfBandTextSource(
- size_t trackIndex,
- const char *mimeType,
- const sp<DataSource>& dataSource) {
-
- if (dataSource == NULL) {
- return ERROR_UNSUPPORTED;
- }
-
- sp<TimedTextSource> source;
- if (strcasecmp(mimeType, MEDIA_MIMETYPE_TEXT_SUBRIP) == 0) {
- source = TimedTextSource::CreateTimedTextSource(
- dataSource, TimedTextSource::OUT_OF_BAND_FILE_SRT);
- }
-
- if (source == NULL) {
- ALOGE("Failed to create timed text source");
- return ERROR_UNSUPPORTED;
- }
-
- Mutex::Autolock autoLock(mLock);
- mTextSourceVector.add(trackIndex, source);
- mTextSourceTypeVector.add(TEXT_SOURCE_TYPE_OUT_OF_BAND);
- return OK;
-}
-
-size_t TimedTextDriver::countExternalTracks() const {
- size_t nTracks = 0;
- for (size_t i = 0, n = mTextSourceTypeVector.size(); i < n; ++i) {
- if (mTextSourceTypeVector[i] == TEXT_SOURCE_TYPE_OUT_OF_BAND) {
- ++nTracks;
- }
- }
- return nTracks;
-}
-
-void TimedTextDriver::getExternalTrackInfo(Parcel *parcel) {
- Mutex::Autolock autoLock(mLock);
- for (size_t i = 0, n = mTextSourceTypeVector.size(); i < n; ++i) {
- if (mTextSourceTypeVector[i] == TEXT_SOURCE_TYPE_IN_BAND) {
- continue;
- }
-
- sp<MetaData> meta = mTextSourceVector.valueAt(i)->getFormat();
-
- // There are two fields.
- parcel->writeInt32(2);
-
- // track type.
- parcel->writeInt32(MEDIA_TRACK_TYPE_TIMEDTEXT);
- const char *lang = "und";
- if (meta != NULL) {
- meta->findCString(kKeyMediaLanguage, &lang);
- }
- parcel->writeString16(String16(lang));
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
deleted file mode 100644
index aecf666..0000000
--- a/media/libstagefright/timedtext/TimedTextPlayer.cpp
+++ /dev/null
@@ -1,316 +0,0 @@
- /*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "TimedTextPlayer"
-#include <utils/Log.h>
-
-#include <inttypes.h>
-#include <limits.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/timedtext/TimedTextDriver.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/MediaPlayerInterface.h>
-
-#include "TimedTextPlayer.h"
-
-#include "TimedTextSource.h"
-
-namespace android {
-
-// Event should be fired a bit earlier considering the processing time till
-// application actually gets the notification message.
-static const int64_t kAdjustmentProcessingTimeUs = 100000ll;
-static const int64_t kMaxDelayUs = 5000000ll;
-static const int64_t kWaitTimeUsToRetryRead = 100000ll;
-static const int64_t kInvalidTimeUs = INT_MIN;
-
-TimedTextPlayer::TimedTextPlayer(const wp<MediaPlayerBase> &listener)
- : mListener(listener),
- mSource(NULL),
- mPendingSeekTimeUs(kInvalidTimeUs),
- mPaused(false),
- mSendSubtitleGeneration(0) {
-}
-
-TimedTextPlayer::~TimedTextPlayer() {
- if (mSource != NULL) {
- mSource->stop();
- mSource.clear();
- mSource = NULL;
- }
-}
-
-void TimedTextPlayer::start() {
- (new AMessage(kWhatStart, this))->post();
-}
-
-void TimedTextPlayer::pause() {
- (new AMessage(kWhatPause, this))->post();
-}
-
-void TimedTextPlayer::resume() {
- (new AMessage(kWhatResume, this))->post();
-}
-
-void TimedTextPlayer::seekToAsync(int64_t timeUs) {
- sp<AMessage> msg = new AMessage(kWhatSeek, this);
- msg->setInt64("seekTimeUs", timeUs);
- msg->post();
-}
-
-void TimedTextPlayer::setDataSource(sp<TimedTextSource> source) {
- sp<AMessage> msg = new AMessage(kWhatSetSource, this);
- msg->setObject("source", source);
- msg->post();
-}
-
-void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatPause: {
- mPaused = true;
- break;
- }
- case kWhatResume: {
- mPaused = false;
- if (mPendingSeekTimeUs != kInvalidTimeUs) {
- seekToAsync(mPendingSeekTimeUs);
- mPendingSeekTimeUs = kInvalidTimeUs;
- } else {
- doRead();
- }
- break;
- }
- case kWhatStart: {
- sp<MediaPlayerBase> listener = mListener.promote();
- if (listener == NULL) {
- ALOGE("Listener is NULL when kWhatStart is received.");
- break;
- }
- mPaused = false;
- mPendingSeekTimeUs = kInvalidTimeUs;
- int32_t positionMs = 0;
- listener->getCurrentPosition(&positionMs);
- int64_t seekTimeUs = positionMs * 1000ll;
-
- notifyListener();
- mSendSubtitleGeneration++;
- doSeekAndRead(seekTimeUs);
- break;
- }
- case kWhatRetryRead: {
- int32_t generation = -1;
- CHECK(msg->findInt32("generation", &generation));
- if (generation != mSendSubtitleGeneration) {
- // Drop obsolete msg.
- break;
- }
- int64_t seekTimeUs;
- int seekMode;
- if (msg->findInt64("seekTimeUs", &seekTimeUs) &&
- msg->findInt32("seekMode", &seekMode)) {
- MediaSource::ReadOptions options;
- options.setSeekTo(
- seekTimeUs,
- static_cast<MediaSource::ReadOptions::SeekMode>(seekMode));
- doRead(&options);
- } else {
- doRead();
- }
- break;
- }
- case kWhatSeek: {
- int64_t seekTimeUs = kInvalidTimeUs;
- // Clear a displayed timed text before seeking.
- notifyListener();
- msg->findInt64("seekTimeUs", &seekTimeUs);
- if (seekTimeUs == kInvalidTimeUs) {
- sp<MediaPlayerBase> listener = mListener.promote();
- if (listener != NULL) {
- int32_t positionMs = 0;
- listener->getCurrentPosition(&positionMs);
- seekTimeUs = positionMs * 1000ll;
- }
- }
- if (mPaused) {
- mPendingSeekTimeUs = seekTimeUs;
- break;
- }
- mSendSubtitleGeneration++;
- doSeekAndRead(seekTimeUs);
- break;
- }
- case kWhatSendSubtitle: {
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
- if (generation != mSendSubtitleGeneration) {
- // Drop obsolete msg.
- break;
- }
- // If current time doesn't reach to the fire time,
- // re-post the message with the adjusted delay time.
- int64_t fireTimeUs = kInvalidTimeUs;
- if (msg->findInt64("fireTimeUs", &fireTimeUs)) {
- // TODO: check if fireTimeUs is not kInvalidTimeUs.
- int64_t delayUs = delayUsFromCurrentTime(fireTimeUs);
- if (delayUs > 0) {
- msg->post(delayUs);
- break;
- }
- }
- sp<RefBase> obj;
- if (msg->findObject("subtitle", &obj)) {
- sp<ParcelEvent> parcelEvent;
- parcelEvent = static_cast<ParcelEvent*>(obj.get());
- notifyListener(&(parcelEvent->parcel));
- doRead();
- } else {
- notifyListener();
- }
- break;
- }
- case kWhatSetSource: {
- mSendSubtitleGeneration++;
- sp<RefBase> obj;
- msg->findObject("source", &obj);
- if (mSource != NULL) {
- mSource->stop();
- mSource.clear();
- mSource = NULL;
- }
- // null source means deselect track.
- if (obj == NULL) {
- mPendingSeekTimeUs = kInvalidTimeUs;
- mPaused = false;
- notifyListener();
- break;
- }
- mSource = static_cast<TimedTextSource*>(obj.get());
- status_t err = mSource->start();
- if (err != OK) {
- notifyError(err);
- break;
- }
- Parcel parcel;
- err = mSource->extractGlobalDescriptions(&parcel);
- if (err != OK) {
- notifyError(err);
- break;
- }
- notifyListener(&parcel);
- break;
- }
- }
-}
-
-void TimedTextPlayer::doSeekAndRead(int64_t seekTimeUs) {
- MediaSource::ReadOptions options;
- options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- doRead(&options);
-}
-
-void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) {
- int64_t startTimeUs = 0;
- int64_t endTimeUs = 0;
- sp<ParcelEvent> parcelEvent = new ParcelEvent();
- CHECK(mSource != NULL);
- status_t err = mSource->read(&startTimeUs, &endTimeUs,
- &(parcelEvent->parcel), options);
- if (err == WOULD_BLOCK) {
- sp<AMessage> msg = new AMessage(kWhatRetryRead, this);
- if (options != NULL) {
- int64_t seekTimeUs = kInvalidTimeUs;
- MediaSource::ReadOptions::SeekMode seekMode =
- MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
- CHECK(options->getSeekTo(&seekTimeUs, &seekMode));
- msg->setInt64("seekTimeUs", seekTimeUs);
- msg->setInt32("seekMode", seekMode);
- }
- msg->setInt32("generation", mSendSubtitleGeneration);
- msg->post(kWaitTimeUsToRetryRead);
- return;
- } else if (err != OK) {
- notifyError(err);
- return;
- }
-
- postTextEvent(parcelEvent, startTimeUs);
- if (endTimeUs > 0) {
- CHECK_GE(endTimeUs, startTimeUs);
- // send an empty timed text to clear the subtitle when it reaches to the
- // end time.
- postTextEvent(NULL, endTimeUs);
- }
-}
-
-void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) {
- int64_t delayUs = delayUsFromCurrentTime(timeUs);
- sp<AMessage> msg = new AMessage(kWhatSendSubtitle, this);
- msg->setInt32("generation", mSendSubtitleGeneration);
- if (parcel != NULL) {
- msg->setObject("subtitle", parcel);
- }
- msg->setInt64("fireTimeUs", timeUs);
- msg->post(delayUs);
-}
-
-int64_t TimedTextPlayer::delayUsFromCurrentTime(int64_t fireTimeUs) {
- sp<MediaPlayerBase> listener = mListener.promote();
- if (listener == NULL) {
- // TODO: it may be better to return kInvalidTimeUs
- ALOGE("%s: Listener is NULL. (fireTimeUs = %" PRId64" )",
- __FUNCTION__, fireTimeUs);
- return 0;
- }
- int32_t positionMs = 0;
- listener->getCurrentPosition(&positionMs);
- int64_t positionUs = positionMs * 1000ll;
-
- if (fireTimeUs <= positionUs + kAdjustmentProcessingTimeUs) {
- return 0;
- } else {
- int64_t delayUs = fireTimeUs - positionUs - kAdjustmentProcessingTimeUs;
- if (delayUs > kMaxDelayUs) {
- return kMaxDelayUs;
- }
- return delayUs;
- }
-}
-
-void TimedTextPlayer::notifyError(int error) {
- sp<MediaPlayerBase> listener = mListener.promote();
- if (listener == NULL) {
- ALOGE("%s(error=%d): Listener is NULL.", __FUNCTION__, error);
- return;
- }
- listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error);
-}
-
-void TimedTextPlayer::notifyListener(const Parcel *parcel) {
- sp<MediaPlayerBase> listener = mListener.promote();
- if (listener == NULL) {
- ALOGE("%s: Listener is NULL.", __FUNCTION__);
- return;
- }
- if (parcel != NULL && (parcel->dataSize() > 0)) {
- listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel);
- } else { // send an empty timed text to clear the screen
- listener->sendEvent(MEDIA_TIMED_TEXT);
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.h b/media/libstagefright/timedtext/TimedTextPlayer.h
deleted file mode 100644
index 9cb49ec..0000000
--- a/media/libstagefright/timedtext/TimedTextPlayer.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TIMEDTEXT_PLAYER_H_
-#define TIMEDTEXT_PLAYER_H_
-
-#include <binder/Parcel.h>
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AHandler.h>
-#include <media/stagefright/MediaSource.h>
-#include <utils/RefBase.h>
-
-#include "TimedTextSource.h"
-
-namespace android {
-
-struct AMessage;
-class MediaPlayerBase;
-class TimedTextDriver;
-class TimedTextSource;
-
-class TimedTextPlayer : public AHandler {
-public:
- TimedTextPlayer(const wp<MediaPlayerBase> &listener);
-
- virtual ~TimedTextPlayer();
-
- void start();
- void pause();
- void resume();
- void seekToAsync(int64_t timeUs);
- void setDataSource(sp<TimedTextSource> source);
-
-protected:
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatPause = 'paus',
- kWhatResume = 'resm',
- kWhatStart = 'strt',
- kWhatSeek = 'seek',
- kWhatRetryRead = 'read',
- kWhatSendSubtitle = 'send',
- kWhatSetSource = 'ssrc',
- };
-
- // To add Parcel into an AMessage as an object, it should be 'RefBase'.
- struct ParcelEvent : public RefBase {
- Parcel parcel;
- };
-
- wp<MediaPlayerBase> mListener;
- sp<TimedTextSource> mSource;
- int64_t mPendingSeekTimeUs;
- bool mPaused;
- int32_t mSendSubtitleGeneration;
-
- void doSeekAndRead(int64_t seekTimeUs);
- void doRead(MediaSource::ReadOptions* options = NULL);
- void onTextEvent();
- void postTextEvent(const sp<ParcelEvent>& parcel = NULL, int64_t timeUs = -1);
- int64_t delayUsFromCurrentTime(int64_t fireTimeUs);
- void notifyError(int error = 0);
- void notifyListener(const Parcel *parcel = NULL);
-
- DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer);
-};
-
-} // namespace android
-
-#endif // TIMEDTEXT_PLAYER_H_
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.cpp b/media/libstagefright/timedtext/TimedTextSRTSource.cpp
deleted file mode 100644
index 2ac1e72..0000000
--- a/media/libstagefright/timedtext/TimedTextSRTSource.cpp
+++ /dev/null
@@ -1,297 +0,0 @@
- /*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "TimedTextSRTSource"
-#include <utils/Log.h>
-
-#include <binder/Parcel.h>
-#include <media/stagefright/foundation/ADebug.h> // for CHECK_xx
-#include <media/stagefright/foundation/AString.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-
-#include "TimedTextSRTSource.h"
-#include "TextDescriptions.h"
-
-namespace android {
-
-TimedTextSRTSource::TimedTextSRTSource(const sp<DataSource>& dataSource)
- : mSource(dataSource),
- mMetaData(new MetaData),
- mIndex(0) {
- // TODO: Need to detect the language, because SRT doesn't give language
- // information explicitly.
- mMetaData->setCString(kKeyMediaLanguage, "und");
-}
-
-TimedTextSRTSource::~TimedTextSRTSource() {
-}
-
-status_t TimedTextSRTSource::start() {
- status_t err = scanFile();
- if (err != OK) {
- reset();
- }
- return err;
-}
-
-void TimedTextSRTSource::reset() {
- mTextVector.clear();
- mIndex = 0;
-}
-
-status_t TimedTextSRTSource::stop() {
- reset();
- return OK;
-}
-
-status_t TimedTextSRTSource::read(
- int64_t *startTimeUs,
- int64_t *endTimeUs,
- Parcel *parcel,
- const MediaSource::ReadOptions *options) {
- AString text;
- status_t err = getText(options, &text, startTimeUs, endTimeUs);
- if (err != OK) {
- return err;
- }
-
- CHECK_GE(*startTimeUs, 0);
- extractAndAppendLocalDescriptions(*startTimeUs, text, parcel);
- return OK;
-}
-
-sp<MetaData> TimedTextSRTSource::getFormat() {
- return mMetaData;
-}
-
-status_t TimedTextSRTSource::scanFile() {
- off64_t offset = 0;
- int64_t startTimeUs;
- bool endOfFile = false;
-
- while (!endOfFile) {
- TextInfo info;
- status_t err = getNextSubtitleInfo(&offset, &startTimeUs, &info);
- switch (err) {
- case OK:
- mTextVector.add(startTimeUs, info);
- break;
- case ERROR_END_OF_STREAM:
- endOfFile = true;
- break;
- default:
- return err;
- }
- }
- if (mTextVector.isEmpty()) {
- return ERROR_MALFORMED;
- }
- return OK;
-}
-
-/* SRT format:
- * Subtitle number
- * Start time --> End time
- * Text of subtitle (one or more lines)
- * Blank lines
- *
- * .srt file example:
- * 1
- * 00:00:20,000 --> 00:00:24,400
- * Altocumulus clouds occr between six thousand
- *
- * 2
- * 00:00:24,600 --> 00:00:27,800
- * and twenty thousand feet above ground level.
- */
-status_t TimedTextSRTSource::getNextSubtitleInfo(
- off64_t *offset, int64_t *startTimeUs, TextInfo *info) {
- AString data;
- status_t err;
-
- // To skip blank lines.
- do {
- if ((err = readNextLine(offset, &data)) != OK) {
- return err;
- }
- data.trim();
- } while (data.empty());
-
- // Just ignore the first non-blank line which is subtitle sequence number.
- if ((err = readNextLine(offset, &data)) != OK) {
- return err;
- }
- int hour1, hour2, min1, min2, sec1, sec2, msec1, msec2;
- // the start time format is: hours:minutes:seconds,milliseconds
- // 00:00:24,600 --> 00:00:27,800
- if (sscanf(data.c_str(), "%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d",
- &hour1, &min1, &sec1, &msec1, &hour2, &min2, &sec2, &msec2) != 8) {
- return ERROR_MALFORMED;
- }
-
- *startTimeUs = ((hour1 * 3600 + min1 * 60 + sec1) * 1000 + msec1) * 1000ll;
- info->endTimeUs = ((hour2 * 3600 + min2 * 60 + sec2) * 1000 + msec2) * 1000ll;
- if (info->endTimeUs <= *startTimeUs) {
- return ERROR_MALFORMED;
- }
-
- info->offset = *offset;
- bool needMoreData = true;
- while (needMoreData) {
- if ((err = readNextLine(offset, &data)) != OK) {
- if (err == ERROR_END_OF_STREAM) {
- break;
- } else {
- return err;
- }
- }
-
- data.trim();
- if (data.empty()) {
- // it's an empty line used to separate two subtitles
- needMoreData = false;
- }
- }
- info->textLen = *offset - info->offset;
- return OK;
-}
-
-status_t TimedTextSRTSource::readNextLine(off64_t *offset, AString *data) {
- data->clear();
- while (true) {
- ssize_t readSize;
- char character;
- if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) {
- if (readSize == 0) {
- return ERROR_END_OF_STREAM;
- }
- return ERROR_IO;
- }
-
- (*offset)++;
-
- // a line could end with CR, LF or CR + LF
- if (character == 10) {
- break;
- } else if (character == 13) {
- if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) {
- if (readSize == 0) { // end of the stream
- return OK;
- }
- return ERROR_IO;
- }
-
- (*offset)++;
- if (character != 10) {
- (*offset)--;
- }
- break;
- }
- data->append(character);
- }
- return OK;
-}
-
-status_t TimedTextSRTSource::getText(
- const MediaSource::ReadOptions *options,
- AString *text, int64_t *startTimeUs, int64_t *endTimeUs) {
- if (mTextVector.size() == 0) {
- return ERROR_END_OF_STREAM;
- }
- text->clear();
- int64_t seekTimeUs;
- MediaSource::ReadOptions::SeekMode mode;
- if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
- int64_t lastEndTimeUs =
- mTextVector.valueAt(mTextVector.size() - 1).endTimeUs;
- if (seekTimeUs < 0) {
- return ERROR_OUT_OF_RANGE;
- } else if (seekTimeUs >= lastEndTimeUs) {
- return ERROR_END_OF_STREAM;
- } else {
- // binary search
- size_t low = 0;
- size_t high = mTextVector.size() - 1;
- size_t mid = 0;
-
- while (low <= high) {
- mid = low + (high - low)/2;
- int diff = compareExtendedRangeAndTime(mid, seekTimeUs);
- if (diff == 0) {
- break;
- } else if (diff < 0) {
- low = mid + 1;
- } else {
- high = mid - 1;
- }
- }
- mIndex = mid;
- }
- }
-
- if (mIndex >= mTextVector.size()) {
- return ERROR_END_OF_STREAM;
- }
-
- const TextInfo &info = mTextVector.valueAt(mIndex);
- *startTimeUs = mTextVector.keyAt(mIndex);
- *endTimeUs = info.endTimeUs;
- mIndex++;
-
- char *str = new char[info.textLen];
- if (mSource->readAt(info.offset, str, info.textLen) < info.textLen) {
- delete[] str;
- return ERROR_IO;
- }
- text->append(str, info.textLen);
- delete[] str;
- return OK;
-}
-
-status_t TimedTextSRTSource::extractAndAppendLocalDescriptions(
- int64_t timeUs, const AString &text, Parcel *parcel) {
- const void *data = text.c_str();
- size_t size = text.size();
- int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS |
- TextDescriptions::OUT_OF_BAND_TEXT_SRT;
-
- if (size > 0) {
- return TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, timeUs / 1000, parcel);
- }
- return OK;
-}
-
-int TimedTextSRTSource::compareExtendedRangeAndTime(size_t index, int64_t timeUs) {
- CHECK_LT(index, mTextVector.size());
- int64_t endTimeUs = mTextVector.valueAt(index).endTimeUs;
- int64_t startTimeUs = (index > 0) ?
- mTextVector.valueAt(index - 1).endTimeUs : 0;
- if (timeUs >= startTimeUs && timeUs < endTimeUs) {
- return 0;
- } else if (endTimeUs <= timeUs) {
- return -1;
- } else {
- return 1;
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.h b/media/libstagefright/timedtext/TimedTextSRTSource.h
deleted file mode 100644
index 232675e..0000000
--- a/media/libstagefright/timedtext/TimedTextSRTSource.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TIMED_TEXT_SRT_SOURCE_H_
-#define TIMED_TEXT_SRT_SOURCE_H_
-
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <utils/Compat.h> // off64_t
-
-#include "TimedTextSource.h"
-
-namespace android {
-
-struct AString;
-class DataSource;
-class MediaBuffer;
-class Parcel;
-
-class TimedTextSRTSource : public TimedTextSource {
-public:
- TimedTextSRTSource(const sp<DataSource>& dataSource);
- virtual status_t start();
- virtual status_t stop();
- virtual status_t read(
- int64_t *startTimeUs,
- int64_t *endTimeUs,
- Parcel *parcel,
- const MediaSource::ReadOptions *options = NULL);
- virtual sp<MetaData> getFormat();
-
-protected:
- virtual ~TimedTextSRTSource();
-
-private:
- sp<DataSource> mSource;
- sp<MetaData> mMetaData;
-
- struct TextInfo {
- int64_t endTimeUs;
- // The offset of the text in the original file.
- off64_t offset;
- int textLen;
- };
-
- size_t mIndex;
- KeyedVector<int64_t, TextInfo> mTextVector;
-
- void reset();
- status_t scanFile();
- status_t getNextSubtitleInfo(
- off64_t *offset, int64_t *startTimeUs, TextInfo *info);
- status_t readNextLine(off64_t *offset, AString *data);
- status_t getText(
- const MediaSource::ReadOptions *options,
- AString *text, int64_t *startTimeUs, int64_t *endTimeUs);
- status_t extractAndAppendLocalDescriptions(
- int64_t timeUs, const AString &text, Parcel *parcel);
-
- // Compares the time range of the subtitle at index to the given timeUs.
- // The time range of the subtitle to match with given timeUs is extended to
- // [endTimeUs of the previous subtitle, endTimeUs of current subtitle).
- //
- // This compare function is used to find a next subtitle when read() is
- // called with seek options. Note that timeUs within gap ranges, such as
- // [200, 300) in the below example, will be matched to the closest future
- // subtitle, [300, 400).
- //
- // For instance, assuming there are 3 subtitles in mTextVector,
- // 0: [100, 200) ----> [0, 200)
- // 1: [300, 400) ----> [200, 400)
- // 2: [500, 600) ----> [400, 600)
- // If the 'index' parameter contains 1, this function
- // returns 0, if timeUs is in [200, 400)
- // returns -1, if timeUs >= 400,
- // returns 1, if timeUs < 200.
- int compareExtendedRangeAndTime(size_t index, int64_t timeUs);
-
- DISALLOW_EVIL_CONSTRUCTORS(TimedTextSRTSource);
-};
-
-} // namespace android
-
-#endif // TIMED_TEXT_SRT_SOURCE_H_
diff --git a/media/libstagefright/timedtext/TimedTextSource.cpp b/media/libstagefright/timedtext/TimedTextSource.cpp
deleted file mode 100644
index e5aa382..0000000
--- a/media/libstagefright/timedtext/TimedTextSource.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
- /*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "TimedTextSource"
-#include <utils/Log.h>
-
-#include <media/stagefright/foundation/ADebug.h> // CHECK_XX macro
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-
-#include "TimedTextSource.h"
-
-#include "TimedText3GPPSource.h"
-#include "TimedTextSRTSource.h"
-
-namespace android {
-
-// static
-sp<TimedTextSource> TimedTextSource::CreateTimedTextSource(
- const sp<IMediaSource>& mediaSource) {
- const char *mime;
- CHECK(mediaSource->getFormat()->findCString(kKeyMIMEType, &mime));
- if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) {
- return new TimedText3GPPSource(mediaSource);
- }
- ALOGE("Unsupported mime type for subtitle. : %s", mime);
- return NULL;
-}
-
-// static
-sp<TimedTextSource> TimedTextSource::CreateTimedTextSource(
- const sp<DataSource>& dataSource, FileType filetype) {
- switch(filetype) {
- case OUT_OF_BAND_FILE_SRT:
- return new TimedTextSRTSource(dataSource);
- case OUT_OF_BAND_FILE_SMI:
- // TODO: Implement for SMI.
- ALOGE("Supporting SMI is not implemented yet");
- break;
- default:
- ALOGE("Undefined subtitle format. : %d", filetype);
- }
- return NULL;
-}
-
-sp<MetaData> TimedTextSource::getFormat() {
- return NULL;
-}
-
-} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextSource.h b/media/libstagefright/timedtext/TimedTextSource.h
deleted file mode 100644
index 9946721..0000000
--- a/media/libstagefright/timedtext/TimedTextSource.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TIMED_TEXT_SOURCE_H_
-#define TIMED_TEXT_SOURCE_H_
-
-#include <media/stagefright/foundation/ABase.h> // for DISALLOW_XXX macro.
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h> // for MediaSource::ReadOptions
-#include <utils/RefBase.h>
-
-namespace android {
-
-class DataSource;
-class MetaData;
-class Parcel;
-
-class TimedTextSource : public RefBase {
- public:
- enum FileType {
- OUT_OF_BAND_FILE_SRT = 1,
- OUT_OF_BAND_FILE_SMI = 2,
- };
- static sp<TimedTextSource> CreateTimedTextSource(
- const sp<IMediaSource>& source);
- static sp<TimedTextSource> CreateTimedTextSource(
- const sp<DataSource>& source, FileType filetype);
- TimedTextSource() {}
- virtual status_t start() = 0;
- virtual status_t stop() = 0;
- // Returns subtitle parcel and its start time.
- virtual status_t read(
- int64_t *startTimeUs,
- int64_t *endTimeUs,
- Parcel *parcel,
- const MediaSource::ReadOptions *options = NULL) = 0;
- virtual status_t extractGlobalDescriptions(Parcel * /* parcel */) {
- return INVALID_OPERATION;
- }
- virtual sp<MetaData> getFormat();
-
- protected:
- virtual ~TimedTextSource() { }
-
- private:
- DISALLOW_EVIL_CONSTRUCTORS(TimedTextSource);
-};
-
-} // namespace android
-
-#endif // TIMED_TEXT_SOURCE_H_
diff --git a/media/libstagefright/timedtext/test/Android.mk b/media/libstagefright/timedtext/test/Android.mk
deleted file mode 100644
index e0e0e0d..0000000
--- a/media/libstagefright/timedtext/test/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# ================================================================
-# Unit tests for libstagefright_timedtext
-# ================================================================
-
-# ================================================================
-# A test for TimedTextSRTSource
-# ================================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := TimedTextSRTSource_test
-
-LOCAL_MODULE_TAGS := eng tests
-
-LOCAL_SRC_FILES := TimedTextSRTSource_test.cpp
-
-LOCAL_C_INCLUDES := \
- $(TOP)/external/expat/lib \
- $(TOP)/frameworks/av/media/libstagefright/timedtext
-
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- libexpat \
- libstagefright \
- libstagefright_foundation \
- libutils
-
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CLANG := true
-
-include $(BUILD_NATIVE_TEST)
diff --git a/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp b/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp
deleted file mode 100644
index 211e732..0000000
--- a/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "TimedTextSRTSource_test"
-#include <utils/Log.h>
-
-#include <gtest/gtest.h>
-
-#include <binder/Parcel.h>
-#include <media/stagefright/foundation/AString.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaErrors.h>
-#include <utils/misc.h>
-
-#include <TimedTextSource.h>
-#include <TimedTextSRTSource.h>
-
-namespace android {
-namespace test {
-
-static const int kSecToUsec = 1000000;
-static const int kSecToMsec = 1000;
-static const int kMsecToUsec = 1000;
-
-/* SRT format (http://en.wikipedia.org/wiki/SubRip)
- * Subtitle number
- * Start time --> End time
- * Text of subtitle (one or more lines)
- * Blank lines
- */
-static const char *kSRTString =
- "1\n00:00:1,000 --> 00:00:1,500\n1\n\n"
- "2\n00:00:2,000 --> 00:00:2,500\n2\n\n"
- "3\n00:00:3,000 --> 00:00:3,500\n3\n\n"
- "4\n00:00:4,000 --> 00:00:4,500\n4\n\n"
- "5\n00:00:5,000 --> 00:00:5,500\n5\n\n"
- // edge case : previos end time = next start time
- "6\n00:00:5,500 --> 00:00:5,800\n6\n\n"
- "7\n00:00:5,800 --> 00:00:6,000\n7\n\n"
- "8\n00:00:6,000 --> 00:00:7,000\n8\n\n";
-
-class SRTDataSourceStub : public DataSource {
-public:
- SRTDataSourceStub(const char *data, size_t size) :
- mData(data), mSize(size) {}
- virtual ~SRTDataSourceStub() {}
-
- virtual status_t initCheck() const {
- return OK;
- }
-
- virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
- if ((size_t)offset >= mSize) return 0;
-
- ssize_t avail = mSize - offset;
- if ((size_t)avail > size) {
- avail = size;
- }
- memcpy(data, mData + offset, avail);
- return avail;
- }
-
-private:
- const char *mData;
- size_t mSize;
-};
-
-class TimedTextSRTSourceTest : public testing::Test {
-protected:
- void SetUp() {
- sp<DataSource> stub= new SRTDataSourceStub(
- kSRTString,
- strlen(kSRTString));
- mSource = new TimedTextSRTSource(stub);
- mSource->start();
- }
-
- void CheckStartTimeMs(const Parcel& parcel, int32_t timeMs) {
- int32_t intval;
- parcel.setDataPosition(8);
- parcel.readInt32(&intval);
- EXPECT_EQ(timeMs, intval);
- }
-
- void CheckDataEquals(const Parcel& parcel, const char* content) {
- int32_t intval;
- parcel.setDataPosition(16);
- parcel.readInt32(&intval);
- parcel.setDataPosition(24);
- const char* data = (const char*) parcel.readInplace(intval);
-
- int32_t content_len = strlen(content);
- EXPECT_EQ(content_len, intval);
- EXPECT_TRUE(strncmp(data, content, content_len) == 0);
- }
-
- sp<TimedTextSource> mSource;
- int64_t startTimeUs;
- int64_t endTimeUs;
- Parcel parcel;
- AString subtitle;
- status_t err;
-};
-
-TEST_F(TimedTextSRTSourceTest, readAll) {
- for (int i = 1; i <= 5; i++) {
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel);
- EXPECT_EQ(OK, err);
- CheckStartTimeMs(parcel, i * kSecToMsec);
- subtitle = AStringPrintf("%d\n\n", i);
- CheckDataEquals(parcel, subtitle.c_str());
- }
- // read edge cases
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel);
- EXPECT_EQ(OK, err);
- CheckStartTimeMs(parcel, 5500);
- subtitle = AStringPrintf("6\n\n");
- CheckDataEquals(parcel, subtitle.c_str());
-
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel);
- EXPECT_EQ(OK, err);
- CheckStartTimeMs(parcel, 5800);
- subtitle = AStringPrintf("7\n\n");
- CheckDataEquals(parcel, subtitle.c_str());
-
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel);
- EXPECT_EQ(OK, err);
- CheckStartTimeMs(parcel, 6000);
- subtitle = AStringPrintf("8\n\n");
- CheckDataEquals(parcel, subtitle.c_str());
-
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel);
- EXPECT_EQ(ERROR_END_OF_STREAM, err);
-}
-
-TEST_F(TimedTextSRTSourceTest, seekTimeIsEarlierThanFirst) {
- MediaSource::ReadOptions options;
- options.setSeekTo(500, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ(1 * kSecToUsec, startTimeUs);
- CheckStartTimeMs(parcel, 1 * kSecToMsec);
-}
-
-TEST_F(TimedTextSRTSourceTest, seekTimeIsLaterThanLast) {
- MediaSource::ReadOptions options;
- options.setSeekTo(7 * kSecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(ERROR_END_OF_STREAM, err);
-
- options.setSeekTo(8 * kSecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(ERROR_END_OF_STREAM, err);
-}
-
-TEST_F(TimedTextSRTSourceTest, seekTimeIsMatched) {
- for (int i = 1; i <= 5; i++) {
- MediaSource::ReadOptions options;
- options.setSeekTo(i * kSecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ(i * kSecToUsec, startTimeUs);
-
- options.setSeekTo(i * kSecToUsec + 100, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ(i * kSecToUsec, startTimeUs);
- }
-}
-
-TEST_F(TimedTextSRTSourceTest, seekTimeInBetweenTwo) {
- for (int i = 1; i <= 4; i++) {
- MediaSource::ReadOptions options;
- options.setSeekTo(i * kSecToUsec + 500000, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ((i + 1) * kSecToUsec, startTimeUs);
-
- options.setSeekTo(i * kSecToUsec + 600000, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ((i + 1) * kSecToUsec, startTimeUs);
- }
-}
-
-TEST_F(TimedTextSRTSourceTest, checkEdgeCase) {
- MediaSource::ReadOptions options;
- options.setSeekTo(5500 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ(5500 * kMsecToUsec, startTimeUs);
- subtitle = AStringPrintf("6\n\n");
- CheckDataEquals(parcel, subtitle.c_str());
-
- options.setSeekTo(5800 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ(5800 * kMsecToUsec, startTimeUs);
- subtitle = AStringPrintf("7\n\n");
- CheckDataEquals(parcel, subtitle.c_str());
-
- options.setSeekTo(6000 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options);
- EXPECT_EQ(OK, err);
- EXPECT_EQ(6000 * kMsecToUsec, startTimeUs);
- subtitle = AStringPrintf("8\n\n");
- CheckDataEquals(parcel, subtitle.c_str());
-}
-
-} // namespace test
-} // namespace android
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index e2014b7..5f70479 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -54,6 +54,10 @@
void handleSyncStartEvent(const sp<SyncEvent>& event);
void clearSyncStartEvent();
+ void updateTrackFrameInfo(int64_t trackFramesReleased,
+ int64_t sourceFramesRead,
+ uint32_t halSampleRate,
+ const ExtendedTimestamp ×tamp);
private:
friend class AudioFlinger; // for mState
@@ -72,6 +76,8 @@
// be dropped and therefore not read by the application.
sp<SyncEvent> mSyncStartEvent;
+ AudioRecordServerProxy *mAudioRecordServerProxy;
+
// number of captured frames to drop after the start sync event has been received.
// when < 0, maximum frames to drop before starting capture even if sync event is
// not received
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 7a29cce..2fd5758 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -220,6 +220,94 @@
}
#endif
+// Track the CLOCK_BOOTTIME versus CLOCK_MONOTONIC timebase offset
+struct {
+ // call when you acquire a partial wakelock
+ void acquire(const sp<IBinder> &wakeLockToken) {
+ pthread_mutex_lock(&mLock);
+ if (wakeLockToken.get() == nullptr) {
+ adjustTimebaseOffset(&mBoottimeOffset, ExtendedTimestamp::TIMEBASE_BOOTTIME);
+ } else {
+ if (mCount == 0) {
+ adjustTimebaseOffset(&mBoottimeOffset, ExtendedTimestamp::TIMEBASE_BOOTTIME);
+ }
+ ++mCount;
+ }
+ pthread_mutex_unlock(&mLock);
+ }
+
+ // call when you release a partial wakelock.
+ void release(const sp<IBinder> &wakeLockToken) {
+ if (wakeLockToken.get() == nullptr) {
+ return;
+ }
+ pthread_mutex_lock(&mLock);
+ if (--mCount < 0) {
+ ALOGE("negative wakelock count");
+ mCount = 0;
+ }
+ pthread_mutex_unlock(&mLock);
+ }
+
+ // retrieves the boottime timebase offset from monotonic.
+ int64_t getBoottimeOffset() {
+ pthread_mutex_lock(&mLock);
+ int64_t boottimeOffset = mBoottimeOffset;
+ pthread_mutex_unlock(&mLock);
+ return boottimeOffset;
+ }
+
+ // Adjusts the timebase offset between TIMEBASE_MONOTONIC
+ // and the selected timebase.
+ // Currently only TIMEBASE_BOOTTIME is allowed.
+ //
+ // This only needs to be called upon acquiring the first partial wakelock
+ // after all other partial wakelocks are released.
+ //
+ // We do an empirical measurement of the offset rather than parsing
+ // /proc/timer_list since the latter is not a formal kernel ABI.
+ static void adjustTimebaseOffset(int64_t *offset, ExtendedTimestamp::Timebase timebase) {
+ int clockbase;
+ switch (timebase) {
+ case ExtendedTimestamp::TIMEBASE_BOOTTIME:
+ clockbase = SYSTEM_TIME_BOOTTIME;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("invalid timebase %d", timebase);
+ break;
+ }
+ // try three times to get the clock offset, choose the one
+ // with the minimum gap in measurements.
+ const int tries = 3;
+ nsecs_t bestGap, measured;
+ for (int i = 0; i < tries; ++i) {
+ const nsecs_t tmono = systemTime(SYSTEM_TIME_MONOTONIC);
+ const nsecs_t tbase = systemTime(clockbase);
+ const nsecs_t tmono2 = systemTime(SYSTEM_TIME_MONOTONIC);
+ const nsecs_t gap = tmono2 - tmono;
+ if (i == 0 || gap < bestGap) {
+ bestGap = gap;
+ measured = tbase - ((tmono + tmono2) >> 1);
+ }
+ }
+
+ // to avoid micro-adjusting, we don't change the timebase
+ // unless it is significantly different.
+ //
+ // Assumption: It probably takes more than toleranceNs to
+ // suspend and resume the device.
+ static int64_t toleranceNs = 10000; // 10 us
+ if (llabs(*offset - measured) > toleranceNs) {
+ ALOGV("Adjusting timebase offset old: %lld new: %lld",
+ (long long)*offset, (long long)measured);
+ *offset = measured;
+ }
+ }
+
+ pthread_mutex_t mLock;
+ int32_t mCount;
+ int64_t mBoottimeOffset;
+} gBoottime = { PTHREAD_MUTEX_INITIALIZER, 0, 0 }; // static, so use POD initialization
// ----------------------------------------------------------------------------
// CPU Stats
@@ -945,6 +1033,7 @@
BatteryNotifier::getInstance().noteStartAudio();
mNotifiedBatteryStart = true;
}
+ gBoottime.acquire(mWakeLockToken);
}
void AudioFlinger::ThreadBase::releaseWakeLock()
@@ -955,6 +1044,7 @@
void AudioFlinger::ThreadBase::releaseWakeLock_l()
{
+ gBoottime.release(mWakeLockToken);
if (mWakeLockToken != 0) {
ALOGV("releaseWakeLock_l() %s", mThreadName);
if (mPowerManager != 0) {
@@ -5685,6 +5775,9 @@
}
}
+ mTimestamp.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_BOOTTIME] =
+ gBoottime.getBoottimeOffset();
+
// used to request a deferred sleep, to be executed later while mutex is unlocked
uint32_t sleepUs = 0;
@@ -5898,6 +5991,28 @@
}
}
+ // Update server timestamp with server stats
+ // systemTime() is optional if the hardware supports timestamps.
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] += framesRead;
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = systemTime();
+
+ // Update server timestamp with kernel stats
+ if (mInput->stream->get_capture_position != nullptr) {
+ int64_t position, time;
+ int ret = mInput->stream->get_capture_position(mInput->stream, &position, &time);
+ if (ret == NO_ERROR) {
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = position;
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = time;
+ // Note: In general record buffers should tend to be empty in
+ // a properly running pipeline.
+ //
+ // Also, it is not advantageous to call get_presentation_position during the read
+ // as the read obtains a lock, preventing the timestamp call from executing.
+ }
+ }
+ // Use this to track timestamp information
+ // ALOGD("%s", mTimestamp.toString().c_str());
+
if (framesRead < 0 || (framesRead == 0 && mPipeSource == 0)) {
ALOGE("read failed: framesRead=%d", framesRead);
// Force input into standby so that it tries to recover at next read attempt
@@ -6026,6 +6141,11 @@
break;
}
+ // update frame information and push timestamp out
+ activeTrack->updateTrackFrameInfo(
+ activeTrack->mAudioRecordServerProxy->framesReleased(),
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER],
+ mSampleRate, mTimestamp);
}
unlock:
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index ae8bbb9..ad47277 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1324,6 +1324,8 @@
// rolling index that is never cleared
int32_t mRsmpInRear; // last filled frame + 1
+ ExtendedTimestamp mTimestamp;
+
// For dumpsys
const sp<NBAIO_Sink> mTeeSink;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 5830f75..b4c1fdd 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1418,8 +1418,10 @@
return;
}
- mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
- mFrameSize, !isExternalTrack());
+ mAudioRecordServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
+ mFrameSize, !isExternalTrack());
+ mServerProxy = mAudioRecordServerProxy;
+
mResamplerBufferProvider = new ResamplerBufferProvider(this);
if (flags & IAudioFlinger::TRACK_FAST) {
@@ -1556,6 +1558,24 @@
mFramesToDrop = 0;
}
+void AudioFlinger::RecordThread::RecordTrack::updateTrackFrameInfo(
+ int64_t trackFramesReleased, int64_t sourceFramesRead,
+ uint32_t halSampleRate, const ExtendedTimestamp ×tamp)
+{
+ ExtendedTimestamp local = timestamp;
+
+ // Convert HAL frames to server-side track frames at track sample rate.
+ // We use trackFramesReleased and sourceFramesRead as an anchor point.
+ for (int i = ExtendedTimestamp::LOCATION_SERVER; i < ExtendedTimestamp::LOCATION_MAX; ++i) {
+ if (local.mTimeNs[i] != 0) {
+ const int64_t relativeServerFrames = local.mPosition[i] - sourceFramesRead;
+ const int64_t relativeTrackFrames = relativeServerFrames
+ * mSampleRate / halSampleRate; // TODO: potential computation overflow
+ local.mPosition[i] = relativeTrackFrames + trackFramesReleased;
+ }
+ }
+ mAudioRecordServerProxy->setExtendedTimestamp(local);
+}
AudioFlinger::RecordThread::PatchRecord::PatchRecord(RecordThread *recordThread,
uint32_t sampleRate,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 941757a..ec70ed4 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1809,6 +1809,7 @@
mpClientInterface->closeInput(mInputs.keyAt(input_index));
}
mInputs.clear();
+ SoundTrigger::setCaptureState(false);
nextAudioPortGeneration();
if (patchRemoved) {
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
new file mode 100644
index 0000000..239b4e1
--- /dev/null
+++ b/services/mediacodec/Android.mk
@@ -0,0 +1,27 @@
+LOCAL_PATH := $(call my-dir)
+
+# service library
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := MediaCodecService.cpp
+LOCAL_SHARED_LIBRARIES := libmedia libbinder libutils liblog libstagefright_omx
+LOCAL_C_INCLUDES := \
+ $(TOP)/frameworks/av/media/libstagefright \
+ $(TOP)/frameworks/native/include/media/openmax
+LOCAL_MODULE:= libmediacodecservice
+LOCAL_32_BIT_ONLY := true
+include $(BUILD_SHARED_LIBRARY)
+
+
+# service executable
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := main_codecservice.cpp
+LOCAL_SHARED_LIBRARIES := libmedia libmediacodecservice libbinder libutils liblog
+LOCAL_C_INCLUDES := \
+ $(TOP)/frameworks/av/media/libstagefright \
+ $(TOP)/frameworks/native/include/media/openmax
+LOCAL_MODULE:= mediacodec
+LOCAL_32_BIT_ONLY := true
+LOCAL_INIT_RC := mediacodec.rc
+include $(BUILD_EXECUTABLE)
+
+
diff --git a/services/mediacodec/MODULE_LICENSE_APACHE2 b/services/mediacodec/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/mediacodec/MODULE_LICENSE_APACHE2
diff --git a/services/mediacodec/MediaCodecService.cpp b/services/mediacodec/MediaCodecService.cpp
new file mode 100644
index 0000000..fc1e5d9
--- /dev/null
+++ b/services/mediacodec/MediaCodecService.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MediaCodecService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "MediaCodecService.h"
+
+namespace android {
+
+sp<IOMX> MediaCodecService::getOMX() {
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (mOMX.get() == NULL) {
+ mOMX = new OMX;
+ }
+
+ return mOMX;
+}
+
+
+status_t MediaCodecService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags)
+{
+ return BnMediaCodecService::onTransact(code, data, reply, flags);
+}
+
+} // namespace android
diff --git a/services/mediacodec/MediaCodecService.h b/services/mediacodec/MediaCodecService.h
new file mode 100644
index 0000000..d64debb
--- /dev/null
+++ b/services/mediacodec/MediaCodecService.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_CODEC_SERVICE_H
+#define ANDROID_MEDIA_CODEC_SERVICE_H
+
+#include <binder/BinderService.h>
+#include <media/IMediaCodecService.h>
+#include <include/OMX.h>
+
+namespace android {
+
+class MediaCodecService : public BinderService<MediaCodecService>, public BnMediaCodecService
+{
+ friend class BinderService<MediaCodecService>; // for MediaCodecService()
+public:
+ MediaCodecService() : BnMediaCodecService() { }
+ virtual ~MediaCodecService() { }
+ virtual void onFirstRef() { }
+
+ static const char* getServiceName() { return "media.codec"; }
+
+ virtual sp<IOMX> getOMX();
+
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags);
+
+private:
+ Mutex mLock;
+ sp<IOMX> mOMX;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_CODEC_SERVICE_H
diff --git a/services/mediacodec/NOTICE b/services/mediacodec/NOTICE
new file mode 100644
index 0000000..34bdaf1
--- /dev/null
+++ b/services/mediacodec/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2015, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/services/mediacodec/main_codecservice.cpp b/services/mediacodec/main_codecservice.cpp
new file mode 100644
index 0000000..aedf0c3
--- /dev/null
+++ b/services/mediacodec/main_codecservice.cpp
@@ -0,0 +1,45 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "mediacodec"
+//#define LOG_NDEBUG 0
+
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+
+// from LOCAL_C_INCLUDES
+#include "MediaCodecService.h"
+
+using namespace android;
+
+int main(int argc __unused, char** argv)
+{
+ ALOGI("@@@ mediacodecservice starting");
+ signal(SIGPIPE, SIG_IGN);
+
+ strcpy(argv[0], "media.codec");
+ sp<ProcessState> proc(ProcessState::self());
+ sp<IServiceManager> sm = defaultServiceManager();
+ MediaCodecService::instantiate();
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+}
diff --git a/services/mediacodec/mediacodec.rc b/services/mediacodec/mediacodec.rc
new file mode 100644
index 0000000..e8df7be
--- /dev/null
+++ b/services/mediacodec/mediacodec.rc
@@ -0,0 +1,5 @@
+service mediacodec /system/bin/mediacodec
+ class main
+ user mediacodec
+ group camera drmrpc mediadrm
+ ioprio rt 4