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

来源:互联网 发布:进驻淘宝协议 编辑:程序博客网 时间:2024/06/06 03:47

虽然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<PlayNode> list = new ArrayList<PlayNode>();    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<PlayNode> 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 <stdio.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#include <stdint.h>#include <string.h>#include <jni.h>#include <android/log.h>#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 <linux/module.h>#include <linux/module.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/file.h>#include <linux/init.h>#include <asm/uaccess.h>#include <linux/proc_fs.h>#include <linux/device.h>#include <linux/mutex.h>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("longchow@126.com");MODULE_DESCRIPTION("pmg-ps-fs");MODULE_LICENSE("GPL");


1 0
原创粉丝点击