am 8ea45aad: am e083d0a2: Merge "Support for Ogg Vorbis decoding in stagefright." into froyo
Merge commit '8ea45aad100ee25067b2e83703454c71a968ba4f' into kraken
* commit '8ea45aad100ee25067b2e83703454c71a968ba4f':
Support for Ogg Vorbis decoding in stagefright.
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 845c854..5a87f4c 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -147,7 +147,7 @@
OMXClient client;
CHECK_EQ(client.connect(), OK);
-#if 1
+#if 0
sp<MediaSource> source = createSource(argv[1]);
if (source == NULL) {
@@ -165,14 +165,15 @@
success = success && meta->findInt32(kKeyHeight, &height);
CHECK(success);
#else
- int width = 800;
+ int width = 720;
int height = 480;
sp<MediaSource> decoder = new DummySource(width, height);
#endif
sp<MetaData> enc_meta = new MetaData;
// enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ // enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
enc_meta->setInt32(kKeyWidth, width);
enc_meta->setInt32(kKeyHeight, height);
@@ -213,6 +214,8 @@
#if 0
CameraSource *source = CameraSource::Create();
+ source->start();
+
printf("source = %p\n", source);
for (int i = 0; i < 100; ++i) {
@@ -227,6 +230,8 @@
buffer = NULL;
}
+ source->stop();
+
delete source;
source = NULL;
#endif
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index 372909a..34f3c4a 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -42,14 +42,12 @@
virtual ~AMRWriter();
private:
- Mutex mLock;
-
FILE *mFile;
status_t mInitCheck;
sp<MediaSource> mSource;
bool mStarted;
volatile bool mDone;
- bool mReachedEOS;
+ volatile bool mReachedEOS;
pthread_t mThread;
static void *ThreadWrapper(void *);
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index ea435de..5f9d982 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -34,7 +34,7 @@
class CameraSource : public MediaSource {
public:
static CameraSource *Create();
- static CameraSource *CreateFromICamera(const sp<ICamera> &icamera);
+ static CameraSource *CreateFromCamera(const sp<Camera> &camera);
virtual ~CameraSource();
@@ -61,12 +61,17 @@
int mWidth, mHeight;
int64_t mFirstFrameTimeUs;
+ int64_t mLastFrameTimestampUs;
int32_t mNumFrames;
+ int32_t mNumFramesReleased;
bool mStarted;
CameraSource(const sp<Camera> &camera);
- void dataCallback(int32_t msgType, const sp<IMemory> &data);
+ void dataCallbackTimestamp(
+ int64_t timestampUs, int32_t msgType, const sp<IMemory> &data);
+
+ void releaseQueuedFrames();
CameraSource(const CameraSource &);
CameraSource &operator=(const CameraSource &);
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index 9b5a1e0..2504d39 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -58,7 +58,7 @@
// When changing these values, the COMPILE_TIME_ASSERT at the end of this
// file need to be updated.
const unsigned int NUM_LAYERS_MAX = 31;
-const unsigned int NUM_BUFFER_MAX = 4;
+const unsigned int NUM_BUFFER_MAX = 16;
const unsigned int NUM_DISPLAY_MAX = 4;
// ----------------------------------------------------------------------------
@@ -69,7 +69,11 @@
// ----------------------------------------------------------------------------
-// should be 128 bytes (32 longs)
+// 4 * (11 + 7 + (1 + 2*NUM_RECT_MAX) * NUM_BUFFER_MAX) * NUM_LAYERS_MAX
+// 4 * (11 + 7 + (1 + 2*7)*16) * 31
+// 1032 * 31
+// = ~27 KiB (31992)
+
class SharedBufferStack
{
friend class SharedClient;
@@ -78,21 +82,31 @@
friend class SharedBufferServer;
public:
- struct FlatRegion { // 12 bytes
- static const unsigned int NUM_RECT_MAX = 1;
- uint32_t count;
- uint16_t rects[4*NUM_RECT_MAX];
- };
-
struct Statistics { // 4 longs
typedef int32_t usecs_t;
usecs_t totalTime;
usecs_t reserved[3];
};
+
+ struct SmallRect {
+ uint16_t l, t, r, b;
+ };
+
+ struct FlatRegion { // 52 bytes = 4 * (1 + 2*N)
+ static const unsigned int NUM_RECT_MAX = 6;
+ uint32_t count;
+ SmallRect rects[NUM_RECT_MAX];
+ };
+
+ struct BufferData {
+ FlatRegion dirtyRegion;
+ SmallRect crop;
+ };
SharedBufferStack();
void init(int32_t identity);
status_t setDirtyRegion(int buffer, const Region& reg);
+ status_t setCrop(int buffer, const Rect& reg);
Region getDirtyRegion(int buffer) const;
// these attributes are part of the conditions/updates
@@ -104,11 +118,13 @@
// not part of the conditions
volatile int32_t reallocMask;
+ volatile int8_t index[NUM_BUFFER_MAX];
int32_t identity; // surface's identity (const)
- int32_t reserved32[9];
+ int32_t reserved32[2];
Statistics stats;
- FlatRegion dirtyRegion[NUM_BUFFER_MAX]; // 12*4=48 bytes
+ int32_t reserved;
+ BufferData buffers[NUM_BUFFER_MAX]; // 960 bytes
};
// ----------------------------------------------------------------------------
@@ -152,6 +168,7 @@
SharedBufferStack* const mSharedStack;
const int mNumBuffers;
const int mIdentity;
+ int32_t computeTail() const;
friend struct Update;
friend struct QueueUpdate;
@@ -160,61 +177,22 @@
SharedBufferStack& stack;
inline ConditionBase(SharedBufferBase* sbc)
: stack(*sbc->mSharedStack) { }
+ virtual ~ConditionBase() { };
+ virtual bool operator()() const = 0;
+ virtual const char* name() const = 0;
};
+ status_t waitForCondition(const ConditionBase& condition);
struct UpdateBase {
SharedBufferStack& stack;
inline UpdateBase(SharedBufferBase* sbb)
: stack(*sbb->mSharedStack) { }
};
-
- template <typename T>
- status_t waitForCondition(T condition);
-
template <typename T>
status_t updateCondition(T update);
};
template <typename T>
-status_t SharedBufferBase::waitForCondition(T condition)
-{
- const SharedBufferStack& stack( *mSharedStack );
- SharedClient& client( *mSharedClient );
- const nsecs_t TIMEOUT = s2ns(1);
- Mutex::Autolock _l(client.lock);
- while ((condition()==false) &&
- (stack.identity == mIdentity) &&
- (stack.status == NO_ERROR))
- {
- status_t err = client.cv.waitRelative(client.lock, TIMEOUT);
-
- // handle errors and timeouts
- if (CC_UNLIKELY(err != NO_ERROR)) {
- if (err == TIMED_OUT) {
- if (condition()) {
- LOGE("waitForCondition(%s) timed out (identity=%d), "
- "but condition is true! We recovered but it "
- "shouldn't happen." , T::name(),
- stack.identity);
- break;
- } else {
- LOGW("waitForCondition(%s) timed out "
- "(identity=%d, status=%d). "
- "CPU may be pegged. trying again.", T::name(),
- stack.identity, stack.status);
- }
- } else {
- LOGE("waitForCondition(%s) error (%s) ",
- T::name(), strerror(-err));
- return err;
- }
- }
- }
- return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status;
-}
-
-
-template <typename T>
status_t SharedBufferBase::updateCondition(T update) {
SharedClient& client( *mSharedClient );
Mutex::Autolock _l(client.lock);
@@ -238,13 +216,12 @@
status_t queue(int buf);
bool needNewBuffer(int buffer) const;
status_t setDirtyRegion(int buffer, const Region& reg);
+ status_t setCrop(int buffer, const Rect& reg);
private:
friend struct Condition;
friend struct DequeueCondition;
friend struct LockCondition;
-
- int32_t computeTail() const;
struct QueueUpdate : public UpdateBase {
inline QueueUpdate(SharedBufferBase* sbb);
@@ -260,18 +237,20 @@
struct DequeueCondition : public ConditionBase {
inline DequeueCondition(SharedBufferClient* sbc);
- inline bool operator()();
- static inline const char* name() { return "DequeueCondition"; }
+ inline bool operator()() const;
+ inline const char* name() const { return "DequeueCondition"; }
};
struct LockCondition : public ConditionBase {
int buf;
inline LockCondition(SharedBufferClient* sbc, int buf);
- inline bool operator()();
- static inline const char* name() { return "LockCondition"; }
+ inline bool operator()() const;
+ inline const char* name() const { return "LockCondition"; }
};
int32_t tail;
+ int32_t undoDequeueTail;
+ int32_t queued_head;
// statistics...
nsecs_t mDequeueTime[NUM_BUFFER_MAX];
};
@@ -318,8 +297,8 @@
struct ReallocateCondition : public ConditionBase {
int buf;
inline ReallocateCondition(SharedBufferBase* sbb, int buf);
- inline bool operator()();
- static inline const char* name() { return "ReallocateCondition"; }
+ inline bool operator()() const;
+ inline const char* name() const { return "ReallocateCondition"; }
};
};
@@ -349,8 +328,7 @@
// ---------------------------------------------------------------------------
-COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 4096)
-COMPILE_TIME_ASSERT(sizeof(SharedBufferStack) == 128)
+COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 32768)
COMPILE_TIME_ASSERT(sizeof(surface_flinger_cblk_t) <= 4096)
// ---------------------------------------------------------------------------
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 01b6737..f3804b8 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -57,7 +57,7 @@
return NULL;
}
- jfieldID surfaceID = env->GetFieldID(surfaceClass, "mSurface", "I");
+ jfieldID surfaceID = env->GetFieldID(surfaceClass, ANDROID_VIEW_SURFACE_JNI_ID, "I");
if (surfaceID == NULL) {
LOGE("Can't find Surface.mSurface");
return NULL;
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index cf97b23..3b678cb 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -37,6 +37,7 @@
libvorbisidec \
libsonivox \
libmedia \
+ libcamera_client \
libandroid_runtime \
libstagefright \
libstagefright_omx \
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 3e1f4a5..3100f6e 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -228,14 +228,10 @@
sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(pid_t pid)
{
-#ifndef NO_OPENCORE
sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid);
wp<MediaRecorderClient> w = recorder;
Mutex::Autolock lock(mLock);
mMediaRecorderClients.add(w);
-#else
- sp<MediaRecorderClient> recorder = NULL;
-#endif
LOGV("Create new media recorder client from pid %d", pid);
return recorder;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 531fd11..a63d94b 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -20,6 +20,7 @@
#include "StagefrightRecorder.h"
+#include <binder/IPCThreadState.h>
#include <media/stagefright/AudioSource.h>
#include <media/stagefright/AMRWriter.h>
#include <media/stagefright/CameraSource.h>
@@ -30,8 +31,11 @@
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
#include <camera/ICamera.h>
+#include <camera/Camera.h>
#include <surfaceflinger/ISurface.h>
#include <utils/Errors.h>
+#include <sys/types.h>
+#include <unistd.h>
namespace android {
@@ -96,7 +100,25 @@
}
status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera) {
- mCamera = camera;
+ LOGV("setCamera: pid %d pid %d", IPCThreadState::self()->getCallingPid(), getpid());
+ if (camera == 0) {
+ LOGE("camera is NULL");
+ return UNKNOWN_ERROR;
+ }
+
+ mFlags &= ~ FLAGS_SET_CAMERA | FLAGS_HOT_CAMERA;
+ mCamera = Camera::create(camera);
+ if (mCamera == 0) {
+ LOGE("Unable to connect to camera");
+ return UNKNOWN_ERROR;
+ }
+
+ LOGV("Connected to camera");
+ mFlags |= FLAGS_SET_CAMERA;
+ if (mCamera->previewEnabled()) {
+ LOGV("camera is hot");
+ mFlags |= FLAGS_HOT_CAMERA;
+ }
return OK;
}
@@ -240,7 +262,7 @@
CHECK(mCamera != NULL);
sp<CameraSource> cameraSource =
- CameraSource::CreateFromICamera(mCamera);
+ CameraSource::CreateFromCamera(mCamera);
CHECK(cameraSource != NULL);
@@ -314,6 +336,17 @@
status_t StagefrightRecorder::close() {
stop();
+ if (mCamera != 0) {
+ if ((mFlags & FLAGS_HOT_CAMERA) == 0) {
+ LOGV("Camera was cold when we started, stopping preview");
+ mCamera->stopPreview();
+ }
+ if (mFlags & FLAGS_SET_CAMERA) {
+ LOGV("Unlocking camera");
+ mCamera->unlock();
+ }
+ mFlags = 0;
+ }
return OK;
}
@@ -329,6 +362,7 @@
mVideoHeight = -1;
mFrameRate = -1;
mOutputFd = -1;
+ mFlags = 0;
return OK;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 7ec412d..2f2f748 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -23,6 +23,7 @@
namespace android {
+class Camera;
struct MediaSource;
struct MediaWriter;
@@ -52,7 +53,12 @@
virtual status_t getMaxAmplitude(int *max);
private:
- sp<ICamera> mCamera;
+ enum CameraFlags {
+ FLAGS_SET_CAMERA = 1L << 0,
+ FLAGS_HOT_CAMERA = 1L << 1,
+ };
+
+ sp<Camera> mCamera;
sp<ISurface> mPreviewSurface;
sp<IMediaPlayerClient> mListener;
sp<MediaWriter> mWriter;
@@ -66,6 +72,7 @@
int mFrameRate;
String8 mParams;
int mOutputFd;
+ int32_t mFlags;
status_t startMPEG4Recording();
status_t startAMRRecording();
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index bf4424b..73ea56d 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -53,8 +53,6 @@
}
status_t AMRWriter::addSource(const sp<MediaSource> &source) {
- Mutex::Autolock autoLock(mLock);
-
if (mInitCheck != OK) {
return mInitCheck;
}
@@ -95,8 +93,6 @@
}
status_t AMRWriter::start() {
- Mutex::Autolock autoLock(mLock);
-
if (mInitCheck != OK) {
return mInitCheck;
}
@@ -127,16 +123,12 @@
}
void AMRWriter::stop() {
- {
- Mutex::Autolock autoLock(mLock);
-
- if (!mStarted) {
- return;
- }
-
- mDone = true;
+ if (!mStarted) {
+ return;
}
+ mDone = true;
+
void *dummy;
pthread_join(mThread, &dummy);
@@ -153,13 +145,7 @@
}
void AMRWriter::threadFunc() {
- for (;;) {
- Mutex::Autolock autoLock(mLock);
-
- if (mDone) {
- break;
- }
-
+ while (!mDone) {
MediaBuffer *buffer;
status_t err = mSource->read(&buffer);
@@ -184,12 +170,10 @@
buffer = NULL;
}
- Mutex::Autolock autoLock(mLock);
mReachedEOS = true;
}
bool AMRWriter::reachedEOS() {
- Mutex::Autolock autoLock(mLock);
return mReachedEOS;
}
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 075b1e3..b07bd0e 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include <sys/time.h>
+//#define LOG_NDEBUG 0
+#define LOG_TAG "CameraSource"
+#include <utils/Log.h>
#include <OMX_Component.h>
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h> // for property_get
#include <media/stagefright/CameraSource.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
@@ -34,13 +34,6 @@
namespace android {
-static int64_t getNowUs() {
- struct timeval tv;
- gettimeofday(&tv, NULL);
-
- return (int64_t)tv.tv_usec + tv.tv_sec * 1000000;
-}
-
struct DummySurface : public BnSurface {
DummySurface() {}
@@ -100,17 +93,15 @@
void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr) {
LOGV("postData(%d, ptr:%p, size:%d)",
msgType, dataPtr->pointer(), dataPtr->size());
-
- sp<CameraSource> source = mSource.promote();
- if (source.get() != NULL) {
- source->dataCallback(msgType, dataPtr);
- }
}
void CameraSourceListener::postDataTimestamp(
nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) {
- LOGV("postDataTimestamp(%lld, %d, ptr:%p, size:%d)",
- timestamp, msgType, dataPtr->pointer(), dataPtr->size());
+
+ sp<CameraSource> source = mSource.promote();
+ if (source.get() != NULL) {
+ source->dataCallbackTimestamp(timestamp/1000, msgType, dataPtr);
+ }
}
// static
@@ -125,9 +116,7 @@
}
// static
-CameraSource *CameraSource::CreateFromICamera(const sp<ICamera> &icamera) {
- sp<Camera> camera = Camera::create(icamera);
-
+CameraSource *CameraSource::CreateFromCamera(const sp<Camera> &camera) {
if (camera.get() == NULL) {
return NULL;
}
@@ -140,15 +129,10 @@
mWidth(0),
mHeight(0),
mFirstFrameTimeUs(0),
+ mLastFrameTimestampUs(0),
mNumFrames(0),
+ mNumFramesReleased(0),
mStarted(false) {
- char value[PROPERTY_VALUE_MAX];
- if (property_get("ro.hardware", value, NULL) && !strcmp(value, "sholes")) {
- // The hardware encoder(s) do not support yuv420, but only YCbYCr,
- // fortunately the camera also supports this, so we needn't transcode.
- mCamera->setParameters(String8("preview-format=yuv422i-yuyv"));
- }
-
String8 s = mCamera->getParameters();
printf("params: \"%s\"\n", s.string());
@@ -167,6 +151,7 @@
}
status_t CameraSource::start(MetaData *) {
+ LOGV("start");
CHECK(!mStarted);
mCamera->setListener(new CameraSourceListener(this));
@@ -176,11 +161,7 @@
mPreviewSurface != NULL ? mPreviewSurface : new DummySurface);
CHECK_EQ(err, OK);
- mCamera->setPreviewCallbackFlags(
- FRAME_CALLBACK_FLAG_ENABLE_MASK
- | FRAME_CALLBACK_FLAG_COPY_OUT_MASK);
-
- err = mCamera->startPreview();
+ err = mCamera->startRecording();
CHECK_EQ(err, OK);
mStarted = true;
@@ -189,15 +170,30 @@
}
status_t CameraSource::stop() {
- CHECK(mStarted);
-
- mCamera->stopPreview();
-
+ LOGV("stop");
+ Mutex::Autolock autoLock(mLock);
mStarted = false;
+ mFrameAvailableCondition.signal();
+ mCamera->setListener(NULL);
+ mCamera->stopRecording();
+ releaseQueuedFrames();
+ LOGI("Frames received/released: %d/%d, timestamp (us) last/first: %lld/%lld",
+ mNumFrames, mNumFramesReleased,
+ mLastFrameTimestampUs, mFirstFrameTimeUs);
return OK;
}
+void CameraSource::releaseQueuedFrames() {
+ List<sp<IMemory> >::iterator it;
+ while (!mFrames.empty()) {
+ it = mFrames.begin();
+ mCamera->releaseRecordingFrame(*it);
+ mFrames.erase(it);
+ ++mNumFramesReleased;
+ }
+}
+
sp<MetaData> CameraSource::getFormat() {
sp<MetaData> meta = new MetaData;
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
@@ -210,7 +206,7 @@
status_t CameraSource::read(
MediaBuffer **buffer, const ReadOptions *options) {
- CHECK(mStarted);
+ LOGV("read");
*buffer = NULL;
@@ -224,20 +220,24 @@
{
Mutex::Autolock autoLock(mLock);
- while (mFrames.empty()) {
+ while (mStarted && mFrames.empty()) {
mFrameAvailableCondition.wait(mLock);
}
-
+ if (!mStarted) {
+ return OK;
+ }
frame = *mFrames.begin();
mFrames.erase(mFrames.begin());
frameTime = *mFrameTimes.begin();
mFrameTimes.erase(mFrameTimes.begin());
+ ++mNumFramesReleased;
}
*buffer = new MediaBuffer(frame->size());
memcpy((*buffer)->data(), frame->pointer(), frame->size());
(*buffer)->set_range(0, frame->size());
+ mCamera->releaseRecordingFrame(frame);
(*buffer)->meta_data()->clear();
(*buffer)->meta_data()->setInt64(kKeyTime, frameTime);
@@ -245,17 +245,25 @@
return OK;
}
-void CameraSource::dataCallback(int32_t msgType, const sp<IMemory> &data) {
+void CameraSource::dataCallbackTimestamp(int64_t timestampUs,
+ int32_t msgType, const sp<IMemory> &data) {
+ LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs);
+ mLastFrameTimestampUs = timestampUs;
Mutex::Autolock autoLock(mLock);
+ if (!mStarted) {
+ mCamera->releaseRecordingFrame(data);
+ ++mNumFrames;
+ ++mNumFramesReleased;
+ return;
+ }
- int64_t nowUs = getNowUs();
if (mNumFrames == 0) {
- mFirstFrameTimeUs = nowUs;
+ mFirstFrameTimeUs = timestampUs;
}
++mNumFrames;
mFrames.push_back(data);
- mFrameTimes.push_back(nowUs - mFirstFrameTimeUs);
+ mFrameTimes.push_back(timestampUs - mFirstFrameTimeUs);
mFrameAvailableCondition.signal();
}
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 2cf0ddf..5ff2abe 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -24,6 +24,7 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/Utils.h>
@@ -59,16 +60,22 @@
void *mCodecSpecificData;
size_t mCodecSpecificDataSize;
+ bool mGotAllCodecSpecificData;
bool mReachedEOS;
static void *ThreadWrapper(void *me);
void threadEntry();
+ status_t makeAVCCodecSpecificData(
+ const uint8_t *data, size_t size);
+
Track(const Track &);
Track &operator=(const Track &);
};
+#define USE_NALLEN_FOUR 1
+
MPEG4Writer::MPEG4Writer(const char *filename)
: mFile(fopen(filename, "wb")),
mOffset(0),
@@ -213,23 +220,55 @@
return old_offset;
}
+static void StripStartcode(MediaBuffer *buffer) {
+ if (buffer->range_length() < 4) {
+ return;
+ }
+
+ const uint8_t *ptr =
+ (const uint8_t *)buffer->data() + buffer->range_offset();
+
+ if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
+ buffer->set_range(
+ buffer->range_offset() + 4, buffer->range_length() - 4);
+ }
+}
+
off_t MPEG4Writer::addLengthPrefixedSample(MediaBuffer *buffer) {
Mutex::Autolock autoLock(mLock);
+ StripStartcode(buffer);
+
off_t old_offset = mOffset;
size_t length = buffer->range_length();
+
+#if USE_NALLEN_FOUR
+ uint8_t x = length >> 24;
+ fwrite(&x, 1, 1, mFile);
+ x = (length >> 16) & 0xff;
+ fwrite(&x, 1, 1, mFile);
+ x = (length >> 8) & 0xff;
+ fwrite(&x, 1, 1, mFile);
+ x = length & 0xff;
+ fwrite(&x, 1, 1, mFile);
+#else
CHECK(length < 65536);
uint8_t x = length >> 8;
fwrite(&x, 1, 1, mFile);
x = length & 0xff;
fwrite(&x, 1, 1, mFile);
+#endif
fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
1, length, mFile);
+#if USE_NALLEN_FOUR
+ mOffset += length + 4;
+#else
mOffset += length + 2;
+#endif
return old_offset;
}
@@ -320,6 +359,7 @@
mMaxTimeStampUs(0),
mCodecSpecificData(NULL),
mCodecSpecificDataSize(0),
+ mGotAllCodecSpecificData(false),
mReachedEOS(false) {
}
@@ -380,6 +420,104 @@
return NULL;
}
+#include <ctype.h>
+static void hexdump(const void *_data, size_t size) {
+ const uint8_t *data = (const uint8_t *)_data;
+ size_t offset = 0;
+ while (offset < size) {
+ printf("0x%04x ", offset);
+
+ size_t n = size - offset;
+ if (n > 16) {
+ n = 16;
+ }
+
+ for (size_t i = 0; i < 16; ++i) {
+ if (i == 8) {
+ printf(" ");
+ }
+
+ if (offset + i < size) {
+ printf("%02x ", data[offset + i]);
+ } else {
+ printf(" ");
+ }
+ }
+
+ printf(" ");
+
+ for (size_t i = 0; i < n; ++i) {
+ if (isprint(data[offset + i])) {
+ printf("%c", data[offset + i]);
+ } else {
+ printf(".");
+ }
+ }
+
+ printf("\n");
+
+ offset += 16;
+ }
+}
+
+
+status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
+ const uint8_t *data, size_t size) {
+ // hexdump(data, size);
+
+ if (mCodecSpecificData != NULL) {
+ LOGE("Already have codec specific data");
+ return ERROR_MALFORMED;
+ }
+
+ if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
+ LOGE("Must start with a start code");
+ return ERROR_MALFORMED;
+ }
+
+ size_t picParamOffset = 4;
+ while (picParamOffset + 3 < size
+ && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) {
+ ++picParamOffset;
+ }
+
+ if (picParamOffset + 3 >= size) {
+ LOGE("Could not find start-code for pictureParameterSet");
+ return ERROR_MALFORMED;
+ }
+
+ size_t seqParamSetLength = picParamOffset - 4;
+ size_t picParamSetLength = size - picParamOffset - 4;
+
+ mCodecSpecificDataSize =
+ 6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2;
+
+ mCodecSpecificData = malloc(mCodecSpecificDataSize);
+ uint8_t *header = (uint8_t *)mCodecSpecificData;
+ header[0] = 1;
+ header[1] = 0x42; // profile
+ header[2] = 0x80;
+ header[3] = 0x1e; // level
+
+#if USE_NALLEN_FOUR
+ header[4] = 0xfc | 3; // length size == 4 bytes
+#else
+ header[4] = 0xfc | 1; // length size == 2 bytes
+#endif
+
+ header[5] = 0xe0 | 1;
+ header[6] = seqParamSetLength >> 8;
+ header[7] = seqParamSetLength & 0xff;
+ memcpy(&header[8], &data[4], seqParamSetLength);
+ header += 8 + seqParamSetLength;
+ header[0] = 1;
+ header[1] = picParamSetLength >> 8;
+ header[2] = picParamSetLength & 0xff;
+ memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength);
+
+ return OK;
+}
+
void MPEG4Writer::Track::threadEntry() {
sp<MetaData> meta = mSource->getFormat();
const char *mime;
@@ -399,54 +537,40 @@
++count;
- if (is_avc && count < 3) {
- size_t size = buffer->range_length();
+ int32_t isCodecConfig;
+ if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig)
+ && isCodecConfig) {
+ CHECK(!mGotAllCodecSpecificData);
- switch (count) {
- case 1:
- {
- CHECK_EQ(mCodecSpecificData, NULL);
- mCodecSpecificData = malloc(size + 8);
- uint8_t *header = (uint8_t *)mCodecSpecificData;
- header[0] = 1;
- header[1] = 0x42; // profile
- header[2] = 0x80;
- header[3] = 0x1e; // level
- header[4] = 0xfc | 3;
- header[5] = 0xe0 | 1;
- header[6] = size >> 8;
- header[7] = size & 0xff;
- memcpy(&header[8],
- (const uint8_t *)buffer->data() + buffer->range_offset(),
- size);
+ if (is_avc) {
+ status_t err = makeAVCCodecSpecificData(
+ (const uint8_t *)buffer->data()
+ + buffer->range_offset(),
+ buffer->range_length());
- mCodecSpecificDataSize = size + 8;
+ if (err != OK) {
+ LOGE("failed to parse avc codec specific data.");
break;
}
-
- case 2:
- {
- size_t offset = mCodecSpecificDataSize;
- mCodecSpecificDataSize += size + 3;
- mCodecSpecificData = realloc(mCodecSpecificData, mCodecSpecificDataSize);
- uint8_t *header = (uint8_t *)mCodecSpecificData;
- header[offset] = 1;
- header[offset + 1] = size >> 8;
- header[offset + 2] = size & 0xff;
- memcpy(&header[offset + 3],
- (const uint8_t *)buffer->data() + buffer->range_offset(),
- size);
- break;
- }
+ } else if (is_mpeg4) {
+ mCodecSpecificDataSize = buffer->range_length();
+ mCodecSpecificData = malloc(mCodecSpecificDataSize);
+ memcpy(mCodecSpecificData,
+ (const uint8_t *)buffer->data()
+ + buffer->range_offset(),
+ buffer->range_length());
}
buffer->release();
buffer = NULL;
+ mGotAllCodecSpecificData = true;
continue;
- }
+ } else if (!mGotAllCodecSpecificData &&
+ count == 1 && is_mpeg4 && mCodecSpecificData == NULL) {
+ // The TI mpeg4 encoder does not properly set the
+ // codec-specific-data flag.
- if (mCodecSpecificData == NULL && is_mpeg4) {
const uint8_t *data =
(const uint8_t *)buffer->data() + buffer->range_offset();
@@ -474,13 +598,74 @@
memcpy(mCodecSpecificData, data, offset);
buffer->set_range(buffer->range_offset() + offset, size - offset);
+
+ if (size == offset) {
+ buffer->release();
+ buffer = NULL;
+
+ continue;
+ }
+
+ mGotAllCodecSpecificData = true;
+ } else if (!mGotAllCodecSpecificData && is_avc && count < 3) {
+ // The TI video encoder does not flag codec specific data
+ // as such and also splits up SPS and PPS across two buffers.
+
+ const uint8_t *data =
+ (const uint8_t *)buffer->data() + buffer->range_offset();
+
+ size_t size = buffer->range_length();
+
+ CHECK(count == 2 || mCodecSpecificData == NULL);
+
+ size_t offset = mCodecSpecificDataSize;
+ mCodecSpecificDataSize += size + 4;
+ mCodecSpecificData =
+ realloc(mCodecSpecificData, mCodecSpecificDataSize);
+
+ memcpy((uint8_t *)mCodecSpecificData + offset,
+ "\x00\x00\x00\x01", 4);
+
+ memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size);
+
+ buffer->release();
+ buffer = NULL;
+
+ if (count == 2) {
+ void *tmp = mCodecSpecificData;
+ size = mCodecSpecificDataSize;
+ mCodecSpecificData = NULL;
+ mCodecSpecificDataSize = 0;
+
+ status_t err = makeAVCCodecSpecificData(
+ (const uint8_t *)tmp, size);
+
+ free(tmp);
+ tmp = NULL;
+
+ if (err != OK) {
+ LOGE("failed to parse avc codec specific data.");
+ break;
+ }
+
+ mGotAllCodecSpecificData = true;
+ }
+
+ continue;
}
off_t offset = is_avc ? mOwner->addLengthPrefixedSample(buffer)
: mOwner->addSample(buffer);
SampleInfo info;
- info.size = is_avc ? buffer->range_length() + 2 : buffer->range_length();
+ info.size = is_avc
+#if USE_NALLEN_FOUR
+ ? buffer->range_length() + 4
+#else
+ ? buffer->range_length() + 2
+#endif
+ : buffer->range_length();
+
info.offset = offset;
int64_t timestampUs;
@@ -733,19 +918,29 @@
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mSampleInfos.size() - 1);
+ mOwner->writeInt32(mSampleInfos.size());
List<SampleInfo>::iterator it = mSampleInfos.begin();
int64_t last = (*it).timestamp;
+ int64_t lastDuration = 1;
+
++it;
while (it != mSampleInfos.end()) {
mOwner->writeInt32(1);
- mOwner->writeInt32((*it).timestamp - last);
+ lastDuration = (*it).timestamp - last;
+ mOwner->writeInt32(lastDuration);
last = (*it).timestamp;
++it;
}
+
+ // We don't really know how long the last frame lasts, since
+ // there is no frame time after it, just repeat the previous
+ // frame's duration.
+ mOwner->writeInt32(1);
+ mOwner->writeInt32(lastDuration);
+
mOwner->endBox(); // stts
mOwner->beginBox("stsz");
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 6ed384c..41be9ac 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -137,6 +137,7 @@
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.encoder.h263" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.encoder" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263enc" },
+ { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.encoder.avc" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcenc" },
};
@@ -679,6 +680,7 @@
case OMX_COLOR_FormatCbYCrY:
return width * height * 2;
+ case OMX_COLOR_FormatYUV420Planar:
case OMX_COLOR_FormatYUV420SemiPlanar:
return (width * height * 3) / 2;
@@ -706,24 +708,49 @@
OMX_COLOR_FORMATTYPE colorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
if (!strcasecmp("OMX.TI.Video.encoder", mComponentName)) {
- colorFormat = OMX_COLOR_FormatYCbYCr;
+ colorFormat = OMX_COLOR_FormatYUV420Planar;
}
+
+
+ status_t err;
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+ //////////////////////// Input port /////////////////////////
CHECK_EQ(setVideoPortFormatType(
kPortIndexInput, OMX_VIDEO_CodingUnused,
colorFormat), OK);
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexInput;
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ def.nBufferSize = getFrameSize(colorFormat, width, height);
+
+ CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+ video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
+ video_def->eColorFormat = colorFormat;
+
+ video_def->xFramerate = (24 << 16); // Q16 format
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ //////////////////////// Output port /////////////////////////
CHECK_EQ(setVideoPortFormatType(
kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused),
OK);
-
- OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
- OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
-
- status_t err = mOMX->getParameter(
+ err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
@@ -739,31 +766,7 @@
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
- ////////////////////////////////////////////////////////////////////////////
-
- InitOMXParams(&def);
- def.nPortIndex = kPortIndexInput;
-
- err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
-
- def.nBufferSize = getFrameSize(colorFormat, width, height);
- CODEC_LOGV("Setting nBufferSize = %ld", def.nBufferSize);
-
- CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
-
- video_def->nFrameWidth = width;
- video_def->nFrameHeight = height;
- video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
- video_def->eColorFormat = colorFormat;
-
- video_def->xFramerate = 24 << 16; // XXX crucial!
-
- err = mOMX->setParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
-
+ /////////////////// Codec-specific ////////////////////////
switch (compressionFormat) {
case OMX_VIDEO_CodingMPEG4:
{
@@ -911,7 +914,7 @@
CHECK_EQ(err, OK);
bitrateType.eControlRate = OMX_Video_ControlRateVariable;
- bitrateType.nTargetBitrate = 1000000;
+ bitrateType.nTargetBitrate = 3000000;
err = mOMX->setParameter(
mNode, OMX_IndexParamVideoBitrate,
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index c1a010c..5db516e 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -264,6 +264,8 @@
return UNKNOWN_ERROR;
}
+ CHECK_EQ(header->pAppPrivate, buffer_meta);
+
*buffer = header;
addActiveBuffer(portIndex, *buffer);
@@ -294,6 +296,8 @@
return UNKNOWN_ERROR;
}
+ CHECK_EQ(header->pAppPrivate, buffer_meta);
+
*buffer = header;
*buffer_data = header->pBuffer;
@@ -325,6 +329,8 @@
return UNKNOWN_ERROR;
}
+ CHECK_EQ(header->pAppPrivate, buffer_meta);
+
*buffer = header;
addActiveBuffer(portIndex, *buffer);