Android平台多个mpg文件连续播放

虽然Android4.1之后版本通过MediaPlayer的setNextMediaPlayer()方法视频无缝播放,但是国产的N多ARM芯片并不支持这一特性,

如何才能让现有的平台达到无缝播放效果,方法有N多种,比如通过http的ts流方式或者将多个文件合并成一个文件在去播放,就可以变相解决该问题。

经过测试可以把同种编码格式的mpg文件cat在一起播放以达到目的,笔者顺着这个思路逐步从Linux kernel层到Android JNI和App层

逐步来实现;


java播放层

1、设置要播放的文件总大小

2、设置播放序列

3、将虚拟的proc内存文件赋值给播放器

    int fd = MpgFS.open("/dev/mpg_fs");

    int fileSize = 8290340 + 16240676 + 16240676 + 16240676;
    MpgFS.setFileSize(fd, fileSize);

    List list = new ArrayList();
    list.add(new PlayNode("/mnt/sdcard/576/FM0.mpg", 8290340, 0));
    list.add(new PlayNode("/mnt/sdcard/576/FM1.mpg", 16240676, 1));
    list.add(new PlayNode("/mnt/sdcard/576/FM2.mpg", 16240676, 2));
    list.add(new PlayNode("/mnt/sdcard/576/FM3.mpg", 16240676, 3));

    MpgFS.setPlayList(list, fd);

    play.setVideoPath("/proc/mpg_fs");


JNI层的java部分

import java.util.List;

public class MpgFS {

    static {
        System.loadLibrary("mpgfs_jni");
    }

    public static final int NODE_SIZE = 128;

    public native static int open(String device);
    public native static int close(int fd);
    private native static int ioctl(int fd, int cmd, byte[] buf, long len);

    public static void int2buf(int value, byte[] buf, int offset) {
        buf[offset + 0] = (byte) ((value >>  0) & 0xff);
        buf[offset + 1] = (byte) ((value >>  8) & 0xff);
        buf[offset + 2] = (byte) ((value >> 16) & 0xff);
        buf[offset + 3] = (byte) ((value >> 24) & 0xff);
    }

    public static int setFileSize(int fd, long size) {
        return ioctl(fd, 5, null, size);
    }

    public static int setPlayList(List list, int fd) {
        if (list == null || list.size() == 0) {
            return -1;
        }

        int size = list.size();
        byte[] buffer = new byte[NODE_SIZE * size];

        for (int i = 0; i < size; i++) {
            int offset = i * NODE_SIZE;
            PlayNode node = list.get(i);

            int2buf(node.size, buffer, offset);
            int2buf(node.seq, buffer, offset+4);
            int2buf(node.fd, buffer, offset+8);
            System.arraycopy(node.name.getBytes(), 0, buffer, offset+12, node.name.length());
        }

        return ioctl(fd, 10000 + size, buffer, buffer.length);
    }
}


JNI层的C部分

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#define LOG_TAG "MpgFS_JNI"

#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
//#define LOGD(...)

typedef struct play_node {
    int   size;
    int   seq;
    int   fd;
    char  name[128-12];
} play_node_t;


static const char *kClassName = "com/signway/mpgfs/MpgFS";

jint openNative(JNIEnv *env, jobject obj, jstring name);
jint closeNative(JNIEnv *env, jobject obj, jint fd);
jint ioctlNative(JNIEnv *env, jobject obj, jint fd, jint cmd, jbyteArray buf, jlong length); 

static JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    {"open",     "(Ljava/lang/String;)I",      (void*)openNative},
    {"close",    "(I)I",                       (void*)closeNative},
    {"ioctl",    "(II[BJ)I",                   (void*)ioctlNative}
};

jint openNative(JNIEnv *env, jobject obj, jstring name)
{
    int fd = -1;
    const char *device = (*env)->GetStringUTFChars(env, name, NULL);
    fd = open(device, O_RDONLY);
    LOGD("device is %s and fd is %d", device, fd);
    (*env)->ReleaseStringUTFChars(env, name, device);
    return fd;
}

jint closeNative(JNIEnv *env, jobject obj, jint fd)
{
    LOGD("close device");
    close(fd);
}

jint ioctlNative(JNIEnv *env, jobject obj, jint fd, jint cmd, jbyteArray buf, jlong length)
{
    int ret = 0;
    play_node_t play_file;
    memset(&play_file, 0, sizeof(play_node_t));

    LOGD("ioctrl, cmd is:%d", cmd);

    switch (cmd) {
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    case 5: // set file length
        ret = ioctl(fd, cmd, &length);
        break;

    default:
        if (cmd > 10000) {
            char *buffer = malloc(length);
            if (buffer) {
                (*env)->GetByteArrayRegion(env, buf, 0, (jsize)length, (jbyte*)buffer);
                ret = ioctl(fd, cmd, buffer);
                free(buffer);
                buffer = NULL;
                break;
            }
            LOGE("malloc error for buffer length is: %d", length);
            ret = -1;
        }
        break;
    }

    return ret;
}

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
    JNIEnv *env = NULL;
    jclass cls;
 
    if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("current JNI not support JNI_VERSION_1_4");
        return JNI_ERR;
    }

    cls = (*env)->FindClass(env, kClassName);
    if (cls == NULL) {
        LOGE("can not find class %s", kClassName);
        return JNI_ERR;
    }

    if ((*env)->RegisterNatives(env, cls, gMethods, sizeof(gMethods)/sizeof(gMethods[0])) != JNI_OK) {
        LOGE("can not register native methods");
        return JNI_ERR;
    }

    return JNI_VERSION_1_4;
}

kernel层的驱动部分

1、实现一个驱动/dev/mpg_fs,用于java层来控制播放列表

2、使用一个内存文件,/proc/mpg_fs,用来给mediaplayer作为播放文件路径

3、在kernel里读取文件,送往内存文件

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static struct proc_dir_entry* entry = NULL;
static loff_t g_size = 0;

static struct file *fd = NULL;

static unsigned char buffer[16*1024];

typedef struct play_node {
    int  size;
    int  seq;
    int  fd;
    char name[128-12];
} play_node_t;

static play_node_t *play_list = NULL;
static int play_list_count = 0;

static DEFINE_MUTEX(mpg_fs_mutex);

#define MUTEX_LOCK()   mutex_lock(&mpg_fs_mutex)
#define MUTEX_UNLOCK() mutex_unlock(&mpg_fs_mutex)

#define       DEVICE_NAME "mpg_fs"
static int    MPG_DEV_Major = 0;
static struct class *mpg_dev_class;

static int mpg_fs_open(struct inode *inode, struct file *file) {
    printk("mpg_fs_open()\n");

    entry->size = g_size;

    return 0;
}

static int mpg_fs_release(struct inode *inode, struct file *file) {
    printk("mpg_fs_release()\n");

    int i = 0;
    set_fs(KERNEL_DS);

    for (i = 0; i < play_list_count; i++) {
        filp_close(play_list[i].fd, NULL);
    }

    kfree(play_list);
    play_list = NULL;
    play_list_count = 0;

    return 0;
}

static int mpg_fs_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) {
    int i = 0;
    int len = 0;
    int left = 0;
    int size = 0;

    loff_t pos = *f_pos;
    loff_t sum = 0;

    set_fs(KERNEL_DS);

    for (i = 0; i < play_list_count; i++) {
        size = play_list[i].size;
        if (pos + count <= sum + size) {
            fd = play_list[i].fd;
            fd->f_op->llseek(fd, pos-sum, 0);
            len = fd->f_op->read(fd, buffer, count, &fd->f_pos);
            break;
        }
        if (pos < sum + size) {
            left = sum + size - pos;
            play_list[i].fd;
            fd->f_op->llseek(fd, pos-sum, 0);
            fd->f_op->read(fd, buffer, left, &fd->f_pos);
            fd = play_list[i+1].fd;
            fd->f_op->llseek(fd, 0, 0);
            fd->f_op->read(fd, buffer+left, count-left, &fd->f_pos);
            len = count;
            break;
        }
        sum += size;
    }

    if (len > 0) {
        *f_pos += len;
        copy_to_user(buf, buffer, len);
    } else if (len == 0) {
        //TODO::
    }

    return len;
}

static loff_t mpg_fs_llseek(struct file *file, loff_t f_pos, int orig) {
    if (orig == 0) {
        file->f_pos = f_pos;
    } else if (orig == 1) {
        file->f_pos += f_pos;
    } else if (orig == 2) {
        file->f_pos = g_size;
    }

    return file->f_pos;
}

static struct file_operations mpg_fs_fops = {
    .owner           = THIS_MODULE,
    .open            = mpg_fs_open,
    .release         = mpg_fs_release,
    .read            = mpg_fs_read,
    .llseek          = mpg_fs_llseek,
};

static int mpg_dev_open(struct inode *inode, struct file *file) {
    printk("mpg_dev_open()\n");
    return 0;
}

static int mpg_dev_release(struct inode *inode, struct file *file) {
    printk("mpg_dev_release()\n");
    return 0;
}

static long mpg_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    int ret = 0;
    printk("mpg_dev_ioctl(), cmd: %d\n", cmd);

    MUTEX_LOCK();
    switch (cmd) {
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    case 5: // set file size
        copy_from_user(&g_size, arg, sizeof(loff_t));
        entry->size = g_size;
        printk("set file size: %lld\n", g_size);
        break;

    default:

        if (cmd > 10000) {
            int i = 0;
            play_list_count = cmd - 10000;
            play_list = kzalloc(play_list_count * sizeof(play_node_t), GFP_KERNEL);
            copy_from_user(play_list, arg, play_list_count * sizeof(play_node_t));
            for (i = 0; i < play_list_count; i++) {
                play_list[i].fd = filp_open(play_list[i].name, O_RDONLY, 0666);
                printk("file name:%s size:%d seq:%d\n", play_list[i].name, play_list[i].size, play_list[i].seq);
            }
        }
        break;
    }
    MUTEX_UNLOCK();

    return ret;
}

static struct file_operations mpg_dev_fops = {
    .owner            = THIS_MODULE,
    .open             = mpg_dev_open,
    .release          = mpg_dev_release,
    .unlocked_ioctl   = mpg_dev_ioctl
};

static int __init mpg_init(void) {
    printk("\nMPG FS DRIVER MODULE INIT\n");
    entry = proc_create("mpg_fs", S_IRUGO, NULL, &mpg_fs_fops);

    MPG_DEV_Major = register_chrdev(0, DEVICE_NAME, &mpg_dev_fops);
    mpg_dev_class = class_create(THIS_MODULE, DEVICE_NAME);
    device_create(mpg_dev_class, NULL, MKDEV(MPG_DEV_Major, 0), NULL, DEVICE_NAME);

    return 0;
}

static void __exit mpg_exit(void) {
    printk("\nMPG FS DRIVER MODULE EXIT\n");

    remove_proc_entry("mpg_fs", NULL);

    unregister_chrdev(MPG_DEV_Major, DEVICE_NAME);
    device_destroy(mpg_dev_class, MKDEV(MPG_DEV_Major, 0));
    class_destroy(mpg_dev_class);
}

module_init(mpg_init);
module_exit(mpg_exit);

MODULE_AUTHOR("[email protected]");
MODULE_DESCRIPTION("pmg-ps-fs");
MODULE_LICENSE("GPL");


你可能感兴趣的:(Android平台多个mpg文件连续播放)