summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/java/android/media/Ringtone.java16
-rw-r--r--media/java/android/media/ThumbnailUtil.java107
-rw-r--r--media/libmedia/IOMX.cpp4
-rw-r--r--media/libmediaplayerservice/Android.mk6
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp35
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.cpp16
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.h5
-rw-r--r--media/libmediaplayerservice/MetadataRetrieverClient.cpp21
-rw-r--r--media/libmediaplayerservice/StagefrightMetadataRetriever.cpp197
-rw-r--r--media/libmediaplayerservice/StagefrightMetadataRetriever.h53
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.cpp241
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.h76
-rw-r--r--media/libmediaplayerservice/TestPlayerStub.cpp2
-rw-r--r--media/libstagefright/AMRExtractor.cpp16
-rw-r--r--media/libstagefright/Android.mk8
-rw-r--r--media/libstagefright/AudioPlayer.cpp10
-rw-r--r--media/libstagefright/CachingDataSource.cpp8
-rw-r--r--media/libstagefright/CameraSource.cpp218
-rw-r--r--media/libstagefright/DataSource.cpp11
-rw-r--r--media/libstagefright/ESDS.cpp2
-rw-r--r--media/libstagefright/FileSource.cpp43
-rw-r--r--media/libstagefright/HTTPDataSource.cpp51
-rw-r--r--media/libstagefright/HTTPStream.cpp3
-rw-r--r--media/libstagefright/JPEGSource.cpp6
-rw-r--r--media/libstagefright/MP3Extractor.cpp55
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp90
-rw-r--r--media/libstagefright/MPEG4Writer.cpp108
-rw-r--r--media/libstagefright/MediaBuffer.cpp4
-rw-r--r--media/libstagefright/MediaDefs.cpp1
-rw-r--r--media/libstagefright/MediaExtractor.cpp36
-rw-r--r--media/libstagefright/MediaPlayerImpl.cpp93
-rw-r--r--media/libstagefright/MetaData.cpp19
-rw-r--r--media/libstagefright/MmapSource.cpp11
-rw-r--r--media/libstagefright/OMXCodec.cpp486
-rw-r--r--media/libstagefright/SampleTable.cpp88
-rw-r--r--media/libstagefright/ShoutcastSource.cpp5
-rw-r--r--media/libstagefright/TimedEventQueue.cpp80
-rw-r--r--media/libstagefright/WAVExtractor.cpp317
-rw-r--r--media/libstagefright/include/AMRExtractor.h54
-rw-r--r--media/libstagefright/include/ESDS.h64
-rw-r--r--media/libstagefright/include/HTTPStream.h75
-rw-r--r--media/libstagefright/include/MP3Extractor.h55
-rw-r--r--media/libstagefright/include/MPEG4Extractor.h69
-rw-r--r--media/libstagefright/include/OMX.h2
-rw-r--r--media/libstagefright/include/SampleTable.h111
-rw-r--r--media/libstagefright/include/SoftwareRenderer.h59
-rw-r--r--media/libstagefright/include/TimedEventQueue.h130
-rw-r--r--media/libstagefright/include/WAVExtractor.h61
-rw-r--r--media/libstagefright/include/stagefright_string.h54
-rw-r--r--media/libstagefright/omx/Android.mk1
-rw-r--r--media/libstagefright/omx/ColorConverter.cpp297
-rw-r--r--media/libstagefright/omx/OMX.cpp4
-rw-r--r--media/libstagefright/omx/SoftwareRenderer.cpp275
-rw-r--r--media/libstagefright/string.cpp (renamed from media/libstagefright/stagefright_string.cpp)2
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java3
55 files changed, 3132 insertions, 732 deletions
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index e80d8aa..1713324 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -137,11 +137,17 @@ public class Ringtone {
cursor = res.query(uri, MEDIA_COLUMNS, null, null, null);
}
- if (cursor != null && cursor.getCount() == 1) {
- cursor.moveToFirst();
- return cursor.getString(2);
- } else {
- title = uri.getLastPathSegment();
+ try {
+ if (cursor != null && cursor.getCount() == 1) {
+ cursor.moveToFirst();
+ return cursor.getString(2);
+ } else {
+ title = uri.getLastPathSegment();
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
}
}
diff --git a/media/java/android/media/ThumbnailUtil.java b/media/java/android/media/ThumbnailUtil.java
index f9d69fb..0cf4e76 100644
--- a/media/java/android/media/ThumbnailUtil.java
+++ b/media/java/android/media/ThumbnailUtil.java
@@ -33,10 +33,9 @@ import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.media.MediaMetadataRetriever;
+import android.media.MediaFile.MediaFileType;
-import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
@@ -295,7 +294,7 @@ public class ThumbnailUtil {
* @param uri URI of original image
* @param origId image id
* @param kind either MINI_KIND or MICRO_KIND
- * @param saveImage Whether to save MINI_KIND thumbnail obtained in this method.
+ * @param saveMini Whether to save MINI_KIND thumbnail obtained in this method.
* @return Bitmap
*/
public static Bitmap createImageThumbnail(ContentResolver cr, String filePath, Uri uri,
@@ -305,16 +304,12 @@ public class ThumbnailUtil {
ThumbnailUtil.THUMBNAIL_TARGET_SIZE : ThumbnailUtil.MINI_THUMB_TARGET_SIZE;
int maxPixels = wantMini ?
ThumbnailUtil.THUMBNAIL_MAX_NUM_PIXELS : ThumbnailUtil.MINI_THUMB_MAX_NUM_PIXELS;
- byte[] thumbData = createThumbnailFromEXIF(filePath, targetSize);
+ SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap();
Bitmap bitmap = null;
-
- if (thumbData != null) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = computeSampleSize(options, targetSize, maxPixels);
- options.inDither = false;
- options.inPreferredConfig = Bitmap.Config.ARGB_8888;
- options.inJustDecodeBounds = false;
- bitmap = BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, options);
+ MediaFileType fileType = MediaFile.getFileType(filePath);
+ if (fileType != null && fileType.fileType == MediaFile.FILE_TYPE_JPEG) {
+ createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap);
+ bitmap = sizedThumbnailBitmap.mBitmap;
}
if (bitmap == null) {
@@ -326,9 +321,11 @@ public class ThumbnailUtil {
}
if (saveMini) {
- if (thumbData != null) {
- ThumbnailUtil.storeThumbnail(cr, origId, thumbData, bitmap.getWidth(),
- bitmap.getHeight());
+ if (sizedThumbnailBitmap.mThumbnailData != null) {
+ ThumbnailUtil.storeThumbnail(cr, origId,
+ sizedThumbnailBitmap.mThumbnailData,
+ sizedThumbnailBitmap.mThumbnailWidth,
+ sizedThumbnailBitmap.mThumbnailHeight);
} else {
ThumbnailUtil.storeThumbnail(cr, origId, bitmap);
}
@@ -454,6 +451,7 @@ public class ThumbnailUtil {
Cursor c = cr.query(thumbUri, THUMB_PROJECTION,
Thumbnails.IMAGE_ID + "=?",
new String[]{String.valueOf(origId)}, null);
+ if (c == null) return null;
try {
if (c.moveToNext()) {
return ContentUris.withAppendedId(thumbUri, c.getLong(0));
@@ -482,6 +480,7 @@ public class ThumbnailUtil {
if (thumb == null) return false;
try {
Uri uri = getImageThumbnailUri(cr, origId, thumb.getWidth(), thumb.getHeight());
+ if (uri == null) return false;
OutputStream thumbOut = cr.openOutputStream(uri);
thumb.compress(Bitmap.CompressFormat.JPEG, 85, thumbOut);
thumbOut.close();
@@ -514,31 +513,69 @@ public class ThumbnailUtil {
}
}
- // Extract thumbnail in image that meets the targetSize criteria.
- static byte[] createThumbnailFromEXIF(String filePath, int targetSize) {
- if (filePath == null) return null;
-
- try {
- ExifInterface exif = new ExifInterface(filePath);
- if (exif == null) return null;
- byte [] thumbData = exif.getThumbnail();
- if (thumbData == null) return null;
- // Sniff the size of the EXIF thumbnail before decoding it. Photos
- // from the device will pass, but images that are side loaded from
- // other cameras may not.
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, options);
+ // SizedThumbnailBitmap contains the bitmap, which is downsampled either from
+ // the thumbnail in exif or the full image.
+ // mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail is not null.
+ // The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight.
+ private static class SizedThumbnailBitmap {
+ public byte[] mThumbnailData;
+ public Bitmap mBitmap;
+ public int mThumbnailWidth;
+ public int mThumbnailHeight;
+ }
- int width = options.outWidth;
- int height = options.outHeight;
+ // Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image.
+ // The functions returns a SizedThumbnailBitmap,
+ // which contains a downsampled bitmap and the thumbnail data in EXIF if exists.
+ private static void createThumbnailFromEXIF(String filePath, int targetSize,
+ int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) {
+ if (filePath == null) return;
- if (width >= targetSize && height >= targetSize) {
- return thumbData;
+ ExifInterface exif = null;
+ byte [] thumbData = null;
+ try {
+ exif = new ExifInterface(filePath);
+ if (exif != null) {
+ thumbData = exif.getThumbnail();
}
} catch (IOException ex) {
Log.w(TAG, ex);
}
- return null;
+
+ BitmapFactory.Options fullOptions = new BitmapFactory.Options();
+ BitmapFactory.Options exifOptions = new BitmapFactory.Options();
+ int exifThumbWidth = 0;
+ int fullThumbWidth = 0;
+
+ // Compute exifThumbWidth.
+ if (thumbData != null) {
+ exifOptions.inJustDecodeBounds = true;
+ BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions);
+ exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels);
+ exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize;
+ }
+
+ // Compute fullThumbWidth.
+ fullOptions.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(filePath, fullOptions);
+ fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels);
+ fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize;
+
+ // Choose the larger thumbnail as the returning sizedThumbBitmap.
+ if (exifThumbWidth >= fullThumbWidth) {
+ int width = exifOptions.outWidth;
+ int height = exifOptions.outHeight;
+ exifOptions.inJustDecodeBounds = false;
+ sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0,
+ thumbData.length, exifOptions);
+ if (sizedThumbBitmap.mBitmap != null) {
+ sizedThumbBitmap.mThumbnailData = thumbData;
+ sizedThumbBitmap.mThumbnailWidth = width;
+ sizedThumbBitmap.mThumbnailHeight = height;
+ }
+ } else {
+ fullOptions.inJustDecodeBounds = false;
+ sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions);
+ }
}
}
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 88a7064..76a9e7d 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -284,7 +284,7 @@ public:
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
data.writeIntPtr((intptr_t)node);
data.writeIntPtr((intptr_t)buffer);
- remote()->transact(FILL_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
+ remote()->transact(FILL_BUFFER, data, &reply);
return reply.readInt32();
}
@@ -302,7 +302,7 @@ public:
data.writeInt32(range_length);
data.writeInt32(flags);
data.writeInt64(timestamp);
- remote()->transact(EMPTY_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
+ remote()->transact(EMPTY_BUFFER, data, &reply);
return reply.readInt32();
}
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index fb569da..4784b8e 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -18,8 +18,10 @@ LOCAL_SRC_FILES:= \
ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
-LOCAL_SRC_FILES += \
- StagefrightPlayer.cpp
+LOCAL_SRC_FILES += \
+ StagefrightMetadataRetriever.cpp \
+ StagefrightPlayer.cpp \
+ StagefrightRecorder.cpp
LOCAL_CFLAGS += -DBUILD_WITH_FULL_STAGEFRIGHT=1
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 0a6c365..b81684b 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -366,11 +366,44 @@ extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
extern "C" void free_malloc_leak_info(uint8_t* info);
+// Use the String-class below instead of String8 to allocate all memory
+// beforehand and not reenter the heap while we are examining it...
+struct MyString8 {
+ static const size_t MAX_SIZE = 256 * 1024;
+
+ MyString8()
+ : mPtr((char *)malloc(MAX_SIZE)) {
+ *mPtr = '\0';
+ }
+
+ ~MyString8() {
+ free(mPtr);
+ }
+
+ void append(const char *s) {
+ strcat(mPtr, s);
+ }
+
+ const char *string() const {
+ return mPtr;
+ }
+
+ size_t size() const {
+ return strlen(mPtr);
+ }
+
+private:
+ char *mPtr;
+
+ MyString8(const MyString8 &);
+ MyString8 &operator=(const MyString8 &);
+};
+
void memStatus(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
- String8 result;
+ MyString8 result;
typedef struct {
size_t size;
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 95ee3e4..2ea7cc3 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -24,6 +24,7 @@
#include <unistd.h>
#include <string.h>
#include <cutils/atomic.h>
+#include <cutils/properties.h> // for property_get
#include <android_runtime/ActivityManager.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -37,6 +38,8 @@
#include "MediaRecorderClient.h"
#include "MediaPlayerService.h"
+#include "StagefrightRecorder.h"
+
namespace android {
const char* cameraPermission = "android.permission.CAMERA";
@@ -286,7 +289,18 @@ MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service,
{
LOGV("Client constructor");
mPid = pid;
- mRecorder = new PVMediaRecorder();
+
+#if BUILD_WITH_FULL_STAGEFRIGHT
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("media.stagefright.enable-record", value, NULL)
+ && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+ mRecorder = new StagefrightRecorder;
+ } else
+#endif
+ {
+ mRecorder = new PVMediaRecorder();
+ }
+
mMediaPlayerService = service;
}
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index 6260441..e07306b 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -22,8 +22,7 @@
namespace android {
-class PVMediaRecorder;
-class ISurface;
+class MediaRecorderBase;
class MediaPlayerService;
class MediaRecorderClient : public BnMediaRecorder
@@ -59,7 +58,7 @@ private:
pid_t mPid;
Mutex mLock;
- PVMediaRecorder *mRecorder;
+ MediaRecorderBase *mRecorder;
sp<MediaPlayerService> mMediaPlayerService;
};
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 2cdc351..866c7bd 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -38,6 +38,7 @@
#include "VorbisMetadataRetriever.h"
#include "MidiMetadataRetriever.h"
#include "MetadataRetrieverClient.h"
+#include "StagefrightMetadataRetriever.h"
/* desktop Linux needs a little help with gettid() */
#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
@@ -118,9 +119,15 @@ static sp<MediaMetadataRetrieverBase> createRetriever(player_type playerType)
LOGV("create midi metadata retriever");
p = new MidiMetadataRetriever();
break;
+#if BUILD_WITH_FULL_STAGEFRIGHT
+ case STAGEFRIGHT_PLAYER:
+ LOGV("create StagefrightMetadataRetriever");
+ p = new StagefrightMetadataRetriever;
+ break;
+#endif
default:
// TODO:
- // support for STAGEFRIGHT_PLAYER and TEST_PLAYER
+ // support for TEST_PLAYER
LOGE("player type %d is not supported", playerType);
break;
}
@@ -138,12 +145,6 @@ status_t MetadataRetrieverClient::setDataSource(const char *url)
return UNKNOWN_ERROR;
}
player_type playerType = getPlayerType(url);
-#if !defined(NO_OPENCORE) && defined(BUILD_WITH_FULL_STAGEFRIGHT)
- if (playerType == STAGEFRIGHT_PLAYER) {
- // Stagefright doesn't support metadata in this branch yet.
- playerType = PV_PLAYER;
- }
-#endif
LOGV("player type = %d", playerType);
sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
if (p == NULL) return NO_INIT;
@@ -182,12 +183,6 @@ status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t
}
player_type playerType = getPlayerType(fd, offset, length);
-#if !defined(NO_OPENCORE) && defined(BUILD_WITH_FULL_STAGEFRIGHT)
- if (playerType == STAGEFRIGHT_PLAYER) {
- // Stagefright doesn't support metadata in this branch yet.
- playerType = PV_PLAYER;
- }
-#endif
LOGV("player type = %d", playerType);
sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
if (p == NULL) {
diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
new file mode 100644
index 0000000..7a3aee8
--- /dev/null
+++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
@@ -0,0 +1,197 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "StagefrightMetadataRetriever"
+#include <utils/Log.h>
+
+#include "StagefrightMetadataRetriever.h"
+
+#include <media/stagefright/CachingDataSource.h>
+#include <media/stagefright/ColorConverter.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/HTTPDataSource.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXCodec.h>
+
+namespace android {
+
+StagefrightMetadataRetriever::StagefrightMetadataRetriever() {
+ LOGV("StagefrightMetadataRetriever()");
+
+ DataSource::RegisterDefaultSniffers();
+ CHECK_EQ(mClient.connect(), OK);
+}
+
+StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
+ LOGV("~StagefrightMetadataRetriever()");
+ mClient.disconnect();
+}
+
+status_t StagefrightMetadataRetriever::setDataSource(const char *uri) {
+ LOGV("setDataSource(%s)", uri);
+
+ mExtractor = MediaExtractor::CreateFromURI(uri);
+
+ return mExtractor.get() != NULL ? OK : UNKNOWN_ERROR;
+}
+
+status_t StagefrightMetadataRetriever::setDataSource(
+ int fd, int64_t offset, int64_t length) {
+ LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
+
+ mExtractor = MediaExtractor::Create(
+ new MmapSource(fd, offset, length));
+
+ return OK;
+}
+
+VideoFrame *StagefrightMetadataRetriever::captureFrame() {
+ LOGV("captureFrame");
+
+ if (mExtractor.get() == NULL) {
+ LOGV("no extractor.");
+ return NULL;
+ }
+
+ size_t n = mExtractor->countTracks();
+ size_t i;
+ for (i = 0; i < n; ++i) {
+ sp<MetaData> meta = mExtractor->getTrackMetaData(i);
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ if (!strncasecmp(mime, "video/", 6)) {
+ break;
+ }
+ }
+
+ if (i == n) {
+ LOGV("no video track found.");
+ return NULL;
+ }
+
+ sp<MetaData> trackMeta = mExtractor->getTrackMetaData(
+ i, MediaExtractor::kIncludeExtensiveMetaData);
+
+ sp<MediaSource> source = mExtractor->getTrack(i);
+
+ if (source.get() == NULL) {
+ LOGV("unable to instantiate video track.");
+ return NULL;
+ }
+
+ sp<MetaData> meta = source->getFormat();
+
+ sp<MediaSource> decoder =
+ OMXCodec::Create(
+ mClient.interface(), meta, false, source,
+ NULL, OMXCodec::kPreferSoftwareCodecs);
+
+ if (decoder.get() == NULL) {
+ LOGV("unable to instantiate video decoder.");
+
+ return NULL;
+ }
+
+ decoder->start();
+
+ // Read one output buffer, ignore format change notifications
+ // and spurious empty buffers.
+
+ MediaSource::ReadOptions options;
+ int64_t thumbNailTime;
+ if (trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) {
+ options.setSeekTo(thumbNailTime);
+ }
+
+ MediaBuffer *buffer = NULL;
+ status_t err;
+ do {
+ if (buffer != NULL) {
+ buffer->release();
+ buffer = NULL;
+ }
+ err = decoder->read(&buffer, &options);
+ options.clearSeekTo();
+ } while (err == INFO_FORMAT_CHANGED
+ || (buffer != NULL && buffer->range_length() == 0));
+
+ if (err != OK) {
+ CHECK_EQ(buffer, NULL);
+
+ LOGV("decoding frame failed.");
+ decoder->stop();
+
+ return NULL;
+ }
+
+ LOGV("successfully decoded video frame.");
+
+ meta = decoder->getFormat();
+
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ VideoFrame *frame = new VideoFrame;
+ frame->mWidth = width;
+ frame->mHeight = height;
+ frame->mDisplayWidth = width;
+ frame->mDisplayHeight = height;
+ frame->mSize = width * height * 2;
+ frame->mData = new uint8_t[frame->mSize];
+
+ int32_t srcFormat;
+ CHECK(meta->findInt32(kKeyColorFormat, &srcFormat));
+
+ ColorConverter converter(
+ (OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565);
+ CHECK(converter.isValid());
+
+ converter.convert(
+ width, height,
+ (const uint8_t *)buffer->data() + buffer->range_offset(),
+ 0,
+ frame->mData, width * 2);
+
+ buffer->release();
+ buffer = NULL;
+
+ decoder->stop();
+
+ return frame;
+}
+
+MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
+ LOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
+
+ return NULL;
+}
+
+const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) {
+ LOGV("extractMetadata %d (extractor: %s)",
+ keyCode, mExtractor.get() != NULL ? "YES" : "NO");
+
+ return NULL;
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.h b/media/libmediaplayerservice/StagefrightMetadataRetriever.h
new file mode 100644
index 0000000..16127d7
--- /dev/null
+++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.h
@@ -0,0 +1,53 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef STAGEFRIGHT_METADATA_RETRIEVER_H_
+
+#define STAGEFRIGHT_METADATA_RETRIEVER_H_
+
+#include <media/MediaMetadataRetrieverInterface.h>
+
+#include <media/stagefright/OMXClient.h>
+
+namespace android {
+
+class MediaExtractor;
+
+struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface {
+ StagefrightMetadataRetriever();
+ virtual ~StagefrightMetadataRetriever();
+
+ virtual status_t setDataSource(const char *url);
+ virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+
+ virtual VideoFrame *captureFrame();
+ virtual MediaAlbumArt *extractAlbumArt();
+ virtual const char *extractMetadata(int keyCode);
+
+private:
+ OMXClient mClient;
+ sp<MediaExtractor> mExtractor;
+
+ StagefrightMetadataRetriever(const StagefrightMetadataRetriever &);
+
+ StagefrightMetadataRetriever &operator=(
+ const StagefrightMetadataRetriever &);
+};
+
+} // namespace android
+
+#endif // STAGEFRIGHT_METADATA_RETRIEVER_H_
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
new file mode 100644
index 0000000..a55273d
--- /dev/null
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "StagefrightRecorder"
+#include <utils/Log.h>
+
+#include "StagefrightRecorder.h"
+
+#include <media/stagefright/CameraSource.h>
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
+#include <ui/ICamera.h>
+#include <ui/ISurface.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+StagefrightRecorder::StagefrightRecorder() {
+ reset();
+}
+
+StagefrightRecorder::~StagefrightRecorder() {
+ stop();
+
+ if (mOutputFd >= 0) {
+ ::close(mOutputFd);
+ mOutputFd = -1;
+ }
+}
+
+status_t StagefrightRecorder::init() {
+ return OK;
+}
+
+status_t StagefrightRecorder::setAudioSource(audio_source as) {
+ mAudioSource = as;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::setVideoSource(video_source vs) {
+ mVideoSource = vs;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::setOutputFormat(output_format of) {
+ mOutputFormat = of;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::setAudioEncoder(audio_encoder ae) {
+ mAudioEncoder = ae;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::setVideoEncoder(video_encoder ve) {
+ mVideoEncoder = ve;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::setVideoSize(int width, int height) {
+ mVideoWidth = width;
+ mVideoHeight = height;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::setVideoFrameRate(int frames_per_second) {
+ mFrameRate = frames_per_second;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera) {
+ mCamera = camera;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::setPreviewSurface(const sp<ISurface> &surface) {
+ mPreviewSurface = surface;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::setOutputFile(const char *path) {
+ // We don't actually support this at all, as the media_server process
+ // no longer has permissions to create files.
+
+ return UNKNOWN_ERROR;
+}
+
+status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t length) {
+ // These don't make any sense, do they?
+ CHECK_EQ(offset, 0);
+ CHECK_EQ(length, 0);
+
+ if (mOutputFd >= 0) {
+ ::close(mOutputFd);
+ }
+ mOutputFd = dup(fd);
+
+ return OK;
+}
+
+status_t StagefrightRecorder::setParameters(const String8 &params) {
+ mParams = params;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::setListener(const sp<IMediaPlayerClient> &listener) {
+ mListener = listener;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::prepare() {
+ return OK;
+}
+
+status_t StagefrightRecorder::start() {
+ if (mWriter != NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (mVideoSource == VIDEO_SOURCE_CAMERA) {
+ CHECK(mCamera != NULL);
+
+ sp<CameraSource> cameraSource =
+ CameraSource::CreateFromICamera(mCamera);
+
+ CHECK(cameraSource != NULL);
+
+ cameraSource->setPreviewSurface(mPreviewSurface);
+
+ sp<MetaData> enc_meta = new MetaData;
+ switch (mVideoEncoder) {
+ case VIDEO_ENCODER_H263:
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
+ break;
+
+ case VIDEO_ENCODER_MPEG_4_SP:
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ break;
+
+ case VIDEO_ENCODER_H264:
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ break;
+
+ default:
+ CHECK(!"Should not be here, unsupported video encoding.");
+ break;
+ }
+
+ sp<MetaData> meta = cameraSource->getFormat();
+
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ enc_meta->setInt32(kKeyWidth, width);
+ enc_meta->setInt32(kKeyHeight, height);
+
+ OMXClient client;
+ CHECK_EQ(client.connect(), OK);
+
+ sp<MediaSource> encoder =
+ OMXCodec::Create(
+ client.interface(), enc_meta,
+ true /* createEncoder */, cameraSource);
+
+ CHECK(mOutputFd >= 0);
+ mWriter = new MPEG4Writer(dup(mOutputFd));
+ mWriter->addSource(encoder);
+ mWriter->start();
+ }
+
+ return OK;
+}
+
+status_t StagefrightRecorder::stop() {
+ if (mWriter == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ mWriter->stop();
+ mWriter = NULL;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::close() {
+ stop();
+
+ return OK;
+}
+
+status_t StagefrightRecorder::reset() {
+ stop();
+
+ 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;
+ mOutputFd = -1;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::getMaxAmplitude(int *max) {
+ return UNKNOWN_ERROR;
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
new file mode 100644
index 0000000..56c4e0e
--- /dev/null
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef STAGEFRIGHT_RECORDER_H_
+
+#define STAGEFRIGHT_RECORDER_H_
+
+#include <media/MediaRecorderBase.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class MPEG4Writer;
+
+struct StagefrightRecorder : public MediaRecorderBase {
+ StagefrightRecorder();
+ virtual ~StagefrightRecorder();
+
+ virtual status_t init();
+ virtual status_t setAudioSource(audio_source as);
+ virtual status_t setVideoSource(video_source vs);
+ virtual status_t setOutputFormat(output_format of);
+ virtual status_t setAudioEncoder(audio_encoder ae);
+ virtual status_t setVideoEncoder(video_encoder ve);
+ virtual status_t setVideoSize(int width, int height);
+ virtual status_t setVideoFrameRate(int frames_per_second);
+ virtual status_t setCamera(const sp<ICamera>& camera);
+ virtual status_t setPreviewSurface(const sp<ISurface>& surface);
+ virtual status_t setOutputFile(const char *path);
+ virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
+ virtual status_t setParameters(const String8& params);
+ virtual status_t setListener(const sp<IMediaPlayerClient>& listener);
+ virtual status_t prepare();
+ virtual status_t start();
+ virtual status_t stop();
+ virtual status_t close();
+ virtual status_t reset();
+ virtual status_t getMaxAmplitude(int *max);
+
+private:
+ sp<ICamera> mCamera;
+ sp<ISurface> mPreviewSurface;
+ sp<IMediaPlayerClient> mListener;
+ sp<MPEG4Writer> mWriter;
+
+ audio_source mAudioSource;
+ video_source mVideoSource;
+ output_format mOutputFormat;
+ audio_encoder mAudioEncoder;
+ video_encoder mVideoEncoder;
+ int mVideoWidth, mVideoHeight;
+ int mFrameRate;
+ String8 mParams;
+ int mOutputFd;
+
+ StagefrightRecorder(const StagefrightRecorder &);
+ StagefrightRecorder &operator=(const StagefrightRecorder &);
+};
+
+} // namespace android
+
+#endif // STAGEFRIGHT_RECORDER_H_
+
diff --git a/media/libmediaplayerservice/TestPlayerStub.cpp b/media/libmediaplayerservice/TestPlayerStub.cpp
index 8627708..aa49429 100644
--- a/media/libmediaplayerservice/TestPlayerStub.cpp
+++ b/media/libmediaplayerservice/TestPlayerStub.cpp
@@ -176,7 +176,7 @@ status_t TestPlayerStub::resetInternal()
mContentUrl = NULL;
if (mPlayer) {
- LOG_ASSERT(mDeletePlayer != NULL);
+ LOG_ASSERT(mDeletePlayer != NULL, "mDeletePlayer is null");
(*mDeletePlayer)(mPlayer);
mPlayer = NULL;
}
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 8d85ce2..1e3c5a4 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -18,7 +18,8 @@
#define LOG_TAG "AMRExtractor"
#include <utils/Log.h>
-#include <media/stagefright/AMRExtractor.h>
+#include "include/AMRExtractor.h"
+
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDebug.h>
@@ -86,7 +87,7 @@ sp<MediaSource> AMRExtractor::getTrack(size_t index) {
return new AMRSource(mDataSource, mIsWide);
}
-sp<MetaData> AMRExtractor::getTrackMetaData(size_t index) {
+sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) {
if (mInitCheck != OK || index != 0) {
return NULL;
}
@@ -155,7 +156,7 @@ status_t AMRSource::read(
*out = NULL;
uint8_t header;
- ssize_t n = mDataSource->read_at(mOffset, &header, 1);
+ ssize_t n = mDataSource->readAt(mOffset, &header, 1);
if (n < 1) {
return ERROR_IO;
@@ -191,7 +192,7 @@ status_t AMRSource::read(
// Round up bits to bytes and add 1 for the header byte.
frameSize = (frameSize + 7) / 8 + 1;
- n = mDataSource->read_at(mOffset, buffer->data(), frameSize);
+ n = mDataSource->readAt(mOffset, buffer->data(), frameSize);
if (n != (ssize_t)frameSize) {
buffer->release();
@@ -201,10 +202,7 @@ status_t AMRSource::read(
}
buffer->set_range(0, frameSize);
- buffer->meta_data()->setInt32(
- kKeyTimeUnits, (mCurrentTimeUs + 500) / 1000);
- buffer->meta_data()->setInt32(
- kKeyTimeScale, 1000);
+ buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
mOffset += frameSize;
mCurrentTimeUs += 20000; // Each frame is 20ms
@@ -220,7 +218,7 @@ bool SniffAMR(
const sp<DataSource> &source, String8 *mimeType, float *confidence) {
char header[9];
- if (source->read_at(0, header, sizeof(header)) != sizeof(header)) {
+ if (source->readAt(0, header, sizeof(header)) != sizeof(header)) {
return false;
}
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 9f71dae..c36e769 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -16,24 +16,26 @@ ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
LOCAL_SRC_FILES += \
AMRExtractor.cpp \
+ AudioPlayer.cpp \
CachingDataSource.cpp \
+ CameraSource.cpp \
DataSource.cpp \
FileSource.cpp \
HTTPDataSource.cpp \
HTTPStream.cpp \
JPEGSource.cpp \
- MediaExtractor.cpp \
MP3Extractor.cpp \
MPEG4Extractor.cpp \
MPEG4Writer.cpp \
+ MediaExtractor.cpp \
MediaPlayerImpl.cpp \
MmapSource.cpp \
SampleTable.cpp \
ShoutcastSource.cpp \
TimeSource.cpp \
TimedEventQueue.cpp \
- AudioPlayer.cpp \
- stagefright_string.cpp
+ WAVExtractor.cpp \
+ string.cpp
endif
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 538facb..d7e3f66 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -210,15 +210,9 @@ void AudioPlayer::fillBuffer(void *data, size_t size) {
break;
}
- int32_t units, scale;
- bool success =
- mInputBuffer->meta_data()->findInt32(kKeyTimeUnits, &units);
- success = success &&
- mInputBuffer->meta_data()->findInt32(kKeyTimeScale, &scale);
- CHECK(success);
-
Mutex::Autolock autoLock(mLock);
- mPositionTimeMediaUs = (int64_t)units * 1000000 / scale;
+ CHECK(mInputBuffer->meta_data()->findInt64(
+ kKeyTime, &mPositionTimeMediaUs));
mPositionTimeRealUs =
((mNumFramesPlayed + size_done / mFrameSize) * 1000000)
diff --git a/media/libstagefright/CachingDataSource.cpp b/media/libstagefright/CachingDataSource.cpp
index fd00576..23f4897 100644
--- a/media/libstagefright/CachingDataSource.cpp
+++ b/media/libstagefright/CachingDataSource.cpp
@@ -61,11 +61,11 @@ CachingDataSource::~CachingDataSource() {
mData = NULL;
}
-status_t CachingDataSource::InitCheck() const {
- return OK;
+status_t CachingDataSource::initCheck() const {
+ return mSource->initCheck();
}
-ssize_t CachingDataSource::read_at(off_t offset, void *data, size_t size) {
+ssize_t CachingDataSource::readAt(off_t offset, void *data, size_t size) {
Mutex::Autolock autoLock(mLock);
size_t total = 0;
@@ -82,7 +82,7 @@ ssize_t CachingDataSource::read_at(off_t offset, void *data, size_t size) {
if (page == NULL) {
page = allocate_page();
page->mOffset = offset - offset % mPageSize;
- ssize_t n = mSource->read_at(page->mOffset, page->mData, mPageSize);
+ ssize_t n = mSource->readAt(page->mOffset, page->mData, mPageSize);
if (n < 0) {
page->mLength = 0;
} else {
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 596ab67..bd862e0 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -19,122 +19,168 @@
#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>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
-#include <ui/ICameraClient.h>
-#include <ui/ICameraService.h>
+#include <ui/Camera.h>
+#include <ui/CameraParameters.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/ISurface.h>
#include <ui/Overlay.h>
-#include <utils/String16.h>
+#include <utils/String8.h>
namespace android {
-class CameraBuffer : public MediaBuffer {
-public:
- CameraBuffer(const sp<IMemory> &frame)
- : MediaBuffer(frame->pointer(), frame->size()),
- mFrame(frame) {
- }
+static int64_t getNowUs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
- sp<IMemory> releaseFrame() {
- sp<IMemory> frame = mFrame;
- mFrame.clear();
- return frame;
- }
+ return (int64_t)tv.tv_usec + tv.tv_sec * 1000000;
+}
-private:
- sp<IMemory> mFrame;
-};
+struct DummySurface : public BnSurface {
+ DummySurface() {}
-class CameraSourceClient : public BnCameraClient {
-public:
- CameraSourceClient()
- : mSource(NULL) {
+ virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) {
+ return NULL;
}
- virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
- CHECK(mSource != NULL);
- mSource->notifyCallback(msgType, ext1, ext2);
+ virtual status_t registerBuffers(const BufferHeap &buffers) {
+ return OK;
}
- virtual void dataCallback(int32_t msgType, const sp<IMemory> &data) {
- CHECK(mSource != NULL);
- mSource->dataCallback(msgType, data);
- }
+ virtual void postBuffer(ssize_t offset) {}
+ virtual void unregisterBuffers() {}
- void setCameraSource(CameraSource *source) {
- mSource = source;
+ virtual sp<OverlayRef> createOverlay(
+ uint32_t w, uint32_t h, int32_t format) {
+ return NULL;
}
+protected:
+ virtual ~DummySurface() {}
+
+ DummySurface(const DummySurface &);
+ DummySurface &operator=(const DummySurface &);
+};
+
+struct CameraSourceListener : public CameraListener {
+ CameraSourceListener(const sp<CameraSource> &source);
+
+ virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
+ virtual void postData(int32_t msgType, const sp<IMemory> &dataPtr);
+
+ virtual void postDataTimestamp(
+ nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
+
+protected:
+ virtual ~CameraSourceListener();
+
private:
- CameraSource *mSource;
+ wp<CameraSource> mSource;
+
+ CameraSourceListener(const CameraSourceListener &);
+ CameraSourceListener &operator=(const CameraSourceListener &);
};
-class DummySurface : public BnSurface {
-public:
- DummySurface() {}
+CameraSourceListener::CameraSourceListener(const sp<CameraSource> &source)
+ : mSource(source) {
+}
- virtual status_t registerBuffers(const BufferHeap &buffers) {
- return OK;
- }
+CameraSourceListener::~CameraSourceListener() {
+}
- virtual void postBuffer(ssize_t offset) {
- }
+void CameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) {
+ LOGV("notify(%d, %d, %d)", msgType, ext1, ext2);
+}
- virtual void unregisterBuffers() {
- }
-
- virtual sp<OverlayRef> createOverlay(
- uint32_t w, uint32_t h, int32_t format) {
- return NULL;
+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());
+}
// static
CameraSource *CameraSource::Create() {
- sp<IServiceManager> sm = defaultServiceManager();
+ sp<Camera> camera = Camera::connect();
+
+ if (camera.get() == NULL) {
+ return NULL;
+ }
- sp<ICameraService> service =
- interface_cast<ICameraService>(
- sm->getService(String16("media.camera")));
+ return new CameraSource(camera);
+}
- sp<CameraSourceClient> client = new CameraSourceClient;
- sp<ICamera> camera = service->connect(client);
+// static
+CameraSource *CameraSource::CreateFromICamera(const sp<ICamera> &icamera) {
+ sp<Camera> camera = Camera::create(icamera);
- CameraSource *source = new CameraSource(camera, client);
- client->setCameraSource(source);
+ if (camera.get() == NULL) {
+ return NULL;
+ }
- return source;
+ return new CameraSource(camera);
}
-CameraSource::CameraSource(
- const sp<ICamera> &camera, const sp<ICameraClient> &client)
+CameraSource::CameraSource(const sp<Camera> &camera)
: mCamera(camera),
- mCameraClient(client),
+ mWidth(0),
+ mHeight(0),
+ mFirstFrameTimeUs(0),
mNumFrames(0),
mStarted(false) {
- printf("params: \"%s\"\n", mCamera->getParameters().string());
+ 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());
+
+ CameraParameters params(s);
+ params.getPreviewSize(&mWidth, &mHeight);
}
CameraSource::~CameraSource() {
if (mStarted) {
stop();
}
+}
- mCamera->disconnect();
+void CameraSource::setPreviewSurface(const sp<ISurface> &surface) {
+ mPreviewSurface = surface;
}
status_t CameraSource::start(MetaData *) {
CHECK(!mStarted);
- status_t err = mCamera->lock();
- CHECK_EQ(err, OK);
+ mCamera->setListener(new CameraSourceListener(this));
- err = mCamera->setPreviewDisplay(new DummySurface);
+ status_t err =
+ mCamera->setPreviewDisplay(
+ mPreviewSurface != NULL ? mPreviewSurface : new DummySurface);
CHECK_EQ(err, OK);
- mCamera->setPreviewCallbackFlag(1);
- mCamera->startPreview();
+
+ mCamera->setPreviewCallbackFlags(
+ FRAME_CALLBACK_FLAG_ENABLE_MASK
+ | FRAME_CALLBACK_FLAG_COPY_OUT_MASK);
+
+ err = mCamera->startPreview();
CHECK_EQ(err, OK);
mStarted = true;
@@ -146,7 +192,6 @@ status_t CameraSource::stop() {
CHECK(mStarted);
mCamera->stopPreview();
- mCamera->unlock();
mStarted = false;
@@ -157,8 +202,8 @@ sp<MetaData> CameraSource::getFormat() {
sp<MetaData> meta = new MetaData;
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420SemiPlanar);
- meta->setInt32(kKeyWidth, 480);
- meta->setInt32(kKeyHeight, 320);
+ meta->setInt32(kKeyWidth, mWidth);
+ meta->setInt32(kKeyHeight, mHeight);
return meta;
}
@@ -175,6 +220,7 @@ status_t CameraSource::read(
}
sp<IMemory> frame;
+ int64_t frameTime;
{
Mutex::Autolock autoLock(mLock);
@@ -184,41 +230,33 @@ status_t CameraSource::read(
frame = *mFrames.begin();
mFrames.erase(mFrames.begin());
- }
- int count = mNumFrames++;
+ frameTime = *mFrameTimes.begin();
+ mFrameTimes.erase(mFrameTimes.begin());
+ }
- *buffer = new CameraBuffer(frame);
+ *buffer = new MediaBuffer(frame->size());
+ memcpy((*buffer)->data(), frame->pointer(), frame->size());
+ (*buffer)->set_range(0, frame->size());
(*buffer)->meta_data()->clear();
- (*buffer)->meta_data()->setInt32(kKeyTimeScale, 15);
- (*buffer)->meta_data()->setInt32(kKeyTimeUnits, count);
-
- (*buffer)->add_ref();
- (*buffer)->setObserver(this);
+ (*buffer)->meta_data()->setInt64(kKeyTime, frameTime);
return OK;
}
-void CameraSource::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
- printf("notifyCallback %d, %d, %d\n", msgType, ext1, ext2);
-}
-
void CameraSource::dataCallback(int32_t msgType, const sp<IMemory> &data) {
Mutex::Autolock autoLock(mLock);
+ int64_t nowUs = getNowUs();
+ if (mNumFrames == 0) {
+ mFirstFrameTimeUs = nowUs;
+ }
+ ++mNumFrames;
+
mFrames.push_back(data);
+ mFrameTimes.push_back(nowUs - mFirstFrameTimeUs);
mFrameAvailableCondition.signal();
}
-void CameraSource::signalBufferReturned(MediaBuffer *_buffer) {
- CameraBuffer *buffer = static_cast<CameraBuffer *>(_buffer);
-
- mCamera->releaseRecordingFrame(buffer->releaseFrame());
-
- buffer->setObserver(NULL);
- buffer->release();
- buffer = NULL;
-}
-
} // namespace android
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index daac539..2a6dbc4 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-#include <media/stagefright/AMRExtractor.h>
+#include "include/AMRExtractor.h"
+#include "include/MP3Extractor.h"
+#include "include/MPEG4Extractor.h"
+#include "include/WAVExtractor.h"
+
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MP3Extractor.h>
-#include <media/stagefright/MPEG4Extractor.h>
#include <utils/String8.h>
namespace android {
@@ -27,7 +29,7 @@ bool DataSource::getUInt16(off_t offset, uint16_t *x) {
*x = 0;
uint8_t byte[2];
- if (read_at(offset, byte, 2) != 2) {
+ if (readAt(offset, byte, 2) != 2) {
return false;
}
@@ -86,6 +88,7 @@ void DataSource::RegisterDefaultSniffers() {
RegisterSniffer(SniffMP3);
RegisterSniffer(SniffMPEG4);
RegisterSniffer(SniffAMR);
+ RegisterSniffer(SniffWAV);
}
} // namespace android
diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp
index 53b92a0..28d338c 100644
--- a/media/libstagefright/ESDS.cpp
+++ b/media/libstagefright/ESDS.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <media/stagefright/ESDS.h>
+#include "include/ESDS.h"
#include <string.h>
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index f6b90b2..130239e 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -20,7 +20,17 @@
namespace android {
FileSource::FileSource(const char *filename)
- : mFile(fopen(filename, "rb")) {
+ : mFile(fopen(filename, "rb")),
+ mOffset(0),
+ mLength(-1) {
+}
+
+FileSource::FileSource(int fd, int64_t offset, int64_t length)
+ : mFile(fdopen(fd, "rb")),
+ mOffset(offset),
+ mLength(length) {
+ CHECK(offset >= 0);
+ CHECK(length >= 0);
}
FileSource::~FileSource() {
@@ -30,19 +40,40 @@ FileSource::~FileSource() {
}
}
-status_t FileSource::InitCheck() const {
+status_t FileSource::initCheck() const {
return mFile != NULL ? OK : NO_INIT;
}
-ssize_t FileSource::read_at(off_t offset, void *data, size_t size) {
+ssize_t FileSource::readAt(off_t offset, void *data, size_t size) {
Mutex::Autolock autoLock(mLock);
- int err = fseeko(mFile, offset, SEEK_SET);
+ if (mLength >= 0) {
+ if (offset >= mLength) {
+ return 0; // read beyond EOF.
+ }
+ int64_t numAvailable = mLength - offset;
+ if ((int64_t)size > numAvailable) {
+ size = numAvailable;
+ }
+ }
+
+ int err = fseeko(mFile, offset + mOffset, SEEK_SET);
CHECK(err != -1);
- ssize_t result = fread(data, 1, size, mFile);
+ return fread(data, 1, size, mFile);
+}
+
+status_t FileSource::getSize(off_t *size) {
+ if (mLength >= 0) {
+ *size = mLength;
+
+ return OK;
+ }
+
+ fseek(mFile, SEEK_END, 0);
+ *size = ftello(mFile);
- return result;
+ return OK;
}
} // namespace android
diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp
index 4dedebd..5536801 100644
--- a/media/libstagefright/HTTPDataSource.cpp
+++ b/media/libstagefright/HTTPDataSource.cpp
@@ -14,17 +14,19 @@
* limitations under the License.
*/
+#include "include/stagefright_string.h"
+#include "include/HTTPStream.h"
+
#include <stdlib.h>
#include <media/stagefright/HTTPDataSource.h>
-#include <media/stagefright/HTTPStream.h>
#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/stagefright_string.h>
namespace android {
HTTPDataSource::HTTPDataSource(const char *uri)
- : mHost(NULL),
+ : mHttp(new HTTPStream),
+ mHost(NULL),
mPort(0),
mPath(NULL),
mBuffer(malloc(kBufferSize)),
@@ -65,33 +67,40 @@ HTTPDataSource::HTTPDataSource(const char *uri)
mPort = port;
mPath = strdup(path.c_str());
- status_t err = mHttp.connect(mHost, mPort);
- CHECK_EQ(err, OK);
+ mInitCheck = mHttp->connect(mHost, mPort);
}
HTTPDataSource::HTTPDataSource(const char *host, int port, const char *path)
- : mHost(strdup(host)),
+ : mHttp(new HTTPStream),
+ mHost(strdup(host)),
mPort(port),
mPath(strdup(path)),
mBuffer(malloc(kBufferSize)),
mBufferLength(0),
mBufferOffset(0) {
- status_t err = mHttp.connect(mHost, mPort);
- CHECK_EQ(err, OK);
+ mInitCheck = mHttp->connect(mHost, mPort);
+}
+
+status_t HTTPDataSource::initCheck() const {
+ return mInitCheck;
}
HTTPDataSource::~HTTPDataSource() {
- mHttp.disconnect();
+ mHttp->disconnect();
free(mBuffer);
mBuffer = NULL;
free(mPath);
mPath = NULL;
+
+ delete mHttp;
+ mHttp = NULL;
}
-ssize_t HTTPDataSource::read_at(off_t offset, void *data, size_t size) {
- if (offset >= mBufferOffset && offset < mBufferOffset + mBufferLength) {
+ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) {
+ if (offset >= mBufferOffset
+ && offset < (off_t)(mBufferOffset + mBufferLength)) {
size_t num_bytes_available = mBufferLength - (offset - mBufferOffset);
size_t copy = num_bytes_available;
@@ -119,19 +128,19 @@ ssize_t HTTPDataSource::read_at(off_t offset, void *data, size_t size) {
status_t err;
int attempt = 1;
for (;;) {
- if ((err = mHttp.send("GET ")) != OK
- || (err = mHttp.send(mPath)) != OK
- || (err = mHttp.send(" HTTP/1.1\r\n")) != OK
- || (err = mHttp.send(host)) != OK
- || (err = mHttp.send(range)) != OK
- || (err = mHttp.send("\r\n")) != OK
- || (err = mHttp.receive_header(&http_status)) != OK) {
+ if ((err = mHttp->send("GET ")) != OK
+ || (err = mHttp->send(mPath)) != OK
+ || (err = mHttp->send(" HTTP/1.1\r\n")) != OK
+ || (err = mHttp->send(host)) != OK
+ || (err = mHttp->send(range)) != OK
+ || (err = mHttp->send("\r\n")) != OK
+ || (err = mHttp->receive_header(&http_status)) != OK) {
if (attempt == 3) {
return err;
}
- mHttp.connect(mHost, mPort);
+ mHttp->connect(mHost, mPort);
++attempt;
} else {
break;
@@ -143,14 +152,14 @@ ssize_t HTTPDataSource::read_at(off_t offset, void *data, size_t size) {
}
string value;
- if (!mHttp.find_header_value("Content-Length", &value)) {
+ if (!mHttp->find_header_value("Content-Length", &value)) {
return UNKNOWN_ERROR;
}
char *end;
unsigned long contentLength = strtoul(value.c_str(), &end, 10);
- ssize_t num_bytes_received = mHttp.receive(mBuffer, contentLength);
+ ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength);
if (num_bytes_received <= 0) {
return num_bytes_received;
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index 6af7df9..02f9439 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "include/HTTPStream.h"
+
#include <sys/socket.h>
#include <arpa/inet.h>
@@ -25,7 +27,6 @@
#include <string.h>
#include <unistd.h>
-#include <media/stagefright/HTTPStream.h>
#include <media/stagefright/MediaDebug.h>
namespace android {
diff --git a/media/libstagefright/JPEGSource.cpp b/media/libstagefright/JPEGSource.cpp
index d1dfd83..a4be2dd 100644
--- a/media/libstagefright/JPEGSource.cpp
+++ b/media/libstagefright/JPEGSource.cpp
@@ -119,7 +119,7 @@ status_t JPEGSource::read(
MediaBuffer *buffer;
mGroup->acquire_buffer(&buffer);
- ssize_t n = mSource->read_at(mOffset, buffer->data(), mSize - mOffset);
+ ssize_t n = mSource->readAt(mOffset, buffer->data(), mSize - mOffset);
if (n <= 0) {
buffer->release();
@@ -156,13 +156,13 @@ status_t JPEGSource::parseJPEG() {
for (;;) {
uint8_t marker;
- if (mSource->read_at(i++, &marker, 1) != 1) {
+ if (mSource->readAt(i++, &marker, 1) != 1) {
return ERROR_IO;
}
CHECK_EQ(marker, 0xff);
- if (mSource->read_at(i++, &marker, 1) != 1) {
+ if (mSource->readAt(i++, &marker, 1) != 1) {
return ERROR_IO;
}
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 7fd699f..8dd8ea9 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -18,8 +18,9 @@
#define LOG_TAG "MP3Extractor"
#include <utils/Log.h>
+#include "include/MP3Extractor.h"
+
#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MP3Extractor.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDebug.h>
@@ -147,7 +148,12 @@ static bool get_mp3_frame_size(
*out_bitrate = bitrate;
}
- *frame_size = 144000 * bitrate / sampling_rate + padding;
+ if (version == 3 /* V1 */) {
+ *frame_size = 144000 * bitrate / sampling_rate + padding;
+ } else {
+ // V2 or V2.5
+ *frame_size = 72000 * bitrate / sampling_rate + padding;
+ }
}
if (out_sampling_rate) {
@@ -166,6 +172,33 @@ static bool get_mp3_frame_size(
static bool Resync(
const sp<DataSource> &source, uint32_t match_header,
off_t *inout_pos, uint32_t *out_header) {
+ if (*inout_pos == 0) {
+ // Skip an optional ID3 header if syncing at the very beginning
+ // of the datasource.
+
+ uint8_t id3header[10];
+ if (source->readAt(0, id3header, sizeof(id3header))
+ < (ssize_t)sizeof(id3header)) {
+ // If we can't even read these 10 bytes, we might as well bail out,
+ // even if there _were_ 10 bytes of valid mp3 audio data...
+ return false;
+ }
+
+ if (id3header[0] == 'I' && id3header[1] == 'D' && id3header[2] == '3') {
+ // Skip the ID3v2 header.
+
+ size_t len =
+ ((id3header[6] & 0x7f) << 21)
+ | ((id3header[7] & 0x7f) << 14)
+ | ((id3header[8] & 0x7f) << 7)
+ | (id3header[9] & 0x7f);
+
+ len += 10;
+
+ *inout_pos += len;
+ }
+ }
+
// Everything must match except for
// protection, bitrate, padding, private bits and mode extension.
const uint32_t kMask = 0xfffe0ccf;
@@ -195,7 +228,7 @@ static bool Resync(
buffer_length = buffer_length - buffer_offset;
buffer_offset = 0;
- ssize_t n = source->read_at(
+ ssize_t n = source->readAt(
pos, &buffer[buffer_length], kMaxFrameSize - buffer_length);
if (n <= 0) {
@@ -232,7 +265,7 @@ static bool Resync(
valid = true;
for (int j = 0; j < 3; ++j) {
uint8_t tmp[4];
- if (source->read_at(test_pos, tmp, 4) < 4) {
+ if (source->readAt(test_pos, tmp, 4) < 4) {
valid = false;
break;
}
@@ -338,10 +371,9 @@ MP3Extractor::MP3Extractor(const sp<DataSource> &source)
off_t fileSize;
if (mDataSource->getSize(&fileSize) == OK) {
- mMeta->setInt32(
+ mMeta->setInt64(
kKeyDuration,
- 8 * (fileSize - mFirstFramePos) / bitrate);
- mMeta->setInt32(kKeyTimeScale, 1000);
+ 8000LL * (fileSize - mFirstFramePos) / bitrate);
}
}
}
@@ -362,7 +394,7 @@ sp<MediaSource> MP3Extractor::getTrack(size_t index) {
mMeta, mDataSource, mFirstFramePos, mFixedHeader);
}
-sp<MetaData> MP3Extractor::getTrackMetaData(size_t index) {
+sp<MetaData> MP3Extractor::getTrackMetaData(size_t index, uint32_t flags) {
if (mFirstFramePos < 0 || index != 0) {
return NULL;
}
@@ -448,7 +480,7 @@ status_t MP3Source::read(
size_t frame_size;
for (;;) {
- ssize_t n = mDataSource->read_at(mCurrentPos, buffer->data(), 4);
+ ssize_t n = mDataSource->readAt(mCurrentPos, buffer->data(), 4);
if (n < 4) {
buffer->release();
buffer = NULL;
@@ -482,7 +514,7 @@ status_t MP3Source::read(
CHECK(frame_size <= buffer->size());
- ssize_t n = mDataSource->read_at(mCurrentPos, buffer->data(), frame_size);
+ ssize_t n = mDataSource->readAt(mCurrentPos, buffer->data(), frame_size);
if (n < (ssize_t)frame_size) {
buffer->release();
buffer = NULL;
@@ -492,8 +524,7 @@ status_t MP3Source::read(
buffer->set_range(0, frame_size);
- buffer->meta_data()->setInt32(kKeyTimeUnits, mCurrentTimeUs / 1000);
- buffer->meta_data()->setInt32(kKeyTimeScale, 1000);
+ buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
mCurrentPos += frame_size;
mCurrentTimeUs += 1152 * 1000000 / 44100;
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 9174d19..9d17064 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -17,6 +17,9 @@
#define LOG_TAG "MPEG4Extractor"
#include <utils/Log.h>
+#include "include/MPEG4Extractor.h"
+#include "include/SampleTable.h"
+
#include <arpa/inet.h>
#include <ctype.h>
@@ -25,14 +28,12 @@
#include <string.h>
#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MPEG4Extractor.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/SampleTable.h>
#include <media/stagefright/Utils.h>
#include <utils/String8.h>
@@ -43,6 +44,7 @@ public:
// Caller retains ownership of both "dataSource" and "sampleTable".
MPEG4Source(const sp<MetaData> &format,
const sp<DataSource> &dataSource,
+ int32_t timeScale,
const sp<SampleTable> &sampleTable);
virtual status_t start(MetaData *params = NULL);
@@ -177,7 +179,8 @@ size_t MPEG4Extractor::countTracks() {
return n;
}
-sp<MetaData> MPEG4Extractor::getTrackMetaData(size_t index) {
+sp<MetaData> MPEG4Extractor::getTrackMetaData(
+ size_t index, uint32_t flags) {
status_t err;
if ((err = readMetaData()) != OK) {
return NULL;
@@ -197,6 +200,25 @@ sp<MetaData> MPEG4Extractor::getTrackMetaData(size_t index) {
return NULL;
}
+ if ((flags & kIncludeExtensiveMetaData)
+ && !track->includes_expensive_metadata) {
+ track->includes_expensive_metadata = true;
+
+ const char *mime;
+ CHECK(track->meta->findCString(kKeyMIMEType, &mime));
+ if (!strncasecmp("video/", mime, 6)) {
+ uint32_t sampleIndex;
+ uint32_t sampleTime;
+ if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK
+ && track->sampleTable->getDecodingTime(
+ sampleIndex, &sampleTime) == OK) {
+ track->meta->setInt64(
+ kKeyThumbnailTime,
+ ((int64_t)sampleTime * 1000000) / track->timescale);
+ }
+ }
+ }
+
return track->meta;
}
@@ -227,7 +249,7 @@ static void MakeFourCCString(uint32_t x, char *s) {
status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
uint32_t hdr[2];
- if (mDataSource->read_at(*offset, hdr, 8) < 8) {
+ if (mDataSource->readAt(*offset, hdr, 8) < 8) {
return ERROR_IO;
}
uint64_t chunk_size = ntohl(hdr[0]);
@@ -235,7 +257,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
off_t data_offset = *offset + 8;
if (chunk_size == 1) {
- if (mDataSource->read_at(*offset + 8, &chunk_size, 8) < 8) {
+ if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
return ERROR_IO;
}
chunk_size = ntoh64(chunk_size);
@@ -252,7 +274,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
char buffer[256];
if (chunk_size <= sizeof(buffer)) {
- if (mDataSource->read_at(*offset, buffer, chunk_size) < chunk_size) {
+ if (mDataSource->readAt(*offset, buffer, chunk_size) < chunk_size) {
return ERROR_IO;
}
@@ -298,7 +320,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
CHECK(chunk_data_size >= 4);
uint8_t version;
- if (mDataSource->read_at(data_offset, &version, 1) < 1) {
+ if (mDataSource->readAt(data_offset, &version, 1) < 1) {
return ERROR_IO;
}
@@ -312,7 +334,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
}
uint8_t buffer[36 + 60];
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
@@ -329,7 +351,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
}
uint8_t buffer[24 + 60];
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
@@ -351,6 +373,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
mLastTrack = track;
track->meta = new MetaData;
+ track->includes_expensive_metadata = false;
track->timescale = 0;
track->sampleTable = new SampleTable(mDataSource);
track->meta->setCString(kKeyMIMEType, "application/octet-stream");
@@ -366,7 +389,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
}
uint8_t version;
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, &version, sizeof(version))
< (ssize_t)sizeof(version)) {
return ERROR_IO;
@@ -383,18 +406,17 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
}
uint32_t timescale;
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
timescale_offset, &timescale, sizeof(timescale))
< (ssize_t)sizeof(timescale)) {
return ERROR_IO;
}
mLastTrack->timescale = ntohl(timescale);
- mLastTrack->meta->setInt32(kKeyTimeScale, mLastTrack->timescale);
int64_t duration;
if (version == 1) {
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
timescale_offset + 4, &duration, sizeof(duration))
< (ssize_t)sizeof(duration)) {
return ERROR_IO;
@@ -402,14 +424,15 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
duration = ntoh64(duration);
} else {
int32_t duration32;
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
timescale_offset + 4, &duration32, sizeof(duration32))
< (ssize_t)sizeof(duration32)) {
return ERROR_IO;
}
duration = ntohl(duration32);
}
- mLastTrack->meta->setInt32(kKeyDuration, duration);
+ mLastTrack->meta->setInt64(
+ kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
*offset += chunk_size;
break;
@@ -422,7 +445,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
}
uint8_t buffer[24];
- if (mDataSource->read_at(data_offset, buffer, 24) < 24) {
+ if (mDataSource->readAt(data_offset, buffer, 24) < 24) {
return ERROR_IO;
}
@@ -449,7 +472,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
uint8_t buffer[8];
CHECK(chunk_data_size >= (off_t)sizeof(buffer));
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, buffer, 8) < 8) {
return ERROR_IO;
}
@@ -492,7 +515,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
return ERROR_MALFORMED;
}
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
@@ -544,7 +567,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
return ERROR_MALFORMED;
}
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
@@ -655,7 +678,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
return ERROR_BUFFER_TOO_SMALL;
}
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, buffer, chunk_data_size) < chunk_data_size) {
return ERROR_IO;
}
@@ -679,7 +702,7 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
return ERROR_BUFFER_TOO_SMALL;
}
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, buffer, chunk_data_size) < chunk_data_size) {
return ERROR_IO;
}
@@ -722,7 +745,7 @@ sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
}
return new MPEG4Source(
- track->meta, mDataSource, track->sampleTable);
+ track->meta, mDataSource, track->timescale, track->sampleTable);
}
////////////////////////////////////////////////////////////////////////////////
@@ -730,10 +753,11 @@ sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
MPEG4Source::MPEG4Source(
const sp<MetaData> &format,
const sp<DataSource> &dataSource,
+ int32_t timeScale,
const sp<SampleTable> &sampleTable)
: mFormat(format),
mDataSource(dataSource),
- mTimescale(0),
+ mTimescale(timeScale),
mSampleTable(sampleTable),
mCurrentSampleIndex(0),
mIsAVC(false),
@@ -746,9 +770,6 @@ MPEG4Source::MPEG4Source(
bool success = mFormat->findCString(kKeyMIMEType, &mime);
CHECK(success);
- success = mFormat->findInt32(kKeyTimeScale, &mTimescale);
- CHECK(success);
-
mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
}
@@ -868,7 +889,7 @@ status_t MPEG4Source::read(
if (!mIsAVC || mWantsNALFragments) {
if (newBuffer) {
ssize_t num_bytes_read =
- mDataSource->read_at(offset, (uint8_t *)mBuffer->data(), size);
+ mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
@@ -879,8 +900,8 @@ status_t MPEG4Source::read(
mBuffer->set_range(0, size);
mBuffer->meta_data()->clear();
- mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
- mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
+ mBuffer->meta_data()->setInt64(
+ kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
++mCurrentSampleIndex;
}
@@ -923,7 +944,7 @@ status_t MPEG4Source::read(
// the start code (0x00 00 00 01).
ssize_t num_bytes_read =
- mDataSource->read_at(offset, mSrcBuffer, size);
+ mDataSource->readAt(offset, mSrcBuffer, size);
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
@@ -959,8 +980,8 @@ status_t MPEG4Source::read(
mBuffer->set_range(0, dstOffset);
mBuffer->meta_data()->clear();
- mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
- mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
+ mBuffer->meta_data()->setInt64(
+ kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
++mCurrentSampleIndex;
*out = mBuffer;
@@ -974,13 +995,14 @@ bool SniffMPEG4(
const sp<DataSource> &source, String8 *mimeType, float *confidence) {
uint8_t header[8];
- ssize_t n = source->read_at(4, header, sizeof(header));
+ ssize_t n = source->readAt(4, header, sizeof(header));
if (n < (ssize_t)sizeof(header)) {
return false;
}
if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8)
- || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)) {
+ || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)
+ || !memcmp(header, "ftypM4A ", 8)) {
*mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
*confidence = 0.1;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index fa35768..367459f 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -75,6 +75,13 @@ MPEG4Writer::MPEG4Writer(const char *filename)
CHECK(mFile != NULL);
}
+MPEG4Writer::MPEG4Writer(int fd)
+ : mFile(fdopen(fd, "wb")),
+ mOffset(0),
+ mMdatOffset(0) {
+ CHECK(mFile != NULL);
+}
+
MPEG4Writer::~MPEG4Writer() {
stop();
@@ -203,6 +210,27 @@ off_t MPEG4Writer::addSample(MediaBuffer *buffer) {
return old_offset;
}
+off_t MPEG4Writer::addLengthPrefixedSample(MediaBuffer *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ off_t old_offset = mOffset;
+
+ size_t length = buffer->range_length();
+ CHECK(length < 65536);
+
+ uint8_t x = length >> 8;
+ fwrite(&x, 1, 1, mFile);
+ x = length & 0xff;
+ fwrite(&x, 1, 1, mFile);
+
+ fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
+ 1, length, mFile);
+
+ mOffset += length + 2;
+
+ return old_offset;
+}
+
void MPEG4Writer::beginBox(const char *fourcc) {
CHECK_EQ(strlen(fourcc), 4);
@@ -348,11 +376,12 @@ void *MPEG4Writer::Track::ThreadWrapper(void *me) {
}
void MPEG4Writer::Track::threadEntry() {
- bool is_mpeg4 = false;
sp<MetaData> meta = mSource->getFormat();
const char *mime;
meta->findCString(kKeyMIMEType, &mime);
- is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+ int32_t count = 0;
MediaBuffer *buffer;
while (!mDone && mSource->read(&buffer) == OK) {
@@ -363,6 +392,55 @@ void MPEG4Writer::Track::threadEntry() {
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;
+ }
+ }
+
+ buffer->release();
+ buffer = NULL;
+
+ continue;
+ }
+
if (mCodecSpecificData == NULL && is_mpeg4) {
const uint8_t *data =
(const uint8_t *)buffer->data() + buffer->range_offset();
@@ -393,21 +471,18 @@ void MPEG4Writer::Track::threadEntry() {
buffer->set_range(buffer->range_offset() + offset, size - offset);
}
- off_t offset = mOwner->addSample(buffer);
+ off_t offset = is_avc ? mOwner->addLengthPrefixedSample(buffer)
+ : mOwner->addSample(buffer);
SampleInfo info;
- info.size = buffer->range_length();
+ info.size = is_avc ? buffer->range_length() + 2 : buffer->range_length();
info.offset = offset;
- int32_t units, scale;
- bool success =
- buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
- CHECK(success);
- success =
- buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
- CHECK(success);
+ int64_t timestampUs;
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, &timestampUs));
- info.timestamp = (int64_t)units * 1000 / scale;
+ // Our timestamp is in ms.
+ info.timestamp = (timestampUs + 500) / 1000;
mSampleInfos.push_back(info);
@@ -560,6 +635,8 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
mOwner->beginBox("mp4v");
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
mOwner->beginBox("s263");
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+ mOwner->beginBox("avc1");
} else {
LOGE("Unknown mime type '%s'.", mime);
CHECK(!"should not be here, unknown mime type.");
@@ -635,8 +712,13 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
mOwner->writeInt8(0); // profile: 0
mOwner->endBox(); // d263
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+ mOwner->beginBox("avcC");
+ mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+ mOwner->endBox(); // avcC
}
- mOwner->endBox(); // mp4v or s263
+
+ mOwner->endBox(); // mp4v, s263 or avc1
}
mOwner->endBox(); // stsd
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
index f3c0e73..b973745 100644
--- a/media/libstagefright/MediaBuffer.cpp
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -108,10 +108,10 @@ size_t MediaBuffer::range_length() const {
}
void MediaBuffer::set_range(size_t offset, size_t length) {
- if (offset < 0 || offset + length > mSize) {
+ if (offset + length > mSize) {
LOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize);
}
- CHECK(offset >= 0 && offset + length <= mSize);
+ CHECK(offset + length <= mSize);
mRangeOffset = offset;
mRangeLength = length;
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 87b5b24..04b1454 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -32,5 +32,6 @@ const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4";
+const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav";
} // namespace android
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 8535f52..19a1f85 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -18,12 +18,17 @@
#define LOG_TAG "MediaExtractor"
#include <utils/Log.h>
-#include <media/stagefright/AMRExtractor.h>
+#include "include/AMRExtractor.h"
+#include "include/MP3Extractor.h"
+#include "include/MPEG4Extractor.h"
+#include "include/WAVExtractor.h"
+
+#include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/DataSource.h>
+#include <media/stagefright/HTTPDataSource.h>
#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MP3Extractor.h>
-#include <media/stagefright/MPEG4Extractor.h>
#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MmapSource.h>
#include <utils/String8.h>
namespace android {
@@ -41,7 +46,7 @@ sp<MediaExtractor> MediaExtractor::Create(
}
mime = tmp.string();
- LOGI("Autodetected media content as '%s' with confidence %.2f",
+ LOGV("Autodetected media content as '%s' with confidence %.2f",
mime, confidence);
}
@@ -53,9 +58,32 @@ sp<MediaExtractor> MediaExtractor::Create(
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)
|| !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
return new AMRExtractor(source);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {
+ return new WAVExtractor(source);
}
return NULL;
}
+// static
+sp<MediaExtractor> MediaExtractor::CreateFromURI(
+ const char *uri, const char *mime) {
+ sp<DataSource> source;
+ if (!strncasecmp("file://", uri, 7)) {
+ source = new MmapSource(uri + 7);
+ } else if (!strncasecmp("http://", uri, 7)) {
+ source = new HTTPDataSource(uri);
+ source = new CachingDataSource(source, 64 * 1024, 10);
+ } else {
+ // Assume it's a filename.
+ source = new MmapSource(uri);
+ }
+
+ if (source == NULL || source->initCheck() != OK) {
+ return NULL;
+ }
+
+ return Create(source, mime);
+}
+
} // namespace android
diff --git a/media/libstagefright/MediaPlayerImpl.cpp b/media/libstagefright/MediaPlayerImpl.cpp
index 8300422..c1044a3 100644
--- a/media/libstagefright/MediaPlayerImpl.cpp
+++ b/media/libstagefright/MediaPlayerImpl.cpp
@@ -18,16 +18,17 @@
#define LOG_TAG "MediaPlayerImpl"
#include "utils/Log.h"
+#include "include/stagefright_string.h"
+#include "include/HTTPStream.h"
+
#include <OMX_Component.h>
#include <unistd.h>
#include <media/stagefright/AudioPlayer.h>
-#include <media/stagefright/CachingDataSource.h>
// #include <media/stagefright/CameraSource.h>
-#include <media/stagefright/HTTPDataSource.h>
-#include <media/stagefright/HTTPStream.h>
#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaPlayerImpl.h>
#include <media/stagefright/MetaData.h>
@@ -40,13 +41,6 @@
namespace android {
-static void releaseBufferIfNonNULL(MediaBuffer **buffer) {
- if (*buffer) {
- (*buffer)->release();
- *buffer = NULL;
- }
-}
-
MediaPlayerImpl::MediaPlayerImpl(const char *uri)
: mInitCheck(NO_INIT),
mTimeSource(NULL),
@@ -76,18 +70,7 @@ MediaPlayerImpl::MediaPlayerImpl(const char *uri)
mVideoDecoder = CameraSource::Create();
#endif
} else {
- sp<DataSource> source;
- if (!strncasecmp("file://", uri, 7)) {
- source = new MmapSource(uri + 7);
- } else if (!strncasecmp("http://", uri, 7)) {
- source = new HTTPDataSource(uri);
- source = new CachingDataSource(source, 64 * 1024, 10);
- } else {
- // Assume it's a filename.
- source = new MmapSource(uri);
- }
-
- mExtractor = MediaExtractor::Create(source);
+ mExtractor = MediaExtractor::CreateFromURI(uri);
if (mExtractor == NULL) {
return;
@@ -236,8 +219,6 @@ void MediaPlayerImpl::videoEntry() {
bool firstFrame = true;
bool eof = false;
- MediaBuffer *lastBuffer = NULL;
-
status_t err = mVideoDecoder->start();
CHECK_EQ(err, OK);
@@ -250,8 +231,6 @@ void MediaPlayerImpl::videoEntry() {
{
Mutex::Autolock autoLock(mLock);
if (mSeeking) {
- releaseBufferIfNonNULL(&lastBuffer);
-
LOGV("seek-options to %lld", mSeekTimeUs);
options.setSeekTo(mSeekTimeUs);
@@ -269,6 +248,13 @@ void MediaPlayerImpl::videoEntry() {
status_t err = mVideoDecoder->read(&buffer, &options);
CHECK((err == OK && buffer != NULL) || (err != OK && buffer == NULL));
+ if (err == INFO_FORMAT_CHANGED) {
+ LOGV("format changed.");
+ depopulateISurface();
+ populateISurface();
+ continue;
+ }
+
if (err == ERROR_END_OF_STREAM || err != OK) {
eof = true;
continue;
@@ -280,15 +266,9 @@ void MediaPlayerImpl::videoEntry() {
continue;
}
- int32_t units, scale;
- bool success =
- buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
- CHECK(success);
- success =
- buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
- CHECK(success);
+ int64_t pts_us;
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, &pts_us));
- int64_t pts_us = (int64_t)units * 1000000 / scale;
{
Mutex::Autolock autoLock(mLock);
mVideoPosition = pts_us;
@@ -312,21 +292,19 @@ void MediaPlayerImpl::videoEntry() {
firstFrame = false;
}
- displayOrDiscardFrame(&lastBuffer, buffer, pts_us);
+ displayOrDiscardFrame(buffer, pts_us);
}
- releaseBufferIfNonNULL(&lastBuffer);
-
mVideoDecoder->stop();
}
void MediaPlayerImpl::displayOrDiscardFrame(
- MediaBuffer **lastBuffer,
MediaBuffer *buffer, int64_t pts_us) {
for (;;) {
if (!mPlaying || mPaused) {
- releaseBufferIfNonNULL(lastBuffer);
- *lastBuffer = buffer;
+ buffer->release();
+ buffer = NULL;
+
return;
}
@@ -348,10 +326,12 @@ void MediaPlayerImpl::displayOrDiscardFrame(
LOGV("we're late by %lld ms, dropping a frame\n",
-delay_us / 1000);
- releaseBufferIfNonNULL(lastBuffer);
- *lastBuffer = buffer;
+ buffer->release();
+ buffer = NULL;
return;
} else if (delay_us > 100000) {
+ LOGV("we're much too early (by %lld ms)\n",
+ delay_us / 1000);
usleep(100000);
continue;
} else if (delay_us > 0) {
@@ -363,14 +343,13 @@ void MediaPlayerImpl::displayOrDiscardFrame(
{
Mutex::Autolock autoLock(mLock);
-
if (mVideoRenderer.get() != NULL) {
sendFrameToISurface(buffer);
}
}
- releaseBufferIfNonNULL(lastBuffer);
- *lastBuffer = buffer;
+ buffer->release();
+ buffer = NULL;
}
void MediaPlayerImpl::init() {
@@ -403,12 +382,10 @@ void MediaPlayerImpl::init() {
sp<MediaSource> source = mExtractor->getTrack(i);
- int32_t units, scale;
- if (meta->findInt32(kKeyDuration, &units)
- && meta->findInt32(kKeyTimeScale, &scale)) {
- int64_t duration_us = (int64_t)units * 1000000 / scale;
- if (duration_us > mDuration) {
- mDuration = duration_us;
+ int64_t durationUs;
+ if (meta->findInt64(kKeyDuration, &durationUs)) {
+ if (durationUs > mDuration) {
+ mDuration = durationUs;
}
}
@@ -427,8 +404,15 @@ void MediaPlayerImpl::setAudioSource(const sp<MediaSource> &source) {
sp<MetaData> meta = source->getFormat();
- mAudioDecoder = OMXCodec::Create(
- mClient.interface(), meta, false /* createEncoder */, source);
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
+ mAudioDecoder = source;
+ } else {
+ mAudioDecoder = OMXCodec::Create(
+ mClient.interface(), meta, false /* createEncoder */, source);
+ }
}
void MediaPlayerImpl::setVideoSource(const sp<MediaSource> &source) {
@@ -633,6 +617,9 @@ void MediaPlayerImpl::populateISurface() {
success = success && meta->findInt32(kKeyHeight, &decodedHeight);
CHECK(success);
+ LOGV("mVideoWidth=%d, mVideoHeight=%d, decodedWidth=%d, decodedHeight=%d",
+ mVideoWidth, mVideoHeight, decodedWidth, decodedHeight);
+
if (mSurface.get() != NULL) {
mVideoRenderer =
mClient.interface()->createRenderer(
diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp
index 6b067cb..63b476e 100644
--- a/media/libstagefright/MetaData.cpp
+++ b/media/libstagefright/MetaData.cpp
@@ -58,6 +58,10 @@ bool MetaData::setInt32(uint32_t key, int32_t value) {
return setData(key, TYPE_INT32, &value, sizeof(value));
}
+bool MetaData::setInt64(uint32_t key, int64_t value) {
+ return setData(key, TYPE_INT64, &value, sizeof(value));
+}
+
bool MetaData::setFloat(uint32_t key, float value) {
return setData(key, TYPE_FLOAT, &value, sizeof(value));
}
@@ -94,6 +98,21 @@ bool MetaData::findInt32(uint32_t key, int32_t *value) {
return true;
}
+bool MetaData::findInt64(uint32_t key, int64_t *value) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_INT64) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(int64_t *)data;
+
+ return true;
+}
+
bool MetaData::findFloat(uint32_t key, float *value) {
uint32_t type;
const void *data;
diff --git a/media/libstagefright/MmapSource.cpp b/media/libstagefright/MmapSource.cpp
index 47d95f9..42749cf 100644
--- a/media/libstagefright/MmapSource.cpp
+++ b/media/libstagefright/MmapSource.cpp
@@ -34,7 +34,10 @@ MmapSource::MmapSource(const char *filename)
mBase(NULL),
mSize(0) {
LOGV("MmapSource '%s'", filename);
- CHECK(mFd >= 0);
+
+ if (mFd < 0) {
+ return;
+ }
off_t size = lseek(mFd, 0, SEEK_END);
mSize = (size_t)size;
@@ -78,12 +81,12 @@ MmapSource::~MmapSource() {
}
}
-status_t MmapSource::InitCheck() const {
+status_t MmapSource::initCheck() const {
return mFd == -1 ? NO_INIT : OK;
}
-ssize_t MmapSource::read_at(off_t offset, void *data, size_t size) {
- LOGV("read_at offset:%ld data:%p size:%d", offset, data, size);
+ssize_t MmapSource::readAt(off_t offset, void *data, size_t size) {
+ LOGV("readAt offset:%ld data:%p size:%d", offset, data, size);
CHECK(offset >= 0);
size_t avail = 0;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index ebf1e0c..d36653e 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -18,11 +18,12 @@
#define LOG_TAG "OMXCodec"
#include <utils/Log.h>
+#include "include/ESDS.h"
+
#include <binder/IServiceManager.h>
#include <binder/MemoryDealer.h>
#include <binder/ProcessState.h>
#include <media/IMediaPlayerService.h>
-#include <media/stagefright/ESDS.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDebug.h>
@@ -39,6 +40,8 @@
namespace android {
+static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
struct CodecInfo {
const char *mime;
const char *codec;
@@ -169,52 +172,37 @@ static void InitOMXParams(T *params) {
params->nVersion.s.nStep = 0;
}
-// static
-sp<OMXCodec> OMXCodec::Create(
- const sp<IOMX> &omx,
- const sp<MetaData> &meta, bool createEncoder,
- const sp<MediaSource> &source,
- const char *matchComponentName) {
- const char *mime;
- bool success = meta->findCString(kKeyMIMEType, &mime);
- CHECK(success);
+static bool IsSoftwareCodec(const char *componentName) {
+ if (!strncmp("OMX.PV.", componentName, 7)) {
+ return true;
+ }
- const char *componentName = NULL;
- sp<OMXCodecObserver> observer = new OMXCodecObserver;
- IOMX::node_id node = 0;
- for (int index = 0;; ++index) {
- if (createEncoder) {
- componentName = GetCodec(
- kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
- mime, index);
- } else {
- componentName = GetCodec(
- kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
- mime, index);
- }
+ return false;
+}
- if (!componentName) {
- return NULL;
- }
+static int CompareSoftwareCodecsFirst(
+ const String8 *elem1, const String8 *elem2) {
+ bool isSoftwareCodec1 = IsSoftwareCodec(elem1->string());
+ bool isSoftwareCodec2 = IsSoftwareCodec(elem2->string());
- // If a specific codec is requested, skip the non-matching ones.
- if (matchComponentName && strcmp(componentName, matchComponentName)) {
- continue;
- }
-
- LOGV("Attempting to allocate OMX node '%s'", componentName);
+ if (isSoftwareCodec1) {
+ if (isSoftwareCodec2) { return 0; }
+ return -1;
+ }
- status_t err = omx->allocateNode(componentName, observer, &node);
- if (err == OK) {
- LOGI("Successfully allocated OMX node '%s'", componentName);
- break;
- }
+ if (isSoftwareCodec2) {
+ return 1;
}
+ return 0;
+}
+
+// static
+uint32_t OMXCodec::getComponentQuirks(const char *componentName) {
uint32_t quirks = 0;
+
if (!strcmp(componentName, "OMX.PV.avcdec")) {
quirks |= kWantsNALFragments;
- quirks |= kOutputDimensionsAre16Aligned;
}
if (!strcmp(componentName, "OMX.TI.MP3.decode")) {
quirks |= kNeedsFlushBeforeDisable;
@@ -222,20 +210,15 @@ sp<OMXCodec> OMXCodec::Create(
if (!strcmp(componentName, "OMX.TI.AAC.decode")) {
quirks |= kNeedsFlushBeforeDisable;
quirks |= kRequiresFlushCompleteEmulation;
-
- // The following is currently necessary for proper shutdown
- // behaviour, but NOT enabled by default in order to make the
- // bug reproducible...
- // quirks |= kRequiresFlushBeforeShutdown;
}
if (!strncmp(componentName, "OMX.qcom.video.encoder.", 23)) {
quirks |= kRequiresLoadedToIdleAfterAllocation;
quirks |= kRequiresAllocateBufferOnInputPorts;
+ quirks |= kRequiresAllocateBufferOnOutputPorts;
}
if (!strncmp(componentName, "OMX.qcom.video.decoder.", 23)) {
// XXX Required on P....on only.
quirks |= kRequiresAllocateBufferOnOutputPorts;
- quirks |= kOutputDimensionsAre16Aligned;
}
if (!strncmp(componentName, "OMX.TI.", 7)) {
@@ -248,8 +231,94 @@ sp<OMXCodec> OMXCodec::Create(
quirks |= kRequiresAllocateBufferOnOutputPorts;
}
+ return quirks;
+}
+
+// static
+void OMXCodec::findMatchingCodecs(
+ const char *mime,
+ bool createEncoder, const char *matchComponentName,
+ uint32_t flags,
+ Vector<String8> *matchingCodecs) {
+ matchingCodecs->clear();
+
+ for (int index = 0;; ++index) {
+ const char *componentName;
+
+ if (createEncoder) {
+ componentName = GetCodec(
+ kEncoderInfo,
+ sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
+ mime, index);
+ } else {
+ componentName = GetCodec(
+ kDecoderInfo,
+ sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
+ mime, index);
+ }
+
+ if (!componentName) {
+ break;
+ }
+
+ // If a specific codec is requested, skip the non-matching ones.
+ if (matchComponentName && strcmp(componentName, matchComponentName)) {
+ continue;
+ }
+
+ matchingCodecs->push(String8(componentName));
+ }
+
+ if (flags & kPreferSoftwareCodecs) {
+ matchingCodecs->sort(CompareSoftwareCodecsFirst);
+ }
+}
+
+// static
+sp<OMXCodec> OMXCodec::Create(
+ const sp<IOMX> &omx,
+ const sp<MetaData> &meta, bool createEncoder,
+ const sp<MediaSource> &source,
+ const char *matchComponentName,
+ uint32_t flags) {
+ const char *mime;
+ bool success = meta->findCString(kKeyMIMEType, &mime);
+ CHECK(success);
+
+ Vector<String8> matchingCodecs;
+ findMatchingCodecs(
+ mime, createEncoder, matchComponentName, flags, &matchingCodecs);
+
+ if (matchingCodecs.isEmpty()) {
+ return NULL;
+ }
+
+ sp<OMXCodecObserver> observer = new OMXCodecObserver;
+ IOMX::node_id node = 0;
+ success = false;
+
+ const char *componentName;
+ for (size_t i = 0; i < matchingCodecs.size(); ++i) {
+ componentName = matchingCodecs[i].string();
+
+ LOGV("Attempting to allocate OMX node '%s'", componentName);
+
+ status_t err = omx->allocateNode(componentName, observer, &node);
+ if (err == OK) {
+ LOGV("Successfully allocated OMX node '%s'", componentName);
+
+ success = true;
+ break;
+ }
+ }
+
+ if (!success) {
+ return NULL;
+ }
+
sp<OMXCodec> codec = new OMXCodec(
- omx, node, quirks, createEncoder, mime, componentName,
+ omx, node, getComponentQuirks(componentName),
+ createEncoder, mime, componentName,
source);
observer->setCodec(codec);
@@ -331,7 +400,7 @@ sp<OMXCodec> OMXCodec::Create(
size -= length;
}
- LOGI("AVC profile = %d (%s), level = %d",
+ LOGV("AVC profile = %d (%s), level = %d",
(int)profile, AVCProfileToString(profile), (int)level / 10);
if (!strcmp(componentName, "OMX.TI.Video.Decoder")
@@ -452,7 +521,7 @@ status_t OMXCodec::setVideoPortFormatType(
// CHECK_EQ(format.nIndex, index);
#if 1
- CODEC_LOGI("portIndex: %ld, index: %ld, eCompressionFormat=%d eColorFormat=%d",
+ CODEC_LOGV("portIndex: %ld, index: %ld, eCompressionFormat=%d eColorFormat=%d",
portIndex,
index, format.eCompressionFormat, format.eColorFormat);
#endif
@@ -493,9 +562,25 @@ status_t OMXCodec::setVideoPortFormatType(
return err;
}
+static size_t getFrameSize(
+ OMX_COLOR_FORMATTYPE colorFormat, int32_t width, int32_t height) {
+ switch (colorFormat) {
+ case OMX_COLOR_FormatYCbYCr:
+ case OMX_COLOR_FormatCbYCrY:
+ return width * height * 2;
+
+ case OMX_COLOR_FormatYUV420SemiPlanar:
+ return (width * height * 3) / 2;
+
+ default:
+ CHECK(!"Should not be here. Unsupported color format.");
+ break;
+ }
+}
+
void OMXCodec::setVideoInputFormat(
const char *mime, OMX_U32 width, OMX_U32 height) {
- CODEC_LOGI("setVideoInputFormat width=%ld, height=%ld", width, height);
+ CODEC_LOGV("setVideoInputFormat width=%ld, height=%ld", width, height);
OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
@@ -509,19 +594,18 @@ void OMXCodec::setVideoInputFormat(
CHECK(!"Should not be here. Not a supported video mime type.");
}
- OMX_COLOR_FORMATTYPE colorFormat =
- 0 ? OMX_COLOR_FormatYCbYCr : OMX_COLOR_FormatCbYCrY;
-
- if (!strncmp("OMX.qcom.video.encoder.", mComponentName, 23)) {
- colorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
+ OMX_COLOR_FORMATTYPE colorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
+ if (!strcasecmp("OMX.TI.Video.encoder", mComponentName)) {
+ colorFormat = OMX_COLOR_FormatYCbYCr;
}
- setVideoPortFormatType(
+ CHECK_EQ(setVideoPortFormatType(
kPortIndexInput, OMX_VIDEO_CodingUnused,
- colorFormat);
+ colorFormat), OK);
- setVideoPortFormatType(
- kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused);
+ CHECK_EQ(setVideoPortFormatType(
+ kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused),
+ OK);
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
@@ -554,8 +638,8 @@ void OMXCodec::setVideoInputFormat(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
- def.nBufferSize = (width * height * 2); // (width * height * 3) / 2;
- CODEC_LOGI("Setting nBufferSize = %ld", def.nBufferSize);
+ def.nBufferSize = getFrameSize(colorFormat, width, height);
+ CODEC_LOGV("Setting nBufferSize = %ld", def.nBufferSize);
CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
@@ -564,14 +648,172 @@ void OMXCodec::setVideoInputFormat(
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);
+
+ switch (compressionFormat) {
+ case OMX_VIDEO_CodingMPEG4:
+ {
+ CHECK_EQ(setupMPEG4EncoderParameters(), OK);
+ break;
+ }
+
+ case OMX_VIDEO_CodingH263:
+ break;
+
+ case OMX_VIDEO_CodingAVC:
+ {
+ CHECK_EQ(setupAVCEncoderParameters(), OK);
+ break;
+ }
+
+ default:
+ CHECK(!"Support for this compressionFormat to be implemented.");
+ break;
+ }
+}
+
+status_t OMXCodec::setupMPEG4EncoderParameters() {
+ OMX_VIDEO_PARAM_MPEG4TYPE mpeg4type;
+ InitOMXParams(&mpeg4type);
+ mpeg4type.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
+ CHECK_EQ(err, OK);
+
+ mpeg4type.nSliceHeaderSpacing = 0;
+ mpeg4type.bSVH = OMX_FALSE;
+ mpeg4type.bGov = OMX_FALSE;
+
+ mpeg4type.nAllowedPictureTypes =
+ OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+
+ mpeg4type.nPFrames = 23;
+ mpeg4type.nBFrames = 0;
+
+ mpeg4type.nIDCVLCThreshold = 0;
+ mpeg4type.bACPred = OMX_TRUE;
+ mpeg4type.nMaxPacketSize = 256;
+ mpeg4type.nTimeIncRes = 1000;
+ mpeg4type.nHeaderExtension = 0;
+ mpeg4type.bReversibleVLC = OMX_FALSE;
+
+ mpeg4type.eProfile = OMX_VIDEO_MPEG4ProfileCore;
+ mpeg4type.eLevel = OMX_VIDEO_MPEG4Level2;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
+ CHECK_EQ(err, OK);
+
+ // ----------------
+
+ OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
+ InitOMXParams(&bitrateType);
+ bitrateType.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoBitrate,
+ &bitrateType, sizeof(bitrateType));
+ CHECK_EQ(err, OK);
+
+ bitrateType.eControlRate = OMX_Video_ControlRateVariable;
+ bitrateType.nTargetBitrate = 1000000;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoBitrate,
+ &bitrateType, sizeof(bitrateType));
+ CHECK_EQ(err, OK);
+
+ // ----------------
+
+ OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType;
+ InitOMXParams(&errorCorrectionType);
+ errorCorrectionType.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoErrorCorrection,
+ &errorCorrectionType, sizeof(errorCorrectionType));
+ CHECK_EQ(err, OK);
+
+ errorCorrectionType.bEnableHEC = OMX_FALSE;
+ errorCorrectionType.bEnableResync = OMX_TRUE;
+ errorCorrectionType.nResynchMarkerSpacing = 256;
+ errorCorrectionType.bEnableDataPartitioning = OMX_FALSE;
+ errorCorrectionType.bEnableRVLC = OMX_FALSE;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoErrorCorrection,
+ &errorCorrectionType, sizeof(errorCorrectionType));
+ CHECK_EQ(err, OK);
+
+ return OK;
+}
+
+status_t OMXCodec::setupAVCEncoderParameters() {
+ OMX_VIDEO_PARAM_AVCTYPE h264type;
+ InitOMXParams(&h264type);
+ h264type.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
+ CHECK_EQ(err, OK);
+
+ h264type.nAllowedPictureTypes =
+ OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+
+ h264type.nSliceHeaderSpacing = 0;
+ h264type.nBFrames = 0;
+ h264type.bUseHadamard = OMX_TRUE;
+ h264type.nRefFrames = 1;
+ h264type.nRefIdx10ActiveMinus1 = 0;
+ h264type.nRefIdx11ActiveMinus1 = 0;
+ h264type.bEnableUEP = OMX_FALSE;
+ h264type.bEnableFMO = OMX_FALSE;
+ h264type.bEnableASO = OMX_FALSE;
+ h264type.bEnableRS = OMX_FALSE;
+ h264type.eProfile = OMX_VIDEO_AVCProfileBaseline;
+ h264type.eLevel = OMX_VIDEO_AVCLevel1b;
+ h264type.bFrameMBsOnly = OMX_TRUE;
+ h264type.bMBAFF = OMX_FALSE;
+ h264type.bEntropyCodingCABAC = OMX_FALSE;
+ h264type.bWeightedPPrediction = OMX_FALSE;
+ h264type.bconstIpred = OMX_FALSE;
+ h264type.bDirect8x8Inference = OMX_FALSE;
+ h264type.bDirectSpatialTemporal = OMX_FALSE;
+ h264type.nCabacInitIdc = 0;
+ h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
+ CHECK_EQ(err, OK);
+
+ OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
+ InitOMXParams(&bitrateType);
+ bitrateType.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoBitrate,
+ &bitrateType, sizeof(bitrateType));
+ CHECK_EQ(err, OK);
+
+ bitrateType.eControlRate = OMX_Video_ControlRateVariable;
+ bitrateType.nTargetBitrate = 1000000;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoBitrate,
+ &bitrateType, sizeof(bitrateType));
+ CHECK_EQ(err, OK);
+
+ return OK;
}
void OMXCodec::setVideoOutputFormat(
const char *mime, OMX_U32 width, OMX_U32 height) {
- CODEC_LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height);
+ CODEC_LOGV("setVideoOutputFormat width=%ld, height=%ld", width, height);
OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
@@ -639,6 +881,7 @@ void OMXCodec::setVideoOutputFormat(
video_def->nFrameWidth = width;
video_def->nFrameHeight = height;
+ video_def->eCompressionFormat = compressionFormat;
video_def->eColorFormat = OMX_COLOR_FormatUnused;
err = mOMX->setParameter(
@@ -668,7 +911,6 @@ void OMXCodec::setVideoOutputFormat(
CHECK_EQ(err, OK);
}
-
OMXCodec::OMXCodec(
const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks,
bool isEncoder,
@@ -687,6 +929,7 @@ OMXCodec::OMXCodec(
mInitialBufferSubmit(true),
mSignalledEOS(false),
mNoMoreOutputData(false),
+ mOutputPortSettingsHaveChanged(false),
mSeekTimeUs(-1) {
mPortStatus[kPortIndexInput] = ENABLED;
mPortStatus[kPortIndexOutput] = ENABLED;
@@ -990,16 +1233,15 @@ void OMXCodec::on_message(const omx_message &msg) {
buffer->meta_data()->clear();
- buffer->meta_data()->setInt32(
- kKeyTimeUnits,
- (msg.u.extended_buffer_data.timestamp + 500) / 1000);
-
- buffer->meta_data()->setInt32(
- kKeyTimeScale, 1000);
+ buffer->meta_data()->setInt64(
+ kKeyTime, msg.u.extended_buffer_data.timestamp);
if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) {
buffer->meta_data()->setInt32(kKeyIsSyncFrame, true);
}
+ if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_CODECCONFIG) {
+ buffer->meta_data()->setInt32(kKeyIsCodecConfig, true);
+ }
buffer->meta_data()->setPointer(
kKeyPlatformPrivate,
@@ -1064,6 +1306,71 @@ void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
}
}
+// Has the format changed in any way that the client would have to be aware of?
+static bool formatHasNotablyChanged(
+ const sp<MetaData> &from, const sp<MetaData> &to) {
+ if (from.get() == NULL && to.get() == NULL) {
+ return false;
+ }
+
+ if ((from.get() == NULL && to.get() != NULL)
+ || (from.get() != NULL && to.get() == NULL)) {
+ return true;
+ }
+
+ const char *mime_from, *mime_to;
+ CHECK(from->findCString(kKeyMIMEType, &mime_from));
+ CHECK(to->findCString(kKeyMIMEType, &mime_to));
+
+ if (strcasecmp(mime_from, mime_to)) {
+ return true;
+ }
+
+ if (!strcasecmp(mime_from, MEDIA_MIMETYPE_VIDEO_RAW)) {
+ int32_t colorFormat_from, colorFormat_to;
+ CHECK(from->findInt32(kKeyColorFormat, &colorFormat_from));
+ CHECK(to->findInt32(kKeyColorFormat, &colorFormat_to));
+
+ if (colorFormat_from != colorFormat_to) {
+ return true;
+ }
+
+ int32_t width_from, width_to;
+ CHECK(from->findInt32(kKeyWidth, &width_from));
+ CHECK(to->findInt32(kKeyWidth, &width_to));
+
+ if (width_from != width_to) {
+ return true;
+ }
+
+ int32_t height_from, height_to;
+ CHECK(from->findInt32(kKeyHeight, &height_from));
+ CHECK(to->findInt32(kKeyHeight, &height_to));
+
+ if (height_from != height_to) {
+ return true;
+ }
+ } else if (!strcasecmp(mime_from, MEDIA_MIMETYPE_AUDIO_RAW)) {
+ int32_t numChannels_from, numChannels_to;
+ CHECK(from->findInt32(kKeyChannelCount, &numChannels_from));
+ CHECK(to->findInt32(kKeyChannelCount, &numChannels_to));
+
+ if (numChannels_from != numChannels_to) {
+ return true;
+ }
+
+ int32_t sampleRate_from, sampleRate_to;
+ CHECK(from->findInt32(kKeySampleRate, &sampleRate_from));
+ CHECK(to->findInt32(kKeySampleRate, &sampleRate_to));
+
+ if (sampleRate_from != sampleRate_to) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
switch (cmd) {
case OMX_CommandStateSet:
@@ -1086,6 +1393,15 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
if (mState == RECONFIGURING) {
CHECK_EQ(portIndex, kPortIndexOutput);
+ sp<MetaData> oldOutputFormat = mOutputFormat;
+ initOutputFormat(mSource->getFormat());
+
+ // Don't notify clients if the output port settings change
+ // wasn't of importance to them, i.e. it may be that just the
+ // number of buffers has changed and nothing else.
+ mOutputPortSettingsHaveChanged =
+ formatHasNotablyChanged(oldOutputFormat, mOutputFormat);
+
enablePortAsync(portIndex);
status_t err = allocateBuffersOnPort(portIndex);
@@ -1443,7 +1759,7 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) {
}
OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME;
- OMX_TICKS timestamp = 0;
+ OMX_TICKS timestampUs = 0;
size_t srcLength = 0;
if (err != OK) {
@@ -1463,15 +1779,11 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) {
(const uint8_t *)srcBuffer->data() + srcBuffer->range_offset(),
srcLength);
- int32_t units, scale;
- if (srcBuffer->meta_data()->findInt32(kKeyTimeUnits, &units)
- && srcBuffer->meta_data()->findInt32(kKeyTimeScale, &scale)) {
- timestamp = ((OMX_TICKS)units * 1000000) / scale;
-
- CODEC_LOGV("Calling empty_buffer on buffer %p (length %d)",
+ if (srcBuffer->meta_data()->findInt64(kKeyTime, &timestampUs)) {
+ CODEC_LOGV("Calling emptyBuffer on buffer %p (length %d)",
info->mBuffer, srcLength);
- CODEC_LOGV("Calling empty_buffer with timestamp %lld us (%.2f secs)",
- timestamp, timestamp / 1E6);
+ CODEC_LOGV("Calling emptyBuffer with timestamp %lld us (%.2f secs)",
+ timestampUs, timestampUs / 1E6);
}
}
@@ -1482,7 +1794,7 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) {
err = mOMX->emptyBuffer(
mNode, info->mBuffer, 0, srcLength,
- flags, timestamp);
+ flags, timestampUs);
if (err != OK) {
setState(ERROR);
@@ -1490,6 +1802,13 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) {
}
info->mOwnedByComponent = true;
+
+ // This component does not ever signal the EOS flag on output buffers,
+ // Thanks for nothing.
+ if (mSignalledEOS && !strcmp(mComponentName, "OMX.TI.Video.encoder")) {
+ mNoMoreOutputData = true;
+ mBufferFilled.signal();
+ }
}
void OMXCodec::fillOutputBuffer(BufferInfo *info) {
@@ -1794,6 +2113,7 @@ status_t OMXCodec::start(MetaData *) {
mInitialBufferSubmit = true;
mSignalledEOS = false;
mNoMoreOutputData = false;
+ mOutputPortSettingsHaveChanged = false;
mSeekTimeUs = -1;
mFilledBuffers.clear();
@@ -1864,6 +2184,8 @@ status_t OMXCodec::stop() {
}
sp<MetaData> OMXCodec::getFormat() {
+ Mutex::Autolock autoLock(mLock);
+
return mOutputFormat;
}
@@ -1941,6 +2263,12 @@ status_t OMXCodec::read(
return ERROR_END_OF_STREAM;
}
+ if (mOutputPortSettingsHaveChanged) {
+ mOutputPortSettingsHaveChanged = false;
+
+ return INFO_FORMAT_CHANGED;
+ }
+
size_t index = *mFilledBuffers.begin();
mFilledBuffers.erase(mFilledBuffers.begin());
@@ -2041,8 +2369,6 @@ static const char *colorFormatString(OMX_COLOR_FORMATTYPE type) {
size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
- static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
-
if (type == OMX_QCOM_COLOR_FormatYVU420SemiPlanar) {
return "OMX_QCOM_COLOR_FormatYVU420SemiPlanar";
} else if (type < 0 || (size_t)type >= numNames) {
@@ -2410,7 +2736,7 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {
CHECK(!"Unknown compression format.");
}
- if (mQuirks & kOutputDimensionsAre16Aligned) {
+ if (!strcmp(mComponentName, "OMX.PV.avcdec")) {
// This component appears to be lying to me.
mOutputFormat->setInt32(
kKeyWidth, (video_def->nFrameWidth + 15) & -16);
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 8efa7c7..4aec0e9 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -17,11 +17,12 @@
#define LOG_TAG "SampleTable"
#include <utils/Log.h>
+#include "include/SampleTable.h"
+
#include <arpa/inet.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/SampleTable.h>
#include <media/stagefright/Utils.h>
namespace android {
@@ -54,7 +55,7 @@ SampleTable::~SampleTable() {
}
status_t SampleTable::setChunkOffsetParams(
- uint32_t type, off_t data_offset, off_t data_size) {
+ uint32_t type, off_t data_offset, size_t data_size) {
if (mChunkOffsetOffset >= 0) {
return ERROR_MALFORMED;
}
@@ -69,7 +70,7 @@ status_t SampleTable::setChunkOffsetParams(
}
uint8_t header[8];
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
return ERROR_IO;
}
@@ -95,7 +96,7 @@ status_t SampleTable::setChunkOffsetParams(
}
status_t SampleTable::setSampleToChunkParams(
- off_t data_offset, off_t data_size) {
+ off_t data_offset, size_t data_size) {
if (mSampleToChunkOffset >= 0) {
return ERROR_MALFORMED;
}
@@ -107,7 +108,7 @@ status_t SampleTable::setSampleToChunkParams(
}
uint8_t header[8];
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
return ERROR_IO;
}
@@ -127,7 +128,7 @@ status_t SampleTable::setSampleToChunkParams(
}
status_t SampleTable::setSampleSizeParams(
- uint32_t type, off_t data_offset, off_t data_size) {
+ uint32_t type, off_t data_offset, size_t data_size) {
if (mSampleSizeOffset >= 0) {
return ERROR_MALFORMED;
}
@@ -141,7 +142,7 @@ status_t SampleTable::setSampleSizeParams(
}
uint8_t header[12];
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
return ERROR_IO;
}
@@ -187,13 +188,13 @@ status_t SampleTable::setSampleSizeParams(
}
status_t SampleTable::setTimeToSampleParams(
- off_t data_offset, off_t data_size) {
+ off_t data_offset, size_t data_size) {
if (mTimeToSample != NULL || data_size < 8) {
return ERROR_MALFORMED;
}
uint8_t header[8];
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
return ERROR_IO;
}
@@ -207,7 +208,7 @@ status_t SampleTable::setTimeToSampleParams(
mTimeToSample = new uint32_t[mTimeToSampleCount * 2];
size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2;
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset + 8, mTimeToSample, size) < (ssize_t)size) {
return ERROR_IO;
}
@@ -219,7 +220,7 @@ status_t SampleTable::setTimeToSampleParams(
return OK;
}
-status_t SampleTable::setSyncSampleParams(off_t data_offset, off_t data_size) {
+status_t SampleTable::setSyncSampleParams(off_t data_offset, size_t data_size) {
if (mSyncSampleOffset >= 0 || data_size < 8) {
return ERROR_MALFORMED;
}
@@ -227,7 +228,7 @@ status_t SampleTable::setSyncSampleParams(off_t data_offset, off_t data_size) {
mSyncSampleOffset = data_offset;
uint8_t header[8];
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
return ERROR_IO;
}
@@ -263,7 +264,7 @@ status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) {
if (mChunkOffsetType == kChunkOffsetType32) {
uint32_t offset32;
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
mChunkOffsetOffset + 8 + 4 * chunk_index,
&offset32,
sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
@@ -275,7 +276,7 @@ status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) {
CHECK_EQ(mChunkOffsetType, kChunkOffsetType64);
uint64_t offset64;
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
mChunkOffsetOffset + 8 + 8 * chunk_index,
&offset64,
sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
@@ -312,7 +313,7 @@ status_t SampleTable::getChunkForSample(
uint32_t index = 0;
while (index < mNumSampleToChunkOffsets) {
uint8_t buffer[12];
- if (mDataSource->read_at(mSampleToChunkOffset + 8 + index * 12,
+ if (mDataSource->readAt(mSampleToChunkOffset + 8 + index * 12,
buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
@@ -361,7 +362,7 @@ status_t SampleTable::getSampleSize(
switch (mSampleSizeFieldSize) {
case 32:
{
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
mSampleSizeOffset + 12 + 4 * sample_index,
sample_size, sizeof(*sample_size)) < (ssize_t)sizeof(*sample_size)) {
return ERROR_IO;
@@ -374,7 +375,7 @@ status_t SampleTable::getSampleSize(
case 16:
{
uint16_t x;
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
mSampleSizeOffset + 12 + 2 * sample_index,
&x, sizeof(x)) < (ssize_t)sizeof(x)) {
return ERROR_IO;
@@ -387,7 +388,7 @@ status_t SampleTable::getSampleSize(
case 8:
{
uint8_t x;
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
mSampleSizeOffset + 12 + sample_index,
&x, sizeof(x)) < (ssize_t)sizeof(x)) {
return ERROR_IO;
@@ -402,7 +403,7 @@ status_t SampleTable::getSampleSize(
CHECK_EQ(mSampleSizeFieldSize, 4);
uint8_t x;
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
mSampleSizeOffset + 12 + sample_index / 2,
&x, sizeof(x)) < (ssize_t)sizeof(x)) {
return ERROR_IO;
@@ -553,7 +554,7 @@ status_t SampleTable::findClosestSyncSample(
uint32_t right = mNumSyncSamples;
while (left < right) {
uint32_t mid = (left + right) / 2;
- if (mDataSource->read_at(
+ if (mDataSource->readAt(
mSyncSampleOffset + 8 + (mid - 1) * 4, &x, 4) != 4) {
return ERROR_IO;
}
@@ -574,5 +575,52 @@ status_t SampleTable::findClosestSyncSample(
return OK;
}
+status_t SampleTable::findThumbnailSample(uint32_t *sample_index) {
+ if (mSyncSampleOffset < 0) {
+ // All samples are sync-samples.
+ *sample_index = 0;
+ return OK;
+ }
+
+ uint32_t bestSampleIndex = 0;
+ size_t maxSampleSize = 0;
+
+ static const size_t kMaxNumSyncSamplesToScan = 20;
+
+ // Consider the first kMaxNumSyncSamplesToScan sync samples and
+ // pick the one with the largest (compressed) size as the thumbnail.
+
+ size_t numSamplesToScan = mNumSyncSamples;
+ if (numSamplesToScan > kMaxNumSyncSamplesToScan) {
+ numSamplesToScan = kMaxNumSyncSamplesToScan;
+ }
+
+ for (size_t i = 0; i < numSamplesToScan; ++i) {
+ uint32_t x;
+ if (mDataSource->readAt(
+ mSyncSampleOffset + 8 + i * 4, &x, 4) != 4) {
+ return ERROR_IO;
+ }
+ x = ntohl(x);
+ --x;
+
+ // Now x is a sample index.
+ size_t sampleSize;
+ status_t err = getSampleSize(x, &sampleSize);
+ if (err != OK) {
+ return err;
+ }
+
+ if (i == 0 || sampleSize > maxSampleSize) {
+ bestSampleIndex = x;
+ maxSampleSize = sampleSize;
+ }
+ }
+
+ *sample_index = bestSampleIndex;
+
+ return OK;
+}
+
} // namespace android
diff --git a/media/libstagefright/ShoutcastSource.cpp b/media/libstagefright/ShoutcastSource.cpp
index 346b5aa..ec25430 100644
--- a/media/libstagefright/ShoutcastSource.cpp
+++ b/media/libstagefright/ShoutcastSource.cpp
@@ -14,16 +14,17 @@
* limitations under the License.
*/
+#include "include/stagefright_string.h"
+#include "include/HTTPStream.h"
+
#include <stdlib.h>
-#include <media/stagefright/HTTPStream.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/ShoutcastSource.h>
-#include <media/stagefright/stagefright_string.h>
namespace android {
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index 3d85f75..fa68771 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -22,15 +22,17 @@
#define LOG_TAG "TimedEventQueue"
#include <utils/Log.h>
+#include "include/TimedEventQueue.h"
+
#include <sys/time.h>
#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/TimedEventQueue.h>
namespace android {
TimedEventQueue::TimedEventQueue()
- : mRunning(false),
+ : mNextEventID(1),
+ mRunning(false),
mStopped(false) {
}
@@ -75,26 +77,29 @@ void TimedEventQueue::stop(bool flush) {
mRunning = false;
}
-void TimedEventQueue::postEvent(const sp<Event> &event) {
+TimedEventQueue::event_id TimedEventQueue::postEvent(const sp<Event> &event) {
// Reserve an earlier timeslot an INT64_MIN to be able to post
// the StopEvent to the absolute head of the queue.
- postTimedEvent(event, INT64_MIN + 1);
+ return postTimedEvent(event, INT64_MIN + 1);
}
-void TimedEventQueue::postEventToBack(const sp<Event> &event) {
- postTimedEvent(event, INT64_MAX);
+TimedEventQueue::event_id TimedEventQueue::postEventToBack(
+ const sp<Event> &event) {
+ return postTimedEvent(event, INT64_MAX);
}
-void TimedEventQueue::postEventWithDelay(
+TimedEventQueue::event_id TimedEventQueue::postEventWithDelay(
const sp<Event> &event, int64_t delay_us) {
CHECK(delay_us >= 0);
- postTimedEvent(event, getRealTimeUs() + delay_us);
+ return postTimedEvent(event, getRealTimeUs() + delay_us);
}
-void TimedEventQueue::postTimedEvent(
+TimedEventQueue::event_id TimedEventQueue::postTimedEvent(
const sp<Event> &event, int64_t realtime_us) {
Mutex::Autolock autoLock(mLock);
+ event->setEventID(mNextEventID++);
+
List<QueueItem>::iterator it = mQueue.begin();
while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
++it;
@@ -111,29 +116,60 @@ void TimedEventQueue::postTimedEvent(
mQueue.insert(it, item);
mQueueNotEmptyCondition.signal();
-}
-bool TimedEventQueue::cancelEvent(const sp<Event> &event) {
- Mutex::Autolock autoLock(mLock);
+ return event->eventID();
+}
- List<QueueItem>::iterator it = mQueue.begin();
- while (it != mQueue.end() && (*it).event != event) {
- ++it;
- }
+static bool MatchesEventID(
+ void *cookie, const sp<TimedEventQueue::Event> &event) {
+ TimedEventQueue::event_id *id =
+ static_cast<TimedEventQueue::event_id *>(cookie);
- if (it == mQueue.end()) {
+ if (event->eventID() != *id) {
return false;
}
- if (it == mQueue.begin()) {
- mQueueHeadChangedCondition.signal();
- }
-
- mQueue.erase(it);
+ *id = 0;
return true;
}
+bool TimedEventQueue::cancelEvent(event_id id) {
+ CHECK(id != 0);
+
+ cancelEvents(&MatchesEventID, &id, true /* stopAfterFirstMatch */);
+
+ // if MatchesEventID found a match, it will have set id to 0
+ // (which is not a valid event_id).
+
+ return id == 0;
+}
+
+void TimedEventQueue::cancelEvents(
+ bool (*predicate)(void *cookie, const sp<Event> &event),
+ void *cookie,
+ bool stopAfterFirstMatch) {
+ Mutex::Autolock autoLock(mLock);
+
+ List<QueueItem>::iterator it = mQueue.begin();
+ while (it != mQueue.end()) {
+ if (!(*predicate)(cookie, (*it).event)) {
+ ++it;
+ continue;
+ }
+
+ if (it == mQueue.begin()) {
+ mQueueHeadChangedCondition.signal();
+ }
+
+ it = mQueue.erase(it);
+
+ if (stopAfterFirstMatch) {
+ return;
+ }
+ }
+}
+
// static
int64_t TimedEventQueue::getRealTimeUs() {
struct timeval tv;
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
new file mode 100644
index 0000000..542c764
--- /dev/null
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "WAVExtractor"
+#include <utils/Log.h>
+
+#include "include/WAVExtractor.h"
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/String8.h>
+
+namespace android {
+
+static uint16_t WAVE_FORMAT_PCM = 1;
+
+static uint32_t U32_LE_AT(const uint8_t *ptr) {
+ return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0];
+}
+
+static uint16_t U16_LE_AT(const uint8_t *ptr) {
+ return ptr[1] << 8 | ptr[0];
+}
+
+struct WAVSource : public MediaSource {
+ WAVSource(
+ const sp<DataSource> &dataSource,
+ int32_t sampleRate, int32_t numChannels,
+ off_t offset, size_t size);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+ virtual ~WAVSource();
+
+private:
+ static const size_t kMaxFrameSize;
+
+ sp<DataSource> mDataSource;
+ int32_t mSampleRate;
+ int32_t mNumChannels;
+ off_t mOffset;
+ size_t mSize;
+ bool mStarted;
+ MediaBufferGroup *mGroup;
+ off_t mCurrentPos;
+
+ WAVSource(const WAVSource &);
+ WAVSource &operator=(const WAVSource &);
+};
+
+WAVExtractor::WAVExtractor(const sp<DataSource> &source)
+ : mDataSource(source),
+ mValidFormat(false) {
+ mInitCheck = init();
+}
+
+WAVExtractor::~WAVExtractor() {
+}
+
+size_t WAVExtractor::countTracks() {
+ return mInitCheck == OK ? 1 : 0;
+}
+
+sp<MediaSource> WAVExtractor::getTrack(size_t index) {
+ if (mInitCheck != OK || index > 0) {
+ return NULL;
+ }
+
+ return new WAVSource(
+ mDataSource, mSampleRate, mNumChannels, mDataOffset, mDataSize);
+}
+
+sp<MetaData> WAVExtractor::getTrackMetaData(
+ size_t index, uint32_t flags) {
+ if (mInitCheck != OK || index > 0) {
+ return NULL;
+ }
+
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ meta->setInt32(kKeyChannelCount, mNumChannels);
+ meta->setInt32(kKeySampleRate, mSampleRate);
+
+ int64_t durationUs =
+ 1000000LL * (mDataSize / (mNumChannels * 2)) / mSampleRate;
+
+ meta->setInt64(kKeyDuration, durationUs);
+
+ return meta;
+}
+
+status_t WAVExtractor::init() {
+ uint8_t header[12];
+ if (mDataSource->readAt(
+ 0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return NO_INIT;
+ }
+
+ if (memcmp(header, "RIFF", 4) || memcmp(&header[8], "WAVE", 4)) {
+ return NO_INIT;
+ }
+
+ size_t totalSize = U32_LE_AT(&header[4]);
+
+ off_t offset = 12;
+ size_t remainingSize = totalSize;
+ while (remainingSize >= 8) {
+ uint8_t chunkHeader[8];
+ if (mDataSource->readAt(offset, chunkHeader, 8) < 8) {
+ return NO_INIT;
+ }
+
+ remainingSize -= 8;
+ offset += 8;
+
+ uint32_t chunkSize = U32_LE_AT(&chunkHeader[4]);
+
+ if (chunkSize > remainingSize) {
+ return NO_INIT;
+ }
+
+ if (!memcmp(chunkHeader, "fmt ", 4)) {
+ if (chunkSize < 16) {
+ return NO_INIT;
+ }
+
+ uint8_t formatSpec[16];
+ if (mDataSource->readAt(offset, formatSpec, 16) < 16) {
+ return NO_INIT;
+ }
+
+ uint16_t format = U16_LE_AT(formatSpec);
+ if (format != WAVE_FORMAT_PCM) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ mNumChannels = U16_LE_AT(&formatSpec[2]);
+ if (mNumChannels != 1 && mNumChannels != 2) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ mSampleRate = U32_LE_AT(&formatSpec[4]);
+
+ if (U16_LE_AT(&formatSpec[14]) != 16) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ mValidFormat = true;
+ } else if (!memcmp(chunkHeader, "data", 4)) {
+ if (mValidFormat) {
+ mDataOffset = offset;
+ mDataSize = chunkSize;
+
+ return OK;
+ }
+ }
+
+ offset += chunkSize;
+ }
+
+ return NO_INIT;
+}
+
+const size_t WAVSource::kMaxFrameSize = 32768;
+
+WAVSource::WAVSource(
+ const sp<DataSource> &dataSource,
+ int32_t sampleRate, int32_t numChannels,
+ off_t offset, size_t size)
+ : mDataSource(dataSource),
+ mSampleRate(sampleRate),
+ mNumChannels(numChannels),
+ mOffset(offset),
+ mSize(size),
+ mStarted(false),
+ mGroup(NULL) {
+}
+
+WAVSource::~WAVSource() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t WAVSource::start(MetaData *params) {
+ LOGV("WAVSource::start");
+
+ CHECK(!mStarted);
+
+ mGroup = new MediaBufferGroup;
+ mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+
+ mCurrentPos = mOffset;
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t WAVSource::stop() {
+ LOGV("WAVSource::stop");
+
+ CHECK(mStarted);
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> WAVSource::getFormat() {
+ LOGV("WAVSource::getFormat");
+
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ meta->setInt32(kKeyChannelCount, mNumChannels);
+ meta->setInt32(kKeySampleRate, mSampleRate);
+
+ int64_t durationUs =
+ 1000000LL * (mSize / (mNumChannels * 2)) / mSampleRate;
+
+ meta->setInt64(kKeyDuration, durationUs);
+
+ return meta;
+}
+
+status_t WAVSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ if (options != NULL && options->getSeekTo(&seekTimeUs)) {
+ int64_t pos = (seekTimeUs * mSampleRate) / 1000000 * mNumChannels * 2;
+ if (pos > mSize) {
+ pos = mSize;
+ }
+ mCurrentPos = pos + mOffset;
+ }
+
+ MediaBuffer *buffer;
+ status_t err = mGroup->acquire_buffer(&buffer);
+ if (err != OK) {
+ return err;
+ }
+
+ ssize_t n = mDataSource->readAt(
+ mCurrentPos, buffer->data(), kMaxFrameSize);
+
+ if (n <= 0) {
+ buffer->release();
+ buffer = NULL;
+
+ return ERROR_END_OF_STREAM;
+ }
+
+ mCurrentPos += n;
+
+ buffer->set_range(0, n);
+ buffer->meta_data()->setInt64(
+ kKeyTime,
+ 1000000LL * (mCurrentPos - mOffset)
+ / (mNumChannels * 2) / mSampleRate);
+
+
+ *out = buffer;
+
+ return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool SniffWAV(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+ char header[12];
+ if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return false;
+ }
+
+ if (memcmp(header, "RIFF", 4) || memcmp(&header[8], "WAVE", 4)) {
+ return false;
+ }
+
+ *mimeType = MEDIA_MIMETYPE_CONTAINER_WAV;
+ *confidence = 0.3f;
+
+ return true;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h
new file mode 100644
index 0000000..debf006
--- /dev/null
+++ b/media/libstagefright/include/AMRExtractor.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AMR_EXTRACTOR_H_
+
+#define AMR_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class String8;
+
+class AMRExtractor : public MediaExtractor {
+public:
+ AMRExtractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+ static sp<MetaData> makeAMRFormat(bool isWide);
+
+protected:
+ virtual ~AMRExtractor();
+
+private:
+ sp<DataSource> mDataSource;
+ status_t mInitCheck;
+ bool mIsWide;
+
+ AMRExtractor(const AMRExtractor &);
+ AMRExtractor &operator=(const AMRExtractor &);
+};
+
+bool SniffAMR(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
+
+} // namespace android
+
+#endif // AMR_EXTRACTOR_H_
diff --git a/media/libstagefright/include/ESDS.h b/media/libstagefright/include/ESDS.h
new file mode 100644
index 0000000..01bcd18
--- /dev/null
+++ b/media/libstagefright/include/ESDS.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ESDS_H_
+
+#define ESDS_H_
+
+#include <stdint.h>
+
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+class ESDS {
+public:
+ ESDS(const void *data, size_t size);
+ ~ESDS();
+
+ status_t InitCheck() const;
+
+ status_t getCodecSpecificInfo(const void **data, size_t *size) const;
+
+private:
+ enum {
+ kTag_ESDescriptor = 0x03,
+ kTag_DecoderConfigDescriptor = 0x04,
+ kTag_DecoderSpecificInfo = 0x05
+ };
+
+ uint8_t *mData;
+ size_t mSize;
+
+ status_t mInitCheck;
+
+ size_t mDecoderSpecificOffset;
+ size_t mDecoderSpecificLength;
+
+ status_t skipDescriptorHeader(
+ size_t offset, size_t size,
+ uint8_t *tag, size_t *data_offset, size_t *data_size) const;
+
+ status_t parse();
+ status_t parseESDescriptor(size_t offset, size_t size);
+ status_t parseDecoderConfigDescriptor(size_t offset, size_t size);
+
+ ESDS(const ESDS &);
+ ESDS &operator=(const ESDS &);
+};
+
+} // namespace android
+#endif // ESDS_H_
diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h
new file mode 100644
index 0000000..43ef614
--- /dev/null
+++ b/media/libstagefright/include/HTTPStream.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HTTP_STREAM_H_
+
+#define HTTP_STREAM_H_
+
+#include "stagefright_string.h"
+
+#include <sys/types.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+class HTTPStream {
+public:
+ HTTPStream();
+ ~HTTPStream();
+
+ status_t connect(const char *server, int port = 80);
+ status_t disconnect();
+
+ status_t send(const char *data, size_t size);
+
+ // Assumes data is a '\0' terminated string.
+ status_t send(const char *data);
+
+ // Receive up to "size" bytes of data.
+ ssize_t receive(void *data, size_t size);
+
+ status_t receive_header(int *http_status);
+
+ // The header key used to retrieve the status line.
+ static const char *kStatusKey;
+
+ bool find_header_value(
+ const string &key, string *value) const;
+
+private:
+ enum State {
+ READY,
+ CONNECTED
+ };
+
+ State mState;
+ int mSocket;
+
+ KeyedVector<string, string> mHeaders;
+
+ // Receive a line of data terminated by CRLF, line will be '\0' terminated
+ // _excluding_ the termianting CRLF.
+ status_t receive_line(char *line, size_t size);
+
+ HTTPStream(const HTTPStream &);
+ HTTPStream &operator=(const HTTPStream &);
+};
+
+} // namespace android
+
+#endif // HTTP_STREAM_H_
diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h
new file mode 100644
index 0000000..074973b
--- /dev/null
+++ b/media/libstagefright/include/MP3Extractor.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MP3_EXTRACTOR_H_
+
+#define MP3_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class DataSource;
+class String8;
+
+class MP3Extractor : public MediaExtractor {
+public:
+ // Extractor assumes ownership of "source".
+ MP3Extractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+protected:
+ virtual ~MP3Extractor();
+
+private:
+ sp<DataSource> mDataSource;
+ off_t mFirstFramePos;
+ sp<MetaData> mMeta;
+ uint32_t mFixedHeader;
+
+ MP3Extractor(const MP3Extractor &);
+ MP3Extractor &operator=(const MP3Extractor &);
+};
+
+bool SniffMP3(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
+
+} // namespace android
+
+#endif // MP3_EXTRACTOR_H_
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
new file mode 100644
index 0000000..ce4736d
--- /dev/null
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPEG4_EXTRACTOR_H_
+
+#define MPEG4_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class DataSource;
+class SampleTable;
+class String8;
+
+class MPEG4Extractor : public MediaExtractor {
+public:
+ // Extractor assumes ownership of "source".
+ MPEG4Extractor(const sp<DataSource> &source);
+
+ size_t countTracks();
+ sp<MediaSource> getTrack(size_t index);
+ sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+protected:
+ virtual ~MPEG4Extractor();
+
+private:
+ struct Track {
+ Track *next;
+ sp<MetaData> meta;
+ uint32_t timescale;
+ sp<SampleTable> sampleTable;
+ bool includes_expensive_metadata;
+ };
+
+ sp<DataSource> mDataSource;
+ bool mHaveMetadata;
+
+ Track *mFirstTrack, *mLastTrack;
+
+ uint32_t mHandlerType;
+
+ status_t readMetaData();
+ status_t parseChunk(off_t *offset, int depth);
+
+ MPEG4Extractor(const MPEG4Extractor &);
+ MPEG4Extractor &operator=(const MPEG4Extractor &);
+};
+
+bool SniffMPEG4(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
+
+} // namespace android
+
+#endif // MPEG4_EXTRACTOR_H_
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index d0bd61e..a4b62b2 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -99,7 +99,7 @@ public:
OMX_IN OMX_U32 nData1,
OMX_IN OMX_U32 nData2,
OMX_IN OMX_PTR pEventData);
-
+
OMX_ERRORTYPE OnEmptyBufferDone(
node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
new file mode 100644
index 0000000..ead3431
--- /dev/null
+++ b/media/libstagefright/include/SampleTable.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SAMPLE_TABLE_H_
+
+#define SAMPLE_TABLE_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class DataSource;
+
+class SampleTable : public RefBase {
+public:
+ SampleTable(const sp<DataSource> &source);
+
+ // type can be 'stco' or 'co64'.
+ status_t setChunkOffsetParams(
+ uint32_t type, off_t data_offset, size_t data_size);
+
+ status_t setSampleToChunkParams(off_t data_offset, size_t data_size);
+
+ // type can be 'stsz' or 'stz2'.
+ status_t setSampleSizeParams(
+ uint32_t type, off_t data_offset, size_t data_size);
+
+ status_t setTimeToSampleParams(off_t data_offset, size_t data_size);
+
+ status_t setSyncSampleParams(off_t data_offset, size_t data_size);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ uint32_t countChunkOffsets() const;
+ status_t getChunkOffset(uint32_t chunk_index, off_t *offset);
+
+ status_t getChunkForSample(
+ uint32_t sample_index, uint32_t *chunk_index,
+ uint32_t *chunk_relative_sample_index, uint32_t *desc_index);
+
+ uint32_t countSamples() const;
+ status_t getSampleSize(uint32_t sample_index, size_t *sample_size);
+
+ status_t getSampleOffsetAndSize(
+ uint32_t sample_index, off_t *offset, size_t *size);
+
+ status_t getMaxSampleSize(size_t *size);
+
+ status_t getDecodingTime(uint32_t sample_index, uint32_t *time);
+
+ enum {
+ kSyncSample_Flag = 1
+ };
+ status_t findClosestSample(
+ uint32_t req_time, uint32_t *sample_index, uint32_t flags);
+
+ status_t findClosestSyncSample(
+ uint32_t start_sample_index, uint32_t *sample_index);
+
+ status_t findThumbnailSample(uint32_t *sample_index);
+
+protected:
+ ~SampleTable();
+
+private:
+ sp<DataSource> mDataSource;
+ Mutex mLock;
+
+ off_t mChunkOffsetOffset;
+ uint32_t mChunkOffsetType;
+ uint32_t mNumChunkOffsets;
+
+ off_t mSampleToChunkOffset;
+ uint32_t mNumSampleToChunkOffsets;
+
+ off_t mSampleSizeOffset;
+ uint32_t mSampleSizeFieldSize;
+ uint32_t mDefaultSampleSize;
+ uint32_t mNumSampleSizes;
+
+ uint32_t mTimeToSampleCount;
+ uint32_t *mTimeToSample;
+
+ off_t mSyncSampleOffset;
+ uint32_t mNumSyncSamples;
+
+ SampleTable(const SampleTable &);
+ SampleTable &operator=(const SampleTable &);
+};
+
+} // namespace android
+
+#endif // SAMPLE_TABLE_H_
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
new file mode 100644
index 0000000..9eed089
--- /dev/null
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SOFTWARE_RENDERER_H_
+
+#define SOFTWARE_RENDERER_H_
+
+#include <media/stagefright/ColorConverter.h>
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class ISurface;
+class MemoryHeapBase;
+
+class SoftwareRenderer : public VideoRenderer {
+public:
+ SoftwareRenderer(
+ OMX_COLOR_FORMATTYPE colorFormat,
+ const sp<ISurface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight);
+
+ virtual ~SoftwareRenderer();
+
+ virtual void render(
+ const void *data, size_t size, void *platformPrivate);
+
+private:
+ OMX_COLOR_FORMATTYPE mColorFormat;
+ ColorConverter mConverter;
+ sp<ISurface> mISurface;
+ size_t mDisplayWidth, mDisplayHeight;
+ size_t mDecodedWidth, mDecodedHeight;
+ size_t mFrameSize;
+ sp<MemoryHeapBase> mMemoryHeap;
+ int mIndex;
+
+ SoftwareRenderer(const SoftwareRenderer &);
+ SoftwareRenderer &operator=(const SoftwareRenderer &);
+};
+
+} // namespace android
+
+#endif // SOFTWARE_RENDERER_H_
diff --git a/media/libstagefright/include/TimedEventQueue.h b/media/libstagefright/include/TimedEventQueue.h
new file mode 100644
index 0000000..21eade3
--- /dev/null
+++ b/media/libstagefright/include/TimedEventQueue.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TIMED_EVENT_QUEUE_H_
+
+#define TIMED_EVENT_QUEUE_H_
+
+#include <pthread.h>
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct TimedEventQueue {
+
+ typedef int32_t event_id;
+
+ struct Event : public RefBase {
+ Event()
+ : mEventID(0) {
+ }
+
+ virtual ~Event() {}
+
+ event_id eventID() {
+ return mEventID;
+ }
+
+ protected:
+ virtual void fire(TimedEventQueue *queue, int64_t now_us) = 0;
+
+ private:
+ friend class TimedEventQueue;
+
+ event_id mEventID;
+
+ void setEventID(event_id id) {
+ mEventID = id;
+ }
+
+ Event(const Event &);
+ Event &operator=(const Event &);
+ };
+
+ TimedEventQueue();
+ ~TimedEventQueue();
+
+ // Start executing the event loop.
+ void start();
+
+ // Stop executing the event loop, if flush is false, any pending
+ // events are discarded, otherwise the queue will stop (and this call
+ // return) once all pending events have been handled.
+ void stop(bool flush = false);
+
+ // Posts an event to the front of the queue (after all events that
+ // have previously been posted to the front but before timed events).
+ event_id postEvent(const sp<Event> &event);
+
+ event_id postEventToBack(const sp<Event> &event);
+
+ // It is an error to post an event with a negative delay.
+ event_id postEventWithDelay(const sp<Event> &event, int64_t delay_us);
+
+ // If the event is to be posted at a time that has already passed,
+ // it will fire as soon as possible.
+ event_id postTimedEvent(const sp<Event> &event, int64_t realtime_us);
+
+ // Returns true iff event is currently in the queue and has been
+ // successfully cancelled. In this case the event will have been
+ // removed from the queue and won't fire.
+ bool cancelEvent(event_id id);
+
+ // Cancel any pending event that satisfies the predicate.
+ // If stopAfterFirstMatch is true, only cancels the first event
+ // satisfying the predicate (if any).
+ void cancelEvents(
+ bool (*predicate)(void *cookie, const sp<Event> &event),
+ void *cookie,
+ bool stopAfterFirstMatch = false);
+
+ static int64_t getRealTimeUs();
+
+private:
+ struct QueueItem {
+ sp<Event> event;
+ int64_t realtime_us;
+ };
+
+ struct StopEvent : public TimedEventQueue::Event {
+ virtual void fire(TimedEventQueue *queue, int64_t now_us) {
+ queue->mStopped = true;
+ }
+ };
+
+ pthread_t mThread;
+ List<QueueItem> mQueue;
+ Mutex mLock;
+ Condition mQueueNotEmptyCondition;
+ Condition mQueueHeadChangedCondition;
+ event_id mNextEventID;
+
+ bool mRunning;
+ bool mStopped;
+
+ static void *ThreadWrapper(void *me);
+ void threadEntry();
+
+ TimedEventQueue(const TimedEventQueue &);
+ TimedEventQueue &operator=(const TimedEventQueue &);
+};
+
+} // namespace android
+
+#endif // TIMED_EVENT_QUEUE_H_
diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h
new file mode 100644
index 0000000..10b9700
--- /dev/null
+++ b/media/libstagefright/include/WAVExtractor.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WAV_EXTRACTOR_H_
+
+#define WAV_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class DataSource;
+class String8;
+
+class WAVExtractor : public MediaExtractor {
+public:
+ // Extractor assumes ownership of "source".
+ WAVExtractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+protected:
+ virtual ~WAVExtractor();
+
+private:
+ sp<DataSource> mDataSource;
+ status_t mInitCheck;
+ bool mValidFormat;
+ uint16_t mNumChannels;
+ uint32_t mSampleRate;
+ off_t mDataOffset;
+ size_t mDataSize;
+
+ status_t init();
+
+ WAVExtractor(const WAVExtractor &);
+ WAVExtractor &operator=(const WAVExtractor &);
+};
+
+bool SniffWAV(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
+
+} // namespace android
+
+#endif // WAV_EXTRACTOR_H_
+
diff --git a/media/libstagefright/include/stagefright_string.h b/media/libstagefright/include/stagefright_string.h
new file mode 100644
index 0000000..5dc7116
--- /dev/null
+++ b/media/libstagefright/include/stagefright_string.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef STRING_H_
+
+#define STRING_H_
+
+#include <utils/String8.h>
+
+namespace android {
+
+class string {
+public:
+ typedef size_t size_type;
+ static size_type npos;
+
+ string();
+ string(const char *s);
+ string(const char *s, size_t length);
+ string(const string &from, size_type start, size_type length = npos);
+
+ const char *c_str() const;
+ size_type size() const;
+
+ void clear();
+ void erase(size_type from, size_type length);
+
+ size_type find(char c) const;
+
+ bool operator<(const string &other) const;
+ bool operator==(const string &other) const;
+
+ string &operator+=(char c);
+
+private:
+ String8 mString;
+};
+
+} // namespace android
+
+#endif // STRING_H_
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index 25da813..389c2c9 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -9,6 +9,7 @@ LOCAL_CFLAGS := $(PV_CFLAGS_MINUS_VISIBILITY)
LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
LOCAL_SRC_FILES:= \
+ ColorConverter.cpp \
OMX.cpp \
OMXNodeInstance.cpp \
SoftwareRenderer.cpp
diff --git a/media/libstagefright/omx/ColorConverter.cpp b/media/libstagefright/omx/ColorConverter.cpp
new file mode 100644
index 0000000..e74782f
--- /dev/null
+++ b/media/libstagefright/omx/ColorConverter.cpp
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/ColorConverter.h>
+#include <media/stagefright/MediaDebug.h>
+
+namespace android {
+
+static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
+ColorConverter::ColorConverter(
+ OMX_COLOR_FORMATTYPE from, OMX_COLOR_FORMATTYPE to)
+ : mSrcFormat(from),
+ mDstFormat(to),
+ mClip(NULL) {
+}
+
+ColorConverter::~ColorConverter() {
+ delete[] mClip;
+ mClip = NULL;
+}
+
+bool ColorConverter::isValid() const {
+ if (mDstFormat != OMX_COLOR_Format16bitRGB565) {
+ return false;
+ }
+
+ switch (mSrcFormat) {
+ case OMX_COLOR_FormatYUV420Planar:
+ case OMX_COLOR_FormatCbYCrY:
+ case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+void ColorConverter::convert(
+ size_t width, size_t height,
+ const void *srcBits, size_t srcSkip,
+ void *dstBits, size_t dstSkip) {
+ CHECK_EQ(mDstFormat, OMX_COLOR_Format16bitRGB565);
+
+ switch (mSrcFormat) {
+ case OMX_COLOR_FormatYUV420Planar:
+ convertYUV420Planar(
+ width, height, srcBits, srcSkip, dstBits, dstSkip);
+ break;
+
+ case OMX_COLOR_FormatCbYCrY:
+ convertCbYCrY(
+ width, height, srcBits, srcSkip, dstBits, dstSkip);
+ break;
+
+ case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
+ convertQCOMYUV420SemiPlanar(
+ width, height, srcBits, srcSkip, dstBits, dstSkip);
+ break;
+
+ default:
+ {
+ CHECK(!"Should not be here. Unknown color conversion.");
+ break;
+ }
+ }
+}
+
+void ColorConverter::convertCbYCrY(
+ size_t width, size_t height,
+ const void *srcBits, size_t srcSkip,
+ void *dstBits, size_t dstSkip) {
+ CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats.
+ CHECK(dstSkip >= width * 2);
+ CHECK((dstSkip & 3) == 0);
+
+ uint8_t *kAdjustedClip = initClip();
+
+ uint32_t *dst_ptr = (uint32_t *)dstBits;
+
+ const uint8_t *src = (const uint8_t *)srcBits;
+
+ for (size_t y = 0; y < height; ++y) {
+ for (size_t x = 0; x < width; x += 2) {
+ signed y1 = (signed)src[2 * x + 1] - 16;
+ signed y2 = (signed)src[2 * x + 3] - 16;
+ signed u = (signed)src[2 * x] - 128;
+ signed v = (signed)src[2 * x + 2] - 128;
+
+ signed u_b = u * 517;
+ signed u_g = -u * 100;
+ signed v_g = -v * 208;
+ signed v_r = v * 409;
+
+ signed tmp1 = y1 * 298;
+ signed b1 = (tmp1 + u_b) / 256;
+ signed g1 = (tmp1 + v_g + u_g) / 256;
+ signed r1 = (tmp1 + v_r) / 256;
+
+ signed tmp2 = y2 * 298;
+ signed b2 = (tmp2 + u_b) / 256;
+ signed g2 = (tmp2 + v_g + u_g) / 256;
+ signed r2 = (tmp2 + v_r) / 256;
+
+ uint32_t rgb1 =
+ ((kAdjustedClip[r1] >> 3) << 11)
+ | ((kAdjustedClip[g1] >> 2) << 5)
+ | (kAdjustedClip[b1] >> 3);
+
+ uint32_t rgb2 =
+ ((kAdjustedClip[r2] >> 3) << 11)
+ | ((kAdjustedClip[g2] >> 2) << 5)
+ | (kAdjustedClip[b2] >> 3);
+
+ dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ }
+
+ src += width * 2;
+ dst_ptr += dstSkip / 4;
+ }
+}
+
+void ColorConverter::convertYUV420Planar(
+ size_t width, size_t height,
+ const void *srcBits, size_t srcSkip,
+ void *dstBits, size_t dstSkip) {
+ CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats.
+ CHECK(dstSkip >= width * 2);
+ CHECK((dstSkip & 3) == 0);
+
+ uint8_t *kAdjustedClip = initClip();
+
+ uint32_t *dst_ptr = (uint32_t *)dstBits;
+ const uint8_t *src_y = (const uint8_t *)srcBits;
+
+ const uint8_t *src_u =
+ (const uint8_t *)src_y + width * height;
+
+ const uint8_t *src_v =
+ (const uint8_t *)src_u + (width / 2) * (height / 2);
+
+ for (size_t y = 0; y < height; ++y) {
+ for (size_t x = 0; x < width; x += 2) {
+ // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
+ // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
+ // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
+
+ // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
+ // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
+ // R = .................. + 409/256 * (V - 128)
+
+ // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
+ // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
+ // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
+
+ // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
+ // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
+ // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
+
+ // clip range -278 .. 535
+
+ signed y1 = (signed)src_y[x] - 16;
+ signed y2 = (signed)src_y[x + 1] - 16;
+
+ signed u = (signed)src_u[x / 2] - 128;
+ signed v = (signed)src_v[x / 2] - 128;
+
+ signed u_b = u * 517;
+ signed u_g = -u * 100;
+ signed v_g = -v * 208;
+ signed v_r = v * 409;
+
+ signed tmp1 = y1 * 298;
+ signed b1 = (tmp1 + u_b) / 256;
+ signed g1 = (tmp1 + v_g + u_g) / 256;
+ signed r1 = (tmp1 + v_r) / 256;
+
+ signed tmp2 = y2 * 298;
+ signed b2 = (tmp2 + u_b) / 256;
+ signed g2 = (tmp2 + v_g + u_g) / 256;
+ signed r2 = (tmp2 + v_r) / 256;
+
+ uint32_t rgb1 =
+ ((kAdjustedClip[r1] >> 3) << 11)
+ | ((kAdjustedClip[g1] >> 2) << 5)
+ | (kAdjustedClip[b1] >> 3);
+
+ uint32_t rgb2 =
+ ((kAdjustedClip[r2] >> 3) << 11)
+ | ((kAdjustedClip[g2] >> 2) << 5)
+ | (kAdjustedClip[b2] >> 3);
+
+ dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ }
+
+ src_y += width;
+
+ if (y & 1) {
+ src_u += width / 2;
+ src_v += width / 2;
+ }
+
+ dst_ptr += dstSkip / 4;
+ }
+}
+
+void ColorConverter::convertQCOMYUV420SemiPlanar(
+ size_t width, size_t height,
+ const void *srcBits, size_t srcSkip,
+ void *dstBits, size_t dstSkip) {
+ CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats.
+ CHECK(dstSkip >= width * 2);
+ CHECK((dstSkip & 3) == 0);
+
+ uint8_t *kAdjustedClip = initClip();
+
+ uint32_t *dst_ptr = (uint32_t *)dstBits;
+ const uint8_t *src_y = (const uint8_t *)srcBits;
+
+ const uint8_t *src_u =
+ (const uint8_t *)src_y + width * height;
+
+ for (size_t y = 0; y < height; ++y) {
+ for (size_t x = 0; x < width; x += 2) {
+ signed y1 = (signed)src_y[x] - 16;
+ signed y2 = (signed)src_y[x + 1] - 16;
+
+ signed u = (signed)src_u[x & ~1] - 128;
+ signed v = (signed)src_u[(x & ~1) + 1] - 128;
+
+ signed u_b = u * 517;
+ signed u_g = -u * 100;
+ signed v_g = -v * 208;
+ signed v_r = v * 409;
+
+ signed tmp1 = y1 * 298;
+ signed b1 = (tmp1 + u_b) / 256;
+ signed g1 = (tmp1 + v_g + u_g) / 256;
+ signed r1 = (tmp1 + v_r) / 256;
+
+ signed tmp2 = y2 * 298;
+ signed b2 = (tmp2 + u_b) / 256;
+ signed g2 = (tmp2 + v_g + u_g) / 256;
+ signed r2 = (tmp2 + v_r) / 256;
+
+ uint32_t rgb1 =
+ ((kAdjustedClip[b1] >> 3) << 11)
+ | ((kAdjustedClip[g1] >> 2) << 5)
+ | (kAdjustedClip[r1] >> 3);
+
+ uint32_t rgb2 =
+ ((kAdjustedClip[b2] >> 3) << 11)
+ | ((kAdjustedClip[g2] >> 2) << 5)
+ | (kAdjustedClip[r2] >> 3);
+
+ dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ }
+
+ src_y += width;
+
+ if (y & 1) {
+ src_u += width;
+ }
+
+ dst_ptr += dstSkip / 4;
+ }
+}
+
+uint8_t *ColorConverter::initClip() {
+ static const signed kClipMin = -278;
+ static const signed kClipMax = 535;
+
+ if (mClip == NULL) {
+ mClip = new uint8_t[kClipMax - kClipMin + 1];
+
+ for (signed i = kClipMin; i <= kClipMax; ++i) {
+ mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
+ }
+ }
+
+ return &mClip[-kClipMin];
+}
+
+} // namespace android
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 4ccd4bd..5b3cc1b 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -24,10 +24,10 @@
#include "pv_omxcore.h"
#include "../include/OMXNodeInstance.h"
+#include "../include/SoftwareRenderer.h"
#include <binder/IMemory.h>
#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/SoftwareRenderer.h>
#include <media/stagefright/VideoRenderer.h>
#include <OMX_Component.h>
@@ -233,7 +233,7 @@ status_t OMX::allocateNode(
&OMXNodeInstance::kCallbacks);
if (err != OMX_ErrorNone) {
- LOGE("FAILED to allocate omx component '%s'", name);
+ LOGV("FAILED to allocate omx component '%s'", name);
instance->onGetHandleFailed();
diff --git a/media/libstagefright/omx/SoftwareRenderer.cpp b/media/libstagefright/omx/SoftwareRenderer.cpp
index 39de504..ef6ede0 100644
--- a/media/libstagefright/omx/SoftwareRenderer.cpp
+++ b/media/libstagefright/omx/SoftwareRenderer.cpp
@@ -17,21 +17,21 @@
#define LOG_TAG "SoftwareRenderer"
#include <utils/Log.h>
+#include "../include/SoftwareRenderer.h"
+
#include <binder/MemoryHeapBase.h>
#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/SoftwareRenderer.h>
#include <ui/ISurface.h>
namespace android {
-#define QCOM_YUV 0
-
SoftwareRenderer::SoftwareRenderer(
OMX_COLOR_FORMATTYPE colorFormat,
const sp<ISurface> &surface,
size_t displayWidth, size_t displayHeight,
size_t decodedWidth, size_t decodedHeight)
: mColorFormat(colorFormat),
+ mConverter(colorFormat, OMX_COLOR_Format16bitRGB565),
mISurface(surface),
mDisplayWidth(displayWidth),
mDisplayHeight(displayHeight),
@@ -39,12 +39,12 @@ SoftwareRenderer::SoftwareRenderer(
mDecodedHeight(decodedHeight),
mFrameSize(mDecodedWidth * mDecodedHeight * 2), // RGB565
mMemoryHeap(new MemoryHeapBase(2 * mFrameSize)),
- mIndex(0),
- mClip(NULL) {
+ mIndex(0) {
CHECK(mISurface.get() != NULL);
CHECK(mDecodedWidth > 0);
CHECK(mDecodedHeight > 0);
CHECK(mMemoryHeap->heapID() >= 0);
+ CHECK(mConverter.isValid());
ISurface::BufferHeap bufferHeap(
mDisplayWidth, mDisplayHeight,
@@ -58,278 +58,19 @@ SoftwareRenderer::SoftwareRenderer(
SoftwareRenderer::~SoftwareRenderer() {
mISurface->unregisterBuffers();
-
- delete[] mClip;
- mClip = NULL;
}
void SoftwareRenderer::render(
const void *data, size_t size, void *platformPrivate) {
- static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
-
- switch (mColorFormat) {
- case OMX_COLOR_FormatYUV420Planar:
- return renderYUV420Planar(data, size);
-
- case OMX_COLOR_FormatCbYCrY:
- return renderCbYCrY(data, size);
-
- case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
- return renderQCOMYUV420SemiPlanar(data, size);
-
- default:
- {
- LOGW("Cannot render color format %d", mColorFormat);
- break;
- }
- }
-}
-
-void SoftwareRenderer::renderYUV420Planar(
- const void *data, size_t size) {
- if (size != (mDecodedHeight * mDecodedWidth * 3) / 2) {
- LOGE("size is %d, expected %d",
- size, (mDecodedHeight * mDecodedWidth * 3) / 2);
- }
- CHECK(size >= (mDecodedWidth * mDecodedHeight * 3) / 2);
-
- uint8_t *kAdjustedClip = initClip();
-
size_t offset = mIndex * mFrameSize;
-
void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
- uint32_t *dst_ptr = (uint32_t *)dst;
-
- const uint8_t *src_y = (const uint8_t *)data;
-
- const uint8_t *src_u =
- (const uint8_t *)src_y + mDecodedWidth * mDecodedHeight;
-
-#if !QCOM_YUV
- const uint8_t *src_v =
- (const uint8_t *)src_u + (mDecodedWidth / 2) * (mDecodedHeight / 2);
-#endif
-
- for (size_t y = 0; y < mDecodedHeight; ++y) {
- for (size_t x = 0; x < mDecodedWidth; x += 2) {
- // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
- // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
- // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
-
- // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
- // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
- // R = .................. + 409/256 * (V - 128)
-
- // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
- // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
- // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
-
- // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
- // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
- // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
-
- // clip range -278 .. 535
-
- signed y1 = (signed)src_y[x] - 16;
- signed y2 = (signed)src_y[x + 1] - 16;
-
-#if QCOM_YUV
- signed u = (signed)src_u[x & ~1] - 128;
- signed v = (signed)src_u[(x & ~1) + 1] - 128;
-#else
- signed u = (signed)src_u[x / 2] - 128;
- signed v = (signed)src_v[x / 2] - 128;
-#endif
-
- signed u_b = u * 517;
- signed u_g = -u * 100;
- signed v_g = -v * 208;
- signed v_r = v * 409;
-
- signed tmp1 = y1 * 298;
- signed b1 = (tmp1 + u_b) / 256;
- signed g1 = (tmp1 + v_g + u_g) / 256;
- signed r1 = (tmp1 + v_r) / 256;
-
- signed tmp2 = y2 * 298;
- signed b2 = (tmp2 + u_b) / 256;
- signed g2 = (tmp2 + v_g + u_g) / 256;
- signed r2 = (tmp2 + v_r) / 256;
-
- uint32_t rgb1 =
- ((kAdjustedClip[r1] >> 3) << 11)
- | ((kAdjustedClip[g1] >> 2) << 5)
- | (kAdjustedClip[b1] >> 3);
-
- uint32_t rgb2 =
- ((kAdjustedClip[r2] >> 3) << 11)
- | ((kAdjustedClip[g2] >> 2) << 5)
- | (kAdjustedClip[b2] >> 3);
-
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
- }
-
- src_y += mDecodedWidth;
-
- if (y & 1) {
-#if QCOM_YUV
- src_u += mDecodedWidth;
-#else
- src_u += mDecodedWidth / 2;
- src_v += mDecodedWidth / 2;
-#endif
- }
-
- dst_ptr += mDecodedWidth / 2;
- }
-
- mISurface->postBuffer(offset);
- mIndex = 1 - mIndex;
-}
-
-void SoftwareRenderer::renderCbYCrY(
- const void *data, size_t size) {
- if (size != (mDecodedHeight * mDecodedWidth * 2)) {
- LOGE("size is %d, expected %d",
- size, (mDecodedHeight * mDecodedWidth * 2));
- }
- CHECK(size >= (mDecodedWidth * mDecodedHeight * 2));
-
- uint8_t *kAdjustedClip = initClip();
-
- size_t offset = mIndex * mFrameSize;
- void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
- uint32_t *dst_ptr = (uint32_t *)dst;
-
- const uint8_t *src = (const uint8_t *)data;
-
- for (size_t y = 0; y < mDecodedHeight; ++y) {
- for (size_t x = 0; x < mDecodedWidth; x += 2) {
- signed y1 = (signed)src[2 * x + 1] - 16;
- signed y2 = (signed)src[2 * x + 3] - 16;
- signed u = (signed)src[2 * x] - 128;
- signed v = (signed)src[2 * x + 2] - 128;
-
- signed u_b = u * 517;
- signed u_g = -u * 100;
- signed v_g = -v * 208;
- signed v_r = v * 409;
-
- signed tmp1 = y1 * 298;
- signed b1 = (tmp1 + u_b) / 256;
- signed g1 = (tmp1 + v_g + u_g) / 256;
- signed r1 = (tmp1 + v_r) / 256;
-
- signed tmp2 = y2 * 298;
- signed b2 = (tmp2 + u_b) / 256;
- signed g2 = (tmp2 + v_g + u_g) / 256;
- signed r2 = (tmp2 + v_r) / 256;
-
- uint32_t rgb1 =
- ((kAdjustedClip[r1] >> 3) << 11)
- | ((kAdjustedClip[g1] >> 2) << 5)
- | (kAdjustedClip[b1] >> 3);
-
- uint32_t rgb2 =
- ((kAdjustedClip[r2] >> 3) << 11)
- | ((kAdjustedClip[g2] >> 2) << 5)
- | (kAdjustedClip[b2] >> 3);
-
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
- }
-
- src += mDecodedWidth * 2;
- dst_ptr += mDecodedWidth / 2;
- }
-
- mISurface->postBuffer(offset);
- mIndex = 1 - mIndex;
-}
-
-void SoftwareRenderer::renderQCOMYUV420SemiPlanar(
- const void *data, size_t size) {
- if (size != (mDecodedHeight * mDecodedWidth * 3) / 2) {
- LOGE("size is %d, expected %d",
- size, (mDecodedHeight * mDecodedWidth * 3) / 2);
- }
- CHECK(size >= (mDecodedWidth * mDecodedHeight * 3) / 2);
-
- uint8_t *kAdjustedClip = initClip();
-
- size_t offset = mIndex * mFrameSize;
-
- void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
-
- uint32_t *dst_ptr = (uint32_t *)dst;
-
- const uint8_t *src_y = (const uint8_t *)data;
-
- const uint8_t *src_u =
- (const uint8_t *)src_y + mDecodedWidth * mDecodedHeight;
-
- for (size_t y = 0; y < mDecodedHeight; ++y) {
- for (size_t x = 0; x < mDecodedWidth; x += 2) {
- signed y1 = (signed)src_y[x] - 16;
- signed y2 = (signed)src_y[x + 1] - 16;
-
- signed u = (signed)src_u[x & ~1] - 128;
- signed v = (signed)src_u[(x & ~1) + 1] - 128;
-
- signed u_b = u * 517;
- signed u_g = -u * 100;
- signed v_g = -v * 208;
- signed v_r = v * 409;
-
- signed tmp1 = y1 * 298;
- signed b1 = (tmp1 + u_b) / 256;
- signed g1 = (tmp1 + v_g + u_g) / 256;
- signed r1 = (tmp1 + v_r) / 256;
-
- signed tmp2 = y2 * 298;
- signed b2 = (tmp2 + u_b) / 256;
- signed g2 = (tmp2 + v_g + u_g) / 256;
- signed r2 = (tmp2 + v_r) / 256;
-
- uint32_t rgb1 =
- ((kAdjustedClip[b1] >> 3) << 11)
- | ((kAdjustedClip[g1] >> 2) << 5)
- | (kAdjustedClip[r1] >> 3);
-
- uint32_t rgb2 =
- ((kAdjustedClip[b2] >> 3) << 11)
- | ((kAdjustedClip[g2] >> 2) << 5)
- | (kAdjustedClip[r2] >> 3);
-
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
- }
-
- src_y += mDecodedWidth;
-
- if (y & 1) {
- src_u += mDecodedWidth;
- }
-
- dst_ptr += mDecodedWidth / 2;
- }
+ mConverter.convert(
+ mDecodedWidth, mDecodedHeight,
+ data, 0, dst, 2 * mDecodedWidth);
mISurface->postBuffer(offset);
mIndex = 1 - mIndex;
}
-uint8_t *SoftwareRenderer::initClip() {
- static const signed kClipMin = -278;
- static const signed kClipMax = 535;
-
- if (mClip == NULL) {
- mClip = new uint8_t[kClipMax - kClipMin + 1];
-
- for (signed i = kClipMin; i <= kClipMax; ++i) {
- mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
- }
- }
-
- return &mClip[-kClipMin];
-}
-
} // namespace android
diff --git a/media/libstagefright/stagefright_string.cpp b/media/libstagefright/string.cpp
index 2aedb80..bd6204b 100644
--- a/media/libstagefright/stagefright_string.cpp
+++ b/media/libstagefright/string.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <media/stagefright/stagefright_string.h>
+#include "include/stagefright_string.h"
namespace android {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
index e66e560..fa0986a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
@@ -226,8 +226,7 @@ public class CameraTest extends ActivityInstrumentationTestCase<MediaFrameworkTe
* Test case 1: Take a picture and verify all the callback
* functions are called properly.
*/
- // TODO: add this back to LargeTest once bug 2141755 is fixed
- // @LargeTest
+ @LargeTest
public void testTakePicture() throws Exception {
synchronized (lock) {
initializeMessageLooper();