Android - MediaStore.Video.query() is returning null

Danilo Caetano picture Danilo Caetano · Nov 28, 2012 · Viewed 10.2k times · Source

I'm trying to retrieve the metadata from a video file (title, language, artist) using the method MediaStore.Video.query(). However, the method is always returning null. The code is bellow:

String[] columns = {
    MediaStore.Video.VideoColumns._ID,
    MediaStore.Video.VideoColumns.TITLE,
    MediaStore.Video.VideoColumns.ARTIST
};

Cursor cursor = MediaStore.Video.query(getApplicationContext().getContentResolver(), videoUri,columns);

if (cursor != null) {
  cursor.moveToNext();
}

String title = cursor.getString(cursor.getColumnIndex(MediaStore.Video.VideoColumns.TITLE));

Any suggestion about how to return video metadata using android?

==Update

As I searched in many places, I tried one solution using CursorLoader. However, the method loadInBackground() from CursorLoader is also returning null. The code is showed bellow:

String[] columns = {
                MediaStore.Video.VideoColumns.TITLE
        };

        Uri videoUri = Uri.parse("content://mnt/sdcard/Movies/landscapes.mp4");

        CursorLoader loader = new CursorLoader(getBaseContext(), videoUri, columns, null, null, null);

        Cursor cursor = loader.loadInBackground();

        cursor.moveToFirst();

        String title = cursor.getString(cursor.getColumnIndex(MediaStore.Video.VideoColumns.TITLE));

Answer

zapl picture zapl · Nov 29, 2012

Uri.parse("content://mnt/sdcard/Movies/landscapes.mp4") is not an Uri for MediaStore. It would try to find a ContentProvider for authority mnt which does not exist.

MediaStore can handle only content://media/... Uris which you should get exclusively via MediaStore, not by using Uri.parse().

In your case use the following for example

Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
String[] columns = {
        MediaStore.Video.VideoColumns._ID,
        MediaStore.Video.VideoColumns.TITLE,
        MediaStore.Video.VideoColumns.ARTIST
    };

String selection = MediaStore.Video.VideoColumns.DATA + "=?";
String selectionArgs[] = { "/mnt/sdcard/Movies/landscapes.mp4" };

Cursor cursor = context.getContentResolver().query(uri, columns, selection, selectionArgs, null);

The MediaStore.Video.VideoColumns.DATA field holds the path to the videos and you search for a certain video this way. At least for now, future versions of Android may change that.


Your second example is using CursorLoader the wrong way. If you call loader.loadInBackground() yourself, you load the data in foreground. See e.g. http://mobile.tutsplus.com/tutorials/android/android-sdk_loading-data_cursorloader/

The next thing you do is

    Cursor cursor = getCursor();
    cursor.moveToFirst();
    String title = cursor.getString(/* some index */);

This will lead to a CursorIndexOutOfBoundsException if your cursor has 0 rows and cursor.moveToFirst() failed because there is no first row. The cursor stays before the first row (at -1) and that index does not exist. That would mean in your case that the file was not found in the database.

To prevent that use the return value of moveToFirst - it will only be true if there is a first row.

    Cursor cursor = getCursor(); // from somewhere
    if (cursor.moveToFirst()) {
        String title = cursor.getString(/* some index */);
    }

A more complete example including checks for null and closing the cursor in all cases

    Cursor cursor = getCursor(); // from somewhere
    String title = "not found";
    if (cursor != null) {
        if (cursor.moveToFirst()) {
            title = cursor.getString(/* some index */);
        }
        cursor.close();
    }

I guess the file you try to find is either not indexed in the database (rebooting forces the indexer to run again) or the path is wrong.

Or the path you use is actually a symlink in which case MediaStore might use a different path.

Use this to get rid of symlinks

    String path  = "/mnt/sdcard/Movies/landscapes.mp4";
    try {
        path = new File(path).getCanonicalPath();
    } catch (IOException e) {
        e.printStackTrace();
    }

Yes, I tested now and it is throwing IndexOutOfBoundsException. When I'm using cursor.getColumnCount() it returns 1

cursor.getColumnCount() is the column count, not the row count. It should always be the same as the number of columns you requested in columns. You need to check cursor.getCount() if you want to check the row count.


Try dumping all the videos known to MediaStore into logcat in case it does not show as expected.

public static void dumpVideos(Context context) {
    Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
    String[] projection = { MediaStore.Video.VideoColumns.DATA };
    Cursor c = context.getContentResolver().query(uri, projection, null, null, null);
    int vidsCount = 0;
    if (c != null) {
        vidsCount = c.getCount();
        while (c.moveToNext()) {
            Log.d("VIDEO", c.getString(0));
        }
        c.close();
    }
    Log.d("VIDEO", "Total count of videos: " + vidsCount);
}