summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camera/Camera.cpp11
-rw-r--r--camera/CameraParameters.cpp1
-rw-r--r--camera/ICameraService.cpp19
-rw-r--r--cmds/stagefright/record.cpp11
-rw-r--r--include/camera/Camera.h3
-rw-r--r--include/camera/CameraParameters.h6
-rw-r--r--include/camera/ICameraService.h7
-rw-r--r--include/media/stagefright/AMRWriter.h4
-rw-r--r--include/media/stagefright/CameraSource.h12
-rw-r--r--include/media/stagefright/MPEG4Writer.h19
-rw-r--r--include/private/surfaceflinger/SharedBufferStack.h104
-rw-r--r--media/libmedia/IOMX.cpp2
-rw-r--r--media/libmediaplayerservice/Android.mk1
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp4
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.cpp441
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.h31
-rw-r--r--media/libstagefright/AMRWriter.cpp26
-rw-r--r--media/libstagefright/AudioSource.cpp4
-rw-r--r--media/libstagefright/CameraSource.cpp105
-rw-r--r--media/libstagefright/MPEG4Writer.cpp700
-rw-r--r--media/libstagefright/OMXCodec.cpp129
-rw-r--r--media/libstagefright/omx/OMXNodeInstance.cpp6
22 files changed, 1296 insertions, 350 deletions
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index f19c502..3346b1f 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -95,13 +95,20 @@ Camera::~Camera()
disconnect();
}
-sp<Camera> Camera::connect()
+int32_t Camera::getNumberOfCameras()
+{
+ const sp<ICameraService>& cs = getCameraService();
+ if (cs == 0) return 0;
+ return cs->getNumberOfCameras();
+}
+
+sp<Camera> Camera::connect(int cameraId)
{
LOGV("connect");
sp<Camera> c = new Camera();
const sp<ICameraService>& cs = getCameraService();
if (cs != 0) {
- c->mCamera = cs->connect(c);
+ c->mCamera = cs->connect(c, cameraId);
}
if (c->mCamera != 0) {
c->mCamera->asBinder()->linkToDeath(c);
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp
index 65fd7ac..cfb7ba1 100644
--- a/camera/CameraParameters.cpp
+++ b/camera/CameraParameters.cpp
@@ -137,6 +137,7 @@ const char CameraParameters::FOCUS_MODE_INFINITY[] = "infinity";
const char CameraParameters::FOCUS_MODE_MACRO[] = "macro";
const char CameraParameters::FOCUS_MODE_FIXED[] = "fixed";
const char CameraParameters::FOCUS_MODE_EDOF[] = "edof";
+const char CameraParameters::FOCUS_MODE_CONTINUOUS[] = "continuous";
CameraParameters::CameraParameters()
: mMap()
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index 46b5478..db1dca6 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -34,12 +34,22 @@ public:
{
}
+ // get number of cameras available
+ virtual int32_t getNumberOfCameras()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+ remote()->transact(BnCameraService::GET_NUMBER_OF_CAMERAS, data, &reply);
+ return reply.readInt32();
+ }
+
// connect to camera service
- virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient)
+ virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId)
{
Parcel data, reply;
data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
data.writeStrongBinder(cameraClient->asBinder());
+ data.writeInt32(cameraId);
remote()->transact(BnCameraService::CONNECT, data, &reply);
return interface_cast<ICamera>(reply.readStrongBinder());
}
@@ -53,10 +63,15 @@ status_t BnCameraService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
+ case GET_NUMBER_OF_CAMERAS: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ reply->writeInt32(getNumberOfCameras());
+ return NO_ERROR;
+ } break;
case CONNECT: {
CHECK_INTERFACE(ICameraService, data, reply);
sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder());
- sp<ICamera> camera = connect(cameraClient);
+ sp<ICamera> camera = connect(cameraClient, data.readInt32());
reply->writeStrongBinder(camera->asBinder());
return NO_ERROR;
} break;
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 @@ int main(int argc, char **argv) {
OMXClient client;
CHECK_EQ(client.connect(), OK);
-#if 1
+#if 0
sp<MediaSource> source = createSource(argv[1]);
if (source == NULL) {
@@ -165,14 +165,15 @@ int main(int argc, char **argv) {
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 @@ int main(int argc, char **argv) {
#if 0
CameraSource *source = CameraSource::Create();
+ source->start();
+
printf("source = %p\n", source);
for (int i = 0; i < 100; ++i) {
@@ -227,6 +230,8 @@ int main(int argc, char **argv) {
buffer = NULL;
}
+ source->stop();
+
delete source;
source = NULL;
#endif
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index ee2b30c..1beac27 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -113,7 +113,8 @@ class Camera : public BnCameraClient, public IBinder::DeathRecipient
public:
// construct a camera client from an existing remote
static sp<Camera> create(const sp<ICamera>& camera);
- static sp<Camera> connect();
+ static int32_t getNumberOfCameras();
+ static sp<Camera> connect(int cameraId);
~Camera();
void init();
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 5ea83a5..979df9f 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -309,6 +309,12 @@ public:
// continuously. Applications should not call
// CameraHardwareInterface.autoFocus in this mode.
static const char FOCUS_MODE_EDOF[];
+ // Continuous focus mode. The camera continuously tries to focus. This is
+ // ideal for shooting video or shooting photo of moving object. Continuous
+ // focus starts when CameraHardwareInterface.autoFocus is called. Focus
+ // callback will be only called once as soon as the picture is in focus.
+ static const char FOCUS_MODE_CONTINUOUS[];
+
private:
DefaultKeyedVector<String8,String8> mMap;
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index 82b1283..dcd434f 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -30,13 +30,16 @@ class ICameraService : public IInterface
{
public:
enum {
- CONNECT = IBinder::FIRST_CALL_TRANSACTION,
+ GET_NUMBER_OF_CAMERAS = IBinder::FIRST_CALL_TRANSACTION,
+ CONNECT
};
public:
DECLARE_META_INTERFACE(CameraService);
- virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient) = 0;
+ virtual int32_t getNumberOfCameras() = 0;
+ virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient,
+ int cameraId) = 0;
};
// ----------------------------------------------------------------------------
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 @@ protected:
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..3afa598 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -34,7 +34,7 @@ class Camera;
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,18 @@ private:
int mWidth, mHeight;
int64_t mFirstFrameTimeUs;
- int32_t mNumFrames;
+ int64_t mLastFrameTimestampUs;
+ int32_t mNumFramesReceived;
+ int32_t mNumFramesEncoded;
+ int32_t mNumFramesDropped;
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/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 6b93f19..6064fc4 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -49,6 +49,8 @@ public:
void writeFourcc(const char *fourcc);
void write(const void *data, size_t size);
void endBox();
+ uint32_t interleaveDuration() const { return mInterleaveDurationUs; }
+ status_t setInterleaveDuration(uint32_t duration);
protected:
virtual ~MPEG4Writer();
@@ -59,14 +61,27 @@ private:
FILE *mFile;
off_t mOffset;
off_t mMdatOffset;
+ uint8_t *mMoovBoxBuffer;
+ off_t mMoovBoxBufferOffset;
+ bool mWriteMoovBoxToMemory;
+ off_t mFreeBoxOffset;
+ bool mStreamableFile;
+ off_t mEstimatedMoovBoxSize;
+ uint32_t mInterleaveDurationUs;
Mutex mLock;
List<Track *> mTracks;
List<off_t> mBoxes;
- off_t addSample(MediaBuffer *buffer);
- off_t addLengthPrefixedSample(MediaBuffer *buffer);
+ void lock();
+ void unlock();
+
+ // Acquire lock before calling these methods
+ off_t addSample_l(MediaBuffer *buffer);
+ off_t addLengthPrefixedSample_l(MediaBuffer *buffer);
+
+ inline size_t write(const void *ptr, size_t size, size_t nmemb, FILE* stream);
MPEG4Writer(const MPEG4Writer &);
MPEG4Writer &operator=(const MPEG4Writer &);
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 @@ namespace android {
// 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 @@ class SharedClient;
// ----------------------------------------------------------------------------
-// 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 @@ class SharedBufferStack
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 @@ public:
// 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 @@ protected:
SharedBufferStack* const mSharedStack;
const int mNumBuffers;
const int mIdentity;
+ int32_t computeTail() const;
friend struct Update;
friend struct QueueUpdate;
@@ -160,61 +177,22 @@ protected:
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 @@ public:
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 @@ private:
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 @@ private:
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 @@ struct surface_flinger_cblk_t // 4KB max
// ---------------------------------------------------------------------------
-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 @@ sp<IOMXRenderer> IOMX::createRendererFromJavaSurface(
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 @@ LOCAL_SHARED_LIBRARIES := \
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 @@ MediaPlayerService::~MediaPlayerService()
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..12e8fe6 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,13 @@
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
#include <camera/ICamera.h>
+#include <camera/Camera.h>
+#include <camera/CameraParameters.h>
#include <surfaceflinger/ISurface.h>
#include <utils/Errors.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <ctype.h>
namespace android {
@@ -83,6 +89,12 @@ status_t StagefrightRecorder::setVideoEncoder(video_encoder ve) {
}
status_t StagefrightRecorder::setVideoSize(int width, int height) {
+ if (width <= 0 || height <= 0) {
+ LOGE("Invalid video size: %dx%d", width, height);
+ return BAD_VALUE;
+ }
+
+ // Additional check on the dimension will be performed later
mVideoWidth = width;
mVideoHeight = height;
@@ -90,13 +102,37 @@ status_t StagefrightRecorder::setVideoSize(int width, int height) {
}
status_t StagefrightRecorder::setVideoFrameRate(int frames_per_second) {
+ if (frames_per_second <= 0 || frames_per_second > 30) {
+ LOGE("Invalid video frame rate: %d", frames_per_second);
+ return BAD_VALUE;
+ }
+
+ // Additional check on the frame rate will be performed later
mFrameRate = frames_per_second;
return OK;
}
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;
}
@@ -127,9 +163,216 @@ status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t leng
return OK;
}
-status_t StagefrightRecorder::setParameters(const String8 &params) {
- mParams = params;
+// Attempt to parse an int64 literal optionally surrounded by whitespace,
+// returns true on success, false otherwise.
+static bool safe_strtoi64(const char *s, int64_t *val) {
+ char *end;
+ *val = strtoll(s, &end, 10);
+
+ if (end == s || errno == ERANGE) {
+ return false;
+ }
+
+ // Skip trailing whitespace
+ while (isspace(*end)) {
+ ++end;
+ }
+
+ // For a successful return, the string must contain nothing but a valid
+ // int64 literal optionally surrounded by whitespace.
+
+ return *end == '\0';
+}
+
+// Return true if the value is in [0, 0x007FFFFFFF]
+static bool safe_strtoi32(const char *s, int32_t *val) {
+ int64_t temp;
+ if (safe_strtoi64(s, &temp)) {
+ if (temp >= 0 && temp <= 0x007FFFFFFF) {
+ *val = static_cast<int32_t>(temp);
+ return true;
+ }
+ }
+ return false;
+}
+
+// Trim both leading and trailing whitespace from the given string.
+static void TrimString(String8 *s) {
+ size_t num_bytes = s->bytes();
+ const char *data = s->string();
+
+ size_t leading_space = 0;
+ while (leading_space < num_bytes && isspace(data[leading_space])) {
+ ++leading_space;
+ }
+
+ size_t i = num_bytes;
+ while (i > leading_space && isspace(data[i - 1])) {
+ --i;
+ }
+
+ s->setTo(String8(&data[leading_space], i - leading_space));
+}
+
+status_t StagefrightRecorder::setParamAudioSamplingRate(int32_t sampleRate) {
+ LOGV("setParamAudioSamplingRate: %d", sampleRate);
+ if (sampleRate <= 0) {
+ LOGE("Invalid audio sampling rate: %d", sampleRate);
+ return BAD_VALUE;
+ }
+
+ // Additional check on the sample rate will be performed later.
+ mSampleRate = sampleRate;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamAudioNumberOfChannels(int32_t channels) {
+ LOGV("setParamAudioNumberOfChannels: %d", channels);
+ if (channels <= 0 || channels >= 3) {
+ LOGE("Invalid number of audio channels: %d", channels);
+ }
+
+ // Additional check on the number of channels will be performed later.
+ mAudioChannels = channels;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamAudioEncodingBitRate(int32_t bitRate) {
+ LOGV("setParamAudioEncodingBitRate: %d", bitRate);
+ if (bitRate <= 0) {
+ LOGE("Invalid audio encoding bit rate: %d", bitRate);
+ return BAD_VALUE;
+ }
+ // The target bit rate may not be exactly the same as the requested.
+ // It depends on many factors, such as rate control, and the bit rate
+ // range that a specific encoder supports. The mismatch between the
+ // the target and requested bit rate will NOT be treated as an error.
+ mAudioBitRate = bitRate;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamVideoEncodingBitRate(int32_t bitRate) {
+ LOGV("setParamVideoEncodingBitRate: %d", bitRate);
+ if (bitRate <= 0) {
+ LOGE("Invalid video encoding bit rate: %d", bitRate);
+ return BAD_VALUE;
+ }
+
+ // The target bit rate may not be exactly the same as the requested.
+ // It depends on many factors, such as rate control, and the bit rate
+ // range that a specific encoder supports. The mismatch between the
+ // the target and requested bit rate will NOT be treated as an error.
+ mVideoBitRate = bitRate;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamMaxDurationOrFileSize(int64_t limit,
+ bool limit_is_duration) {
+ LOGV("setParamMaxDurationOrFileSize: limit (%d) for %s",
+ limit, limit_is_duration?"duration":"size");
+ if (limit_is_duration) { // limit is in ms
+ if (limit <= 1000) { // XXX: 1 second
+ LOGE("Max file duration is too short: %lld us", limit);
+ }
+ mMaxFileDurationUs = limit * 1000LL;
+ } else {
+ if (limit <= 1024) { // XXX: 1 kB
+ LOGE("Max file size is too small: %lld bytes", limit);
+ }
+ mMaxFileSizeBytes = limit;
+ }
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamInterleaveDuration(int32_t durationUs) {
+ LOGV("setParamInterleaveDuration: %d", durationUs);
+ if (durationUs <= 20000) { // XXX: 20 ms
+ LOGE("Audio/video interleave duration is too small: %d us", durationUs);
+ return BAD_VALUE;
+ }
+ mInterleaveDurationUs = durationUs;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParameter(
+ const String8 &key, const String8 &value) {
+ LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
+ if (key == "max-duration") {
+ int64_t max_duration_ms;
+ if (safe_strtoi64(value.string(), &max_duration_ms)) {
+ return setParamMaxDurationOrFileSize(
+ max_duration_ms, true /* limit_is_duration */);
+ }
+ } else if (key == "max-filesize") {
+ int64_t max_filesize_bytes;
+ if (safe_strtoi64(value.string(), &max_filesize_bytes)) {
+ return setParamMaxDurationOrFileSize(
+ max_filesize_bytes, false /* limit is filesize */);
+ }
+ } else if (key == "audio-param-sampling-rate") {
+ int32_t sampling_rate;
+ if (safe_strtoi32(value.string(), &sampling_rate)) {
+ return setParamAudioSamplingRate(sampling_rate);
+ }
+ } else if (key == "audio-param-number-of-channels") {
+ int32_t number_of_channels;
+ if (safe_strtoi32(value.string(), &number_of_channels)) {
+ return setParamAudioNumberOfChannels(number_of_channels);
+ }
+ } else if (key == "audio-param-encoding-bitrate") {
+ int32_t audio_bitrate;
+ if (safe_strtoi32(value.string(), &audio_bitrate)) {
+ return setParamAudioEncodingBitRate(audio_bitrate);
+ }
+ } else if (key == "video-param-encoding-bitrate") {
+ int32_t video_bitrate;
+ if (safe_strtoi32(value.string(), &video_bitrate)) {
+ return setParamVideoEncodingBitRate(video_bitrate);
+ }
+ } else if (key == "param-interleave-duration-us") {
+ int32_t durationUs;
+ if (safe_strtoi32(value.string(), &durationUs)) {
+ return setParamInterleaveDuration(durationUs);
+ }
+ } else {
+ LOGE("setParameter: failed to find key %s", key.string());
+ }
+ return BAD_VALUE;
+}
+
+status_t StagefrightRecorder::setParameters(const String8 &params) {
+ LOGV("setParameters: %s", params.string());
+ const char *cparams = params.string();
+ const char *key_start = cparams;
+ for (;;) {
+ const char *equal_pos = strchr(key_start, '=');
+ if (equal_pos == NULL) {
+ LOGE("Parameters %s miss a value", cparams);
+ return BAD_VALUE;
+ }
+ String8 key(key_start, equal_pos - key_start);
+ TrimString(&key);
+ if (key.length() == 0) {
+ LOGE("Parameters %s contains an empty key", cparams);
+ return BAD_VALUE;
+ }
+ const char *value_start = equal_pos + 1;
+ const char *semicolon_pos = strchr(value_start, ';');
+ String8 value;
+ if (semicolon_pos == NULL) {
+ value.setTo(value_start);
+ } else {
+ value.setTo(value_start, semicolon_pos - value_start);
+ }
+ if (setParameter(key, value) != OK) {
+ return BAD_VALUE;
+ }
+ if (semicolon_pos == NULL) {
+ break; // Reaches the end
+ }
+ key_start = semicolon_pos + 1;
+ }
return OK;
}
@@ -158,40 +401,56 @@ status_t StagefrightRecorder::start() {
case OUTPUT_FORMAT_AMR_WB:
return startAMRRecording();
+ case OUTPUT_FORMAT_AAC_ADIF:
+ case OUTPUT_FORMAT_AAC_ADTS:
+ return startAACRecording();
+
default:
return UNKNOWN_ERROR;
}
}
-sp<MediaSource> StagefrightRecorder::createAMRAudioSource() {
- uint32_t sampleRate =
- mAudioEncoder == AUDIO_ENCODER_AMR_NB ? 8000 : 16000;
-
+sp<MediaSource> StagefrightRecorder::createAudioSource() {
sp<AudioSource> audioSource =
new AudioSource(
mAudioSource,
- sampleRate,
+ mSampleRate,
AudioSystem::CHANNEL_IN_MONO);
status_t err = audioSource->initCheck();
if (err != OK) {
+ LOGE("audio source is not initialized");
return NULL;
}
sp<MetaData> encMeta = new MetaData;
- encMeta->setCString(
- kKeyMIMEType,
- mAudioEncoder == AUDIO_ENCODER_AMR_NB
- ? MEDIA_MIMETYPE_AUDIO_AMR_NB : MEDIA_MIMETYPE_AUDIO_AMR_WB);
+ const char *mime;
+ switch (mAudioEncoder) {
+ case AUDIO_ENCODER_AMR_NB:
+ case AUDIO_ENCODER_DEFAULT:
+ mime = MEDIA_MIMETYPE_AUDIO_AMR_NB;
+ break;
+ case AUDIO_ENCODER_AMR_WB:
+ mime = MEDIA_MIMETYPE_AUDIO_AMR_WB;
+ break;
+ case AUDIO_ENCODER_AAC:
+ mime = MEDIA_MIMETYPE_AUDIO_AAC;
+ break;
+ default:
+ LOGE("Unknown audio encoder: %d", mAudioEncoder);
+ return NULL;
+ }
+ encMeta->setCString(kKeyMIMEType, mime);
int32_t maxInputSize;
CHECK(audioSource->getFormat()->findInt32(
kKeyMaxInputSize, &maxInputSize));
encMeta->setInt32(kKeyMaxInputSize, maxInputSize);
- encMeta->setInt32(kKeyChannelCount, 1);
- encMeta->setInt32(kKeySampleRate, sampleRate);
+ encMeta->setInt32(kKeyChannelCount, mAudioChannels);
+ encMeta->setInt32(kKeySampleRate, mSampleRate);
+ encMeta->setInt32(kKeyBitRate, mAudioBitRate);
OMXClient client;
CHECK_EQ(client.connect(), OK);
@@ -203,22 +462,59 @@ sp<MediaSource> StagefrightRecorder::createAMRAudioSource() {
return audioEncoder;
}
+status_t StagefrightRecorder::startAACRecording() {
+ CHECK(mOutputFormat == OUTPUT_FORMAT_AAC_ADIF ||
+ mOutputFormat == OUTPUT_FORMAT_AAC_ADTS);
+
+ CHECK(mAudioEncoder == AUDIO_ENCODER_AAC);
+ CHECK(mAudioSource != AUDIO_SOURCE_LIST_END);
+ CHECK(mOutputFd >= 0);
+
+ CHECK(0 == "AACWriter is not implemented yet");
+
+ return OK;
+}
+
status_t StagefrightRecorder::startAMRRecording() {
- if (mAudioSource == AUDIO_SOURCE_LIST_END
- || mVideoSource != VIDEO_SOURCE_LIST_END) {
+ CHECK(mOutputFormat == OUTPUT_FORMAT_AMR_NB ||
+ mOutputFormat == OUTPUT_FORMAT_AMR_WB);
+
+ if (mOutputFormat == OUTPUT_FORMAT_AMR_NB) {
+ if (mAudioEncoder != AUDIO_ENCODER_DEFAULT &&
+ mAudioEncoder != AUDIO_ENCODER_AMR_NB) {
+ LOGE("Invalid encoder %d used for AMRNB recording",
+ mAudioEncoder);
+ return UNKNOWN_ERROR;
+ }
+ if (mSampleRate != 8000) {
+ LOGE("Invalid sampling rate %d used for AMRNB recording",
+ mSampleRate);
+ return UNKNOWN_ERROR;
+ }
+ } else { // mOutputFormat must be OUTPUT_FORMAT_AMR_WB
+ if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) {
+ LOGE("Invlaid encoder %d used for AMRWB recording",
+ mAudioEncoder);
+ return UNKNOWN_ERROR;
+ }
+ if (mSampleRate != 16000) {
+ LOGE("Invalid sample rate %d used for AMRWB recording",
+ mSampleRate);
+ return UNKNOWN_ERROR;
+ }
+ }
+ if (mAudioChannels != 1) {
+ LOGE("Invalid number of audio channels %d used for amr recording",
+ mAudioChannels);
return UNKNOWN_ERROR;
}
- if (mOutputFormat == OUTPUT_FORMAT_AMR_NB
- && mAudioEncoder != AUDIO_ENCODER_DEFAULT
- && mAudioEncoder != AUDIO_ENCODER_AMR_NB) {
- return UNKNOWN_ERROR;
- } else if (mOutputFormat == OUTPUT_FORMAT_AMR_WB
- && mAudioEncoder != AUDIO_ENCODER_AMR_WB) {
+ if (mAudioSource >= AUDIO_SOURCE_LIST_END) {
+ LOGE("Invalid audio source: %d", mAudioSource);
return UNKNOWN_ERROR;
}
- sp<MediaSource> audioEncoder = createAMRAudioSource();
+ sp<MediaSource> audioEncoder = createAudioSource();
if (audioEncoder == NULL) {
return UNKNOWN_ERROR;
@@ -235,18 +531,71 @@ status_t StagefrightRecorder::startAMRRecording() {
status_t StagefrightRecorder::startMPEG4Recording() {
mWriter = new MPEG4Writer(dup(mOutputFd));
+ // Add audio source first if it exists
+ if (mAudioSource != AUDIO_SOURCE_LIST_END) {
+ sp<MediaSource> audioEncoder;
+ switch(mAudioEncoder) {
+ case AUDIO_ENCODER_AMR_NB:
+ case AUDIO_ENCODER_AMR_WB:
+ case AUDIO_ENCODER_AAC:
+ audioEncoder = createAudioSource();
+ break;
+ default:
+ LOGE("Unsupported audio encoder: %d", mAudioEncoder);
+ return UNKNOWN_ERROR;
+ }
+
+ if (audioEncoder == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ mWriter->addSource(audioEncoder);
+ }
if (mVideoSource == VIDEO_SOURCE_DEFAULT
|| mVideoSource == VIDEO_SOURCE_CAMERA) {
CHECK(mCamera != NULL);
+ if (mCamera == 0) {
+ mCamera = Camera::connect(0);
+ }
+ CHECK(mCamera != NULL);
+
+ // Set the actual video recording frame size
+ CameraParameters params(mCamera->getParameters());
+ params.setPreviewSize(mVideoWidth, mVideoHeight);
+ params.setPreviewFrameRate(mFrameRate);
+ String8 s = params.flatten();
+ CHECK_EQ(OK, mCamera->setParameters(s));
+ CameraParameters newCameraParams(mCamera->getParameters());
+
+ // Check on video frame size
+ int frameWidth = 0, frameHeight = 0;
+ newCameraParams.getPreviewSize(&frameWidth, &frameHeight);
+ if (frameWidth < 0 || frameWidth != mVideoWidth ||
+ frameHeight < 0 || frameHeight != mVideoHeight) {
+ LOGE("Failed to set the video frame size to %dx%d",
+ mVideoWidth, mVideoHeight);
+ return UNKNOWN_ERROR;
+ }
+
+ // Check on video frame rate
+ int frameRate = newCameraParams.getPreviewFrameRate();
+ if (frameRate < 0 || (frameRate - mFrameRate) != 0) {
+ LOGE("Failed to set frame rate to %d", mFrameRate);
+ return UNKNOWN_ERROR;
+ }
+
sp<CameraSource> cameraSource =
- CameraSource::CreateFromICamera(mCamera);
+ CameraSource::CreateFromCamera(mCamera);
CHECK(cameraSource != NULL);
cameraSource->setPreviewSurface(mPreviewSurface);
sp<MetaData> enc_meta = new MetaData;
+ enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
+ enc_meta->setInt32(kKeySampleRate, mFrameRate); // XXX: kKeySampleRate?
+
switch (mVideoEncoder) {
case VIDEO_ENCODER_H263:
enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
@@ -286,14 +635,10 @@ status_t StagefrightRecorder::startMPEG4Recording() {
mWriter->addSource(encoder);
}
- if (mAudioSource != AUDIO_SOURCE_LIST_END) {
- sp<MediaSource> audioEncoder = createAMRAudioSource();
-
- if (audioEncoder == NULL) {
- return UNKNOWN_ERROR;
- }
-
- mWriter->addSource(audioEncoder);
+ {
+ // MPEGWriter specific handling
+ MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get()); // mWriter is an MPEGWriter
+ writer->setInterleaveDuration(mInterleaveDurationUs);
}
mWriter->start();
@@ -314,21 +659,41 @@ status_t StagefrightRecorder::stop() {
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;
}
status_t StagefrightRecorder::reset() {
stop();
+ // No audio or video source by default
mAudioSource = AUDIO_SOURCE_LIST_END;
mVideoSource = VIDEO_SOURCE_LIST_END;
- mOutputFormat = OUTPUT_FORMAT_LIST_END;
- mAudioEncoder = AUDIO_ENCODER_LIST_END;
- mVideoEncoder = VIDEO_ENCODER_LIST_END;
- mVideoWidth = -1;
- mVideoHeight = -1;
- mFrameRate = -1;
+
+ // Default parameters
+ mOutputFormat = OUTPUT_FORMAT_THREE_GPP;
+ mAudioEncoder = AUDIO_ENCODER_AMR_NB;
+ mVideoEncoder = VIDEO_ENCODER_H263;
+ mVideoWidth = 176;
+ mVideoHeight = 144;
+ mFrameRate = 20;
+ mVideoBitRate = 192000;
+ mSampleRate = 8000;
+ mAudioChannels = 1;
+ mAudioBitRate = 12200;
+
mOutputFd = -1;
+ mFlags = 0;
return OK;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 7ec412d..b7d554b 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 @@ struct StagefrightRecorder : public MediaRecorderBase {
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;
@@ -62,14 +68,31 @@ private:
output_format mOutputFormat;
audio_encoder mAudioEncoder;
video_encoder mVideoEncoder;
- int mVideoWidth, mVideoHeight;
- int mFrameRate;
+ int32_t mVideoWidth, mVideoHeight;
+ int32_t mFrameRate;
+ int32_t mVideoBitRate;
+ int32_t mAudioBitRate;
+ int32_t mAudioChannels;
+ int32_t mSampleRate;
+ int32_t mInterleaveDurationUs;
+ int64_t mMaxFileSizeBytes;
+ int64_t mMaxFileDurationUs;
+
String8 mParams;
int mOutputFd;
+ int32_t mFlags;
status_t startMPEG4Recording();
status_t startAMRRecording();
- sp<MediaSource> createAMRAudioSource();
+ status_t startAACRecording();
+ sp<MediaSource> createAudioSource();
+ status_t setParameter(const String8 &key, const String8 &value);
+ status_t setParamVideoEncodingBitRate(int32_t bitRate);
+ status_t setParamAudioEncodingBitRate(int32_t bitRate);
+ status_t setParamAudioNumberOfChannels(int32_t channles);
+ status_t setParamAudioSamplingRate(int32_t sampleRate);
+ status_t setParamInterleaveDuration(int32_t durationUs);
+ status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration);
StagefrightRecorder(const StagefrightRecorder &);
StagefrightRecorder &operator=(const StagefrightRecorder &);
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::initCheck() const {
}
status_t AMRWriter::addSource(const sp<MediaSource> &source) {
- Mutex::Autolock autoLock(mLock);
-
if (mInitCheck != OK) {
return mInitCheck;
}
@@ -95,8 +93,6 @@ status_t AMRWriter::addSource(const sp<MediaSource> &source) {
}
status_t AMRWriter::start() {
- Mutex::Autolock autoLock(mLock);
-
if (mInitCheck != OK) {
return mInitCheck;
}
@@ -127,16 +123,12 @@ status_t AMRWriter::start() {
}
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::ThreadWrapper(void *me) {
}
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 @@ void AMRWriter::threadFunc() {
buffer = NULL;
}
- Mutex::Autolock autoLock(mLock);
mReachedEOS = true;
}
bool AMRWriter::reachedEOS() {
- Mutex::Autolock autoLock(mLock);
return mReachedEOS;
}
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index edabaf9..abd8abc 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AudioSource"
+#include <utils/Log.h>
+
#include <media/stagefright/AudioSource.h>
#include <media/AudioRecord.h>
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 075b1e3..038c3b3 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,22 +93,20 @@ void CameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) {
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
CameraSource *CameraSource::Create() {
- sp<Camera> camera = Camera::connect();
+ sp<Camera> camera = Camera::connect(0);
if (camera.get() == NULL) {
return NULL;
@@ -125,9 +116,7 @@ CameraSource *CameraSource::Create() {
}
// 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,11 @@ CameraSource::CameraSource(const sp<Camera> &camera)
mWidth(0),
mHeight(0),
mFirstFrameTimeUs(0),
- mNumFrames(0),
+ mLastFrameTimestampUs(0),
+ mNumFramesReceived(0),
+ mNumFramesEncoded(0),
+ mNumFramesDropped(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 +152,7 @@ void CameraSource::setPreviewSurface(const sp<ISurface> &surface) {
}
status_t CameraSource::start(MetaData *) {
+ LOGV("start");
CHECK(!mStarted);
mCamera->setListener(new CameraSourceListener(this));
@@ -176,11 +162,7 @@ status_t CameraSource::start(MetaData *) {
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 +171,32 @@ status_t CameraSource::start(MetaData *) {
}
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/encoded/dropped: %d/%d/%d, timestamp (us) last/first: %lld/%lld",
+ mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped,
+ mLastFrameTimestampUs, mFirstFrameTimeUs);
+ CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped);
return OK;
}
+void CameraSource::releaseQueuedFrames() {
+ List<sp<IMemory> >::iterator it;
+ while (!mFrames.empty()) {
+ it = mFrames.begin();
+ mCamera->releaseRecordingFrame(*it);
+ mFrames.erase(it);
+ ++mNumFramesDropped;
+ }
+}
+
sp<MetaData> CameraSource::getFormat() {
sp<MetaData> meta = new MetaData;
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
@@ -210,7 +209,7 @@ sp<MetaData> CameraSource::getFormat() {
status_t CameraSource::read(
MediaBuffer **buffer, const ReadOptions *options) {
- CHECK(mStarted);
+ LOGV("read");
*buffer = NULL;
@@ -224,20 +223,24 @@ status_t CameraSource::read(
{
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());
+ ++mNumFramesEncoded;
}
*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 +248,25 @@ status_t CameraSource::read(
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);
+ ++mNumFramesReceived;
+ ++mNumFramesDropped;
+ return;
+ }
- int64_t nowUs = getNowUs();
- if (mNumFrames == 0) {
- mFirstFrameTimeUs = nowUs;
+ if (mNumFramesReceived == 0) {
+ mFirstFrameTimeUs = timestampUs;
}
- ++mNumFrames;
+ ++mNumFramesReceived;
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..19cccf7 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MPEG4Writer"
+#include <utils/Log.h>
+
#include <arpa/inet.h>
#include <ctype.h>
@@ -24,6 +28,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>
@@ -52,34 +57,73 @@ private:
struct SampleInfo {
size_t size;
- off_t offset;
int64_t timestamp;
};
- List<SampleInfo> mSampleInfos;
+ List<SampleInfo> mSampleInfos;
+ bool mSamplesHaveSameSize;
+
+ List<MediaBuffer *> mChunkSamples;
+ List<off_t> mChunkOffsets;
+
+ struct StscTableEntry {
+
+ StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id)
+ : firstChunk(chunk),
+ samplesPerChunk(samples),
+ sampleDescriptionId(id) {}
+
+ uint32_t firstChunk;
+ uint32_t samplesPerChunk;
+ uint32_t sampleDescriptionId;
+ };
+ List<StscTableEntry> mStscTableEntries;
+
+ List<int32_t> mStssTableEntries;
+
+ struct SttsTableEntry {
+
+ SttsTableEntry(uint32_t count, uint32_t duration)
+ : sampleCount(count), sampleDuration(duration) {}
+
+ uint32_t sampleCount;
+ uint32_t sampleDuration;
+ };
+ List<SttsTableEntry> mSttsTableEntries;
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);
+ void writeOneChunk(bool isAvc);
+
Track(const Track &);
Track &operator=(const Track &);
};
+#define USE_NALLEN_FOUR 1
+
MPEG4Writer::MPEG4Writer(const char *filename)
: mFile(fopen(filename, "wb")),
mOffset(0),
- mMdatOffset(0) {
+ mMdatOffset(0),
+ mEstimatedMoovBoxSize(0),
+ mInterleaveDurationUs(500000) {
CHECK(mFile != NULL);
}
MPEG4Writer::MPEG4Writer(int fd)
: mFile(fdopen(fd, "wb")),
mOffset(0),
- mMdatOffset(0) {
+ mMdatOffset(0),
+ mEstimatedMoovBoxSize(0),
+ mInterleaveDurationUs(500000) {
CHECK(mFile != NULL);
}
@@ -105,15 +149,33 @@ status_t MPEG4Writer::start() {
return UNKNOWN_ERROR;
}
+ mStreamableFile = true;
+ mWriteMoovBoxToMemory = false;
+ mMoovBoxBuffer = NULL;
+ mMoovBoxBufferOffset = 0;
+
beginBox("ftyp");
writeFourcc("isom");
writeInt32(0);
writeFourcc("isom");
endBox();
- mMdatOffset = mOffset;
- write("\x00\x00\x00\x01mdat????????", 16);
+ mFreeBoxOffset = mOffset;
+ if (mEstimatedMoovBoxSize == 0) {
+ // XXX: Estimate the moov box size
+ // based on max file size or duration limit
+ mEstimatedMoovBoxSize = 0x0F00;
+ }
+ CHECK(mEstimatedMoovBoxSize >= 8);
+ fseeko(mFile, mFreeBoxOffset, SEEK_SET);
+ writeInt32(mEstimatedMoovBoxSize);
+ write("free", 4);
+
+ mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize;
+ mOffset = mMdatOffset;
+ fseeko(mFile, mMdatOffset, SEEK_SET);
+ write("\x00\x00\x00\x01mdat????????", 16);
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
status_t err = (*it)->start();
@@ -147,14 +209,20 @@ void MPEG4Writer::stop() {
}
}
+
// Fix up the size of the 'mdat' chunk.
- fseek(mFile, mMdatOffset + 8, SEEK_SET);
+ fseeko(mFile, mMdatOffset + 8, SEEK_SET);
int64_t size = mOffset - mMdatOffset;
size = hton64(size);
fwrite(&size, 1, 8, mFile);
- fseek(mFile, mOffset, SEEK_SET);
+ fseeko(mFile, mOffset, SEEK_SET);
time_t now = time(NULL);
+ const off_t moovOffset = mOffset;
+ mWriteMoovBoxToMemory = true;
+ mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
+ mMoovBoxBufferOffset = 0;
+ CHECK(mMoovBoxBuffer != NULL);
beginBox("moov");
@@ -194,15 +262,48 @@ void MPEG4Writer::stop() {
}
endBox(); // moov
+ mWriteMoovBoxToMemory = false;
+ if (mStreamableFile) {
+ CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize);
+
+ // Moov box
+ fseeko(mFile, mFreeBoxOffset, SEEK_SET);
+ mOffset = mFreeBoxOffset;
+ write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile);
+
+ // Free box
+ mFreeBoxOffset = mStreamableFile? mOffset: mFreeBoxOffset;
+ fseeko(mFile, mFreeBoxOffset, SEEK_SET);
+ writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
+ write("free", 4);
+
+ // Free temp memory
+ free(mMoovBoxBuffer);
+ mMoovBoxBuffer = NULL;
+ mMoovBoxBufferOffset = 0;
+ }
+
CHECK(mBoxes.empty());
+ fflush(mFile);
fclose(mFile);
mFile = NULL;
}
-off_t MPEG4Writer::addSample(MediaBuffer *buffer) {
- Mutex::Autolock autoLock(mLock);
+status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
+ mInterleaveDurationUs = durationUs;
+ return OK;
+}
+
+void MPEG4Writer::lock() {
+ mLock.lock();
+}
+
+void MPEG4Writer::unlock() {
+ mLock.unlock();
+}
+off_t MPEG4Writer::addSample_l(MediaBuffer *buffer) {
off_t old_offset = mOffset;
fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
@@ -213,31 +314,92 @@ off_t MPEG4Writer::addSample(MediaBuffer *buffer) {
return old_offset;
}
-off_t MPEG4Writer::addLengthPrefixedSample(MediaBuffer *buffer) {
- Mutex::Autolock autoLock(mLock);
+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_l(MediaBuffer *buffer) {
+ 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;
}
+size_t MPEG4Writer::write(
+ const void *ptr, size_t size, size_t nmemb, FILE *stream) {
+
+ const size_t bytes = size * nmemb;
+ if (mWriteMoovBoxToMemory) {
+ if (8 + mMoovBoxBufferOffset + bytes > mEstimatedMoovBoxSize) {
+ for (List<off_t>::iterator it = mBoxes.begin();
+ it != mBoxes.end(); ++it) {
+ (*it) += mOffset;
+ }
+ fseeko(mFile, mOffset, SEEK_SET);
+ fwrite(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, stream);
+ fwrite(ptr, size, nmemb, stream);
+ mOffset += (bytes + mMoovBoxBufferOffset);
+ free(mMoovBoxBuffer);
+ mMoovBoxBuffer = NULL;
+ mMoovBoxBufferOffset = 0;
+ mWriteMoovBoxToMemory = false;
+ mStreamableFile = false;
+ } else {
+ memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes);
+ mMoovBoxBufferOffset += bytes;
+ }
+ } else {
+ fwrite(ptr, size, nmemb, stream);
+ mOffset += bytes;
+ }
+ return bytes;
+}
+
void MPEG4Writer::beginBox(const char *fourcc) {
CHECK_EQ(strlen(fourcc), 4);
- mBoxes.push_back(mOffset);
+ mBoxes.push_back(mWriteMoovBoxToMemory?
+ mMoovBoxBufferOffset: mOffset);
writeInt32(0);
writeFourcc(fourcc);
@@ -249,51 +411,48 @@ void MPEG4Writer::endBox() {
off_t offset = *--mBoxes.end();
mBoxes.erase(--mBoxes.end());
- fseek(mFile, offset, SEEK_SET);
- writeInt32(mOffset - offset);
- mOffset -= 4;
- fseek(mFile, mOffset, SEEK_SET);
+ if (mWriteMoovBoxToMemory) {
+ int32_t x = htonl(mMoovBoxBufferOffset - offset);
+ memcpy(mMoovBoxBuffer + offset, &x, 4);
+ } else {
+ fseeko(mFile, offset, SEEK_SET);
+ writeInt32(mOffset - offset);
+ mOffset -= 4;
+ fseeko(mFile, mOffset, SEEK_SET);
+ }
}
void MPEG4Writer::writeInt8(int8_t x) {
- fwrite(&x, 1, 1, mFile);
- ++mOffset;
+ write(&x, 1, 1, mFile);
}
void MPEG4Writer::writeInt16(int16_t x) {
x = htons(x);
- fwrite(&x, 1, 2, mFile);
- mOffset += 2;
+ write(&x, 1, 2, mFile);
}
void MPEG4Writer::writeInt32(int32_t x) {
x = htonl(x);
- fwrite(&x, 1, 4, mFile);
- mOffset += 4;
+ write(&x, 1, 4, mFile);
}
void MPEG4Writer::writeInt64(int64_t x) {
x = hton64(x);
- fwrite(&x, 1, 8, mFile);
- mOffset += 8;
+ write(&x, 1, 8, mFile);
}
void MPEG4Writer::writeCString(const char *s) {
size_t n = strlen(s);
-
- fwrite(s, 1, n + 1, mFile);
- mOffset += n + 1;
+ write(s, 1, n + 1, mFile);
}
void MPEG4Writer::writeFourcc(const char *s) {
CHECK_EQ(strlen(s), 4);
- fwrite(s, 1, 4, mFile);
- mOffset += 4;
+ write(s, 1, 4, mFile);
}
void MPEG4Writer::write(const void *data, size_t size) {
- fwrite(data, 1, size, mFile);
- mOffset += size;
+ write(data, 1, size, mFile);
}
bool MPEG4Writer::reachedEOS() {
@@ -318,8 +477,10 @@ MPEG4Writer::Track::Track(
mSource(source),
mDone(false),
mMaxTimeStampUs(0),
+ mSamplesHaveSameSize(true),
mCodecSpecificData(NULL),
mCodecSpecificDataSize(0),
+ mGotAllCodecSpecificData(false),
mReachedEOS(false) {
}
@@ -380,73 +541,162 @@ void *MPEG4Writer::Track::ThreadWrapper(void *me) {
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;
meta->findCString(kKeyMIMEType, &mime);
- bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
+ !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
int32_t count = 0;
+ const int64_t interleaveDurationUs = mOwner->interleaveDuration();
+ int64_t chunkTimestampUs = 0;
+ int32_t nChunks = 0;
+ int32_t nZeroLengthFrames = 0;
+ int64_t lastTimestamp = 0; // Timestamp of the previous sample
+ int64_t lastDuration = 0; // Time spacing between the previous two samples
+ int32_t sampleCount = 1; // Sample count in the current stts table entry
+ uint32_t previousSampleSize = 0; // Size of the previous sample
MediaBuffer *buffer;
while (!mDone && mSource->read(&buffer) == OK) {
if (buffer->range_length() == 0) {
buffer->release();
buffer = NULL;
-
+ ++nZeroLengthFrames;
continue;
}
++count;
- if (is_avc && count < 3) {
- size_t size = buffer->range_length();
-
- 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);
-
- mCodecSpecificDataSize = size + 8;
- 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;
- }
+ int32_t isCodecConfig;
+ if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig)
+ && isCodecConfig) {
+ CHECK(!mGotAllCodecSpecificData);
+
+ if (is_avc) {
+ status_t err = makeAVCCodecSpecificData(
+ (const uint8_t *)buffer->data()
+ + buffer->range_offset(),
+ buffer->range_length());
+ CHECK_EQ(OK, err);
+ } 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,14 +724,67 @@ void MPEG4Writer::Track::threadEntry() {
memcpy(mCodecSpecificData, data, offset);
buffer->set_range(buffer->range_offset() + offset, size - offset);
- }
- off_t offset = is_avc ? mOwner->addLengthPrefixedSample(buffer)
- : mOwner->addSample(buffer);
+ 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;
+ CHECK_EQ(OK, err);
+
+ mGotAllCodecSpecificData = true;
+ }
+
+ continue;
+ }
SampleInfo info;
- info.size = is_avc ? buffer->range_length() + 2 : buffer->range_length();
- info.offset = offset;
+ info.size = is_avc
+#if USE_NALLEN_FOUR
+ ? buffer->range_length() + 4
+#else
+ ? buffer->range_length() + 2
+#endif
+ : buffer->range_length();
+
+ bool is_audio = !strncasecmp(mime, "audio/", 6);
int64_t timestampUs;
CHECK(buffer->meta_data()->findInt64(kKeyTime, &timestampUs));
@@ -492,14 +795,109 @@ void MPEG4Writer::Track::threadEntry() {
// Our timestamp is in ms.
info.timestamp = (timestampUs + 500) / 1000;
-
mSampleInfos.push_back(info);
+ if (mSampleInfos.size() > 2) {
+ if (lastDuration != info.timestamp - lastTimestamp) {
+ SttsTableEntry sttsEntry(sampleCount, lastDuration);
+ mSttsTableEntries.push_back(sttsEntry);
+ sampleCount = 1;
+ } else {
+ ++sampleCount;
+ }
+ }
+ if (mSamplesHaveSameSize) {
+ if (mSampleInfos.size() >= 2 && previousSampleSize != info.size) {
+ mSamplesHaveSameSize = false;
+ }
+ previousSampleSize = info.size;
+ }
+ lastDuration = info.timestamp - lastTimestamp;
+ lastTimestamp = info.timestamp;
+
+////////////////////////////////////////////////////////////////////////////////
+ // Make a deep copy of the MediaBuffer less Metadata
+ MediaBuffer *copy = new MediaBuffer(buffer->range_length());
+ memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(),
+ buffer->range_length());
+ copy->set_range(0, buffer->range_length());
+
+ mChunkSamples.push_back(copy);
+ if (interleaveDurationUs == 0) {
+ StscTableEntry stscEntry(++nChunks, 1, 1);
+ mStscTableEntries.push_back(stscEntry);
+ writeOneChunk(is_avc);
+ } else {
+ if (chunkTimestampUs == 0) {
+ chunkTimestampUs = timestampUs;
+ } else {
+ if (timestampUs - chunkTimestampUs > interleaveDurationUs) {
+ ++nChunks;
+ if (nChunks == 1 || // First chunk
+ (--(mStscTableEntries.end()))->samplesPerChunk !=
+ mChunkSamples.size()) {
+ StscTableEntry stscEntry(nChunks,
+ mChunkSamples.size(), 1);
+ mStscTableEntries.push_back(stscEntry);
+ }
+ writeOneChunk(is_avc);
+ chunkTimestampUs = timestampUs;
+ }
+ }
+ }
+
+ int32_t isSync = false;
+ if (buffer->meta_data()->findInt32(kKeyIsSyncFrame, &isSync) &&
+ isSync != 0) {
+ mStssTableEntries.push_back(mSampleInfos.size());
+ }
buffer->release();
buffer = NULL;
}
+ CHECK(!mSampleInfos.empty());
+
+ // Last chunk
+ if (!mChunkSamples.empty()) {
+ ++nChunks;
+ StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1);
+ mStscTableEntries.push_back(stscEntry);
+ writeOneChunk(is_avc);
+ }
+
+ // 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.
+ if (mSampleInfos.size() == 1) {
+ lastDuration = 0; // A single sample's duration
+ } else {
+ ++sampleCount; // Count for the last sample
+ }
+ SttsTableEntry sttsEntry(sampleCount, lastDuration);
+ mSttsTableEntries.push_back(sttsEntry);
mReachedEOS = true;
+ LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames",
+ count, nZeroLengthFrames, mSampleInfos.size());
+}
+
+void MPEG4Writer::Track::writeOneChunk(bool isAvc) {
+ mOwner->lock();
+ for (List<MediaBuffer *>::iterator it = mChunkSamples.begin();
+ it != mChunkSamples.end(); ++it) {
+ off_t offset = isAvc? mOwner->addLengthPrefixedSample_l(*it)
+ : mOwner->addSample_l(*it);
+ if (it == mChunkSamples.begin()) {
+ mChunkOffsets.push_back(offset);
+ }
+ }
+ mOwner->unlock();
+ while (!mChunkSamples.empty()) {
+ List<MediaBuffer *>::iterator it = mChunkSamples.begin();
+ (*it)->release();
+ (*it) = NULL;
+ mChunkSamples.erase(it);
+ }
+ mChunkSamples.clear();
}
int64_t MPEG4Writer::Track::getDurationUs() const {
@@ -550,8 +948,8 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
success = success && mMeta->findInt32(kKeyHeight, &height);
CHECK(success);
- mOwner->writeInt32(width);
- mOwner->writeInt32(height);
+ mOwner->writeInt32(width << 16); // 32-bit fixed-point value
+ mOwner->writeInt32(height << 16); // 32-bit fixed-point value
}
mOwner->endBox(); // tkhd
@@ -569,26 +967,15 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
mOwner->beginBox("hdlr");
mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(0); // predefined
- mOwner->writeFourcc(is_audio ? "soun" : "vide");
+ mOwner->writeInt32(0); // component type: should be mhlr
+ mOwner->writeFourcc(is_audio ? "soun" : "vide"); // component subtype
mOwner->writeInt32(0); // reserved
mOwner->writeInt32(0); // reserved
mOwner->writeInt32(0); // reserved
- mOwner->writeCString(""); // name
+ mOwner->writeCString("SoundHandler"); // name
mOwner->endBox();
mOwner->beginBox("minf");
-
- mOwner->beginBox("dinf");
- mOwner->beginBox("dref");
- mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(1);
- mOwner->beginBox("url ");
- mOwner->writeInt32(1); // version=0, flags=1
- mOwner->endBox(); // url
- mOwner->endBox(); // dref
- mOwner->endBox(); // dinf
-
if (is_audio) {
mOwner->beginBox("smhd");
mOwner->writeInt32(0); // version=0, flags=0
@@ -604,7 +991,18 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
mOwner->writeInt16(0);
mOwner->endBox();
}
- mOwner->endBox(); // minf
+
+ mOwner->beginBox("dinf");
+ mOwner->beginBox("dref");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(1);
+ mOwner->beginBox("url ");
+ mOwner->writeInt32(1); // version=0, flags=1
+ mOwner->endBox(); // url
+ mOwner->endBox(); // dref
+ mOwner->endBox(); // dinf
+
+ mOwner->endBox(); // minf
mOwner->beginBox("stbl");
@@ -617,6 +1015,8 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
fourcc = "samr";
} else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
fourcc = "sawb";
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
+ fourcc = "mp4a";
} else {
LOGE("Unknown mime type '%s'.", mime);
CHECK(!"should not be here, unknown mime type.");
@@ -625,10 +1025,12 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
mOwner->beginBox(fourcc); // audio format
mOwner->writeInt32(0); // reserved
mOwner->writeInt16(0); // reserved
- mOwner->writeInt16(0); // data ref index
+ mOwner->writeInt16(0x1); // data ref index
mOwner->writeInt32(0); // reserved
mOwner->writeInt32(0); // reserved
- mOwner->writeInt16(2); // channel count
+ int32_t nChannels;
+ CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels));
+ mOwner->writeInt16(nChannels); // channel count
mOwner->writeInt16(16); // sample size
mOwner->writeInt16(0); // predefined
mOwner->writeInt16(0); // reserved
@@ -638,6 +1040,38 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
CHECK(success);
mOwner->writeInt32(samplerate << 16);
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
+ mOwner->beginBox("esds");
+
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt8(0x03); // ES_DescrTag
+ mOwner->writeInt8(23 + mCodecSpecificDataSize);
+ mOwner->writeInt16(0x0000);// ES_ID
+ mOwner->writeInt8(0x00);
+
+ mOwner->writeInt8(0x04); // DecoderConfigDescrTag
+ mOwner->writeInt8(15 + mCodecSpecificDataSize);
+ mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2
+ mOwner->writeInt8(0x15); // streamType AudioStream
+
+ mOwner->writeInt16(0x03); // XXX
+ mOwner->writeInt8(0x00); // buffer size 24-bit
+ mOwner->writeInt32(96000); // max bit rate
+ mOwner->writeInt32(96000); // avg bit rate
+
+ mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
+ mOwner->writeInt8(mCodecSpecificDataSize);
+ mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+
+ static const uint8_t kData2[] = {
+ 0x06, // SLConfigDescriptorTag
+ 0x01,
+ 0x02
+ };
+ mOwner->write(kData2, sizeof(kData2));
+
+ mOwner->endBox(); // esds
+ }
mOwner->endBox();
} else {
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
@@ -698,7 +1132,7 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
0x00, 0x03, 0xe8, 0x00
};
mOwner->write(kData, sizeof(kData));
-
+
mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
mOwner->writeInt8(mCodecSpecificDataSize);
@@ -733,49 +1167,59 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mSampleInfos.size() - 1);
-
- List<SampleInfo>::iterator it = mSampleInfos.begin();
- int64_t last = (*it).timestamp;
- ++it;
- while (it != mSampleInfos.end()) {
- mOwner->writeInt32(1);
- mOwner->writeInt32((*it).timestamp - last);
-
- last = (*it).timestamp;
-
- ++it;
+ mOwner->writeInt32(mSttsTableEntries.size());
+ for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
+ it != mSttsTableEntries.end(); ++it) {
+ mOwner->writeInt32(it->sampleCount);
+ mOwner->writeInt32(it->sampleDuration);
}
mOwner->endBox(); // stts
+ if (!is_audio) {
+ mOwner->beginBox("stss");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(mStssTableEntries.size()); // number of sync frames
+ for (List<int32_t>::iterator it = mStssTableEntries.begin();
+ it != mStssTableEntries.end(); ++it) {
+ mOwner->writeInt32(*it);
+ }
+ mOwner->endBox(); // stss
+ }
+
mOwner->beginBox("stsz");
mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(0); // default sample size
+ if (mSamplesHaveSameSize) {
+ List<SampleInfo>::iterator it = mSampleInfos.begin();
+ mOwner->writeInt32(it->size); // default sample size
+ } else {
+ mOwner->writeInt32(0);
+ }
mOwner->writeInt32(mSampleInfos.size());
- for (List<SampleInfo>::iterator it = mSampleInfos.begin();
- it != mSampleInfos.end(); ++it) {
- mOwner->writeInt32((*it).size);
+ if (!mSamplesHaveSameSize) {
+ for (List<SampleInfo>::iterator it = mSampleInfos.begin();
+ it != mSampleInfos.end(); ++it) {
+ mOwner->writeInt32((*it).size);
+ }
}
mOwner->endBox(); // stsz
mOwner->beginBox("stsc");
mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mSampleInfos.size());
- int32_t n = 1;
- for (List<SampleInfo>::iterator it = mSampleInfos.begin();
- it != mSampleInfos.end(); ++it, ++n) {
- mOwner->writeInt32(n);
- mOwner->writeInt32(1);
- mOwner->writeInt32(1);
+ mOwner->writeInt32(mStscTableEntries.size());
+ for (List<StscTableEntry>::iterator it = mStscTableEntries.begin();
+ it != mStscTableEntries.end(); ++it) {
+ mOwner->writeInt32(it->firstChunk);
+ mOwner->writeInt32(it->samplesPerChunk);
+ mOwner->writeInt32(it->sampleDescriptionId);
}
mOwner->endBox(); // stsc
mOwner->beginBox("co64");
mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mSampleInfos.size());
- for (List<SampleInfo>::iterator it = mSampleInfos.begin();
- it != mSampleInfos.end(); ++it, ++n) {
- mOwner->writeInt64((*it).offset);
+ mOwner->writeInt32(mChunkOffsets.size());
+ for (List<off_t>::iterator it = mChunkOffsets.begin();
+ it != mChunkOffsets.end(); ++it) {
+ mOwner->writeInt64((*it));
}
mOwner->endBox(); // co64
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 69da7ef..ce4dd32 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -141,6 +141,7 @@ static const CodecInfo kEncoderInfo[] = {
{ 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" },
};
@@ -523,6 +524,7 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
setAACFormat(numChannels, sampleRate);
}
+
if (!strncasecmp(mMIME, "video/", 6)) {
int32_t width, height;
bool success = meta->findInt32(kKeyWidth, &width);
@@ -570,7 +572,8 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
}
if (!strcmp(mComponentName, "OMX.TI.AMR.encode")
- || !strcmp(mComponentName, "OMX.TI.WBAMR.encode")) {
+ || !strcmp(mComponentName, "OMX.TI.WBAMR.encode")
+ || !strcmp(mComponentName, "OMX.TI.AAC.encode")) {
setMinBufferSize(kPortIndexOutput, 8192); // XXX
}
@@ -683,6 +686,7 @@ static size_t getFrameSize(
case OMX_COLOR_FormatCbYCrY:
return width * height * 2;
+ case OMX_COLOR_FormatYUV420Planar:
case OMX_COLOR_FormatYUV420SemiPlanar:
return (width * height * 3) / 2;
@@ -713,61 +717,62 @@ void OMXCodec::setVideoInputFormat(
colorFormat = OMX_COLOR_FormatYCbYCr;
}
- CHECK_EQ(setVideoPortFormatType(
- kPortIndexInput, OMX_VIDEO_CodingUnused,
- colorFormat), OK);
- CHECK_EQ(setVideoPortFormatType(
- kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused),
- OK);
+ status_t err;
OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = kPortIndexOutput;
-
OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ //////////////////////// 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->eCompressionFormat = compressionFormat;
- video_def->eColorFormat = OMX_COLOR_FormatUnused;
+ 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);
InitOMXParams(&def);
- def.nPortIndex = kPortIndexInput;
+ def.nPortIndex = kPortIndexOutput;
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(err, OK);
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!
+ video_def->eCompressionFormat = compressionFormat;
+ video_def->eColorFormat = OMX_COLOR_FormatUnused;
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
+ /////////////////// Codec-specific ////////////////////////
switch (compressionFormat) {
case OMX_VIDEO_CodingMPEG4:
{
@@ -915,7 +920,7 @@ status_t OMXCodec::setupAVCEncoderParameters() {
CHECK_EQ(err, OK);
bitrateType.eControlRate = OMX_Video_ControlRateVariable;
- bitrateType.nTargetBitrate = 1000000;
+ bitrateType.nTargetBitrate = 3000000;
err = mOMX->setParameter(
mNode, OMX_IndexParamVideoBitrate,
@@ -2128,11 +2133,24 @@ void OMXCodec::setState(State newState) {
void OMXCodec::setRawAudioFormat(
OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels) {
+
+ // port definition
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = portIndex;
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+ def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+ CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition,
+ &def, sizeof(def)), OK);
+
+ // pcm param
OMX_AUDIO_PARAM_PCMMODETYPE pcmParams;
InitOMXParams(&pcmParams);
pcmParams.nPortIndex = portIndex;
- status_t err = mOMX->getParameter(
+ err = mOMX->getParameter(
mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
CHECK_EQ(err, OK);
@@ -2172,6 +2190,8 @@ void OMXCodec::setAMRFormat(bool isWAMR) {
CHECK_EQ(err, OK);
def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+
+ // XXX: Select bandmode based on bit rate
def.eAMRBandMode =
isWAMR ? OMX_AUDIO_AMRBandModeWB0 : OMX_AUDIO_AMRBandModeNB0;
@@ -2192,8 +2212,60 @@ void OMXCodec::setAMRFormat(bool isWAMR) {
}
void OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate) {
+ CHECK(numChannels == 1 || numChannels == 2);
if (mIsEncoder) {
+ //////////////// input port ////////////////////
setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
+
+ //////////////// output port ////////////////////
+ // format
+ OMX_AUDIO_PARAM_PORTFORMATTYPE format;
+ format.nPortIndex = kPortIndexOutput;
+ format.nIndex = 0;
+ status_t err = OMX_ErrorNone;
+ while (OMX_ErrorNone == err) {
+ CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioPortFormat,
+ &format, sizeof(format)), OK);
+ if (format.eEncoding == OMX_AUDIO_CodingAAC) {
+ break;
+ }
+ format.nIndex++;
+ }
+ CHECK_EQ(OK, err);
+ CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioPortFormat,
+ &format, sizeof(format)), OK);
+
+ // port definition
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexOutput;
+ CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamPortDefinition,
+ &def, sizeof(def)), OK);
+ def.format.audio.bFlagErrorConcealment = OMX_TRUE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
+ CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition,
+ &def, sizeof(def)), OK);
+
+ // profile
+ OMX_AUDIO_PARAM_AACPROFILETYPE profile;
+ InitOMXParams(&profile);
+ profile.nPortIndex = kPortIndexOutput;
+ CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioAac,
+ &profile, sizeof(profile)), OK);
+ profile.nChannels = numChannels;
+ profile.eChannelMode = (numChannels == 1?
+ OMX_AUDIO_ChannelModeMono: OMX_AUDIO_ChannelModeStereo);
+ profile.nSampleRate = sampleRate;
+ profile.nBitRate = 96000; // XXX
+ profile.nAudioBandWidth = 0;
+ profile.nFrameLength = 0;
+ profile.nAACtools = OMX_AUDIO_AACToolAll;
+ profile.nAACERtools = OMX_AUDIO_AACERNone;
+ profile.eAACProfile = OMX_AUDIO_AACObjectLC;
+ profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
+ CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioAac,
+ &profile, sizeof(profile)), OK);
+
} else {
OMX_AUDIO_PARAM_AACPROFILETYPE profile;
InitOMXParams(&profile);
@@ -2962,6 +3034,11 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {
} else if (audio_def->eEncoding == OMX_AUDIO_CodingAAC) {
mOutputFormat->setCString(
kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+ int32_t numChannels, sampleRate;
+ inputFormat->findInt32(kKeyChannelCount, &numChannels);
+ inputFormat->findInt32(kKeySampleRate, &sampleRate);
+ mOutputFormat->setInt32(kKeyChannelCount, numChannels);
+ mOutputFormat->setInt32(kKeySampleRate, sampleRate);
} else {
CHECK(!"Should not be here. Unknown audio encoding.");
}
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 @@ status_t OMXNodeInstance::useBuffer(
return UNKNOWN_ERROR;
}
+ CHECK_EQ(header->pAppPrivate, buffer_meta);
+
*buffer = header;
addActiveBuffer(portIndex, *buffer);
@@ -294,6 +296,8 @@ status_t OMXNodeInstance::allocateBuffer(
return UNKNOWN_ERROR;
}
+ CHECK_EQ(header->pAppPrivate, buffer_meta);
+
*buffer = header;
*buffer_data = header->pBuffer;
@@ -325,6 +329,8 @@ status_t OMXNodeInstance::allocateBufferWithBackup(
return UNKNOWN_ERROR;
}
+ CHECK_EQ(header->pAppPrivate, buffer_meta);
+
*buffer = header;
addActiveBuffer(portIndex, *buffer);