/*
* Copyright 2012 Michael Chen <omxcodec@gmail.com>
+ * Copyright 2015 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define LOG_NDEBUG 0
+
+//#define LOG_NDEBUG 0
#define LOG_TAG "FFMPEG"
#include <utils/Log.h>
extern "C" {
#endif
+#include "config.h"
+
#include <unistd.h>
#include <stdlib.h>
#include <inttypes.h>
#include <math.h>
-#include <signal.h>
#include <limits.h> /* INT_MAX */
-
-#include "config.h"
-#include "libavutil/avstring.h"
-#include "libavutil/colorspace.h"
-#include "libavutil/mathematics.h"
-#include "libavutil/pixdesc.h"
-#include "libavutil/imgutils.h"
-#include "libavutil/dict.h"
-#include "libavutil/parseutils.h"
-#include "libavutil/samplefmt.h"
-#include "libavutil/avassert.h"
-#include "libavutil/intreadwrite.h"
-#include "libavformat/avformat.h"
-#include "libavdevice/avdevice.h"
-#include "libswscale/swscale.h"
-#include "libavcodec/audioconvert.h"
-#include "libavutil/opt.h"
-#include "libavutil/internal.h"
-#include "libavcodec/avfft.h"
-#include "libswresample/swresample.h"
-
-#include "cmdutils.h"
+#include <time.h>
#undef strncpy
#include <string.h>
}
#endif
+#include <cutils/properties.h>
+
+#include "ffmpeg_utils.h"
+#include "ffmpeg_source.h"
+
// log
static int flags;
const int program_birth_year = 2012;
// init ffmpeg
-static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
-static int ref_count = 0;
+static pthread_mutex_t s_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int s_ref_count = 0;
namespace android {
//////////////////////////////////////////////////////////////////////////////////
-// dummy
-//////////////////////////////////////////////////////////////////////////////////
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void av_noreturn exit_program(int ret)
-{
- // do nothing
-}
-
-void show_help_default(const char *opt, const char *arg)
-{
- // do nothing
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-//////////////////////////////////////////////////////////////////////////////////
// log
//////////////////////////////////////////////////////////////////////////////////
static void sanitize(uint8_t *line){
}
}
+// TODO, remove static variables to support multi-instances
void nam_av_log_callback(void* ptr, int level, const char* fmt, va_list vl)
{
static int print_prefix = 1;
return;
}
if (count > 0) {
- LOGI("Last message repeated %d times\n", count);
+ ALOGI("Last message repeated %d times\n", count);
count = 0;
}
strcpy(prev, line);
sanitize((uint8_t *)line);
#if 0
- LOGI("%s", line);
+ ALOGI("%s", line);
#else
#define LOG_BUF_SIZE 1024
static char g_msg[LOG_BUF_SIZE];
g_msg_len += 1;
g_msg[g_msg_len] = '\n';
}
- LOGI("%s", g_msg);
+ ALOGI("%s", g_msg);
/* reset g_msg and g_msg_len */
memset(g_msg, 0, LOG_BUF_SIZE);
g_msg_len = 0;
return 1;
}
+/**
+ * To debug ffmpeg", type this command on the console before starting playback:
+ * setprop debug.nam.ffmpeg 1
+ * To disable the debug, type:
+ * setprop debug.nam.ffmpge 0
+*/
status_t initFFmpeg()
{
status_t ret = OK;
+ bool debug_enabled = false;
+ char value[PROPERTY_VALUE_MAX];
+
+ pthread_mutex_lock(&s_init_mutex);
- pthread_mutex_lock(&init_mutex);
+ if (property_get("debug.nam.ffmpeg", value, NULL)
+ && (!strcmp(value, "1") || !av_strcasecmp(value, "true"))) {
+ ALOGI("set ffmpeg debug level to AV_LOG_DEBUG");
+ debug_enabled = true;
+ }
+ if (debug_enabled)
+ av_log_set_level(AV_LOG_DEBUG);
+ else
+ av_log_set_level(AV_LOG_INFO);
- if(ref_count == 0) {
+ if(s_ref_count == 0) {
nam_av_log_set_flags(AV_LOG_SKIP_REPEATED);
- //av_log_set_level(AV_LOG_DEBUG);
av_log_set_callback(nam_av_log_callback);
/* register all codecs, demux and protocols */
avcodec_register_all();
-#if CONFIG_AVDEVICE
+#if 0
avdevice_register_all();
#endif
av_register_all();
avformat_network_init();
- init_opts();
+ /* register android source */
+ ffmpeg_register_android_source();
if (av_lockmgr_register(lockmgr)) {
- LOGE("could not initialize lock manager!");
+ ALOGE("could not initialize lock manager!");
ret = NO_INIT;
}
}
// update counter
- ref_count++;
+ s_ref_count++;
- pthread_mutex_unlock(&init_mutex);
+ pthread_mutex_unlock(&s_init_mutex);
return ret;
}
void deInitFFmpeg()
{
- pthread_mutex_lock(&init_mutex);
+ pthread_mutex_lock(&s_init_mutex);
// update counter
- ref_count--;
+ s_ref_count--;
- if(ref_count == 0) {
+ if(s_ref_count == 0) {
av_lockmgr_register(NULL);
- uninit_opts();
avformat_network_deinit();
}
- pthread_mutex_unlock(&init_mutex);
+ pthread_mutex_unlock(&s_init_mutex);
}
//////////////////////////////////////////////////////////////////////////////////
// parser
//////////////////////////////////////////////////////////////////////////////////
-/* H.264 bitstream with start codes, NOT AVC1! ref: libavcodec/h264_parser.c */
-static int h264_split(AVCodecContext *avctx,
- const uint8_t *buf, int buf_size, int check_compatible_only)
+/* H.264 bitstream with start codes, NOT AVC1! */
+static int h264_split(AVCodecContext *avctx __unused,
+ const uint8_t *buf, int buf_size, int check_compatible_only)
{
int i;
uint32_t state = -1;
for(i=0; i<=buf_size; i++){
if((state&0xFFFFFF1F) == 0x107) {
- LOGI("found NAL_SPS");
+ ALOGI("found NAL_SPS");
has_sps=1;
}
if((state&0xFFFFFF1F) == 0x108) {
- LOGI("found NAL_PPS");
+ ALOGI("found NAL_PPS");
has_pps=1;
if (check_compatible_only)
return (has_sps & has_pps);
}
- if((state&0xFFFFFF00) == 0x100 && ((state&0xFFFFFF1F) == 0x101 || (state&0xFFFFFF1F) == 0x102 || (state&0xFFFFFF1F) == 0x105)){
+ if((state&0xFFFFFF00) == 0x100
+ && ((state&0xFFFFFF1F) == 0x101
+ || (state&0xFFFFFF1F) == 0x102
+ || (state&0xFFFFFF1F) == 0x105)){
if(has_pps){
while(i>4 && buf[i-5]==0) i--;
return i-4;
return 0;
}
-/* ref: libavcodec/mpegvideo_parser.c */
-static int mpegvideo_split(AVCodecContext *avctx,
- const uint8_t *buf, int buf_size, int check_compatible_only)
+static int mpegvideo_split(AVCodecContext *avctx __unused,
+ const uint8_t *buf, int buf_size, int check_compatible_only __unused)
{
int i;
uint32_t state= -1;
/* split extradata from buf for Android OMXCodec */
int parser_split(AVCodecContext *avctx,
- const uint8_t *buf, int buf_size)
+ const uint8_t *buf, int buf_size)
{
if (!avctx || !buf || buf_size <= 0) {
- LOGE("parser split, valid params");
+ ALOGE("parser split, valid params");
return 0;
}
- if (avctx->codec_id == CODEC_ID_H264) {
+ if (avctx->codec_id == AV_CODEC_ID_H264) {
return h264_split(avctx, buf, buf_size, 0);
- } else if (avctx->codec_id == CODEC_ID_MPEG2VIDEO ||
- avctx->codec_id == CODEC_ID_MPEG4) {
+ } else if (avctx->codec_id == AV_CODEC_ID_MPEG2VIDEO ||
+ avctx->codec_id == AV_CODEC_ID_MPEG4) {
return mpegvideo_split(avctx, buf, buf_size, 0);
} else {
- LOGE("parser split, unsupport the codec, id: 0x%0x", avctx->codec_id);
+ ALOGE("parser split, unsupport the codec, id: 0x%0x", avctx->codec_id);
}
return 0;
int is_extradata_compatible_with_android(AVCodecContext *avctx)
{
if (avctx->extradata_size <= 0) {
- LOGI("extradata_size <= 0, extradata is not compatible with android decoder, the codec id: 0x%0x", avctx->codec_id);
+ ALOGI("extradata_size <= 0, extradata is not compatible with "
+ "android decoder, the codec id: 0x%0x", avctx->codec_id);
return 0;
}
- if (avctx->codec_id == CODEC_ID_H264 && avctx->extradata[0] != 1 /* configurationVersion */) {
+ if (avctx->codec_id == AV_CODEC_ID_H264
+ && avctx->extradata[0] != 1 /* configurationVersion */) {
// SPS + PPS
- return !!(h264_split(avctx, avctx->extradata, avctx->extradata_size, 1) > 0);
+ return !!(h264_split(avctx, avctx->extradata,
+ avctx->extradata_size, 1) > 0);
} else {
// default, FIXME
return !!(avctx->extradata_size > 0);
}
}
+//////////////////////////////////////////////////////////////////////////////////
+// packet queue
+//////////////////////////////////////////////////////////////////////////////////
+void packet_queue_init(PacketQueue *q)
+{
+ memset(q, 0, sizeof(PacketQueue));
+ q->abort_request = 1;
+}
+
+void packet_queue_destroy(PacketQueue *q)
+{
+ packet_queue_abort(q);
+ packet_queue_flush(q);
+}
+
+void packet_queue_flush(PacketQueue *q)
+{
+ AVPacketList *pkt, *pkt1;
+
+ Mutex::Autolock autoLock(q->lock);
+ for (pkt = q->first_pkt; pkt != NULL; pkt = pkt1) {
+ pkt1 = pkt->next;
+ av_free_packet(&pkt->pkt);
+ av_freep(&pkt);
+ }
+ q->last_pkt = NULL;
+ q->first_pkt = NULL;
+ q->nb_packets = 0;
+ q->size = 0;
+}
+
+void packet_queue_abort(PacketQueue *q)
+{
+ q->abort_request = 1;
+ Mutex::Autolock autoLock(q->lock);
+ q->cond.signal();
+}
+
+static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt)
+{
+ AVPacketList *pkt1;
+
+ if (q->abort_request)
+ return -1;
+
+ pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));
+ if (!pkt1)
+ return -1;
+ pkt1->pkt = *pkt;
+ pkt1->next = NULL;
+
+ if (!q->last_pkt)
+ q->first_pkt = pkt1;
+ else
+ q->last_pkt->next = pkt1;
+ q->last_pkt = pkt1;
+ q->nb_packets++;
+ //q->size += pkt1->pkt.size + sizeof(*pkt1);
+ q->size += pkt1->pkt.size;
+ q->cond.signal();
+ return 0;
+}
+
+int packet_queue_put(PacketQueue *q, AVPacket *pkt)
+{
+ int ret;
+
+ /* duplicate the packet */
+ if (pkt != &q->flush_pkt && av_dup_packet(pkt) < 0)
+ return -1;
+
+ q->lock.lock();
+ ret = packet_queue_put_private(q, pkt);
+ q->lock.unlock();
+
+ if (pkt != &q->flush_pkt && ret < 0)
+ av_free_packet(pkt);
+
+ return ret;
+}
+
+int packet_queue_is_wait_for_data(PacketQueue *q)
+{
+ Mutex::Autolock autoLock(q->lock);
+ return q->wait_for_data;
+}
+
+int packet_queue_put_nullpacket(PacketQueue *q, int stream_index)
+{
+ AVPacket pkt1, *pkt = &pkt1;
+ av_init_packet(pkt);
+ pkt->data = NULL;
+ pkt->size = 0;
+ pkt->stream_index = stream_index;
+ return packet_queue_put(q, pkt);
+}
+
+/* packet queue handling */
+/* return < 0 if aborted, 0 if no packet and > 0 if packet. */
+int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
+{
+ AVPacketList *pkt1;
+ int ret = -1;
+
+ Mutex::Autolock autoLock(q->lock);
+
+ while (!q->abort_request) {
+ pkt1 = q->first_pkt;
+ if (pkt1) {
+ q->first_pkt = pkt1->next;
+ if (!q->first_pkt)
+ q->last_pkt = NULL;
+ q->nb_packets--;
+ //q->size -= pkt1->pkt.size + sizeof(*pkt1);
+ q->size -= pkt1->pkt.size;
+ *pkt = pkt1->pkt;
+ av_free(pkt1);
+ ret = 1;
+ break;
+ } else if (!block) {
+ ret = 0;
+ break;
+ } else {
+ q->wait_for_data = 1;
+ q->cond.waitRelative(q->lock, 10000000LL);
+ }
+ }
+ q->wait_for_data = 0;
+ return ret;
+}
+
+void packet_queue_start(PacketQueue *q)
+{
+ Mutex::Autolock autoLock(q->lock);
+ av_init_packet(&q->flush_pkt);
+ q->flush_pkt.data = (uint8_t *)&q->flush_pkt;
+ q->flush_pkt.size = 0;
+ q->abort_request = 0;
+ packet_queue_put_private(q, &q->flush_pkt);
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// misc
+//////////////////////////////////////////////////////////////////////////////////
+bool setup_vorbis_extradata(uint8_t **extradata, int *extradata_size,
+ const uint8_t *header_start[3], const int header_len[3])
+{
+ uint8_t *p = NULL;
+ int len = 0;
+ int i = 0;
+
+ len = header_len[0] + header_len[1] + header_len[2];
+ p = *extradata = (uint8_t *)av_mallocz(64 + len + len/255);
+ if (!p) {
+ ALOGE("oom for vorbis extradata");
+ return false;
+ }
+
+ *p++ = 2;
+ p += av_xiphlacing(p, header_len[0]);
+ p += av_xiphlacing(p, header_len[1]);
+ for (i = 0; i < 3; i++) {
+ if (header_len[i] > 0) {
+ memcpy(p, header_start[i], header_len[i]);
+ p += header_len[i];
+ }
+ }
+ *extradata_size = p - *extradata;
+
+ return true;
+}
+
+int64_t get_timestamp() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+audio_format_t to_android_audio_format(enum AVSampleFormat fmt) {
+ AVSampleFormat packed = av_get_packed_sample_fmt(fmt);
+ if (packed == AV_SAMPLE_FMT_U8)
+ return AUDIO_FORMAT_PCM_8_BIT;
+ if (packed == AV_SAMPLE_FMT_S16)
+ return AUDIO_FORMAT_PCM_16_BIT;
+ if (packed == AV_SAMPLE_FMT_S32)
+ return AUDIO_FORMAT_PCM_32_BIT;
+ if (packed == AV_SAMPLE_FMT_FLT)
+ return AUDIO_FORMAT_PCM_FLOAT;
+ return AUDIO_FORMAT_DEFAULT;
+}
+
} // namespace android